Netzpläne als Weathermaps

Dokumentation ist das Stiefkind der IT. Aktuelle Doku is jedoch das Rückrat erfolgreicher Projekte. Wie hält man Dokumentation aktuell? In dem man sie freiwillig benutzt, weil sie einen Mehrwert abwirft. Netzplänen sollten also nicht nur das "Was ist" dokumentieren, sondern auch das "Was geschieht".

Admin kontra Managment

Typischerweise benutzt man zur Beantwortung der Frage, was denn da im Netz geschieht Trafficgraphen und für den groben Überblick eine Weathermap. Installation und Aufbereitung solcher Systeme ist ziemlich aufwendig. Wenn man sie schon hat, will man möglichst komplette Informationen an einer Stelle abrufen können.

Darüberhinaus benutzt man für die Projektdokumentation gern vereinfachende AdHoc Darstellungen, die schnell und auf Zuruf z.B. mit Visio erstellt werden. Diese verstauben dann in den Projektunterlagen, bis irgend ein Manager ein seit Monaten veraltetes Dokument in den Händen hält, um damit neue Planungen vorzunehmen und Entscheidungen zu fällen.

Obwohl Visio mächtig genug ist, die Dokumentation auch mit externen Datenquellen zu animieren, weigert sich der Netzwerkadmin, diesem Tool direkten Zugriffe auf die Netzwerkknoten zu erlauben. Außerdem müßte der Zugriff dann auch beim Kunden oder auf Präsentationen funktionieren.

Andererseits ist das Netzwerkmonitoring der Admins für den Manager zu komplex und zu mächtig. Endkunden will man gar nicht alle Details des realen Netzes zeigen. Schon gar nicht will man einen externen Zugriff des Endkunden auf die Managementsysteme selbst.

Einfach erstellt, einfach genutzt

Was liegt also näher, die Visio-Dokumente, die für den Endkunden erstellt wurden, auf eine Supportwebseite zu legen und dieser Webseite zu erlauben, die Graphik zu animieren? Zum einen sind die Dokumente nicht webtauglich und andererseits will man auf den Webseiten keine Office-Software aktiv laufen lassen.

Glücklicherweise kann Visio nach SVG exportieren. Und dieser Output ist hervorragend maschinell nachbearbeitbar. Mittlerweile ist SVG auch browserseitig ausreichend unterstützt, um dort bedenkenlos den Einsatz freizugeben. Die Anfangszeiten, bei denen extra Plugins benötigt wurden, sind glücklicherweise vorbei.

<g id="shape69-50" v:mID="69" v:groupContext="shape" v:layerMember="2"
     transform="translate(276.378,-523.305)">
 <title>Dynamic connector.69</title>
 <desc>G0/2</desc>
 <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
 <v:textRect cx="21.292" cy="602.833" width="40" height="17.6036"/>
 <path d="M0 601.45 L336.41 603.27" class="st1" />
 <rect v:rectContext="textBkgnd" x="12.2454" y="598.034"
         width="17.3436" height="9.59985" class="st5"/>
 <text x="12.62" y="605.23" class="st6" v:langID="1031">
   <v:paragraph v:horizAlign="1"/>
   <v:tabList/>
   G0/2
  </text>
</g>

Um aus einem Visio-SVG eine Weathermap zu machen, müssen die manuell angelegten Verbinder eingefärbt werden. Je nach Auslastung der Leitung ändern sich die Farben von einem leichten Grün bis zu einem kräftigen Rot. (Hier mit einer anderen Linie.)

 <path d="M-6.07 595.28 L-8.11 806.72" class="st1" style='stroke:red'/>
netzplan-linie1

Da Leitungen in beide Richtungen Daten transportieren können, müssen die Verbinderlinien doppelt eingefärbt werden. Weathermaps lösen dies, in dem sie zwei Pfeile statt einer Verbindung einzeichnen und diese Pfeile separate einfärben. Schöner wäre es aber, wenn der Verbinder selbst seine Farbe ändern könnte. Glücklicherweise beherscht SVG Gradienten, um lineare Farbverläufe vorzugeben.

<linearGradient id="gruen-rot">
 <stop offset="0" style="stop-color:#00ff00;stop-opacity:1"/>
 <stop offset="1" style="stop-color:#ff0000;stop-opacity:1"/>
</linearGradient>
[...]
<path d="M-6.07 595.28 L-8.11 806.72" class="st1" style='stroke:url(#gruen-rot)'/>

