Skip to content

Commit

Permalink
feat(zabbix): ajoute un module de monitoring pour Zabbix
Browse files Browse the repository at this point in the history
Closes #25
  • Loading branch information
jboulen committed Apr 11, 2024
1 parent f217597 commit 146ecec
Show file tree
Hide file tree
Showing 12 changed files with 694 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Isou est une application permettant d'informer de l'état des services numériqu

## Fonctionnalités
- propose différentes vues des états passés, présents et à venir
- s'interface avec un ou plusieurs logiciels de monitoring comme Nagios (en natif) ou Nagios, Icinga, Shinken et Naemon (via Thruk)
- s'interface avec un ou plusieurs logiciels de monitoring comme Nagios (en natif), Nagios, Icinga, Shinken et Naemon (via Thruk) ou Zabbix (en natif)
- permet de regrouper les services en catégorie
- dispose de plusieurs types d'évènements
- garde un historique des incidents
Expand All @@ -22,6 +22,7 @@ Isou est une application permettant d'informer de l'état des services numériqu
- Un système d'authentification CAS/LDAP
- Un système de monitoring Nagios
- Un système de monitoring Thruk
- Un système de monitoring Zabbix

## Installation et mise à jour
```bash
Expand Down
13 changes: 13 additions & 0 deletions plugins/monitoring/zabbix/html/delete.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<form action="{$smarty.const.URL}/index.php/services/{$plugin->codename}/delete/{$service->id}" method="post">
<div class="text-center alert alert-danger">
<p>Voulez-vous vraiment supprimer le service "{$service->name}", ses évènements et ses dépendances ?</p>
<ul class="list-inline form-submit-buttons-ul">
<li class="list-inline-item">
<input class="btn btn-danger" type="submit" name="delete" value="supprimer" />
</li>
<li class="list-inline-item">
<a class="btn btn-secondary" href="{$smarty.const.URL}/index.php/services/{$plugin->codename}">annuler</a>
</li>
</ul>
</div>
</form>
54 changes: 54 additions & 0 deletions plugins/monitoring/zabbix/html/edit.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<form action="{$smarty.const.URL}/index.php/services/zabbix/edit/{$service->id}" class="form" method="post">
{if empty($service->id) === true}
<h2>Ajouter un service Zabbix</h2>
{else}
<h2>Remplacer le service {$service->name}</h2>
{/if}

{include file="common/messages_form.tpl"}

<dl>
<dt>
<label for="pattern">Service Zabbix</label>
</dt>
<dd class="form-group">
<input class="form-control" id="pattern" list="suggestions" name="pattern" placeholder="Rechercher un service Zabbix. Les expressions régulières sont acceptées. Exemple : https.*myhostname" value="{$smarty.post.pattern|default:""}" type="search" />
<datalist id="suggestions">
{foreach $services as $service}
<option value="{$service}">
{/foreach}
</datalist>
</dd>
</dl>

<ul class="list-inline">
<li class="list-inline-item">
<input class="btn btn-primary" name="search" type="submit" value="rechercher" />
</li>

<li class="list-inline-item">
<a class="btn btn-secondary" href="{$smarty.const.URL}/index.php/services/zabbix">annuler</a>
</li>
</ul>

{if isset($results[0]) === true}
<div class="border mb-4 px-4 py-2 rounded">
<p>{count($results)} services trouvés :</p>
{if empty($service->id) === true}
{html_checkboxes id="services" name="services" output=$results separator='<br />' style="margin-right:.5em;" values=$results}
{else}
{html_radios id="services" name="services" output=$results separator='<br />' style="margin-right:.5em;" values=$results}
{/if}

<ul class="list-inline">
<li class="list-inline-item">
<input class="btn btn-primary" name="submit" type="submit" value="{if empty($service->id) === true}ajouter{else}modifier{/if}" />
</li>

<li class="list-inline-item">
<a class="btn btn-secondary" href="{$smarty.const.URL}/index.php/services/zabbix">annuler</a>
</li>
</ul>
</div>
{/if}
</form>
47 changes: 47 additions & 0 deletions plugins/monitoring/zabbix/html/list.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{if empty($plugin->active) === true}
<p class="alert alert-info">Le plugin {$plugin->name} n'est pas activé dans la <a href="{$smarty.const.URL}/index.php/configuration/monitoring/{$plugin->codename}">page de configuration</a>.</p>
{else}
{include file="common/messages_session.tpl"}

<p class="text-end">
<a class="btn btn-primary" href="{$smarty.const.URL}/index.php/services/{$plugin->codename}/edit/0">Ajouter un service {$plugin->name}</a>
</p>

{if count($services) === 0}
<p class="alert alert-info">Aucun service {$plugin->name} utilisé.</p>
{else}
<table class="caption-top table table-condensed" summary="liste des services {$plugin->name}">
<caption>{count($services)} services {$plugin->name} gérés dans Isou</caption>
<thead>
<tr>
<th class="col-md-5" id="head-name">Nom du service</th>
<th class="col-md-2" id="head-action">Actions</th>
<th class="col-md-5" id="head-note">Notes</th>
</tr>
</thead>
<tbody>
{foreach $services as $service}
<tr>
<td headers="head-name">{$service->name}</td>
<td headers="head-action">
<ul class="list-inline">
<li class="list-inline-item"><a class="btn btn-sm btn-primary" href="{$smarty.const.URL}/index.php/services/{$plugin->codename}/edit/{$service->id}">modifier</a></li>
<li class="list-inline-item"><a class="btn btn-sm btn-danger" href="{$smarty.const.URL}/index.php/services/{$plugin->codename}/delete/{$service->id}">supprimer</a></li>
</ul>
</td>
<td headers="head-note">
{* <!-- ex: service Zabbix non utilisé dans ISOU, service retiré de Zabbix --> *}
{if isset($service->notes[0])}
<ul class="list-unstyled">
{foreach $service->notes as $note}
<li class="alert alert-{$note['style']}">{$note['label']}</li>
{/foreach}
</ul>
{/if}
</td>
</tr>
{/foreach}
</tbody>
</table>
{/if}
{/if}
49 changes: 49 additions & 0 deletions plugins/monitoring/zabbix/html/settings.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<form method="post" action="{$smarty.const.URL}/index.php/configuration/monitoring/{$plugin->codename}">

{include file="common/messages_form.tpl"}

<fieldset>
<legend>{$plugin->name}</legend>

<dl>
<div class="form-group">
<dt id="plugin-zabbix-enable">Activer</dt>
<dd>
{html_radios class="isou-radios" aria-labelledby="plugin-zabbix-enable" name="plugin_zabbix_enable" options=$options_yes_no selected=$plugin->active}
</dd>
</div>

<div class="form-group">
<dt>
<label for="plugin-zabbix-url">URL de Zabbix</label>
</dt>
<dd>
<input class="form-control" type="text" name="plugin_zabbix_url" id="plugin-zabbix-url" value="{$plugin->settings->zabbix_url}" />
</dd>
</div>
<div class="form-group">
<dt>
<label for="plugin-zabbix-api-key">Jeton d'API Zabbix</label>
</dt>
<dd>
<input class="form-control" type="password" name="plugin_zabbix_api_token" id="plugin-zabbix-api-key" value="{if empty($plugin->settings->zabbix_api_token) === false}* * * * *{/if}" />
</dd>
</div>
<div class="form-group">
<dt>
<label for="plugin-zabbix-tags">Tags Zabbix</label>
<p class="help-block small">Permet de filtrer par tags et de limiter le nombre de services remontés par l'API. Le format attendu est <em>tag1=value1,tag2=value2</em>. Exemple : <em>class=os,target=linux</em></p>
</dt>
<dd>
<input class="form-control" type="text" name="plugin_zabbix_tags" id="plugin-zabbix-tags" value="{$plugin->settings->zabbix_tags}" />
</dd>
</div>
</dl>
</fieldset>

<ul class="list-inline">
<li class="list-inline-item">
<input class="btn btn-primary" type="submit" value="enregistrer" />
</li>
</ul>
</form>
36 changes: 36 additions & 0 deletions plugins/monitoring/zabbix/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/**
* This file is part of isou project.
*
* @author Université Rennes 2 - DSI <[email protected]>
* @license The Unlicense <http://unlicense.org>
*/

declare(strict_types=1);

use UniversiteRennes2\Isou\Plugin;

$smarty->addTemplateDir(PRIVATE_PATH.'/plugins/monitoring/zabbix/html');

$plugin = Plugin::get_record(array('id' => PLUGIN_ZABBIX));

if (empty($plugin->active) === true) {
$PAGE_NAME[2] = '';
}

if (isset($PAGE_NAME[2]) === false) {
$PAGE_NAME[2] = '';
}

switch ($PAGE_NAME[2]) {
case 'edit':
require PRIVATE_PATH.'/plugins/monitoring/zabbix/php/edit.php';
break;
case 'delete':
require PRIVATE_PATH.'/plugins/monitoring/zabbix/php/delete.php';
break;
default:
require PRIVATE_PATH.'/plugins/monitoring/zabbix/php/list.php';
}

$smarty->assign('plugin', $plugin);
148 changes: 148 additions & 0 deletions plugins/monitoring/zabbix/lib.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php
/**
* This file is part of isou project.
*
* Fonctions liées aux mises à jour des services du plugin Zabbix.
*
* @author Université Rennes 2 - DSI <[email protected]>
* @license The Unlicense <http://unlicense.org>
*/

declare(strict_types=1);

use UniversiteRennes2\Isou\Plugin;
use UniversiteRennes2\Isou\Service;
use UniversiteRennes2\Isou\State;

/**
* Met l'état des services du plugin Zabbix.
*
* @param Plugin $plugin Une instance du plugin Zabbix.
*
* @return boolean True si la mise à jour s'est déroulée correctement ; False si une erreur est survenue.
*/
function plugin_zabbix_update(Plugin $plugin) {
global $LOGGER;

$url = $plugin->settings->zabbix_url;
if (empty($url) === true) {
$LOGGER->error('Le paramètre "URL de Zabbix" semble être vide.');

return false;
}

$apitoken = $plugin->settings->zabbix_api_token;
$equals = 1;

$tags = array();
if (empty($plugin->settings->zabbix_tags) === false) {
foreach (explode(',', $plugin->settings->zabbix_tags) as $tagvalue) {
list($tag, $value) = explode('=', $tagvalue);
$tags[] = array('tag' => $tag, 'value' => $value, 'operator' => $equals);
}
}

// Appelle le webservice de Zabbix.
$params = array('jsonrpc' => '2.0', 'method' => 'host.get', 'params' => array('selectItems' => array('key_', 'status'), 'output' => array('host'), 'inheritedTags' => true), 'id' => rand());
if (isset($tags[0]) === true) {
$params['params']['tags'] = $tags;
}

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json-rpc', 'Authorization: Bearer '.$apitoken));
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));
$data = curl_exec($curl);
$error = curl_errno($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);

// Vérifie si le fichier peut être ouvert.
if ($error !== 0) {
$LOGGER->error('L\'url "'.$url.'" n\'est pas accessible.');

return false;
}

if ($http_code > 399) {
$LOGGER->error('Le code de retour HTTP n\'est pas correct (code '.$http_code.')');

return false;
}

$response = json_decode($data);
if ($response === false) {
$LOGGER->error('Les données de l\'API Zabbix n\'ont pas pu être lues.');

return false;
}

if (isset($response->result) === false) {
$LOGGER->error('Le JSON reçu par l\'API Zabbix n\'a pas pu être décodé.');

return false;
}

$services = array();
foreach ($response->result as $element) {
if (isset($element->host) === false) {
continue;
}

if (isset($element->items) === false) {
continue;
}

foreach ($element->items as $item) {
if (isset($item->key_) === false) {
continue;
}

if (isset($item->status) === false) {
continue;
}

$service = new stdClass();
$service->name = $item->key_.'@'.$element->host;
$service->state = (string) $item->status;

$id = md5($service->name);
if (isset($services[$id]) === false) {
$services[$id] = $service;
} else {
$LOGGER->info('Un service Zabbix porte déjà le nom "'.$service->name.'" (id: '.$id.').');
}
}
}

// Enregistre le cache.
$cache_path = PRIVATE_PATH.'/cache/plugins/monitoring/zabbix';

if (is_dir($cache_path) === false) {
if (mkdir($cache_path, 0755, $recursive = true) === false) {
$LOGGER->error('Impossible de créer le dossier "'.$cache_path.'"');
}
}

if (file_put_contents($cache_path.'/services.json', json_encode($services, JSON_PRETTY_PRINT)) === false) {
$LOGGER->error('Le cache n\'a pas pu être écrit dans le répertoire "'.$cache_path.'".');
}

// Met à jour les états des services Zabbix dans la base de données d'Isou.
foreach (Service::get_records(array('plugin' => PLUGIN_ZABBIX)) as $service) {
$id = md5($service->name);

if (isset($services[$id]) === false) {
continue;
}

if ($service->state !== $services[$id]->state) {
$LOGGER->info(' Le service "'.$service->name.'" (id #'.$service->id.') passe de l\'état '.$service->state.' à '.$services[$id]->state.'.');
$service->change_state($services[$id]->state);
}
}

return true;
}
Loading

0 comments on commit 146ecec

Please sign in to comment.