Ces programmes se divisent essentiellement en deux catégories :
- les sondes, qui vérifient l'état d'une ressource particulière ;
- les gestionnaires d'événements ou de notifications, qui sont utilisés pour réagir à des états particuliers de ces ressources.
Si la deuxième catégorie correspond souvent à des tâches génériques, la première requiert par contre souvent une spécialisation poussée, pour tester de façon réaliste le fonctionnement d'un système. En conséquence, il est souvent nécessaire de développer soi-même des sondes pour ses propres besoins. Il est possible d'utiliser n'importe quel langage à cette fin. Néanmoins, le langage Perl s'y prête particulièrement bien, pour plusieurs raisons. C'est ce que présente cet article, en s'accompagnant d'exemples concrets tirés de l'utilisation réelle de Nagios.
1. Principe de base
Une sonde est un exécutable, invoqué régulièrement par le démon Nagios. Le protocole de communication entre les deux parties est simple et robuste. En entrée, cette sonde récupère ses paramètres de deux façons différentes :
- les paramètres définis par l'utilisateur sont passés explicitement ;
- un certain nombre de paramètres contextuels sont passés sous forme de variables d'environnement [1].
En sortie, elle retourne deux informations :
- Le code de retour correspond au résultat du test et peut prendre 4 valeurs distinctes :
- 1 pour l'état normal (OK) ;
- 2 pour l'état avertissement (warning) ;
- 3 pour l'état critique (critical) ;
- 4 pour l'état inconnu (unknown).
- La sortie standard correspond à un texte purement informatif, destiné à l'utilisateur, suivi éventuellement d'informations de performances, que Nagios peut exploiter à des fins statistiques.
Voici un exemple d'une telle sonde, en shell pour plus de simplicité :
#!/bin/sh
cores=`ls /*.core 2>/dev/null`
if [ -n "$cores" ]; then
echo "cores files found: $cores"
exit 2
fi
exit 0
Cette sonde se contente de vérifier l'existence d'un fichier avec un nom quelconque, mais l'extension .core au niveau de la racine du système de fichiers, et de déclencher une alerte dans ce cas, en précisant le nom du fichier en question.
Pour la petite histoire, cette vérification a été mise en place pour détecter des crashs récurrents, mais imprévisibles, du démon rpc.idmapd, un des composants de NFSv4, dus à un bogue des bibliothèques OpenLDAP, et difficile à détecter autrement, à part attendre les plaintes des utilisateurs. De manière plus générale, cette vérification peut se faire pour n'importe quel programme tournant en tâche de fond et souffrant de problèmes similaires, à deux conditions :
- Imposer le format de nom des fichiers core, en configurant le noyau. Par exemple :
sysctl -w kernel.core_pattern=%e-%p.core
- S'assurer que la génération des fichiers core est autorisée pour ce programme. Sous Red Hat et dérivées, la fonction démon utilisée dans les scripts d'initialisation de services la désactive par défaut, sauf si la variable DAEMON_COREFILE_LIMIT est valorisée. Il suffit d'insérer la valeur DAEMON_COREFILE_LIMIT=unlimited dans le fichier /etc/sysconfig/foo correspondant au service visé.
Cet exemple est minimaliste, dans la mesure où aucun paramètre n'est nécessaire, le test principal est simplissime et il n'y a pas besoin d'évaluer la gravité du problème par rapport à un seuil. En ce sens, il s'agit plutôt d'un cas particulier. Dans la grande majorité des cas, la complexité est plus importante, ce qui nécessite d'utiliser des outils plus adaptés, comme Perl, par exemple…
2. Utilisation de Perl
Vu la simplicité du fonctionnement décrit dans la partie précédente, il est bien évidemment possible d'utiliser n'importe quel langage. La majorité des sondes incluses dans le projet Nagios Plugins sont écrites en C, pour des raisons de performances. Dans le cadre d'un besoin spécifique, il est généralement plus simple d'utiliser un langage dynamique, tel que shell, Perl, Python, Ruby, PHP, etc. C'est d'ailleurs le cas de la quasi totalité des sondes disponibles sur le site MonitoringExchange.org [2].
Le shell a l'avantage de la simplicité, mais devient très vite pénible à utiliser dès lors que la complexité du problème augmente. Et la nécessité d'invoquer des programmes externes pour interagir avec des serveurs variés (LDAP, Web, …), puis d'analyser leur sortie, pose rapidement des problèmes de lisibilité, de fragilité, et éventuellement de performances. Bref, ce n'est pas à mon sens le meilleur choix possible.
Les autres langages cités sont relativement équivalents en termes de simplicité, de disponibilité de bibliothèques d'accès à des services externes, ou encore de facilité de déploiement. Mais Perl possède deux atouts particuliers, qui sont présentés ici.
2.1 Nagios::Plugin
Le premier atout, c'est l'existence d'un ensemble de modules dédié au développement de sondes Nagios, autrement dit un framework en langage de décideur pressé, ou un cadriciel en français normalisé. Il s'agit de Nagios::Plugin, auquel peut s'ajouter Nagios::Plugin::WWW::Mechanize pour des tâches plus spécifiques de surveillances d'applications web.
Les besoins classiques d'une sonde Nagios sont prises en charge automatiquement :
- analyse des arguments passés en ligne de commandes ;
- analyse des intervalles définissant les seuils d'alerte ;
- évaluation des valeurs recueillies par rapport à ces seuils ;
- formatage des affichages ;
- etc.
Au niveau architecture, ces fonctionnalités sont réparties entre plusieurs modules : Nagios::Plugin::Getopt gère la ligne de commandes, Nagios::Plugin::Threshold gère les seuils, etc. L'utilisation reste simple, puisqu'il suffit d'instancier un objet appartenant à la classe principale, Nagios::Plugin, les autres objets étant encapsulés par celui-ci.
Au niveau du fonctionnement, il y a deux façons de procéder :
- La plus simple consiste à s'arrêter immédiatement si une erreur est constatée, via la méthode nagios_exit().
- L'autre consiste à poursuivre l'exécution jusqu'au bout, en accumulant les messages assortis d'un niveau de gravité au fur et à mesure, via la méthode add_message(), et à récupérer ceux-ci à la fin via la méthode check_messages().
Dans les cas simples, où la sonde n'effectue en fait qu'un test unique, la première méthode est bien évidemment la plus simple à mettre en œuvre. Par contre, dans le cas où la sonde vérifie plusieurs paramètres, la deuxième garantit que l'ensemble des problèmes constatés sont remontés. Les exemples qui suivent utilisent l'une ou l'autre en fonction du besoin.
2.1.1 Surveillance d'agrégat d'interfaces réseau
Cette sonde surveille l'état d'un agrégat d'interfaces réseau (interfaces bonding). Il s'agit d'une sonde locale, qui doit être exécutée sur la machine cible, et qui vérifie l'état de chacun des membres de cet agrégat.
#!/usr/bin/perl
use strict;
use warnings;
use Nagios::Plugin;
use File::Basename;
my $plugin = Nagios::Plugin->new(
usage => "Usage: %s [-i <interface>]"
);
$plugin->add_arg(
spec => 'interface|i=s@',
help => "--interface NAME\n interface to check (may repeat)"
);
$plugin->getopts();
my $interfaces = $plugin->opts()->get('interface');
if (defined $interfaces) {
foreach my $interface (@$interfaces) {
my $file = "/proc/net/bonding/$interface";
if (-f $file) {
check_interface($plugin, $file);
} else {
$plugin->add_message(CRITICAL, "no such interface $interface");
}
}
} else {
$plugin->nagios_exit(CRITICAL, "No bonded interface")
unless -d '/proc/net/bonding';
foreach my $file (</proc/net/bonding/*>) {
check_interface($plugin, $file);
}
}
my ($code, $message) = $plugin->check_messages();
$plugin->nagios_exit($code, $message);
sub check_interface {
my ($plugin, $file) = @_;
my (%master, %slaves, $slave);
my $master = basename($file);
open (my $fh, '<', $file)
or plugin->nagios_exit(UNKNOWN, "Can't open $file: $!");
while (my $line = <$fh>) {
if ($line =~ /^Bonding Mode: (.*)$/) {
$master{mode} = $1;
} elsif ($line =~ /^Slave Interface: (\S+)$/) {
$slave = $1;
} elsif ($line =~ /^MII Status: (\S+)$/) {
if ($slave) {
$slaves{$slave}->{status} = $1;
} else {
$master{status} = $1;
}
} elsif ($line =~ /^\t?Aggregator ID: (\d+)$/) {
if ($slave) {
$slaves{$slave}->{aggregator} = $1;
} else {
$master{aggregator} = $1;
}
}
}
close ($fh);
foreach my $slave (keys %slaves) {
if ($slaves{$slave}->{status} ne 'up') {
$plugin->add_message(CRITICAL,
"$slave interface of $master is not up");
}
if ($slaves{$slave}->{aggregator} != $master{aggregator}) {
$plugin->add_message(WARNING,
"$slave interface of $master is not aggregated");
}
}
$plugin->add_message(OK,
sprintf "$master: mode %s, slaves %s",
$master{mode}, scalar keys %slaves);
}
Les premières lignes sont classiques : chargement des modules nécessaires et activation des pragmas warning et strict.
On crée ensuite une instance de la classe Nagios::Plugin, avec pour seul paramètre le message d'aide minimal, utilisé lorsque la sonde est invoquée avec l'option –usage, qui est gérée automatiquement.
On passe après à la définition successive de chacune des options, en utilisant la syntaxe classique de Getopt::Long. Pour chacune d'elles, il est possible de préciser un message d'explication. La concaténation de l'ensemble de ces messages, ainsi que du message passé au constructeur, constitue le résultat de l'exécution de la sonde avec l'option –help, qui est donc elle aussi gérée automatiquement. Le formatage de ces messages (fin de ligne, indentation) correspond à celui des options par défaut, pour obtenir un affichage homogène.
Après analyse de la ligne de commandes, si une ou plusieurs interfaces sont précisées explicitement, on vérifie leur existence avant de les vérifier elles-mêmes. Si aucune interface n'a été précisée, toutes les interfaces existantes sont vérifiées.
La vérification proprement dite consiste à lire le fichier /proc/net/bonding/$interface correspondant, à extraire par le biais d'expressions régulières les informations essentielles comme le mode d'agrégation, l'identifiant de l'agrégat et le statut de chacune des interfaces agrégées. Ensuite, il est facile de vérifier si l'une de celles-ci n'est pas active, ou ne fait pas partie du bon agrégat. Enfin, un message informatif est ajouté systématiquement. Si aucun autre message avec un niveau de gravité supérieur n'a été ajouté durant l'exécution, c'est celui-ci qui sera produit en sortie.
2.1.2 Surveillance du nombre d'entrées d'un serveur LDAP
Cette sonde surveille le nombre d'objets d'un serveur LDAP, à la suite d'incidents répétés de synchronisation. Si la politique de gestion de l'annuaire assure qu'aucun objet n'est jamais censé être supprimé volontairement, le simple fait d'en trouver moins par rapport à l'exécution précédente de la sonde indique un problème…
#!/usr/bin/perl
use strict;
use warnings;
use Nagios::Plugin;
use Net::LDAP;
my $plugin = Nagios::Plugin->new(
usage => "Usage: %s [-H <host>] [-b <base>] [-f <filter>]\n"
. "\t [-s <scope>] [-F <file>] [-t <timeout>]"
);
$plugin->add_arg(
spec => 'host|H=s',
help => "--host=NAME\n LDAP server",
required => 1,
);
$plugin->add_arg(
spec => 'base|b=s',
help => "--base NAME\n LDAP base",
required => 1,
);
$plugin->add_arg(
spec => 'filter|f=s',
help => "--filter NAME\n LDAP filter",
required => 1,
);
$plugin->add_arg(
spec => 'scope|s=s',
help => "--scope NAME\n LDAP scope",
default => 'sub'
);
$plugin->add_arg(
spec => 'file|F=s',
help => "--file NAME\n Status file",
required => 1,
);
$plugin->getopts();
my $opts = $plugin->opts();
$SIG{ALRM} = sub {
$plugin->nagios_exit(UNKNOWN, "Timeout reached");
};
alarm $opts->get('timeout');
# open state file
my $file = $opts->get('file');
my $handle;
my $old_count = 0;
if (-f $file) {
open($handle, '+<', $file)
or die "Can't open $file in read-write mode: $!\n";
my $line = <$handle>;
chomp $line;
$old_count = $line;
seek($handle, 0, 0);
} else {
open($handle, '>', $file)
or die "Can't open $file in write mode: $!\n";
}
my $ldap = Net::LDAP->new(
$opts->get('host'),
);
$plugin->nagios_exit(
UNKNOWN, "Error while connecting to LDAP: $@"
) if !defined $ldap;
my $result = $ldap->bind();
$plugin->nagios_exit(
UNKNOWN, "Error while binding to LDAP: " . $result->error()
) if $result->code();
my $result = $ldap->search(
base => $opts->get('base'),
filter => $opts->get('filter'),
scope => $opts->get('scope'),
attrs => ['1.1']
);
$plugin->nagios_exit(
UNKNOWN, "Error while searching LDAP: " . $result->error()
) if $result->code();
$ldap->unbind();
my $new_count = $result->count();
# save state
print $handle "$new_count\n";
close($handle);
$plugin->set_thresholds(
critical => "$old_count:",
);
my $code = $plugin->check_threshold($new_count);
my $message = "$new_count entries (at least $old_count expected)";
$plugin->nagios_exit($code, $message);
Par rapport à l'exemple précédent, il y a quelques changements.
Le premier, c'est la mise en place d'un délai d'expiration (timeout), via un gestionnaire de signal pour s'assurer que le statut de sortie est bien UNKNOWN si ce délai est atteint. C'est une nécessité pour toute sonde utilisant le réseau.
Le second, c'est l'utilisation d'un fichier pour gérer un état. Ce fichier contient une seule ligne, contenant l'information nécessaire, c'est-à-dire le nombre d'objets trouvés par la requête précédente. S'il existe déjà, ce fichier est ouvert en lecture/écriture, et l'opération seek() réinitialise le curseur au début après la lecture initiale, de façon à éviter d'avoir à l'ouvrir deux fois.
Enfin, comme il n'y a qu'un seul test effectif, il n'y a pas besoin d'ajouter des messages au fur et à mesure. Le statut et le message de sortie sont déterminés à la volée.
Sinon, il s'agit d'une utilisation standard du module Net::LDAP. Le seul point intéressant est l'utilisation de l'argument attrs lors de la requête pour limiter la quantité effective d'informations à récupérer : après tout, le détail des entrées ne nous concerne pas, et il peut y en avoir beaucoup.
2.1.3 Surveillance d'un serveur CUPS
Cette sonde surveille un serveur d'impression CUPS, et vérifie l'âge et le nombre des tâches actives, afin de détecter des blocages. En fait, il existe déjà des sondes similaires, disponibles sur le site MonitoringExchange.org, telles que check_cups_queue [3], par exemple. Mais ce programme est un parfait exemple des limitations de l'utilisation du shell : code affreux, passage par un fichier temporaire, utilisation de sous-shell pour le moindre calcul, etc. La version Perl ci-dessous est largement plus lisible.
#!/usr/bin/perl
use strict;
use warnings;
use Nagios::Plugin;
use Net::CUPS;
my $plugin = Nagios::Plugin->new(
usage => "Usage: %s [ -v|--verbose ] [-H <host>] [-t <timeout>]"
);
$plugin->add_arg(
spec => 'host|H=s',
default => 'localhost',
help => "-H, --host=NAME\n Cups server hostname"
);
$plugin->add_arg(
spec => 'age-warning|w=i',
default => 60,
help => "-w, --age-warning=INTEGER\n Warning level for job age"
);
$plugin->add_arg(
spec => 'age-critical|c=i',
default => 120,
help => "-c, --age-critical=INTEGER\n Critical level for job age"
);
$plugin->add_arg(
spec => 'count-warning|W=i',
default => 3,
help => "-W, --count-warning=INTEGER\n Warning level for job counts"
);
$plugin->add_arg(
spec => 'count-critical|C=i',
default => 5,
help => "-C, --count-critical=INTEGER\n Critical level for job count"
);
$plugin->getopts();
my $opts = $plugin->opts();
$SIG{ALRM} = sub {
$plugin->nagios_exit(UNKNOWN, "Timeout reached");
};
alarm $opts->get('timeout');
my $verbose = $opts->get('verbose');
my $age_warning_threshold = $opts->get('age-warning');
my $age_critical_threshold = $opts->get('age-critical');
my $count_warning_threshold = $opts->get('count-warning');
my $count_critical_threshold = $opts->get('count-critical');
$plugin->nagios_exit(
UNKNOWN,
"age critical treshold $age_critical_threshold less than warning " .
"treshold $age_warning_threshold"
) if $age_critical_threshold < $age_warning_threshold;
$plugin->nagios_exit(
UNKNOWN,
"count critical treshold $count_critical_threshold less than warning " .
"treshold $count_warning_threshold"
) if $count_critical_threshold < $count_warning_threshold;
my $cups = Net::CUPS->new();
my $host = $opts->get('host');
$cups->setServer($host);
my @printers = $cups->getDestinations();
if (!@printers) {
$plugin->add_message(CRITICAL, "no printers configured on host $host");
} else {
foreach my $printer (@printers) {
my $name = $printer->getName();
print "checking printer $name\n" if $verbose;
my $state = $printer->getOptionValue('printer-state');
if ($state == 5) {
$plugin->add_message(WARNING, "printer $name disabled");
} else {
my $count = 0;
my $now = time();
foreach my $id ($printer->getJobs(0, 0)) {
my $job = $printer->getJob($id);
print "checking job $id\n" if $verbose;
my $creation = $job->{creation_time};
my $age = $now - $creation;
my $code = $plugin->check_threshold(
check => $age,
warning => $age_warning_threshold,
critical => $age_critical_threshold,
);
$plugin->add_message(
$code,
"job $id waiting for $age seconds on printer $name"
) if $code != OK;
$count++;
}
my $code = $plugin->check_threshold(
check => $count,
warning => $count_warning_threshold,
critical => $count_critical_threshold,
);
$plugin->add_message(
$code,
"job count $count on printer $name"
) if $code != OK;
}
}
}
my ($code, $message) = $plugin->check_messages();
$plugin->nagios_exit($code, $message);
Ce programme utilise le module Net::CUPS pour s'interfacer avec le serveur Cups et récupérer les informations nécessaires. Le principe est similaire au cas précédent, à quelques exceptions près.
Il y a deux quantités pour lesquelles des limites sont données, l'âge maximum d'une tâche et le nombre maximal de tâches par imprimante. Pour chacune de ces quantités, deux valeurs définissent des seuils d'avertissement et d'erreur. La cohérence de ces valeurs entre elles est vérifiée lors de l'initialisation de la sonde, et l'exécution s'arrête immédiatement avec le statut UNKNOWN si celle-ci est erronée.
La comparaison de la valeur courante avec ces valeurs seuils se fait par le biais de la méthode check_threshold(). Comme il y a deux jeux d'options, il faut préciser explicitement les seuils. Le résultat donne directement le code de retour. Il suffit donc d'appeler la méthode add_message() avec celui-ci s'il correspond à un problème.
2.1.4 Surveillance d'une application web
La sonde check_http, qui est incluse dans nagios-plugins, fournit de quoi tester le fonctionnement d'un serveur web, mais au niveau HTTP seulement. C'est-à-dire qu'il est possible d'envoyer une requête arbitraire et d'analyser le statut de retour, le contenu de la page renvoyée ou la réussite d'une éventuelle authentification, à condition que celle-ci soit gérée par le serveur web. Mais tester le fonctionnement d'une application web par ce biais est plus difficile. Il est toujours possible de vérifier une authentification applicative, en analysant manuellement le formulaire utilisé, et en rejouant le résultat de celui-ci, mais c'est relativement fragile. Et il est de toute façon impossible d'enchaîner plusieurs requêtes à la suite.
Le module Nagios::Plugin::WWW::Mechanize permet justement de remédier à ce problème d'une façon simple, grâce au module WWW::Mechanize, déjà présenté dans le numéro 75 de Linux Magazine [4]. Celui-ci se charge de tout le travail d'analyse des pages obtenues, il suffit donc d'indiquer les champs à remplir ou les liens à suivre pour simuler une session complète. L'exemple ci-dessous montre ainsi comment surveiller le bon fonctionnement de l'authentification d'un webmail horde/imp.
#!/usr/bin/perl
use strict;
use warnings;
use Nagios::Plugin::WWW::Mechanize;
my $plugin = Nagios::Plugin::WWW::Mechanize->new(
usage => "Usage: %s [-U <url>] [-u <user>] [-p <password>]"
);
$plugin->add_arg(
spec => 'url|U=s',
help => "--url URL\n url",
required => 1
);
$plugin->add_arg(
spec => 'user|u=s',
help => "--user NAME\n user",
required => 1
);
$plugin->add_arg(
spec => 'password|p=s',
help => "--password NAME\n password",
required => 1
);
$plugin->getopts();
my $opts = $plugin->opts();
$plugin->get($opts->get('url'));
$plugin->submit_form(
form_name => "horde_login",
fields => {
horde_user => $opts->get('user'),
horde_pass => $opts->get('password'),
}
);
my $title = $plugin->mech()->title();
if ($title eq 'Horde') {
$plugin->nagios_exit(OK, "OK");
} else {
$plugin->nagios_exit(CRITICAL, "Cannot log in");
}
Le fonctionnement est simple, surtout si l'on est déjà familiarisé avec WWW::Mechanize. En effet, il suffit de récupérer la page d'accueil de l'application, de sélectionner le formulaire voulu par son nom, de remplir les champs nécessaires via les options passées à la sonde, puis de soumettre le tout. Ensuite, il suffit de vérifier le titre de la page obtenue, qui est toujours la page d'accueil si l'authentification a échoué.
2.2 L'interpréteur embarqué
L'interpréteur embarqué (ePN, embedded Perl Nagios) est à Nagios ce que mod_perl est à Apache : un moyen d'exécuter du code Perl sans lancer un processus externe. Il s'agit donc d'économiser les ressources associées (appel à fork(), puis chargement de l'interpréteur perl), et ce chaque fois qu'il faut exécuter un greffon écrit en Perl. Le gain est donc proportionnel au nombre de tests à exécuter, donc à la charge du serveur.
Néanmoins, comme pour mod_perl, l'utilisation de cet interpréteur impose un certain nombre de contraintes sur l'écriture du code. En effet, le cycle d'exécution du greffon est largement modifié, puisqu'il est transformé en une fonction, appelée à chaque exécution. Ces différentes contraintes (ne pas utiliser de bloc BEGIN, ne pas utiliser de section DATA ou END, …), sont détaillées dans la documentation de Nagios [5]. Les mêmes causes produisant les mêmes effets, un œil averti remarquera d'ailleurs les liens vers la documentation de mod_perl.
Pour pouvoir utiliser ePN, il y d'abord un prérequis, à savoir l'activation de cette fonctionnalité lors de la configuration (option –enable-embedded-perl). Si vous ne l'avez pas compilé vous-même (cas typique lorsque l'on utilise un paquetage binaire), un moyen simple de vérifier est de regarder si le binaire est lié à libperl.so :
[root@ryu ~]# ldd /usr/sbin/nagios
linux-gate.so.1 => (0xffffe000)
libperl.so => /usr/lib/perl5/5.10.0/i386-linux-thread-multi/CORE/libperl.so (0xb776c000)
libnsl.so.1 => /lib/libnsl.so.1 (0xb774c000)
libdl.so.2 => /lib/libdl.so.2 (0xb7748000)
libm.so.6 => /lib/i686/libm.so.6 (0xb7722000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0xb76d9000)
libutil.so.1 => /lib/libutil.so.1 (0xb76d5000)
libpthread.so.0 => /lib/i686/libpthread.so.0 (0xb76bc000)
libc.so.6 => /lib/i686/libc.so.6 (0xb756e000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7560000)
/lib/ld-linux.so.2 (0xb78d3000)
Si cette condition est remplie, il faut ensuite l'activer via l'option enable_embedded_perl dans le fichier de configuration, puis choisir s'il sera utilisé par défaut ou non, via l'option use_embedded_perl_implicitly. Dans le premier cas, tout greffon Perl (pas juste les sondes…) sera exécuté dans cet environnement, tandis que dans le second, il faudra le spécifier au cas par cas. Ceci se fait via l'utilisation d'un commentaire dans les premières lignes du fichier :
# nagios: +epn
Inversement, pour spécifier que l'on ne veut pas de l'interpréteur embarqué, dans le cas de l'utilisation implicite :
# nagios: -epn
Mesurer les performances de Nagios via nagiostat permet effectivement de constater que l'activation de l'interpréteur fait baisser le temps moyen d'exécution des tests. Lors de mes essais, ce gain tournait autour de 10%, mais ce chiffre dépend énormément de la proportion des programmes Perl parmi les différents greffons utilisés, il est donc à relativiser.
Par contre, l'interpréteur embarqué est largement plus sensible aux détails. Par exemple, un simple warning dans le code, ou même dans un module utilisé, suffit à provoquer une erreur à la compilation, et donc à rendre le greffon inutilisable. Si l'erreur apparaît à la compilation, le greffon sort avec un statut UNKWNOWN et le message d'erreur apparaît clairement dans la sortie du test, le résultat est beaucoup moins prévisible et se traduit souvent par un obscur message d'erreur sans aucune explication… Le seul moyen d'obtenir alors la cause du problème est d'activer les traces relatives à l'exécution des tests (directive debug_level=16) et de regarder dans le fichier de traces (celui indiqué par la directive debug_file).
En conséquence, il est recommandé d'être strict dans le développement (utilisation des pragmas strict et warnings) dès le départ, de tester intégralement tous les cas de figure du plugin dans un mode de fonctionnement normal, avant de basculer progressivement le mode d'exécution de chaque greffon. Et de ne basculer en utilisation par défaut que lorsque tous ceux-ci sont testés et validés.
Et il faut garder à l'esprit que si cette fonctionnalité est intéressante, les gains restent relativement modestes, et qu'il existe d'autres optimisations de Nagios à mettre éventuellement en œuvre avant celle-ci. De même qu'un meilleur algorithme prime sur les optimisations venant du compilateur, le code du greffon compte plus que leur mode d'exécution.
Références
[1] Cette fonctionnalité étant coûteuse en ressources, il est néanmoins recommandé de la désactiver pour des déploiements importants, par le biais de la directive enable_environment_macros=0
[2] http://www.monitoringexchange.org
[3] http://www.monitoringexchange.org/cgi-bin/page.cgi?g=Detailed%2F2110.html;d=1
[4] http://articles.mongueurs.net/magazines/linuxmag75.html
[5] http://nagios.sourceforge.net/docs/3_0/epnplugins.html