Русский
Русский
English
Статистика
Реклама

Glpi

Инвентаризация RAID-массивов LSI в GLPI

14.08.2020 16:23:02 | Автор: admin

В своей работе я часто испытываю обсессии по поводу нехватки информации об инфраструктуре, а при увеличении парка обслуживаемых серверов это превращается в настоящую пытку. Даже когда я админил в маленьких организациях, мне всегда хотелось знать, что где стоит, куда воткнуто, кто из людей за какую железку или сервис отвечает, и самое главное фиксировать изменения в этом всем. Когда приходишь в новое место и сталкиваешься с каким-нибудь инцидентом, уйма времени уходит именно на поиск этой информации. Далее я расскажу, с чем мне пришлось столкнуться в RuVDS, и как решил проблему, обозначенную в заголовке.

Предыстория


Будучи энтерпрайзным админом, я имел мало опыта работы в дата-центре, но краем глаза увидел RackTables. В нём наглядно была представлена стойка со всеми серверами, ИБП, коммутаторами и всеми соединениями между ними. В RuVDS не было подобной системы, а только Excel/бумажные файлы с информацией о серверах, их некоторых компонентах, номерами стоек и и.п. При таком подходе очень тяжело отслеживать изменения и за мелкими компонентами. А ведь самый главный и часто сменяемый расходный материал у серверов это диски. Очень важно поддерживать актуальную информацию о состоянии дисков и их стратегический запас. Если диск вылетит из RAID-массива и ему не будет быстрой замены, это в итоге может привести к фатальным последствиям. Поэтому нам очень нужна система, отслеживающая местонахождение дисков и их состояние, чтобы понять, чего нам может не хватить и какие именно модели нужно закупить.

На помощь пришел GLPI, продукт с открытым исходным кодом, созданный для улучшения работы ИТ-отделов и доведения их до идеалов ITIL. В нем, помимо инвентаризации оборудования и менеджмента стоек, есть база знаний, сервис-деск, управление документами и многое другое. У GLPI есть множество плагинов, в том числе FusionInventory и OCS Inventory, которые позволяют автоматически собирать информацию о компьютерах и других устройствах посредством установки агентов и по SNMP. Подробнее про установку GLPI и плагинов вы можете почитать в других статьях, лучше всего официальной документации. Можно установить его у нас на хостинге на уже готовый шаблон .

Однако, после деплоя агента мы откроем компоненты компьютера в GLPI и увидим это:



Проблема в том, что ни один из плагинов не может увидеть информацию о физических дисках в RAID-массивах LSI. Увидев, как этот вопрос решен для мониторинга в Zabbix с помощью PowerShell-скрипта я решил написать аналогичный для передачи инфы в GLPI.
Данные о дисках в массиве можно получить с помощью утилит от производителя контроллера, в случае с LSI это StorCLI. Из нее можно получить данные в формате JSON, распарсить и прокинуть их в API GLPI. Диски будем привязывать к компьютерам, которые уже создал FusionInventory. При повторном выполнении скрипт будет обновлять данные по дискам и добавлять новые. Сам скрипт Send-RAIDtoGLPI.ps1 лежит . Далее я расскажу, как им воспользоваться.

Что потребуется


  1. версии 9.5.1 (на этой тестировалось)
  2. Плагин и агент под Windows
  3. Windows 2012 R2 (и выше) в качестве хостовой системы, либо management-VM с проброшенным в нее контроллером, PowerShell версии 4 или выше
  4. Установленный драйвер MegaRAID
  5. Модуль для PowerShell
  6. Учетка в GLPI с профилем Admin для авторизации через API, сгенерированные UserToken и AppToken