Unglücklicherweise ist der Farbverlauf streng horizontal, so daß diese steile Linie den Farbwechsel nur in ihrer Breite durchmacht. Der Farbverlauf hätte eigentlich der Linie folgen sollen. Alle Versuchen mit Transformationen den Farbverlauf zu beeinflussen, waren vergeblich. Denn der Farbverlauf kann und muß separat gedreht werden.

<linearGradient id="gruen-rot" gradientTransform="rotate(90)">
 <stop offset="0" style="stop-color:#00ff00;stop-opacity:1"/>
 <stop offset="1" style="stop-color:#ff0000;stop-opacity:1"/>
</linearGradient>

Jetzt verlaufen die Farben wie gewünscht der Linie entlang.

Für jede Linie ist nun ein extra Gradient anzulegen. Dabei ist darauf zu achten, daß der Verlauf der Definition der Linie folgt, damit man später automatisiert die Leitungsauslastungen als Farbwerte an den Endstellen einsetzen kann. Die Zeichenrichtung im Visio hat wenig mit der Zeichenrichtung im SVG zu tun. Dies kann also komplexere Transformationen erfordern, denn eine Rotation um 180° dreht den Farbverlauf komplett aus dem Liniensegment heraus. Mit einer Matixoperation kann man den Verlauf gleich wieder an die richtige Stelle schieben.

Darüberhinaus empfiehlt es sich, kleine Ansätze an den Verbindern stehen zu lassen, um keinen so abrupten Farbwechsel am Gerät zu bekommen. Man setzt dazu seine Start- und Endwerte erst bei 10% vorm Ende. Da die Farbwerte später ersetzt werden sollen, muß bereits hier vermerkt werden, welches Interface an welchem Gerät zum Einsatz kommen soll.

<!-- device:interface max_bandwith -->
<linearGradient id="line13" gradientTransform="matrix(-1,0,0,-1,1,1)">
 <stop offset="0" style="stop-color:#000000;stop-opacity:1"/>
 <stop offset=".1" style="stop-color:#00ff00;stop-opacity:1"/>
 <stop offset=".9" style="stop-color:#ff0000;stop-opacity:1"/>
 <stop offset="1" style="stop-color:#000000;stop-opacity:1"/>
</linearGradient>

Es kann aber passieren, daß die Umfärbung völlig scheitert: Die Linie verschwindet ganz. Dies passiert, wenn eine Linie exakt horizontal oder vertikal liegt. Dann ist die Höhe oder Breite der Linie exakt Null und die Gradientenoperation bricht mit einer Division durch Null ab. In dem Fall muß man einfach einen Endpunkt der Linie um ein winziges Stück verschieben. (Wieder eine andere Linie.)

<path d="M0 588.19 L222.79 588.20" class="st1" style='stroke:url(#line3)'/>

Alles zusammen sollte sich dann ein Bild ergeben, bei dem klar ersichtlich ist, welche Meßstelle die Daten liefern wird. Diese wurde hier beschriftet und muß das grüne Ende des Verbinders erhalten. Damit sind die Voraussetzungen für eine maschinelle Überarbeitung geschaffen.

netzplan-animiert

Besonders hübsch ist, wie der Farbverlauf auch den gebogenen Pfaden folgt.

Diese manuellen Anpassungen des Visio-Exportes sind in einer guten halben Stunde zu erledigen. Mit etwas Übung geht dauert es nur einige Minuten. Komplexere Netzpläne oder ewig wiederkehrende Exports kann man automatisieren.

Die SVG Graphik kann nur auf einem beliebigen Webserver als statische Datei abgelegt werden (Mime-Type: image/svg+xml). Die URL kann zur Abnahme herumgeschickt werden.

Leben einhauchen

Zur Weathermap mutiert dieses Bild dann, indem es von einem Perl/PHP/Whatever-Script überarbeitet wird. Dieses holt sich bei Bedarf die notwendigen Daten aus einer Datenbank (MRTG, etc.) oder live von den Geräten. Der Farbwert wechselt gemäß rgb(255*load, 255*(1-load), 0) für die Hin- und Rückrichtung des Links.

Es spricht überhaupt nichts dagegen, weitere Angaben mit der Graphik zu verknüpfen. So kann das Symbol der aktiven Technik zwischen grau und rot umgefärbt werden, um die CPU-Last zu visualisieren. Ebenso bietet es sich an, die Speicherauslastung als "Füllhöhe" der Füllfarbe anzuzeigen. Wer noch Ideen hat, möge sie in die Kommentare schreiben oder einfach selbst umsetzen.

