Advertisement
poodad

CDP N to description

May 28th, 2024
1,166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 16.92 KB | None | 0 0
  1. use strict;
  2. use Net::SNMP;
  3. use CGI qw/:standard/;
  4. use CGI::Carp qw(fatalsToBrowser);
  5. use Net::Telnet::Cisco;
  6. use DBI;
  7. use Win32::TieRegistry( Delimiter=>"/", "KEY_READ" );
  8. use Data::Dumper;
  9.  
  10. print "Content-type: text/html\n\n";
  11.  
  12. my $userid = remote_user();
  13. $userid = '' unless defined $userid;
  14. $userid =~ s/..*\\//;
  15. $userid = lc($userid);
  16. my $password = $ENV{AUTH_PASSWORD};
  17.  
  18. if ( param('switch') and param('text') ){
  19.     step3();
  20. } elsif (param('switch') ) {
  21.     step2();
  22. } else {
  23.     step1();
  24. }
  25.  
  26. exit 0;
  27.  
  28. ###############################################################################
  29. #
  30. # Draw the initial form
  31. #
  32. ###############################################################################
  33.  
  34. sub step1 {
  35.  
  36.  
  37.     my %netinv;
  38.     LoadNetInventory(\%netinv);
  39.  
  40.     my @switches = grep /^[A-Z]+[1-9][A-Z]S[0-9][0-9]$/i, keys %netinv;
  41.  
  42.     print start_form;
  43.     print "<p>Pick the switch: \n";
  44.  
  45.     print "<select name=switch>\n";
  46.     foreach my $switch (sort @switches) {
  47.         $switch = uc $switch;
  48.         print "<option value=\"$switch\">$switch\n";
  49.     }
  50.     print "</select>\n";
  51.     print '<p><input type=submit value="Next" name=button>';
  52.     print end_form;    
  53. }
  54.  
  55. ###############################################################################
  56. #
  57. #
  58. ###############################################################################
  59.  
  60. sub step2 {
  61.  
  62.     my $switch = uc param('switch');
  63.  
  64.     my $obj = new switch($switch);
  65.    
  66.     die "Could not create switch object for $switch\n" unless $obj;
  67.    
  68.     print "<h3>Switch: $switch</h3><p>\n";
  69.    
  70.     my @ports = $obj->LoadPorts();
  71.    
  72.     my %portchannel;
  73.  
  74.     my @lines;
  75.  
  76.     foreach my $ifIndex ( sort @ports ) {
  77.         my $port = $obj->GetPort($ifIndex);
  78.    
  79.         next if $port->name() =~ /^vl/i;            # skip vlans
  80.        
  81.         my $old_desc = $port->desc();
  82.         my $tmp = $old_desc;
  83.         my $new_desc = $old_desc;
  84.        
  85.         my $jack = "";
  86.         if ( $tmp =~ /^([1-9][A-Z][0-9][0-9])/) {                    # Core switches have jack numbers - don't lose it
  87.             $jack = $1;
  88.         }
  89.        
  90.         $new_desc = '' if $new_desc =~ /uplink.+e[0-9][0-9]/i;          # If old description looks like "Uplink to E54SJ01", it is no longer valid
  91.  
  92.         my $cdpn = $port->cdpn();
  93.         $cdpn =~ s/\..+//g;                                             # Get rid of domain name
  94.         $cdpn =~ s/\(..*\)//;
  95.        
  96.        
  97.         if ($cdpn) {
  98.             $new_desc = '';
  99.             $new_desc .= ' ' if $new_desc;
  100.             $new_desc .= "$jack $cdpn";
  101.             if ( $port->pagp() ) {
  102.                 my $name = $obj->GetPort( $port->pagp() )->name();
  103.                 $portchannel{$port->pagp()} = $new_desc if $port->pagp();
  104.                 $new_desc .= " (via $name)";
  105.             } else {
  106.                 #$new_desc .= " " . $port->cdpnport();
  107.             }
  108.            
  109.             $new_desc .= " " . $port->cdpnport() if $cdpn =~ /^vh/i;        # Add the port name if ESX server
  110.            
  111.         }
  112.  
  113.         $new_desc =~ s/^ //;
  114.        
  115.         if ($new_desc ne $old_desc) {
  116.             push @lines, "int " . $port->name();
  117.             push @lines, "! old description: $old_desc";
  118.             if ($new_desc) {
  119.                 push @lines, " description $new_desc";
  120.             } else {
  121.                 push @lines, " no description";
  122.             }
  123.             push @lines, "!";
  124.         }
  125.     }
  126.    
  127.     foreach my $ifIndex ( keys %portchannel ) {
  128.         next unless $portchannel{$ifIndex};
  129.         push @lines, "interface " . $obj->GetPort($ifIndex)->name();
  130.         push @lines, " description $portchannel{$ifIndex}";
  131.         push @lines, "!";
  132.     }
  133.  
  134.     my $lc = scalar @lines;
  135.  
  136.     print start_form;
  137.  
  138.     print qq(<p><textarea autofocus="autofocus" name="text" wrap="hard" rows="$lc" cols="80">\n);
  139.     print join "\n", @lines;
  140.     print qq(</textarea>\n);
  141.     print qq(<input type=hidden name="switch" value="$switch">\n);
  142.  
  143.     print '<p><input type=submit value="Apply Changes" name=button>';
  144.     print end_form;
  145.  
  146. }  
  147.  
  148.    
  149.  
  150. ###############################################################################
  151. #
  152. # Use telnet to send the IOS commands to implelemnt the desired changes
  153. #
  154. ###############################################################################
  155.  
  156. sub step3 {
  157.  
  158.     my $switch = param('switch');
  159.     my $text = param('text');
  160.     my $logfile = "c:/temp/cdpn_desc-$switch.log";
  161.     my $session = Net::Telnet::Cisco->new(Host => $switch, errmode => "return", Input_log=>$logfile);
  162.  
  163.     my $res = $session->login($userid, $password);
  164.     if( ! $res ) {
  165.         print "<font size=+2 color=red>ERROR: Logon failed for switch</font>\n";
  166.         DumpLog($logfile);
  167.         return;
  168.     }
  169.    
  170.     $res = $session->enable(Nunya(2));
  171.     if( ! $res ) {
  172.         print "<font size=+2 color=red>ERROR: enable failed for switch</font>\n";
  173.         $session->Close();
  174.         DumpLog($logfile);
  175.         return;
  176.     }
  177.  
  178.     # send commands
  179.  
  180.     my @lines = split /[\n\r]+/, $text;
  181.  
  182.     $session->cmd('config t');
  183.    
  184.     foreach my $line (@lines) {
  185.         $line =~ s/^ +//;
  186.         if( $line =~ /^int/ or $line =~ /^description/ or $line =~ /^no description/) {
  187.             $session->cmd($line);
  188.         }
  189.     }
  190.  
  191.     $session->cmd("exit");      # exit "interface ..."
  192.     $session->cmd("exit");      # exit "config t"
  193.    
  194.     $session->cmd("copy run start\n");
  195.  
  196.     # Show the user a transcript of the communication with the router.
  197.     DumpLog($logfile);
  198. }
  199.  
  200. ###############################################################################
  201. # Dump the log file to the user
  202. ###############################################################################
  203.  
  204. sub DumpLog {
  205.     my $file = shift;
  206.    
  207.     open FILE, $file or die "$file: $!\n";
  208.    
  209.     print "<b>Transcript of session with switch</b><p><pre>";
  210.    
  211.     while(<FILE>){
  212.         chomp;
  213.         next if /^username/i;
  214.         print "$_\n";
  215.     }
  216.    
  217.     print "</pre>\n";
  218.     close FILE;
  219. }
  220.  
  221.  
  222.  
  223. sub LoadNetInventory {
  224.  
  225. }
  226.  
  227.  
  228.  
  229. ################################################################################
  230. #
  231. # Switch object
  232. #
  233. ################################################################################
  234.  
  235. package switch;
  236.  
  237. sub new {
  238.     my $class = shift;
  239.     my $name = shift;
  240.     my $self  = {};
  241.  
  242.     bless ($self, $class);
  243.    
  244.     $self->{name} = $name;
  245.     $self->{vlans} = {};            # hash of VLAN objects indexed by VLAN number                
  246.     $self->{ports} = {};            # hash of port obects indexed by ifindex number
  247.     $self->{arp} = {};              # arp cache
  248.  
  249.    
  250.     # Open SNMP connection. Don't translate octet strings - Net::SNMP doesn't always get it right - we'll do it ourself
  251.  
  252.     my $community = "public";
  253.     my $timeout = 60;
  254.    
  255.     ($self->{snmp}, my $err) = Net::SNMP->session( -hostname => $name, -version => '2c', -community => $community, -timeout  => $timeout, -port => "161", -translate=>[-octetstring => 0]);
  256.  
  257.     if ( !defined($self->{snmp}) ) {
  258.         print STDERR "$name: SNMP open error: $err\n";
  259.         return undef;
  260.     }
  261.  
  262.     return $self;
  263. }
  264.  
  265. sub LoadPorts {                     # Grab ports, create port objects, populate, and index by ifindex
  266.     my $self = shift;
  267.  
  268.     # get the port names
  269.  
  270.     my $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.31.1.1.1.1", 1);     # ifName
  271.  
  272.     foreach my $ifindex ( keys %{$res} ) {                                     # entries will look like '234' => 'Gi6/39'
  273.         my $ifname = $res->{$ifindex};
  274.         $self->{ports}{$ifindex} = port->new($ifname);
  275.     }
  276.  
  277.     # port descriptions
  278.  
  279.     $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.31.1.1.1.18", 1);       # ifAlias
  280.  
  281.     foreach my $ifindex ( keys %{$res} ) {                                     # entries will look like '236' => 'Uplink to E54APG06'
  282.         my $desc = $res->{$ifindex};
  283.         $self->{ports}->{$ifindex}->desc($desc);
  284.     }
  285.    
  286.     # port mode (trunk or access)
  287.  
  288.     $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.46.1.6.1.1.14", 1);     # vlanTrunkPortDynamicStatus
  289.  
  290.     foreach my $ifindex ( keys %{$res} ) {
  291.         my $mode = $res->{$ifindex};
  292.         $self->{ports}->{$ifindex}->mode('Access') if $mode == 2;
  293.         $self->{ports}->{$ifindex}->mode('Trunk') if $mode == 1;
  294.     }
  295.  
  296.     # get port status
  297.     # ( 1=>'up', 2=>'down', 3=>'testing', 4=>'unknown', 5=>'dormant', 6=>'notPresent', 7=>'lowerLayerDown');
  298.  
  299.    
  300.     $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.2.2.1.8", 1);    
  301.  
  302.     foreach my $ifindex ( keys %{$res} ) {
  303.         my $mode = $res->{$ifindex};
  304.         $self->{ports}->{$ifindex}->status('up') if $mode == 1;
  305.         $self->{ports}->{$ifindex}->status('down') if $mode == 2;
  306.         $self->{ports}->{$ifindex}->status('testing') if $mode == 3;
  307.         $self->{ports}->{$ifindex}->status('unknown') if $mode == 4;
  308.         $self->{ports}->{$ifindex}->status('dormant') if $mode == 5;
  309.         $self->{ports}->{$ifindex}->status('notPresent') if $mode == 6;
  310.         $self->{ports}->{$ifindex}->status('lowerLayerDown') if $mode == 7;
  311.     }
  312.    
  313.     # CDP neighbor 
  314.    
  315.     $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.23.1.2.1.1.6", 2);
  316.    
  317.     foreach my $x ( keys %{$res} ) {
  318.         my ($ifIndex, $junk) = split /\./, $x;
  319.         $self->{ports}->{$ifIndex}->cdpn($res->{$x});
  320.     }
  321.  
  322.     # CDP neighbor port
  323.    
  324.     $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.23.1.2.1.1.7", 2);
  325.    
  326.     foreach my $x ( keys %{$res} ) {
  327.         my ($ifIndex, $junk) = split /\./, $x;
  328.         $self->{ports}->{$ifIndex}->cdpnport($res->{$x});
  329.     }
  330.  
  331.     # Get corresponding VLANs
  332.    
  333.     $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.68.1.2.2.1.2", 1);
  334.  
  335.     foreach my $ifindex ( keys %{$res} ) {                                    
  336.         my $vlan = $res->{$ifindex};
  337.         $self->{ports}->{$ifindex}->vlan($vlan);
  338.     }
  339.  
  340.     # Get any PAGP ports that this port is a member of
  341.    
  342.     $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.98.1.1.1.1.8", 1);
  343.  
  344.     foreach my $ifindex ( keys %{$res} ) {                                    
  345.         my $po = $res->{$ifindex};
  346.         $self->{ports}->{$ifindex}->pagp($po) unless $po == $ifindex;       # the ifindex of the port channel this interface is a member of
  347.     }
  348.    
  349.  
  350.     return keys %{$self->{ports}};
  351. }
  352.  
  353.  
  354. sub LoadArpCache {
  355.     my $self = shift;
  356.    
  357.     my $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.3.1.1.2", 4);
  358.  
  359.     foreach my $ip ( keys %{$res} ) {                                     # entries will look like '1.2.3.4' => '00112233445566'
  360.         my $mac = $res->{$ip};
  361.         $self->{arp}{$ip} = $mac;
  362.     }
  363.    
  364.     foreach my $i (keys %{$self->{arp}}) {
  365.         $self->{arp}->{$i} = unpack "H*", $self->{arp}->{$i};
  366.     }
  367.    
  368.     return keys %{$self->{arp}};
  369. }
  370.  
  371.  
  372. sub ArpEntry {
  373.     my $self = shift;
  374.     my $ip = shift;
  375.    
  376.     return $self->{arp}->{$ip} if exists $self->{arp}->{$ip};
  377.     return undef;
  378.    
  379. }
  380.  
  381.  
  382. sub GetPort {                   # Given an ifindex value, return port object
  383.     my $self = shift;
  384.     my $p = shift;
  385.    
  386.     if( ! $self->{ports}->{$p} ) {              # No port exists for this ifindex?
  387.         $self->{ports}->{$p} = port->new($p);   # create one - use the ifindex as the name  
  388.     }
  389.    
  390.     return $self->{ports}->{$p};
  391. }
  392.  
  393. sub GetVlan {                   # Given a VLAN number, return VLAN object
  394.     my $self = shift;
  395.     my $p = shift;
  396.    
  397.     return $self->{vlans}->{$p};
  398. }
  399.  
  400. sub LoadVlans {                 # Get a list of VLANs, create VLAN objects, populate, index by VLAN number
  401.     my $self = shift;
  402.    
  403.     # Get a list of the VLANs present on this switch and create a VLAN object
  404.    
  405.     my $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.46.1.3.1.1.2", 1); # vtpVlanState
  406.     foreach my $vlan ( keys %{$res} ) {
  407.         next unless $res->{$vlan};                                            # VLAN is not operational
  408.         $self->{vlans}->{$vlan} = vlan->new(($self->{name}, $vlan));          # Create a VLAN object
  409.     }
  410.      
  411.     # get VLAN descriptions
  412.    
  413.     $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.46.1.3.1.1.4.1", 1);    # vtpVlanName
  414.     foreach my $vlan ( keys %{$res} ) {                                            # entries will look like  '3' => '85175_wire_room_E'
  415.         $self->{vlans}->{$vlan}->desc($res->{$vlan});
  416.     }    
  417.    
  418.     return keys %{$self->{vlans}};
  419. }
  420.  
  421. package vlan;
  422.  
  423. sub new {
  424.     my $class = shift;
  425.     my $switch = shift;
  426.     my $vlan = shift;
  427.  
  428.     my $self  = {};
  429.  
  430.     bless ($self, $class);
  431.    
  432.     $self->{vlan} = $vlan;
  433.     $self->{switch} = $switch;
  434.  
  435.     $self->{bpindex} = undef;        # ifindex indexed by bridge port number
  436.     $self->{macindex} = undef;       # MAC addresses indexed by mac index
  437.     $self->{desc} = '';              # description
  438.     $self->{cam} = {};               # CAM table - ifIndexes indexed by MAC address
  439.    
  440.     # Open SNMP connection. Don't translate octet strings - Net::SNMP doesn't always get it right - we'll do it ourself
  441.    
  442.     my $community = "public";
  443.     my $timeout = 5;
  444.    
  445.     ($self->{snmp}, my $err) = Net::SNMP->session( -hostname => $switch, -version => '2c', -community => $community . '@' . $vlan, -timeout  => $timeout, -port => "161", -translate=>[-octetstring => 0]);
  446.  
  447.     if (!defined($self->{snmp})) {
  448.         print STDERR "swtich $switch VLAN $vlan SNMP open error: $err\n";
  449.         return undef;
  450.     }
  451.  
  452.     return $self;
  453. }
  454.  
  455. sub LoadCam {                       # Grab the CAM table, translate bridge ports to ifindex, and translate MAC indexes to MAC addresses
  456.     my $self = shift;
  457.  
  458.     $self->{bpindex} = util::tablehash($self->{snmp}, "1.3.6.1.2.1.17.1.4.1.2", 1);        # dot1dBasePortIfIndex bridgeport to ifindex xref
  459.  
  460.     $self->{macindex} = util::tablehash($self->{snmp}, "1.3.6.1.2.1.17.4.3.1.1", 6);       # dot1dTpFdbAddress  MAC index to MAC
  461.  
  462.     # MAC addresses in this table are in binary because we told Net::SNMP not to translate. Translate them now
  463.  
  464.     foreach my $i (keys %{$self->{macindex}}) {
  465.         $self->{macindex}->{$i} = unpack "H*", $self->{macindex}->{$i};
  466.     }
  467.  
  468.     # Grab the CAM table for this VLAN
  469.  
  470.     my $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.17.4.3.1.2", 6);     # dot1dTpFdbPort
  471.     foreach my $macindex ( keys %{$res} ) {
  472.         if( ! $self->{macindex}->{$macindex} ) {            # No entry for this MAC index
  473.             my @tmp = split /\./, $macindex;                # Build a MAC address
  474.             $self->{macindex}->{$macindex} = join "", @tmp; # save it
  475.         }
  476.  
  477.         my $mac = $self->{macindex}->{$macindex};
  478.         my $bp = $res->{$macindex};
  479.        
  480.         if( ! $self->{bpindex}->{$bp} ) {                   # No ifindex exists for this bridgeport - this happens when a MAC address is learned via the ARP table, or some other way
  481.              $self->{bpindex}->{$bp} = '(other)';           # build it
  482.         }        
  483.        
  484.         my $ifindex = $self->{bpindex}->{$bp};
  485.         $self->{cam}->{$mac} = $ifindex;
  486.     }
  487.  
  488.     return keys %{$self->{cam}};
  489. }
  490.  
  491. sub GetCam {                        # Given a MAC address, return the ifindex for the port it was learned on
  492.     my $self = shift;
  493.     my $p = shift;
  494.     return $self->{cam}->{$p};
  495. }
  496.  
  497. sub desc {                          # for a given VLAN number, return the description
  498.     my $self = shift;
  499.     my $p = shift;
  500.    
  501.     $self->{desc} = $p if defined $p;
  502.     return $self->{desc};
  503. }
  504.  
  505. package port;
  506.  
  507. sub new {
  508.     my $class = shift;
  509.     my $name = shift;
  510.  
  511.     my $self = {};
  512.    
  513.     bless ($self, $class);
  514.  
  515.     $self->{name} = $name;
  516.     $self->{desc} = '';
  517.     $self->{mode} = '';
  518.     $self->{cdpn} = '';
  519.     $self->{status} = '';
  520.     $self->{vlan} = '';
  521.     $self->{pagp} = '';
  522.    
  523.     return $self;
  524. }
  525.  
  526. sub pagp {
  527.     my $self = shift;
  528.     my $p = shift;
  529.    
  530.     $self->{pagp} = $p if defined $p;
  531.     return $self->{pagp};
  532. }
  533.  
  534. sub status {
  535.     my $self = shift;
  536.     my $p = shift;
  537.    
  538.     $self->{status} = $p if defined $p;
  539.     return $self->{status};
  540. }
  541.  
  542.  
  543. sub cdpn {
  544.     my $self = shift;
  545.     my $p = shift;
  546.    
  547.     $self->{cdpn} = $p if defined $p;
  548.     return $self->{cdpn};
  549. }
  550.  
  551. sub cdpnport {
  552.     my $self = shift;
  553.     my $p = shift;
  554.    
  555.     $self->{cdpnport} = $p if defined $p;
  556.     return $self->{cdpnport};
  557. }
  558.  
  559. sub vlan {
  560.     my $self = shift;
  561.     my $p = shift;
  562.    
  563.     $self->{vlan} = $p if defined $p;
  564.     return $self->{vlan};
  565. }
  566.  
  567. sub mode {
  568.     my $self = shift;
  569.     my $p = shift;
  570.    
  571.     $self->{mode} = $p if defined $p;
  572.     return $self->{mode};
  573. }
  574.  
  575.  
  576. sub desc {
  577.     my $self = shift;
  578.     my $p = shift;
  579.    
  580.     $self->{desc} = $p if defined $p;
  581.     return $self->{desc};
  582. }
  583.  
  584. sub name {
  585.     my $self = shift;
  586.     my $p = shift;
  587.    
  588.     $self->{name} = $p if defined $p;
  589.     return $self->{name};
  590. }
  591.  
  592.  
  593. package util;
  594.  
  595. ################################################################################
  596. #
  597. # Build a hash from an SNMP table. The key is created by taking the last $n
  598. # elements of the returned OID value.
  599. #
  600. ################################################################################
  601.  
  602. sub tablehash {
  603.     my $snmp = shift;
  604.     my $oid = shift;
  605.     my $n = shift;
  606.    
  607.     my $ret = ();
  608.    
  609.     my $res = $snmp->get_table(-baseoid => $oid, -maxrepetitions  => 30);
  610.    
  611.     foreach my $x ( keys %{$res} ) {
  612.         my @tmp = split /\./, $x;
  613.         my $key = join ".", splice(@tmp, -1 * $n);
  614.         $ret->{$key} = $res->{$x};
  615.     }
  616.    
  617.     return $ret;
  618. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement