Tuesday, April 7, 2015

How to add interface descriptions to Zabbix



For those who are unsatisfied with default Zabbix discoveries. There is a great way to have both interface Name (i.e. Gi1/1/2) and Alias (i.e. Link to SuperServer) together in the name name of an item itself. This solution was tested on Cisco, Extreme Networks, Dlink and Force10 devices. It uses standard SNMP OID so probably would work on every device.

To make this type of discovery it is essential to use external source of discovered data. It can be done using any language, in this case I decided to use perl. The idea of a script is to get information from a device and return it in JSON format, like this:

{
    "data" : [
        {
            "{#ALIAS}" : "server-2",
            "{#NAME}" : "Gi1/0/7",
            "{#INDEX}" : "10107"
        },
        {
            "{#ALIAS}" : "server-1",
            "{#NAME}" : "Gi1/0/2",
            "{#INDEX}" : "10102"
        },
        {
            "{#ALIAS}" : "provider-1",
            "{#NAME}" : "Te1/1/2",
            "{#INDEX}" : "10402"
        }
    ]
}

For Zabbix to use the script it should be located in External scripts directory (check your Zabbix server conf file, it depends on distribution) and, of course, to have an executable bit set. Script should NOT return any additional data, except for a correct JSON. Then create new template with name "Network interfaces" or something like that, and add a discovery with the setting:


External check means that Zabbix will use a script from an external scripts folder;
Key is a name of a script and parameters. In this case there are two parameters: {HOST.IP}, which is global variable and will take the IP address from an interface of a host this template will attached to, and {$SNMP_COMMUNITY1} is a global macros, defined under Administration -> General -> Macros. As a result, the final command Zabbix will call will look something like "interface-discovery.pl 192.168.1.254 public"

Then add items to the new discovery, for Speed, traffic, statuses and so on. There are a lot of useful examples here:



In SNMP OID field you can use numeric OID, if you do not have MIBs installed. Good ideas to monitor:

IF-MIB::ifHCInOctets - 1.3.6.1.2.1.31.1.1.1.6 - incoming traffic
IF-MIB::ifHCOutOctets - 1.3.6.1.2.1.31.1.1.1.10 - outgoing traffic
IF-MIB::ifAdminStatus - 1.3.6.1.2.1.2.2.1.7 - admin status of interface
IF-MIB::ifAlias - 1.3.6.1.2.1.31.1.1.1.18 - description of interface
IF-MIB::ifOperStatus - 1.3.6.1.2.1.2.2.1.8 - operational status of interface
IF-MIB::ifInErrors - 1.3.6.1.2.1.2.2.1.14 - incoming errors on interface
IF-MIB::ifOutErrors - 1.3.6.1.2.1.2.2.1.20 - outgoing errors on interface

Another good suggestions you may find at cisco site.

Just add ".{#INDEX}" in the end of any OID. Here is a list of items I use for my system:





Create triggers with names that would actually mean something to you, and with expressions you like:





And voila! Newly created items will look like this:




New triggers with adequate names:





And, as a bonus, interface names and aliases on graphs:





Of course, descriptions change over time, but it is not a problem, actually. You just need to set up your discovery to run periodically. I use period of 1 day and it works great. Zabbix realizes that its not a new item but the old one with a new name based on the item key, and just changes name, preserving all the history. So this method will work if you will keep keys persistent. For example, I use interface name as a unique part of a key. After periodical discovery finishes, it will update name of items everywhere - in triggers and graphs.

And here is my script. I am not a programmer so it definitely has some problems. Your suggestions are welcome!


#!/usr/bin/perl

use warnings;
use strict;
use Data::Dumper;
use Net::SNMP;
use JSON;

open(STDERR, '>', '/tmp/int_results_error.log') or warn "Can't open log";

die("Usage: $0 ipaddress community\n") unless ( $#ARGV == 1 ); #we expect to see 2 arguments

my $oid_ifName = '1.3.6.1.2.1.31.1.1.1.1';
my $oid_ifAlias = '1.3.6.1.2.1.31.1.1.1.18';
my $oid_ifType = '1.3.6.1.2.1.2.2.1.3';

my ($session, $error) = Net::SNMP->session(
    -hostname => $ARGV[0],
    -community => $ARGV[1],
    -version => 'snmpv2c',
);

die("ERROR: $error\n") unless ( $session );

my $names = $session->get_table(-baseoid => $oid_ifName,);
die("ERROR getting names: ".$session->error()."\n") unless ( $names );
foreach ( keys %$names) {
    my $new_key;
    ( $new_key = $_ ) =~ s/.*\.//;
    $names->{ $new_key } = delete $names->{$_};
}

my $aliases = $session->get_table(-baseoid => $oid_ifAlias,);
die("ERROR getting aliases: ".$session->error()."\n") unless ( $aliases );
foreach ( keys %$aliases) {
    my $new_key;
    ( $new_key = $_ ) =~ s/.*\.//;
    $aliases->{ $new_key } = delete $aliases->{$_};
    $aliases->{ $new_key } =~ s/[^\x20-\x7E]//g;
}

my $types = $session->get_table(-baseoid => $oid_ifType,);
die("ERROR getting types: ".$session->error()."\n") unless ( $types );
foreach ( keys %$types) {
    my $new_key;
    ( $new_key = $_ ) =~ s/.*\.//;
    $types->{ $new_key } = delete $types->{$_};
}

my @interfaces;
foreach my $index ( keys %$names ) { #for each found port
    no warnings 'uninitialized';

    # hacks
    next if ( $names->{$index} =~ /--(Unc|C)ontrolled/ ); # hack to exempt 4500 strange interfaces
    next if ( $types->{$index} != 6 ); # exempt everything but physical interfaces (type = 6)
    next if ( $names->{$index} =~ /^NDE/ ); #hack to exempt NetFlow interfaces of c6k
    if ( $names->{$index} =~ /^(\d:|)\d{2}$/ ) {
        my $nport;
        ( $nport = $names->{$index} ) =~ s/^(\d:|)(\d+)$/$2/;
        next if ( $nport > 48); # hack to exempt ports 49-62 on extreme switches
    }
    # /hacks

    push(@interfaces, {"{#INDEX}" => $index, "{#NAME}" => $names->{$index}, "{#ALIAS}" => $aliases->{$index}});
}

print JSON::XS->new->utf8->pretty(1)->encode( { "data" => \@interfaces });

END { $session->close() if ( $session ); }

No comments:

Post a Comment