Важный момент. В GLPI зачем-то есть 2 разных сущности для модели диска, но нет свойства тип носителя. Поэтому для записи свойства HDD и SSD я решил использовать выпадающий список Модели жестких дисков (front/devicemodel.php?itemtype=DeviceHardDriveModel). Для скрипта необходимо наличие в базе GLPI этих значений, иначе он не сможет записать данные о модели диска. Поэтому нужно занести в этот пустой список сначала HDD, затем SSD, чтобы ID этих элементов в базе получились 1 и 2. Если будут другие, то замените в этой строчке скрипта Send-RAIDtoGLPI.ps1 после HDD и SSD вместо 1 и 2 соответствующие им ID:

deviceharddrivemodels_id = switch ($MediaType) { "HDD" { "1" }; "SSD" { "2" }; default { "" } }

Если не хочется с этим париться или у вас используется этот выпадающий список иначе, то можете просто удалить эту строчку из скрипта.
Также нужно добавить статусы для дисков в Статусы элементов (/front/state.php). Я добавил статусы MediaError (была хоть одна ошибка обращения к диску) и OK, строчка в скрипте, где передаются их ID, 2 для ОК и 1 для MediaError:
states_id = switch ($MediaError) { 0 { "2" }; { $_ -gt 0 } { "1" } }

Эти статусы нужны для удобства, если вам эти свойства не нужны, можете также удалить эту строчку целиком.

В самом скрипте не забудьте указать переменные на ваши. $GlpiCreds должен содержать URL до сервера API GLPI, UserToken и AppToken.

Что в скрипте


Из-за громоздкого JSON-парсинга и простыни if-ов скрипт плохо читается, поэтому опишу его логику здесь.

При первом запуске на хосте скрипт проходится по всем контроллерам и ищет диски в базе GLPI по серийным номерам, если не находит, ищет модель.Если не находит и модель, то добавляет модель нового диска в GLPI и заносит этот диск в базу.

Каждый новый проход скрипт будет пытаться обнаружить новые диски, но удалять отсутствующие он не умеет, поэтому, придется делать это вручную.

Пример развертывания


В репозитории скрипта есть скрипт Deploy-Send-RAIDtoGLPI.ps1, который скачает ZIP-архив с необходимыми файлами с нашего сервера GLPI и развернет их на каждом хосте.

После копирования файлов скрипт установит агент FusionInventory с запуском в виде ежедневной задачи и создаст такую же задачу для нашего скрипта. После успешного внедрения мы наконец сможем увидеть диски в разделе Компоненты компьютера в GLPI.

Результат


Теперь, зайдя в GLPI в меню Настройки -> Компоненты -> Жесткие диски, мы можем щелкать по моделям дисков и смотреть их количество, чтобы понять, что нам нужно закупить.



Подробнее..

Пишем плагин GLPI для переоткрытия заявок

17.08.2020 20:19:27 | Автор: admin
Всем привет.

Сегодня речь пойдёт о том, как я написал плагин GLPI для работы с новыми заявками, созданными на основе уже закрытых заявок, а точнее переоткрытии закрытых заявок, если по ним поступают дополнительные запросы от пользователя.
Предыстория

Вот уже без малого месяц я копаюсь в исходном коде helpdesk-системы GLPI. И при изучении данного опенсорс-решения обратил внимание на ничтожно малое количество информации для разработчиков. Подавляющее большинство материала связано с настройкой GLPI внутри интерфейса. Немного меньше, но ещё можно встретить информацию по настройке сервера. И всего одна статья (и та на английском) повествует о разработке плагинов для этой чудо-системы.

Из официальных мануалов пара ресурсов на readthedocs, плюс мануал, сгенерированный из phpdoc'а.

Из сообщества чат в телеграме и забугорный форум.


Входные параметры следующие:

Есть helpdesk-система GLPI. Заявки от пользователей поступают на специально выделенный почтовый ящик, подключённый к системе. Ответ службы поддержки также прилетает на почту. Вообще все уведомления о ходе движения заявки от первого обращения до решения и закрытия поступают на почту.

Однако если пользователь посчитает, что его проблема должным образом не решена и решит ответить на письмо о закрытии заявки, то вместо переоткрытия создаётся новое обращение, а старый тикет просто к нему привязывается. Собственно, эту проблему и нужно было решить.

