(WS11-05) SNMP-basiertes Management des WieDAS-AAL-Caches

Aus Verteilte Systeme - Wiki
Zur Navigation springen Zur Suche springen

Einleitung

Dieses Wiki beschreibt, wie der WieDAS AAL-Cache um eine SNMP Schnittstelle erweitert wurde.

Begriffserklärungen und Aufgabenstellung

Der AAL Cache ist ein in C++ geschriebener Key/Value Speicher, an dem sogenannte Konnektoren als Shared Object Libraries angebunden werden können. Diese Konnektoren liefern die Schnittstelle zu den unterschiedlichsten Datenquellen und -senken. Der Cache an sich ist der Vermittler dieser Stellen.

Um den Cache zu überwachen und zu steuern, soll nun eine Management-Schnittstelle eingeführt werden. Im Rahmen dieser Lehrveranstaltung soll diese Schnittstelle mittels SNMP, dem Simple Network Management Protocol, angesprochen werden. Siehe auch: Wikipedia-Artikel.

Analyse

SNMP

Die einzige SNMP Implementierung, die in Betracht kommt, ist die unter der BSD Lizenz stehende Net-SNMP Software Suite, die für GNU/Linux bereitsteht. Andere Implementierungen sind kommerziell oder nicht ausgereift.

Art der Implementierung

Um eine möglichst einfach erweiterbare und modulare Lösung mittels net-snmp zu bauen, habe ich mich dafür entschieden, einen AgentX Subagenten zu schreiben. Dabei fungiert der snmp Deamon als sogenannter Master. AgentX lautet das Protokoll, welches zwischen Master und Subagent gesprochen wird. Subagenten können sich dynamisch zur Laufzeit beim Master anmelden und für sich anmelden, bestimmte Zweige des OID Baums zu kennen. Daraufhin werden Anfragen auf diesen Agenten weitergeleitet.

Eine Alternative wäre es gewesen, snmp selbst zu kompilieren und zur Compilezeit Code zu liefern, der für die entsprechenden OIDs zuständig gewesen wäre. In Anbetracht der Tatsache, dass das Zielgerät eventuell embedded ist, wollte ich mir den Ärger ersparen, möglicherweise auf Probleme mit entsprechender Paketierung oder der Toolchain zu stoßen. So lege ich schlicht fest, dass es net-snmp auf der Zielplattform gibt (eine faire Annahme). Die Portierung eines Subagenten macht diese Problemstellung etwas handhabbarer.

AAL Cache

Kernkomponente des Caches ist treffenderweise die Klasse Core. Sie hält ein Array von Inodes, in der die Nutzlast gespeichert wird. Hier werden außerdem die Konnektoren geladen und hier befinden sich die Methoden, mit denen ein Konnektor letzlich arbeitet. Diese sprichen Konnektoren aber nicht direkt an, sondern über eine API, die in einem C-Header formuliert ist, welche dann die Calls an die C++ Klasse weiterleitet.

Desgin

API

Teil der Aufgabenstellung ist es, sich Gedanken zu machen, welche Art von Managementaufrufen nützlich sein könnten. Hier eine Auflistung:

  • Speicherbedarf: Jede Inode kann beliebig Speicher anfordern, um Key und/oder Value speichern zu können. Dieser Speicherbedarf kann gemessen werden.
  • Zugriffszähler: Lese und Schreibzugriffe werden bereits intern mitgezählt.
  • Konnektoren/Manager-Management: Wieviele Konnektoren/Manager sind geladen, wie ist deren Status?
  • Inode-Verwaltung: Wieviele Inodes sind verfügbar, wieviele sind schon belegt?
  • Traps: Managementmodule sollen zu bestimmten Ereignissen benachrichtigt werden:
    • Keine freien INodes mehr (Verdrängung aktiv?)
    • Speicherbedarf überschreitet bestimmten Grenzwert

Buffering

Die der API zur Verfügung stehenden Daten sollen gepuffert werden. Dazu sollen Timestamps und Lebensdauer eingeführt werden. Die Management-API soll festlegen können, wie lange gepufferte Daten gültig sein sollen. Ist die Gültigkeit abgelaufen, sollen alle Daten aktualisiert weden.