Die Implementation auf dem Webserver kann als PHP Datei die Daten, z.B. per SSH von einem zu SNMP berechtigten Host, holen und die SVG Datei vor der Ausliefung überarbeiten. Der SNMP-Host liefert, z.B. per command="/usr/local/sbin/obtain-line-rates.pl customer", die aktuellen Input- und Outputraten ausschließlich für die hinterlegten Geräte und Interfaces.

#! /usr/bin/perl

use strict;
use warnings;

my %points = ();

if($ARGV[0] eq 'customer') {
    %points = (
        'S1' => ['Port-channel32', 'Port-channel1', 'GigabitEthernet4/3', 'GigabitEthernet1/3', 'GigabitEthernet1/2'],
        'R1' => ['GigabitEthernet0/0', 'GigabitEthernet0/1', 'GigabitEthernet0/3'],
        'R2' => ['GigabitEthernet0/0', 'GigabitEthernet0/1', 'GigabitEthernet0/2'],
        'R3' => ['GigabitEthernet0/0', 'GigabitEthernet0/1', 'GigabitEthernet0/2'],
    );
} else {
    die "Usage: $0 name\n";
}

my %conf = ();
# Fill the config with the SNMP communities

# Read in the interface Name-ID mapping
my %ids = ();
foreach my $host (keys %conf) {
    open(S, "snmpbulkwalk -v2c -c '$conf{$host}' -t 2 -r 2 -L o: $host IF-MIB::ifDescr |") || next;
    while(<S>) {
        next unless my ($id,$val) = /\.([^\s.]+) = \S+: (.*\S)/;
        next unless grep {$_ eq $val} @{$points{$host}};
        $ids{$host}{$id} = $val;
    }
    close(S);
}

# Read the line rates and print them
while(my($host,$hh) = each %ids) {
    my @m = ((map {"1.3.6.1.4.1.9.2.2.1.1.6.$_"}  keys %$hh),
             (map {"1.3.6.1.4.1.9.2.2.1.1.8.$_"} keys %$hh));
    my $mc = $#m + 1;
    my $ms = join(" ", sort @m);

    my %c = ();

    open(S, "snmpget -v2c -c '$conf{$host}' -t 2 -r 2 -L o: $host $ms |") || next
    while(<S>) {
        next unless my ($dir,$id,$val) = /(\d+)\.([^\s.]+) = \S+: (.*\S)/;
        next unless defined $hh->{$id};
        $c{$hh->{$id}}{$dir} = $val;
    }
    close(S);

    while(my($name,$vals) = each %c) {
        print "$host:$name ".$vals->{6}," ".$vals->{8}."\n";
    }
}

Mein PHP Script zur Überarbeitung der Graphik ist dann ebenfalls ziemlich einfach:

<?
$unknown_color = '909090'; // Make links grey if no data is available
$svg_file = 'path/to/plan.svg';

// Read the current in/out bandwith values
$max_bandwidth = 1;
$max_bandwidth = max($max_bandwidth, $match[2], $match[3]);
$traf = popen("ssh -i path/to/sshkey user@snmp-host true", "r");
while($line = fgets($traf)) {
    if(preg_match('/^(\S+:\S+) (\d+) (\d+)$/', $line, $match)) {
        $inrate [$match[1]] = $match[2];
        $outrate[$match[1]] = $match[3];
        $max_bandwidth = max($max_bandwidth, $match[2], $match[3]);
    }
}
pclose($traf);

header("Content-Type: image/svg+xml");

$svg = fopen($svg_file, "r");
while($line = fgets($svg)) {
    if(preg_match('/<!-- (\S+:\S+) (\d+) -->/', $line, $match)) {
        $current_interface = $match[1];
        $current_speed = $match[2];
        if(is_numeric($_REQUEST['maxscale']))
          $current_speed *= $_REQUEST['maxscale'];
        elseif($_REQUEST['maxscale'] == 'max')
          $current_speed = min($max_bandwidth, $current_speed);
    }
    if(preg_match('/<linearGradient id="(\S+)"/', $line, $match)) {
        $current_gradient = $match[1];
    }
    if($current_speed > 0 &&
       preg_match('/^(\s+<stop offset=".(\d)" style="stop-color:#)\S{6}(;.*)/', $line, $match)) {
        if(isset($inrate[$current_interface])) {
            $util = (1.0*$inrate[$current_interface])/$current_speed;
            $incolor = sprintf("%02x%02x00", 255*$util, 255*(1-$util));
            $inline[$current_gradient] = sprintf("%02d%% %.2fMbps", $util*100, $inrate[$current_interface]/1000000.0);
        } else {
            $incolor = $unknown_color;
            $inline[$current_gradient] = "n/a";
        }
        if(isset($outrate[$current_interface])) {
            $util = (1.0*$outrate[$current_interface])/$current_speed;
            $outcolor = sprintf("%02x%02x00", 255*$util, 255*(1-$util));
            $outline[$current_gradient] = sprintf("%02d%% %0.2fMbps", $util*100, $outrate[$current_interface]/1000000.0);
        } else {
            $outcolor = $unknown_color;
            $outline[$current_gradient] = "n/a";
        }
        switch($match[2]) {
         case  1: print "$match[1]$outcolor$match[3]\n"; break;
         case  9: print "$match[1]$incolor$match[3]\n"; break;
         default: print $line; break;
        }
    } elseif(preg_match('/^\s*<title>Dynamic connector[.\d]*<\/title>\s*$/', $line)) {
        // Skip output of title
    } elseif(preg_match('/style=.stroke:url\(#(\S+)\)./', $line, $match) && isset($inline[$match[1]])) {
        print $line;
        print "\t\t\t<title>In: {$inline[$match[1]]}\nOut: {$outline[$match[1]]}</title>\n";
    } else {
        print $line;
    }
}
fclose($svg);
?>

