Étendre Net-SNMP en Perl

Sébastien Aperghis-Tramoni, sebastien@aperghis.net

Petits rappels

SNMP

  • Simple Network Management Protocol

  • protocole de requêtage réseau très simple

  • UDP(161), sans connexion, sans état

  • get, get-next, get-bulk, set, walk

  • clients et serveurs sont nommés "agents"

SNMP

  • s'appuie sur le standard de description ASN.1 (Abstract Syntax Notation One)

    • (en réalité, un sous-ensemble, SMI (Structure of Management Information))

  • valeurs scalaires :

    • entier générique, gauge, compteur, ticks, adresse IP, ID d'objet (OID), chaîne

  • tables multi-indexées

OID

  • "chemin" d'accès à un attribut

  • suite de nombres séparés par des points

    • .1.3.6.1.2.1.1.3

  • peut s'écrire sous forme de noms

    • .iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.0

  • chaque nom doit être unique

MIB

  • Management Information Base

  • définition à la fois de la hiérarchie et de la correspondance entre numéros et noms

  • MIB standards (RFC) fournissant de nombreuses définitions de base

  • MIB fournies par des constructeurs et éditeurs

    • .iso(1).org(3).dod(6).internet(1).private(4).enterprises(1)

  • possibilité de définir ses propres MIB

    • enregistrement gratuit auprès du IANA

Net-SNMP

  • le logiciel standard du monde Unix

    • (anciennement UCD-SNMP)

  • le Apache du SNMP

  • serveurs : snmpd, snmptrapd

  • clients : snmpget, snmpwalk, snmptrap

Net-SNMP

  • informations natives sur les interfaces réseau, processus, volumes, charges CPU et mémoire

  • ajout de hiérarchies d'OID

  • extension par de nombreux mécanismes :

    • exécution de commandes : exec, extend, pass, pass_persist

    • chargement de code : Perl embarqué, dlmod

    • communication avec d'autres agents : proxy, SMUX, AgentX

Net-SNMP

  • pass

  • pass_persist

  • AgentX

pass

  • pass MIBOID COMMAND

  • exécution de la commande pour chaque requête

  •   COMMAND -g OID    # get
      COMMAND -n OID    # get-next
    
      COMMAND -s OID TYPE VALUE  # set
  • facile à mettre en place

  • mais peu efficace en cas d'appels fréquents ou de commande lente

pass_persist

  • pass_persist MIBOID COMMAND

  • exécution de la commande lors de la première requête

  • communication avec Net-SNMP via stdin/stdout, par un petit protocole

  • meilleure tenue en charge

  • mais nécessité de gérer le protocole

SNMP::Extension::PassPersist

  • permet d'écrire simplement des extensions pass et pass_persist

  • conçu pour être utilisable immédiatement

  • peu de dépendances, faible consommation de ressources

  • fonctionnement :

    • ajout d'entrées (OID, type, valeur)

    • boucle d'exécution qui se charge du reste

    • possibilité de forker la partie mise à jour des valeurs

SNMP::Extension::PassPersist

  • synopsis typique pour un programme pass :

      #!/usr/bin/perl
      use strict;
      use SNMP::Extension::PassPersist;
    
      my $root_oid = ".1.3.6.1.4.1.32272";
    
      # create the object
      my $extsnmp = SNMP::Extension::PassPersist->new;
    
      # add a few OID entries
      $extsnmp->add_oid_entry("$root_oid.42.1", "integer", 42);
      $extsnmp->add_oid_entry("$root_oid.42.2", "string" , "the answer");
    
      # run the program
      $extsnmp->run;

SNMP::Extension::PassPersist

  • synopsis typique pour un programme pass_persist :

      #!/usr/bin/perl
      use strict;
      use SNMP::Extension::PassPersist;
    
      my $root_oid = ".1.3.6.1.4.1.32272";
    
      # create the object
      my $extsnmp = SNMP::Extension::PassPersist->new(
          backend_collect => \&update_tree,
      );
    
      # run the program
      $extsnmp->run;
    
      sub update_tree {
          my ($self) = @_;
    
          # add a few OID entries
          $self->add_oid_entry("$root_oid.42.1", "integer", 42);
          $self->add_oid_entry("$root_oid.42.2", "string" , "the answer");
      }