Итак, поехали.

Первым делом нужно установить генератор плагинов он существенно упрощает жизнь путём создания каркаса нашего будущего плагина. Сделать это можно с помощью следующего алгоритма:
  • Идём на гитхаб и клонируем репозиторий в папку с плагинами (/path/to/glpi/plugins)
  • В консоли запускаем команду:
    $ ./plugin.sh YourPluginName 1.0
    

    где YourPluginName название вашего плагина, 1.0 версия плагина.

И всё, каркас готов.

Зайдя в директорию созданного плагина, вы увидите кучу файлов, из которых реально нужны только setup.php и hook.php. Если планируете использовать дополнительные библиотеки, можете оставить composer.json. Мне оные не понадобились, поэтому я оставил только 2 необходимых файла.

В файле setup.php обязательными являются две функции plugin_init_yourpluginname и plugin_version_yourpluginname. Первая инициализирует плагин, вторая возвращает информацию о нём имя, автор, версия и т.д.

Со второй всё более-менее понятно. Поэтому ненадолго остановимся на первой функции. Она работает с глобальной переменной $PLUGIN_HOOKS. Наверняка все знают, что такое хуки, но если кто-то не в курсе, то это т.н. системные крючки, которые дёргает плагин. Более подробно про хуки можно почитать в официальном мануале.

Каждый плагин должен в обязательном порядке зарегистрировать хук csrf_compliant, иначе вы его просто не сможете активировать, будет вот такая ошибка:


Не знаю, что это за параметр, пока не разобрался с системой до конца. Если среди читателей найдутся знатоки GLPI, напишите в комментариях.


Помимо обязательного хука можно зарегистрировать многие другие, например, задать ссылку на страницу настроек:
$PLUGIN_HOOKS['config_page']['yourpluginname'] = 'config.php';

Также в этой функции регистрируются классы плагина:
Plugin::registerClass(PluginYourpluginnameConfig::class);

И, кстати, да, в GLPI есть стандарты по именованию классов и файлов, их содержащих. Название класса складывается из трёх частей: Plugin + Названиевашегоплагина + Назначениекласса. В названии плагина, состоящего из нескольких слов, с заглавной буквы пишется только первое слово.

Файлы именуются следующим образом: если класс носит название PluginYourpluginnameConfig, то файл должен называться config.class.php. И все классы плагина нужно складывать в папку inc (не в ту, которая в корне сайта, а в ту, которая в корне плагина, и если её в папке плагина нет а её там не будет то нужно создать, если вы планируете писать какие-то классы).

Обо всём этом можно узнать из официальной документации.

Вернёмся к нашим хукам. Мне нужно отслеживать создание новой заявки с возможностью отмены, поэтому я зарегистрирую обработчик хука pre_item_add. В обработчик можно передавать параметры, делается это с помощью ассоциативного массива, где ключом является объект нужной сущности (в моём случае Ticket), а значением название функции-обработчика.

В итоге в моём случае две обязательные функции файла setup.php выглядят следующим образом:
/** * Init hooks of the plugin. * REQUIRED * * @return void */function plugin_init_advtickets() {   global $PLUGIN_HOOKS;   Plugin::registerClass(PluginAdvticketsEvent::class);   $PLUGIN_HOOKS['csrf_compliant']['advtickets'] = true;   $PLUGIN_HOOKS['pre_item_add']['advtickets'] = [       Ticket::class => 'plugin_advtickets_pre_item_add'   ];}/** * Get the name and the version of the plugin * REQUIRED * * @return array */function plugin_version_advtickets() {   return [      'name'           => 'Adv Tickets',      'version'        => PLUGIN_ADVTICKETS_VERSION,      'author'         => 'Roman Gonyukov',      'license'        => '',      'homepage'       => 'https://github.com/stayfuneral/advtickets',      'requirements'   => [         'glpi' => [            'min' => '9.2',         ]      ]   ];}

