Perl and LLDP

Dynamic discovery of switches and even mapping.  I will write a better post around this and clean up my code to repost, but this is getting posted quickly to share.

#!/usr/bin/perl

#########################################################
# To run 'qmap.pl <switch name>'                        #
# Written by: Josh Reeley reeley at reeleysoft dot com  #
#########################################################
use Net::SNMP;
#use GraphViz;
#use Term::ReadPassword;

#
#    Debug Flags
#    0 == off, 1 == on
#

my $lldpDebug = 1;

#
#    Pic Print Flag
#    0 == off, 1 == on
#

my $picprint = 0;

#
#   OIDs
#

$sysVendor = "1.3.6.1.2.1.1.2.0";
$sysLoc = "1.3.6.1.2.1.1.6.0";

#   Vendor serial numbers
$serial = "";
$HPserial = "1.3.6.1.4.1.11.2.36.1.1.2.9.0";
$CSserial = "1.3.6.1.4.1.9.3.6.3.0";
$FNserial = "1.3.6.1.4.1.1991.1.1.1.1.2.0";
#$lldpinfo = "1.0.8802.1.1.2.1.4.1.1.4";
$lldpRemChassisId = '1.0.8802.1.1.2.1.4.1.1.5';
#lldpRemChassisId.0.1.1 = mrn01sw002-L1(000d9d-c17280)
#lldpRemChassisId.0.3.1 = 00 11 85 a8 00 00
$lldpRemPortId    = '1.0.8802.1.1.2.1.4.1.1.7';
#lldpRemPortId.0.1.1 = 43
#lldpRemPortId.0.3.1 = 1
$lldpRemPortDesc  = '1.0.8802.1.1.2.1.4.1.1.8';
#lldpRemPortDesc.0.1.1 =
#lldpRemPortDesc.0.3.1 = 1
$lldpRemSysName   = '1.0.8802.1.1.2.1.4.1.1.9';
#lldpRemSysName.0.1.1 =
#lldpRemSysName.0.3.1 = matt-1
$lldpLocPortDesc  = '1.0.8802.1.1.2.1.3.7.1.4';

#
#   Local Variables
#


#hp MIB info:
#
# 1.0.8802.1.1.2.1.4.1.1.4
#lldpRemChassisIdSubtype.0.143.1 = 4
#lldpRemChassisId.0.143.1 = 00 12 f2 1e 06 c0
#lldpRemPortIdSubtype.0.143.1 = 3
#lldpRemPortId.0.143.1 = 00 12 f2 1e 06 c1
#lldpRemPortDesc.0.143.1 = GigabitEthernet9/2
#lldpRemSysName.0.143.1 = bd-grainger
#lldpRemSysDesc.0.143.1 =
#lldpRemSysCapSupported.0.143.1 = (
#lldpRemSysCapEnabled.0.143.1 = (

#
#   Global Variables
#

my %switch = (); # The main hash
my $hostname = "";
$hostname = shift(@ARGV); # The hostname read in
if ($hostname eq "") {
  print "Please input the hostname of the switch you would like data about\n",
  "\n",
  "For Example: $0 sw-josh\n";
  exit;
 }
sub switchinfo;

#
#   Reading in the vendor model information from file
#

open (DATA, "<", "/Users/reeley/Progs/switchmodels.csv") or die $!;
my %model;
%model = ( %model, (split(/,/, $_))[0,1] ) while ( <DATA> );
close DATA;

#
#   Setting the community string
#

my $comstring = "private";
#my $comstring = read_password("SNMP Read: ");
#my $comstring = `head ${csvdir}switchinfo`;
#$comstring =~ s/[A-Z0-9z]//g;
#$comstring =~ s/\s*//g;

#
#   Getting Host information
#

# require host and community string as arguments
($session,$error) = Net::SNMP->session( Hostname => $hostname,
Community => $comstring);
die "session error: $error" unless ($session);

$loc_result = $session->get_request("$sysLoc");
die "request error: ".$session->error unless (defined $loc_result);

$vendor_result = $session->get_request("$sysVendor");
die "request error: ".$session->error unless (defined $vendor_result);

@vend_arr = split(/\./, $vendor_result->{"$sysVendor"});

if($vend_arr[6] == 11){$serial = $HPserial;}
if($vend_arr[6] == 9){$serial = $CSserial;}
if($vend_arr[6] == 1991){$serial = $FNserial;}

#$ser_result = $session->get_request("$serial");
#die "request error: ".$session->error unless (defined $ser_result);

#
#   Getting LLDP information from the host
#

my $remoteDevice = $session->get_table("$lldpRemChassisId");
die "request error: ".$session->error unless (defined $remoteDevice);

my $remotePortNum = $session->get_table("$lldpRemPortId");
die "request error: ".$session->error unless (defined $remotePortNum);

my $remotePortDesc = $session->get_table("$lldpRemPortDesc");
die "request error: ".$session->error unless (defined $remotePortDesc);

my $remoteSysName = $session->get_table("$lldpRemSysName");
die "request error: ".$session->error unless (defined $remoteSysName);

my $localPortDesc = $session->get_table("$lldpLocPortDesc");
die "request error: ".$session->error unless (defined $localPortDesc);

$session->close;

#
#   Adding info to the %switch hash
#

#
#   Adding remote name to the hash indexed by the local devices port index
#