AgentX

  • protocole d'envoi des requêtes à un sous-agent externe

  • permet de décorréler les sous-agents du daemon Net-SNMP

  • typiquement exécutés eux-mêmes comme daemons

  • API disponible en Perl avec NetSNMP::Agent

    • documentation peu claire

    • propre boucle d'exécution

POE::Component::NetSNMP::agent

  • fine couche POE autour de NetSNMP::agent

  • services supplémentaires

POE::Component::NetSNMP::agent

  • comme avec NetSNMP::agent, mais compatible POE :

    •   my $agent = POE::Component::NetSNMP::agent->spawn(
            Alias   => "snmp_agent",
            AgentX  => 1,
        );
      
        $agent->register(".1.3.6.1.4.1.32272", \&agent_handler);
      
        POE::Kernel->run;
        exit;
      
        sub agent_handler {
            my ($kernel, $heap, $args) = @_[ KERNEL, HEAP, ARG1 ];
            my ($handler, $reg_info, $request_info, $requests) = @$args;
      
            # the rest of the code works like a classic NetSNMP::agent callback
            my $mode = $request_info->getMode;
      
            for (my $request = $requests; $request; $request = $request->next) {
                if ($mode == MODE_GET) {
                    # ...
                }
                elsif ($mode == MODE_GETNEXT) {
                    # ...
                }
                else {
                    # ...
                }
            }
        }

POE::Component::NetSNMP::agent

  • plus simple, le module gère les requêtes :

    •     POE::Session->create(
              inline_states => {
                  _start => sub {
                      $_[HEAP]{agent} = POE::Component::NetSNMP::agent->spawn(
                          Alias       => "snmp_agent",
                          AgentX      => 1,
                          AutoHandle  => ".1.3.6.1.4.1.32272",
                      );
                      $_[KERNEL]->yield("update_tree");
                  },
                  update_tree => \&update_tree,
              },
          );
      
          POE::Kernel->run;
          exit;
      
          sub update_tree {
              my ($kernel, $heap) = @_[ KERNEL, HEAP ];
      
              # next update in 30 sec
              $kernel->delay(update_tree => 30);
      
              # add one OID entry
              $kernel->post(AGENT_ALIAS, add_oid_entry =>
                  BASE_OID.".1", ASN_OCTET_STR, "hello");
      
              # add several OID entries at once
              $kernel->post(AGENT_ALIAS, add_oid_tree => {
                  BASE_OID.".2" => [ ASN_INTEGER, 42 ],
                  BASE_OID.".3" => [ ASN_COUNTER, 1873541 ],
                  BASE_OID.".4" => [ ASN_GAUGE, 235 ],
              });
          }

POE::Component::NetSNMP::agent

  • encore plus simple, on ne se charge plus que de mettre à jour les données :

        my $agent = POE::Component::NetSNMP::agent->new(
            AgentX      => 1,
            AutoHandle  => ".1.3.6.1.4.1.32272",
            AutoUpdate  => [[ \&update_tree, 30 ]],
        );
    
        $agent->run;
    
        sub update_tree {
            my ($self) = @_;
    
            # add one OID entry
            $self->add_oid_entry(BASE_OID.".1", ASN_OCTET_STR, "hello");
    
            # add several OID entries at once
            $self->add_oid_tree({
                BASE_OID.".2" => [ ASN_INTEGER, 42 ],
                BASE_OID.".3" => [ ASN_COUNTER, 1873541 ],
                BASE_OID.".4" => [ ASN_GAUGE, 235 ],
            });
        }

POE::Component::NetSNMP::agent

POE::Component::NetSNMP::agent

  • problèmes connus

    • utilise POE >:3

    • bug dans les POE::Loop standards en cas de déconnexion du serveur

      • utilisation de 100% CPU

      • palliatif : utiliser une meilleure boucle comme AnyEvent, EV, EPoll

AnyEvent ?

Questions ?

Merci