Fazit

Kundenspezifische Views wurden mit dem Monitoringsystem verknüpft, so daß alle Beteiligte einen Vorteil davon haben:

  • Der Kunde kann seinen Teil des Netzes anhand der für ihn erstellen Dokumentation live einsehen.
  • Der Admin bekommt einen Einblick der Kundensicht und kann bei Wartungen die Auswirkungen auf den Kunden überprüfen.
  • Der Admin kann - wenn live verknüpft - eine aktuelle Weathermap eines beschränkten Bereiches während einer Umbaus offen halten. Die Dokumentation wurde beispielsweise adhoc für diesen Umbau erstellt und enthält so nur die interessanten Teile.
  • Der Support kann auf einen für den Kunden relevanten Teil der Netzstruktur schauen, während er mit dem Kunden telefoniert.
  • Da die Dokumentation auf der Supportwebseite liegt, kann sie nicht in veralteter Form im Mailpostfach eines Vertrieblers oder in den "Eigenen Dokumenten" eines Managers versauern.

Es gibt also durchaus Gründe, Netze zu dokumentieren und diese Dokumentation aktuell zu halten: Sie aktiv zu nutzen.

Avatar
Lutz Donnerhacke 02.03.2013 00:12
Tote Leitungen kann man prima mit -0 (siehe auch den Beitrag zu "Minus Null") signalisieren. Wenn die Leitung "down" ist, ist der eingehende Datenstrom "-0". Bei "admin-down" ist es der ausgehende Datenstrom.

Für die entsprechenden Einfärbungen bietet sich ein fast weißer Grauton an. Die Leitung wirkt dadurch am "lokalen" oder "entfernten" Ende unterbrochen.
Avatar
Lutz Donnerhacke 02.03.2013 00:09
Mehr bunt! Der Verlauf von grün nach rot ist zu wenig kontrastreich. Man sollte bei 50% Last über blau gehen:

function makecolor($util) {
if($util < .5) {
$util *= 2;
return sprintf("00%02x%02x", 255*(1-$util), 255*$util);
} else {
$util = 2*($util - 0.5);
return sprintf("%02x00%02x", 255*$util, 255*(1-$util));
}
}
Avatar
Lutz Donnerhacke 26.02.2013 12:01
Der Hack zur Anpassung der Leitungslast wurde mittlerweile so häufig benutzt, daß der Wunsch nach einer Automatisierung des Hacks auftrat.

Nun kann die PHP Datei auch mit dem Parameter ?maxscale=max aufgerufen werden, was die maximale Bandbreite als Obergrenze der Leitungsauslastung festlegt.

Dies ist z.B. nützlich, wenn in einem Netz der Spantree umgeschwenkt werden soll. Mit der automatischen Skalierung sieht man schnell, was geschieht.
Avatar
Lutz Donnerhacke 25.02.2013 13:39
Kleiner Hack am Rande, um so zu tun, als seien die Leitungen nicht ganz so dick, wie sie sind.
---
$current_speed = $match[2];
+ if($_REQUEST['maxscale'])
+ $current_speed *= $_REQUEST['maxscale'];
---

Und nun kann man mit http://service.host/kunden/trafficplan.php?maxscale=.5 alle Leitungen auf die halbe Kapazität herunterrechnen und so in schwach ausgelasteten Netzen auch mal Farbänderungen sehen.

4 Kommentare

Post a comment

Verwandter Inhalt