В документации написано, что можно в качестве обработчика регистрировать статичные методы классов, в таком случае они должны иметь следующий вид (кстати, совсем необязательно передавать какие-то параметры в обработчик):
/* пример из официальной документации *///call a function$PLUGIN_HOOKS['hook_name']['plugin_name'] = 'function_name';//call a static method from an object$PLUGIN_HOOKS['other_hook']['plugin_name'] = ['ObjectName', 'methodName'];

Сами же функции-обработчики хранятся в файле hook.php. Там же хранятся функции установки и удаления плагина. К именам функций те же требования plugin_yourpluginname_function_name.

Кстати, я пробовал для обработки хука регистрировать статический метод и передавать в параметры объект Ticket, но почему-то у меня ничего не сработало. А вот с обычной функцией всё получилось. Поэтому, чтобы не засорять hook.php лишним кодом, я зарегистрировал класс, содержащий метод-обработчик хука, а в нужной функции этот метод просто вызывал:
// hook.php/** * @param Ticket $ticket * * @return bool */function plugin_advtickets_pre_item_add(Ticket $ticket){    return PluginAdvticketsEvent::pre_item_add_ticket($ticket);}


Если вам нужно устанавливать таблицы в базу данных, то в официальной документации есть отдельный пункт на эту тему. Мне же таблицы не понадобились, поэтому я данный момент пропускаю.

Ну что же, с установкой/удалением плагина, регистрацией и вызовом обработчиков хуков вроде разобрались, теперь самое время разобраться, как мы новую заявку будем обрабатывать.

Для этого, как уже говорилось выше, я зарегистрировал класс PluginAdvticketsEvent (файл inc/event.php), содержащий один единственный метод pre_item_add_ticket.

class PluginAdvticketsEvent extends CommonDBTM{    static function pre_item_add_ticket(Ticket $ticket)    {        global $DB;// Т.к. заявка ещё не создана, то данные передаются в свойстве input        $fields = $ticket->input;// Если тикет создан на основе существующего, то в данном параметре передаётся его (существующего тикета) идентификатор        if($fields['_link']['tickets_id_2']) {            $relatedTicketId = $fields['_link']['tickets_id_2'];            $relatedTicketParamsForUpdate = [                'itemtype' => \Ticket::class, // тип сущности, в моём случае заявка                'items_id' => $relatedTicketId, // id заявки                'users_id' => $fields['_users_id_requester'], // id написавшего пользователя                'users_id_editor' => 0,                'content' => $fields['content'], содержимое заявки                'date' => date('c'),                'date_mod' => date('c'),                'date_creation' => date('c'),                'is_private' => 0,                'requesttypes_id' => $fields['requesttypes_id'], // источник (helpdesk, email и т.д.)                'timeline_position' => 4,                'sourceitems_id' => 0,                'sourceof_items_id' => 0            ];// В исходном коде пока не нашёл методы для добавления заявок (как в том же битриксе, например), поэтому приходится практически напрямую вставлять данные в БД. Кстати, все комментарии к заявкам хранятся в таблице glpi_itilfollowups            $DB->insert('glpi_itilfollowups', $relatedTicketParamsForUpdate);// Заодно поменяем статус существующей заявки, присвоив ему значение "Новый". Для этого обновим соответствующую запись в таблице glpi_tickets            $DB->update('glpi_tickets', [                'status' => 1            ], [                'id' => $relatedTicketId            ]);// Т.к. новая заявка связана с существующей, присвоим параметру input значение false. В таком случае обращение создано не будет.            $ticket->input = false;            return $ticket->input;        }    }}


На этом всё. Спасибо за внимание. Исходный код, как всегда на гитхабе.

Полезные ссылки:

Официальный сайт GLPI
Официальные форумы
Телеграм-чат
Example-плагин
Документация для разработчиков
APIDoc
Документация по созданию плагинов
Статья по созданию плагинов GLPI
Подробнее..
Категории: Php , Help desk software , Glpi

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru