Développement de sondes Nagios

Article publié dans Linux Magazine 129, juillet/août 2010.

Copyright © 2010 - Guillaume Rousse

[+ del.icio.us] [+ Developers Zone] [+ Bookmarks.fr] [Digg this] [+ My Yahoo!]

Introduction

Nagios est un logiciel de supervision, c'est-à-dire un logiciel de surveillance du fonctionnement d'un ensemble de ressources informatique. Cette surveillance s'effectue par le biais de programmes externes, appelés greffons (plugins) dans la nomenclature consacrés. Ces programmes se divisent essentiellement en deux catégories :

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, de façon à 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.

Principe de base

Une sonde est un exécutable, invoqué régulièrement par le daemon 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 :

En sortie, elle retourne deux informations :

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 fichier, 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 crash récurrents, mais imprévisibles, du daemon 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 :

Cet exemple est minimaliste, dans la mesure où aucun paramètre n'est nécessaire, le test principal est simplissime, et il n'y 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...

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 performance. 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 programme 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 terme 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.

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 :

Au niveau architecture, ces fonctionnalités sont répartis entre plusieurs modules : Nagios::Plugin::Getopt gère la ligne de commande, 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 :

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 est remonté. Les exemples qui suivent utilisent l'une ou l'autre en fonction du besoin.

Surveillance d'agrégat d'interfaces réseaux

Cette sonde surveille l'état d'un agrégat d'interfaces réseaux (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 chacun 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 commande, 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'expression 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.

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'une 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'information à récupérer : après tout, le détail des entrées ne nous concerne pas, et il peut y en avoir beaucoup.

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 exemples 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 tache, et le nombre maximal de taches par imprimantes. 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.

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 le 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é.

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 resources 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 aux nombre de tests à exécuter, donc à la charge du serveur.

Néanmoins, comme pour mod_perl, l'utilisation de cet intepré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ée 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'interpé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'intepré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 oeuvre 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.

Notes

note1

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

note2

http://www.monitoringexchange.org

note3

http://www.monitoringexchange.org/cgi-bin/page.cgi?g=Detailed%2F2110.html;d=1

note4

http://articles.mongueurs.net/magazines/linuxmag75.html

note5

http://nagios.sourceforge.net/docs/3_0/epnplugins.html

[IE7, par Dean Edwards] [Validation du HTML] [Validation du CSS]