my $localid;
my @firstids = ();
foreach $key (sort (keys (%$remoteSysName))) {
  @temp = split(/\./,$key);
  $localid = $temp[12];
  push(@firstids, $localid);
  $switch{$hostname}{$localid}{'remotename'} = $remoteSysName->{$key}
}

#
#   Adding remote port index to the hash indexed by the local devices port index
#

for (keys %$remotePortNum) {
  @temp = split(/\./,$_);
  $localid = $temp[12];
  $switch{$hostname}{$localid}{'remoteportid'} = $remotePortNum->{$_};
}

#
#   Adding remote port (pretty name) to the hash indexed by the local devices port index
#

my $remotename; # a temp holder to clean up the name
for (keys %$remotePortDesc) {
  @temp = split(/\./,$_);
  $localid = $temp[12];
  #   checking to see if the name contains GigabitEthernet and replacing it with Gi if it does
  $remotename = $remotePortDesc->{$_};
  $remotename =~ s/GigabitEthernet/Gi/;
  $switch{$hostname}{$localid}{'remoteport'} = $remotename; #$remotePortDesc->{$_};
}

#
#   Adding local port (pretty name) to the hash indexed by the local devices port index
#

my $localname; # a temp holder to clean up the name
for (keys %$localPortDesc) {
  @temp = split(/\./,$_);
  $localid = $temp[11];
  #   checking to see if the name contains GigabitEthernet and replacing it with Gi if it does
  $localname = $localPortDesc->{$_};
  $localname =~ s/GigabitEthernet/Gi/;
  $switch{$hostname}{$localid}{'localname'} = $localname #$localPortDesc->{$_}
}

#
#   Printing how things connect from this host
#
#$sortfirstids = sort @firstids;
@firstids = sort(@firstids);
#print @firstids;
foreach (@firstids) {
  if ($switch{$hostname}{$_}{'remotename'})  {
    print $switch{$hostname}{$_}{'remotename'} . " " .
    $switch{$hostname}{$_}{'remoteport'} . " connects to " .
    $hostname . " " .
    $switch{$hostname}{$_}{'localname'} . "\n";
  }
}

#
#   Print system information about this host
#
sub switchinfo {
  if($model{ $vendor_result->{$sysVendor}}) {
    print "The System Model is : ".$model{ $vendor_result->{$sysVendor}};
  }else{
    print "The System Model is : Unknown, please add it to ${csvdir}switchmodel.csv\n";
    print "The System ID is: ".  $vendor_result->{"$sysVendor"}. "\n";
  }
  print "The System location is : ".$loc_result->{"$sysLoc"}. "\n";
  print "The Serial Number is : ".$ser_result->{"$serial"}. "\n";
}

#
#   Trying to create diagram
#

if ($picprint == 1) {

  my $g = GraphViz->new(rankdir => 1, overlap => 'orthoyx');

  $g->add_node($hostname, shape => 'box');

  foreach (@firstids) {
    $g->add_node($switch{$hostname}{$_}{'remotename'}, shape => 'box');
    $g->add_edge($hostname => $switch{$hostname}{$_}{'remotename'}, taillabel => $switch{$hostname}{$_}{'localname'}, headlabel => $switch{$hostname}{$_}{'remoteport'}, constraint => 1, arrowhead => 0);
  }

  print $g->as_jpeg("test.jpg");
}

#
#   lldp Debug info
#

$lldpDebug = 1;
if ($lldpDebug == 1) {
 print "\n------------------------------------------------------------\n";
 print "| Remote Device ID (mac-addresses)                         |\n";
 print "------------------------------------------------------------\n";
 print "$remoteDevice->{$_} -- $_\n" for keys %$remoteDevice;

 print "\n------------------------------------------------------------\n";
 print "| Remote Device Port Numbers                               |\n";
 print "------------------------------------------------------------\n";
 print "$remotePortNum->{$_} -- $_\n" for keys %$remotePortNum;

 print "\n------------------------------------------------------------\n";
 print "| Remote Device Port Name                                  |\n";
 print "------------------------------------------------------------\n";
 print "$remotePortDesc->{$_} -- $_\n" for keys %$remotePortDesc;

 print "\n------------------------------------------------------------\n";
 print "| Remote Device Hostname                                   |\n";
 print "------------------------------------------------------------\n";
 print "$remoteSysName->{$_} -- $_\n" for keys %$remoteSysName;

 print "\n------------------------------------------------------------\n";
 print "| Connection Logic (Spelled Out)                           |\n";
 print "------------------------------------------------------------\n";
 print "$localPortDesc->{$_} -- $_\n" for keys %$localPortDesc;
 print "\n\n";

 foreach $key (keys %$remoteSysName) {
  @temp = split(/\./, $key);
  push(@lpid,$temp[12]);
 }
 print "xxxxxxxxxxxxxxxxx";
 print @lpid;
 print "xxxxxxxxxxxxxxxxx\n";
}

print "xxxxxxxxxxxxxxxxx";
print @lpid;
print "xxxxxxxxxxxxxxxxx\n";
}

 

 

You will want a file that has kids in it too so that you can get more info out about the switches the format of that file would look like:

1.3.6.1.4.1.2636.1.1.1.2.31,Juniper_EX4200_24F
1.3.6.1.4.1.2636.1.1.1.2.44,Juniper_EX4500

and so forth…  sorry it is not cleaned up.  I will work on that later, but it worked nicely for me.

 

Be Sociable, Share!

Leave a Reply