Добрый день.
Если кто-то реализовывал данную схему - поделитесь решением плиз.
У нас в качестве мониторинга используется Zabbix. Сейчас интеграция настроена следущим образом: в случае критической проблемы Zabbix шлет письмо на почту тех поддержки и iTop на основе письма создает инцидент. С помощью своей доработке на php можно задать в какую услугу и подуслугу направить этот инцидент. Единственное что не очень нравится - Zabbix шлет письмо и о восстановлении работы сервиса и iTop создает еще один инцидент… В идеале чтобы он или не регистрировал его, или дописывал в существующий (закрывал его).
Читал что есть модуль для интеграции с Nagios и некоторые его адапритовали для Zabbix’а. Интересует его принцип работы. Или может кто-то реализовывал свои схемы?
Первое, что приходит в голову - анализ темы и/или содержимого писем от Zabbix. Сделать это там же, где определяется услуга и подкатегория. Пришло PROBLEM, открываем тикет, пришло OK - закрываем.
А как определить какой именно тикет закрыть? На сервере повысилась нагрузка - пришло три проблемы: проц, память и диск… Надо как-то передавать номер тикета…
Я не спец по заббиксу, но насколько знаю, триггеры в нем можно как-то взаимоувязывать. Если на одном хосте сработало три триггера, отправлять одно письмо в iTop c id хоста, id события и PROBLEM. В случае восстановления отправлять те же id и OK. Идентификатор события уникален, iTop по нему закрывает тикет. А используя id хоста можно подвязать к тикету нужные КЕ.
А вообще, это конечно извращение)) использовать email при наличии в обеих системах задокументированных API.
Вопрос интеграции iTop с Zabbix для нас тоже актуален. Но наши спецы - ребята довольно ленивые, и раскрутить их на работу у меня не получится. Поэтому, если есть время и знания по заббиксу, можно объединить усилия и сделать полноценный модуль.
спецом по заббиксу назвать себя язык не поворачивается установил - сейчас занимаюсь изучением, подключением серверов к нему и настройкой мониторинга критичных сервисов… пока реализовал через отправку почты - по возможности буду изучать вопрос дальше
Здравствуйте коллеги Есть ли какие наработки по интеграции? У самого сейчас стоит такая задача)
К сожалению всё загнулось на этапе определения требований).
Пиши свои мысли о работе модуля, будем думать.
Ну пока сам реализовал всё что нужно bash скрипт, который отправляет все новые Events из Zabbix’а в itop.
Отправляет чем? Что с этим делает iTop?
В данном случае используется Action UserScript. Добавляем для какого-либо юзера данный Media, и создаем Action с отправкой ему сообщений.
В скрипте парсим входные данные от Zabbix, и разбираем их по отдельным переменным. Затем обычным curl POST запросом отправляем данные в itop. В itop создан отдельный класс, и поля которые мы заполняем полученными значениями.
Более подробно что происходит на стороне itop’а, расскажет мой коллега.
Для событий создал отдельный класс и описал метод DBInsertNoReload, в котором по событию создается и привязывается тикет:
<class id="zevent" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<category>bizmodel,searchable,incidentmgmt,requestmgmt,changemgmt,m2prequest</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>zevent</db_table>
<db_key_field>id</db_key_field>
<db_final_class_field/>
<naming>
<format>%1$s</format>
<attributes>
<attribute id="ref"/>
</attributes>
</naming>
<display_template/>
<icon>images/event.png</icon>
<reconciliation>
<attributes>
<attribute id="ref"/>
</attributes>
</reconciliation>
</properties>
<fields>
<field id="ref" xsi:type="AttributeString">
<sql>ref</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="ticket_id" xsi:type="AttributeExternalKey">
<sql>ticket_id</sql>
<target_class>Ticket</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
<field id="ticket_ref" xsi:type="AttributeExternalField">
<extkey_attcode>ticket_id</extkey_attcode>
<target_attcode>ref</target_attcode>
</field>
...
...
<methods>
<method id="DBInsertNoReload">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function DBInsertNoReload()
{
$oMutex = new iTopMutex('zevent_insert');
$oMutex->Lock();
$iNextId = MetaModel::GetNextKey(get_class($this));
$sRef = $this->MakeZeventRef($iNextId);
$this->Set('ref', $sRef);
$oURtitle = $this->Get('hostgroup').' '.$this->Get('host');
$oURdescription = $this->Get('parameter').' ' ;
$oNewUR = new UserRequest();
$oNewUR->Set('org_id', 7);
$oNewUR->Set('caller_id', 88);
$oNewUR->Set('title', $oURtitle);
$oNewUR->Set('description', $oURdescription);
$oNewUR->Set('request_type', 'incident');
$oNewUR->Set('origin', 'monitoring');
$oNewUR->DBInsert();
$this->Set('ticket_id', $oNewUR->GetKey());
$iKey = parent::DBInsertNoReload();
$oMutex->Unlock();
return $iKey;
}
]]></code>
</method>
<method id="MakeZeventRef">
<static>false</static>
<access>protected</access>
<type>Overload-DBObject</type>
<code><![CDATA[
protected function MakeZeventRef($iNextId)
{
$sFormat = 'E-%06d';
return sprintf($sFormat, $iNextId);
}
]]></code>
</method>
</methods>
...
...
Со стороны itop это выглядит так:
Zabbix через API создает новый объект zevent. В процессе создания события создается инцидент,к которому привязывается новое событие.
Супер)
Взаимодействия с Zabbix со стороны iTop нет? Например, для сохранения в событии в Zabbix ссылки на зарегистрированный тикет.
Что происходит, когда приходит событие “OK”? Тикет как-то обновляется или выполняется?
КЕ, затронутые событием, к инциденту не привязываются? Вообще, узлы в Zabbix как-то пересекаются с КЕ в iTop?
Не стояло такой задачи. В принципе это легко реализуется используя API Zabbix, ссылку например можно записывать в Acknowledge текущего Event.
Сейчас события с одинаковых хостов группируются в один тикет если время создания последнего события менее 48 часов назад. Отдельного анализа “ОК” нет.
Планируем с помощью zabbix заполнять узлы, и привязывать к ним. Поля текстовые, привязка будет по названиям КЕ.
Добрый день, а могли бы Вы поделиться скриптом и extension itop? Спасибо
Сделал свою реализацию для Zabbix. Скрипт для zabbix делаем способ уведомления размещаем скрипт /usr/lib/zabbix/alertscripts и делаем оповещения в виде json переменой.
{"tr_name":"{HOST.HOST1}/{TRIGGER.NAME}","tr_statsus":"{TRIGGER.STATUS}","date":"{DATE}","d":"{ACTION.ID}","e":"{ACTION.NAME}","edate":"{EVENT.DATE}","etime":"{EVENT.TIME}","tsever":"{TRIGGER.SEVERITY}","timname":"{ITEM.NAME1}","hostname":"{HOST.NAME1}","itemname":"{ITEM.KEY1}"}
И сам скрипт
<?php
/**
* Created by PhpStorm.
* User: korvin
* Date: 20.03.17
* Time: 12:00
*/
define('sHost', $argv[1]);
define('sALERTMESSAGE', (string)$argv[2]);
define('sServiceSubcategory', $argv[3]);
define('sService', $argv[4]);
define('ITOP_USERNAME', $argv[5]);
define('ITOP_PASSWORD', $argv[6]);
define('ITOP_SERVER_URL', $argv[7]);
define ('DEBUG', $argv[8]); // Set to true to see the actual SOAP enveloppe output
//Запись дебага
if(DEBUG){
$file = $argv[9];
}
//
function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null){
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
$sData = http_build_query($aData);
$aParams = array('http' => array(
'method' => 'POST',
'content' => $sData,
'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
));
if ($sOptionnalHeaders !== null) {
$aParams['http']['header'] .= $sOptionnalHeaders;
}
$ctx = stream_context_create($aParams);
$fp = @fopen($sUrl, 'rb', false, $ctx);
if (!$fp) {
global $php_errormsg;
if (isset($php_errormsg)) {
throw new Exception("Problem with $sUrl, $php_errormsg");
} else {
throw new Exception("Problem with $sUrl");
}
}
$response = @stream_get_contents($fp);
if ($response === false) {
throw new Exception("Problem reading data from $sUrl, $php_errormsg");
}
return $response;
}
// If the library curl is installed.... use this function
//
function DoPostRequest_curl($sUrl, $aData){
$curl = curl_init($sUrl);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $aData);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
////////////////////////////////////////////////////////////////////////////////
//
// Main program
//
////////////////////////////////////////////////////////////////////////////////
$aOperations = array(
array(
'operation' => 'core/get', // operation code
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE caller_name = "Zabbix"',
'output_fields' => '*', // list of fields to show in the results (* or a,b,c)
),
);
if (false) {
exit("Please edit the sample script and configure the server URL");
} else {
$sUrl = ITOP_SERVER_URL;
}
$aData = array();
$aData['auth_user'] = ITOP_USERNAME;
$aData['auth_pwd'] = ITOP_PASSWORD;
//Проверка существования переменных
if(isset(sHost) and isset(sALERTMESSAGE) and isset(sServiceSubcategory) and isset(Service)){
$getData = json_decode(mb_strtolower(sALERTMESSAGE,'UTF-8'), true);
if(DEBUG) {
file_put_contents($file, sALERTMESSAGE."\n", FILE_APPEND | LOCK_EX);
}
//Проверка масив или нет
if(is_array($getData)){
//Запрос просотра существования запроса
$aOperations = array(
array(
'operation' => 'core/get', // operation code
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE operational_status != "closed" AND caller_name = "Zabbix" AND title = "'.$getData['tr_name'].'"',
'output_fields' => '*',
),
);
foreach ($aOperations as $iOp => $aOperation) {
$aData['json_data'] = json_encode($aOperation);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
//Проверка статусса заявки
if($aResults->objects == NULL and $getData['tr_statsus'] != "ok"){
//Создаем носую запись
$aOperationsIns = array(
array(
'operation' => 'core/create',
'comment' => 'Adding a new trigger',
'class' => 'UserRequest',
'output_fields' => 'id, friendlyname',
'fields' => array(
'org_id' => "SELECT Organization WHERE name = 'ADUsers' ",
'caller_id' => "SELECT Person WHERE name = 'Zabbix' ",
'title' => $getData['tr_name'],
'description' => sHost,
'impact' => 2,
'origin' => 'mail',
'zabbix_id' => $getData['tr_statsus']."/".$getData['tsever'],
'servicesubcategory_id' => 'SELECT ServiceSubcategory WHERE name = "'.sServiceSubcategory.'"',
'service_id' => 'SELECT Service WHERE name = "'.sService.'"',
'public_log' => array(
'add_item' => array(
'user_id' => "SELECT Person WHERE name = 'Zabbix'",
'user_login' => "Zabbix Monitoring",
'message' => sALERTMESSAGE,
),
),
),
),
);
foreach ($aOperationsIns as $aOperationsIn) {
unset ($aData['json_data']);
$aData['json_data'] = json_encode($aOperationsIn);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
}
if(DEBUG) {
file_put_contents($file, "Create tikets \n", FILE_APPEND | LOCK_EX);
}
echo "Create tikets \n";
}elseif($aResults->objects != NULL and $getData['tr_statsus'] == "ok"){
$aOperationsIns = array(
array(
'operation' => 'core/update',
'comment' => 'Synchronization from blah...',
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE status != "closed" AND caller_name = "Zabbix" AND title = "'.$getData['tr_name'].'"',
'output_fields' => 'id, friendlyname, title','user_login',
'fields' => array(
'zabbix_id' => $getData['tr_statsus']."/".$getData['tsever'],
'public_log' => array(
'add_item' => array(
'user_id' => "SELECT Person WHERE name = 'Zabbix'",
'user_login' => "Zabbix Monitoring",
'message' => sALERTMESSAGE,
),
),
),
),
);
foreach ($aOperationsIns as $aOperationsIn) {
unset ($aData['json_data']);
$aData['json_data'] = json_encode($aOperationsIn);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
}
if(DEBUG) {
file_put_contents($file, "Ticket can be closed but there is no solution \n", FILE_APPEND | LOCK_EX);
}
echo "Ticket can be closed but there is no solution \n";
}else{
if($aResults->objects != NULL){
$aOperationsIns = array(
array(
'operation' => 'core/update',
'comment' => 'Synchronization from blah...',
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE status != "closed" AND caller_name = "Zabbix" AND title = "'.$getData['tr_name'].'"',
'output_fields' => 'id, friendlyname, title','user_login',
'fields' => array(
'zabbix_id' => $getData['tr_statsus']."/".$getData['tsever'],
'public_log' => array(
'add_item' => array(
'user_id' => "SELECT Person WHERE name = 'Zabbix'",
'user_login' => "Zabbix Monitoring",
'message' => sALERTMESSAGE,
),
),
),
),
);
foreach ($aOperationsIns as $aOperationsIn) {
unset ($aData['json_data']);
$aData['json_data'] = json_encode($aOperationsIn);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
}
if(DEBUG) {
file_put_contents($file, "Update tikets \n", FILE_APPEND | LOCK_EX);
}
echo "Update tikets \n";
}
}
}
}else{
if(DEBUG) {
file_put_contents($file, "Error parametrs array \n", FILE_APPEND | LOCK_EX);
}
exit("Error parametrs array \n");
}
}else{
if(DEBUG) {
file_put_contents($file, "Error parametrs all \n", FILE_APPEND | LOCK_EX);
}
exit("Error parametrs all \n");
}
?>
Исправил ошибки в не ту версию выложил
<?php
/**
* Created by PhpStorm.
* User: korvin
* Date: 20.03.17
* Time: 12:00
*/
$sHost = isset($argv[1]) ? $argv[1] : false;
$sALERTMESSAGE = (string)isset($argv[2]) ? $argv[2] : false;
$sServiceSubcategory = isset($argv[3]) ? $argv[3] : false;
$sService = isset($argv[4]) ? $argv[4] : false;
$ITOP_USERNAME = isset($argv[5]) ? $argv[5] : false;
$ITOP_PASSWORD = isset($argv[6]) ? $argv[6] : false;
$ITOP_SERVER_URL = isset($argv[7]) ? $argv[7] : false;
define ('DEBUG', isset($argv[8]) ? $argv[8] : false); // Set to true to see the actual SOAP enveloppe output
//Запись дебага
if(DEBUG){
$file = isset($argv[9]) ? $argv[9] : "/tmp/itsm.log";
}
//
function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null){
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
$sData = http_build_query($aData);
$aParams = array('http' => array(
'method' => 'POST',
'content' => $sData,
'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
));
if ($sOptionnalHeaders !== null) {
$aParams['http']['header'] .= $sOptionnalHeaders;
}
$ctx = stream_context_create($aParams);
$fp = @fopen($sUrl, 'rb', false, $ctx);
if (!$fp) {
global $php_errormsg;
if (isset($php_errormsg)) {
throw new Exception("Problem with $sUrl, $php_errormsg");
} else {
throw new Exception("Problem with $sUrl");
}
}
$response = @stream_get_contents($fp);
if ($response === false) {
throw new Exception("Problem reading data from $sUrl, $php_errormsg");
}
return $response;
}
// If the library curl is installed.... use this function
//
function DoPostRequest_curl($sUrl, $aData){
$curl = curl_init($sUrl);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $aData);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
////////////////////////////////////////////////////////////////////////////////
//
// Main program
//
////////////////////////////////////////////////////////////////////////////////
$aOperations = array(
array(
'operation' => 'core/get', // operation code
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE caller_name = "Zabbix"',
'output_fields' => '*', // list of fields to show in the results (* or a,b,c)
),
);
if ($ITOP_SERVER_URL) {
if(DEBUG) {
file_put_contents($file, "Please edit the sample script and configure the server URL \n", FILE_APPEND | LOCK_EX);
}
exit("Please edit the sample script and configure the server URL \n");
} else {
$sUrl = $ITOP_SERVER_URL;
}
//Проверка существования переменных
if($sHost and $sALERTMESSAGE and $sServiceSubcategory and $Service and $ITOP_USERNAME and $ITOP_PASSWORD){
$aData = array();
$aData['auth_user'] = $ITOP_USERNAME;
$aData['auth_pwd'] = $ITOP_PASSWORD;
$getData = json_decode(mb_strtolower($sALERTMESSAGE,'UTF-8'), true);
if(DEBUG) {
file_put_contents($file, $sALERTMESSAGE."\n", FILE_APPEND | LOCK_EX);
}
//Проверка масив или нет
if(is_array($getData)){
//Запрос просотра существования запроса
$aOperations = array(
array(
'operation' => 'core/get', // operation code
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE operational_status != "closed" AND caller_name = "Zabbix" AND title = "'.$getData['tr_name'].'"',
'output_fields' => '*',
),
);
foreach ($aOperations as $iOp => $aOperation) {
$aData['json_data'] = json_encode($aOperation);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
//Проверка статусса заявки
if($aResults->objects == NULL and $getData['tr_statsus'] != "ok"){
//Создаем носую запись
$aOperationsIns = array(
array(
'operation' => 'core/create',
'comment' => 'Adding a new trigger',
'class' => 'UserRequest',
'output_fields' => 'id, friendlyname',
'fields' => array(
'org_id' => "SELECT Organization WHERE name = 'ADUsers' ",
'caller_id' => "SELECT Person WHERE name = 'Zabbix' ",
'title' => $getData['tr_name'],
'description' => $sHost,
'impact' => 2,
'origin' => 'mail',
'zabbix_id' => $getData['tr_statsus']."/".$getData['tsever'],
'servicesubcategory_id' => 'SELECT ServiceSubcategory WHERE name = "'.$sServiceSubcategory.'"',
'service_id' => 'SELECT Service WHERE name = "'.$sService.'"',
'public_log' => array(
'add_item' => array(
'user_id' => "SELECT Person WHERE name = 'Zabbix'",
'user_login' => "Zabbix Monitoring",
'message' => $sALERTMESSAGE,
),
),
),
),
);
foreach ($aOperationsIns as $aOperationsIn) {
unset ($aData['json_data']);
$aData['json_data'] = json_encode($aOperationsIn);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
}
if(DEBUG) {
file_put_contents($file, "Create tikets \n", FILE_APPEND | LOCK_EX);
}
echo "Create tikets \n";
}elseif($aResults->objects != NULL and $getData['tr_statsus'] == "ok"){
$aOperationsIns = array(
array(
'operation' => 'core/update',
'comment' => 'Synchronization from blah...',
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE status != "closed" AND caller_name = "Zabbix" AND title = "'.$getData['tr_name'].'"',
'output_fields' => 'id, friendlyname, title','user_login',
'fields' => array(
'zabbix_id' => $getData['tr_statsus']."/".$getData['tsever'],
'public_log' => array(
'add_item' => array(
'user_id' => "SELECT Person WHERE name = 'Zabbix'",
'user_login' => "Zabbix Monitoring",
'message' => $sALERTMESSAGE,
),
),
),
),
);
foreach ($aOperationsIns as $aOperationsIn) {
unset ($aData['json_data']);
$aData['json_data'] = json_encode($aOperationsIn);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
}
if(DEBUG) {
file_put_contents($file, "Ticket can be closed but there is no solution \n", FILE_APPEND | LOCK_EX);
}
echo "Ticket can be closed but there is no solution \n";
}else{
if($aResults->objects != NULL){
$aOperationsIns = array(
array(
'operation' => 'core/update',
'comment' => 'Synchronization from blah...',
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE status != "closed" AND caller_name = "Zabbix" AND title = "'.$getData['tr_name'].'"',
'output_fields' => 'id, friendlyname, title','user_login',
'fields' => array(
'zabbix_id' => $getData['tr_statsus']."/".$getData['tsever'],
'public_log' => array(
'add_item' => array(
'user_id' => "SELECT Person WHERE name = 'Zabbix'",
'user_login' => "Zabbix Monitoring",
'message' => $sALERTMESSAGE,
),
),
),
),
);
foreach ($aOperationsIns as $aOperationsIn) {
unset ($aData['json_data']);
$aData['json_data'] = json_encode($aOperationsIn);
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
}
if(DEBUG) {
file_put_contents($file, "Update tikets \n", FILE_APPEND | LOCK_EX);
}
echo "Update tikets \n";
}
}
}
}else{
if(DEBUG) {
file_put_contents($file, "Error parametrs array \n", FILE_APPEND | LOCK_EX);
}
exit("Error parametrs array \n");
}
}else{
if(DEBUG) {
file_put_contents($file, "Error parametrs all \n", FILE_APPEND | LOCK_EX);
}
exit("Error parametrs all \n");
}
?>