Einbindung in AAL Cache

Die Management-API soll von den Konnektoren unabhängig arbeiten. Die Ideen der Konnektor-API werden aber teilweise übernommen: Manager werden als Shared Objects geladen und starten ggf. einen oder mehrere Threads, um ihre Arbeit zu erledigen. Über eine Management-API in C wird dieselbe Flexibilität gewährleistet, möglichst viele unterschiedliche Manager zulassen zu können, wie bei den Konnektoren.

Implementierung

Schreiben einer MIB

Die Management Information Base ist das Interface, nach dem SNMP Clients gehen können, um Anfragen an einen entsprechenden Server stellen zu können. Sie beschreibt den Unterbaum, jedes einzelne Blatt (OID) mit seinem Zweck, Rückgabetyp, Zugriffsrechten, etc. Clients könnten auch ohne diese MIB den ganzen Baum eines Servers mittels snmpwalk abfragen, würden dann jedoch nur die OIDs und den Wert sehen, ohne zu wissen, was diese repräsentieren.

Die MIB des Projekts lautet AAL-CACHE-MIB, und ihr Hauptknoten namens aalmanagement ist unter enterprises als Knoten Nr. 12 eingeordnet. (Siehe dazu den MIB Tree in der Wikipedia: http://de.wikipedia.org/w/index.php?title=Datei:SNMP.MIB-Tree.PNG&filetimestamp=20060614172655). Sollte diese MIB offiziell registriert werden sollen, so muss eine freie Knotennummer gewählt werden.

Baumstruktur (fett sind Knoten, normal sind Blätter):

  • aalmanagement
    • aalStats
      • aalInfo
      • aalReadCount
      • aalWriteCount
      • aalDataMemoryUsage
      • aalURIMemoryUsage
      • aalCurrentDataTimestamp
      • aalTotalInodes
      • aalUnusedInodes
      • aalConnectorCount
      • aalManagerCount
      • aalManagerBufferTimeout
    • aalNotifications
      • aalInodeCapacityReached

Die genaue Beschreibung kann in der MIB-Datei nachgelesen werden.

MIB Modul zu C Code

mib2c ist ein Perlskript, welches C-Schablonencode aus MIB-Definitionen erzeugt. Um es benutzten zu können, muss die MIB-Datei in

  • ~/.snmp/mibs oder
  • /usr/local/share/snmp/mibs

liegen.

Um Code für Skalare Felder zu erzeugen, verwende ich folgenden Aufruf:

env MIBS="+AAL-CACHE-MIB" mib2c -c mib2c.scalar.conf aalmanagement </code>

Für die Notifications gilt:

env MIBS="+AAL-CACHE-MIB" mib2c -c mib2c.notify.conf aalNotifications </code>

Es werden je eine <knotenname>.c und <knotenname>.h Datei erzeugt, die an den mit XXX und FIXME gekennzeichneten Stellen noch ausgefüllt werden müssen. Eine <knotenname>_init() Funktion muss zur Initialisierung aufgerufen werden. Damit werden die anderen Funktionen als Callbackfunktionen für die entsprechenden OIDs beim Master Agent registriert.

Subagenten schreiben

Wie ein eigenständiger Subagent geschrieben werden kann ist hier beschrieben: http://www.net-snmp.org/wiki/index.php/TUT:Writing_a_Subagent

Die Ideen aus diesem Tutorial wurden angepasst, um eine Shared Library zu schreiben, der dieselben Initialisierungsarbeiten vornimmt. Neben der Subagent-Initialisierung müssen die Initialiserungsfunktionen der von den mib2c erzeugen Module aufgerufen werden, damit snmpd weiß, für welche Teile des MIB Baums der Subagent sich verantwortlich fühlt.

Danach reicht es für GET und SET Operationen, den Agenten in einer whileschleife laufen zu lassen:

  while(1){
    agent_check_and_process(1); // Blocking == 1;
  }

Dieser Methodenaufruf kümmert sich um alle eingehenden Anfragen und ruft die entsprechenden Callbackfunktionen in den von mib2c generierten Modulen auf. Das reicht jedoch nicht aus, um Traps zu verschicken. Diese können mit einem einzigen Methodenaufruf aus dem von mib2c erzeugten Modul ausgelöst werden. Net-SNMP ist jedoch nicht vollständig multithreading-fähig. So kann eine Applikation mehrere Threads haben, solange sich nur ein einziger Thread um die Arbeit mit SNMP kümmert. Die trap-Funktionen nicht vom selben Thread aus zu bedienen die oben genannte whileschleife führt zu Segmentation Faults.

Um nun innerhalb dieses Whileschleifen-Threads Agent-seitige Nachrichten wegzuschicken, benutze ich eine nichtblockierende, unidirektionale Pipe und select(). Mittels der von net-snmp mitgelieferten Funktion snmp_select_info() lässt sich ein fd_set befüllen. Select() kann nun auf eingehende SNMP-Calls reagieren. Mittels snmp_read() werden fd_sets, die zu SNMP gehören, abgearbeitet. Zusätzlich kann aber nun auch noch auf den Filedeskriptor der Leserichtung der Pipe gewartet werden. Ein externer Aufruf, dass bitte ein Trap gesendet werden soll, befüllt die Pipe von einer Seite mit einem Byte. Auf der Gegenseite reagiert select(), der entsprechende Filedeskriptor wird erkannt und die Trap ausgelöst. Diese ganze Arbeit macht die Schleife nun bedeutend größer.

    while (1) {
      bool scheduleTrap = false;
      int fds = 0, block = 1;
      struct timeval timeout;
      fd_set fdset;
      FD_ZERO(&fdset);
      snmp_select_info(&fds, &fdset, &timeout, &block);		  
      /* Fuege noch die Pipe hinzu */
      FD_SET(pipefd[0], &fdset);
      fds++;
      fds = select(fds, &fdset, NULL, NULL, block ? NULL : &timeout);	
      if(fds){
        /* Zuerst nach der Pipe sehen und ggf. entfernen */
        if(FD_ISSET(pipefd[0], &fdset)){
          AALC_DBG("read from pipe %d", pipefd[0]);
          /* Pipe lesen und Trap schicken */
          size_t readRetVal;
          char buf;
          readRetVal = read(pipefd[0], &buf, 1);
          int erno = errno;
          if(readRetVal == 0 && erno != 0){
            AALC_ERR("Read from pipe failed: %d", erno);					
          }
          scheduleTrap = true;
          FD_CLR(pipefd[0], &fdset);
        }
        AALC_DBG("Check SNMP FDs");
        /* snmp-deskriptoren abarbeiten lassen */
        snmp_read(&fdset);
        if(scheduleTrap){
          AALC_DBG("read from pipe %d successful. Sending trap", pipefd[0]);
          send_aalInodeCapacityReached_trap();
          AALC_DBG("trap sent");
          }else{
          snmp_timeout();
      }
    }
    /* at shutdown time */
    close(pipefd[0]);
    close(pipefd[1]);
    snmp_shutdown("snmp_conn");
    SOCK_CLEANUP;
    return NULL;
  }

API definieren & implementieren

Die API zur Bedienung von Managementmodulen ist stark an die von Konnektoren angelehnt und befindet sich in der aal_mngtapi.h. Wie in der aal_connapi.c/h wird eine Struktur definiert, die die Core-Klasse lädt und damit die Initialisierung des Moduls auslöst. Die API definiert eine Reihe von Managementfunktionen, welche in der Coreklasse implementiert wurden.

Test

Aufbau einer Testumgebung (Ubuntu Server 10.04 LTS)

Hier wird von einem Ubuntu Server auf dem aktuellen Stand ausgegangen. Zuerst werden der SNMP Deamon und die Development Libraries installiert.

sudo apt-get install snmp snmpd libboost libsnmp-dev libsnmpkit-dev vim


snmpd.conf

Nun muss die Konfigurationsdatei des SNMP Deamons angepasst werden:

sudo vim /etc/snmp/snmpd.conf

Die folgenden Einträge können/müssen geändert werden:

agentaddress udp:161 Optional, um snmpd auf allen Netzwerkdevices lauschen zu lassen und nicht nur lokal im Netzwerk.

 com2sec readonly  default         public
 com2sec readwrite default         private
 rocommunity public
 rwcommunity private

Andere com2sec Einträge sollten auskommentiert werden, damit auf unseren Teil des MIB-Baums zugegriffen werden kann.

 master  agentx

Die Kommentierung muss hier entfernt werden, damit snmpd als AgentX Master agiert.

 agentXPerms  0660 0550 jan jan

Für jan hier den usernamen eintragen, der den AgentX Subagenten starten wird.

/etc/default/snmpd

/etc/default/snmpd ist das Skript, über das snmpd als service ausgeführt wird. Hier muss folgender Eintrag geändert werden:

 SNMPDOPTS='-Lsd -Lf /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid 127.0.0.1'

zu

 SNMPDOPTS='-Lsd -Lf /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid'

Optional. Wenn man via Internet den Dienst aufrufen will, muss dieser Schritt vorgenommmen werden.


SNMPD neustarten

sudo service snmp restart

AAL Cache

Code muss aus den Quellen geholt, compiliert und gestartet werden. Läuft -.-

Testaufruf

Die AAL-CACHE-MIB wird vom Makefile des SNMP Moduls automatisch in ~/.snmp/mibs kopiert. Deshalb sollte nun folgender beispielhafter Aufruf funktionieren:

  snmpget -v2c -c public localhost AAL-CACHE-MIB::aalDataMemoryUsage.0

Gebrauchsanleitungen

Wie man ein Management-Modul baut

Wie die Konnektoren, so sind auch die Managementmodule shared libraries, welche zur Laufzeit im AAL-Cache geladen werden. Damit dieser das erfolgreich tun kann, muss folgendes getan werden.

  • Es muss im Code ein Struct des Typs aalc_mngt vorliegen (siehe auch "aalc_mngtapi.h") und ausgefüllt worden sein.
  • In der Datei "management.conf" muss ein Eintrag für das Modul hinzugefügt werden. Dieser hat die Form

<Pfad zur .so> <Symbolname des aalc_mngt Structs> <Konfigurationsstring>

Ein Dummy liegt im Verzeichnis "dummy_mngt" bereit, welches man einfach kopieren und beliebig erweitern kann.

Wie man das SNMP Management-Modul erweitert

  • Die im Modulverzeichnis liegende AAL-CACHE-MIB modifizieren
  • "make mibfiles" ausführen
  • Den vorhandenen Code um den neu erzeugen Code (im Unterverzeichnis "generated") erweitern.
  • "make all" ausführen

Zusammenfassung der Änderungen

AAL-Cache

AAL-Core Klasse

  • Erweiterung um Managementfunktionen
  • Eine Queue und einen Threadloop zum Feuern von Callbacks an die Manager
  • Eigene Ladefunktion für Shared Objects

aalc_mngtapi cpp/h (neu)

C-Header und C++ Implementation, um die Management-API nach draußen anzubieten.

management.conf

Eigene Liste zum Nachladen von Managementmodulen (analog zu connectors.conf)

SNMP_MNGT

Neu angelegtes Verzeichnis, beinhaltet SNMP sprechendes Managementmodul

  • AAL-CACHE-MIB eine "Arbeits-MIB", welche modifiziert werden kann und aus der mittels Makefile C-Code generiert wird
  • aalmanagement.c/h und aalNotifications.c/h Fertig ausgefüllter Code aus einem mib2c Aufruf via Makefile
  • Makefile
  • snmp_conn.c/h Kern des Moduls. Initialisiert alles Nötige um SNMP herum und arbeitet in einem Thread die Callbacks des SNMP Masters ab.

Dummy_MNGT

Dummymodul, wie eine Minimalimplementation eines Managementmoduls aussehen könnte

Fazit, Was zu tun bleibt

  • Je nach Wunsch können Management und Konnektorfunktionen miteinander verschmolzen werden, um Platz/Duplikate zu sparen (bspw. die Callback-Threads in der Core-Klasse). Dies ist nicht geschehen, damit man das Management separat mit Ressourcen (Threads) versehen kann.