Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use strict;
- use Net::SNMP;
- use CGI qw/:standard/;
- use CGI::Carp qw(fatalsToBrowser);
- use Net::Telnet::Cisco;
- use DBI;
- use Win32::TieRegistry( Delimiter=>"/", "KEY_READ" );
- use Data::Dumper;
- print "Content-type: text/html\n\n";
- my $userid = remote_user();
- $userid = '' unless defined $userid;
- $userid =~ s/..*\\//;
- $userid = lc($userid);
- my $password = $ENV{AUTH_PASSWORD};
- if ( param('switch') and param('text') ){
- step3();
- } elsif (param('switch') ) {
- step2();
- } else {
- step1();
- }
- exit 0;
- ###############################################################################
- #
- # Draw the initial form
- #
- ###############################################################################
- sub step1 {
- my %netinv;
- LoadNetInventory(\%netinv);
- my @switches = grep /^[A-Z]+[1-9][A-Z]S[0-9][0-9]$/i, keys %netinv;
- print start_form;
- print "<p>Pick the switch: \n";
- print "<select name=switch>\n";
- foreach my $switch (sort @switches) {
- $switch = uc $switch;
- print "<option value=\"$switch\">$switch\n";
- }
- print "</select>\n";
- print '<p><input type=submit value="Next" name=button>';
- print end_form;
- }
- ###############################################################################
- #
- #
- ###############################################################################
- sub step2 {
- my $switch = uc param('switch');
- my $obj = new switch($switch);
- die "Could not create switch object for $switch\n" unless $obj;
- print "<h3>Switch: $switch</h3><p>\n";
- my @ports = $obj->LoadPorts();
- my %portchannel;
- my @lines;
- foreach my $ifIndex ( sort @ports ) {
- my $port = $obj->GetPort($ifIndex);
- next if $port->name() =~ /^vl/i; # skip vlans
- my $old_desc = $port->desc();
- my $tmp = $old_desc;
- my $new_desc = $old_desc;
- my $jack = "";
- if ( $tmp =~ /^([1-9][A-Z][0-9][0-9])/) { # Core switches have jack numbers - don't lose it
- $jack = $1;
- }
- $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
- my $cdpn = $port->cdpn();
- $cdpn =~ s/\..+//g; # Get rid of domain name
- $cdpn =~ s/\(..*\)//;
- if ($cdpn) {
- $new_desc = '';
- $new_desc .= ' ' if $new_desc;
- $new_desc .= "$jack $cdpn";
- if ( $port->pagp() ) {
- my $name = $obj->GetPort( $port->pagp() )->name();
- $portchannel{$port->pagp()} = $new_desc if $port->pagp();
- $new_desc .= " (via $name)";
- } else {
- #$new_desc .= " " . $port->cdpnport();
- }
- $new_desc .= " " . $port->cdpnport() if $cdpn =~ /^vh/i; # Add the port name if ESX server
- }
- $new_desc =~ s/^ //;
- if ($new_desc ne $old_desc) {
- push @lines, "int " . $port->name();
- push @lines, "! old description: $old_desc";
- if ($new_desc) {
- push @lines, " description $new_desc";
- } else {
- push @lines, " no description";
- }
- push @lines, "!";
- }
- }
- foreach my $ifIndex ( keys %portchannel ) {
- next unless $portchannel{$ifIndex};
- push @lines, "interface " . $obj->GetPort($ifIndex)->name();
- push @lines, " description $portchannel{$ifIndex}";
- push @lines, "!";
- }
- my $lc = scalar @lines;
- print start_form;
- print qq(<p><textarea autofocus="autofocus" name="text" wrap="hard" rows="$lc" cols="80">\n);
- print join "\n", @lines;
- print qq(</textarea>\n);
- print qq(<input type=hidden name="switch" value="$switch">\n);
- print '<p><input type=submit value="Apply Changes" name=button>';
- print end_form;
- }
- ###############################################################################
- #
- # Use telnet to send the IOS commands to implelemnt the desired changes
- #
- ###############################################################################
- sub step3 {
- my $switch = param('switch');
- my $text = param('text');
- my $logfile = "c:/temp/cdpn_desc-$switch.log";
- my $session = Net::Telnet::Cisco->new(Host => $switch, errmode => "return", Input_log=>$logfile);
- my $res = $session->login($userid, $password);
- if( ! $res ) {
- print "<font size=+2 color=red>ERROR: Logon failed for switch</font>\n";
- DumpLog($logfile);
- return;
- }
- $res = $session->enable(Nunya(2));
- if( ! $res ) {
- print "<font size=+2 color=red>ERROR: enable failed for switch</font>\n";
- $session->Close();
- DumpLog($logfile);
- return;
- }
- # send commands
- my @lines = split /[\n\r]+/, $text;
- $session->cmd('config t');
- foreach my $line (@lines) {
- $line =~ s/^ +//;
- if( $line =~ /^int/ or $line =~ /^description/ or $line =~ /^no description/) {
- $session->cmd($line);
- }
- }
- $session->cmd("exit"); # exit "interface ..."
- $session->cmd("exit"); # exit "config t"
- $session->cmd("copy run start\n");
- # Show the user a transcript of the communication with the router.
- DumpLog($logfile);
- }
- ###############################################################################
- # Dump the log file to the user
- ###############################################################################
- sub DumpLog {
- my $file = shift;
- open FILE, $file or die "$file: $!\n";
- print "<b>Transcript of session with switch</b><p><pre>";
- while(<FILE>){
- chomp;
- next if /^username/i;
- print "$_\n";
- }
- print "</pre>\n";
- close FILE;
- }
- sub LoadNetInventory {
- }
- ################################################################################
- #
- # Switch object
- #
- ################################################################################
- package switch;
- sub new {
- my $class = shift;
- my $name = shift;
- my $self = {};
- bless ($self, $class);
- $self->{name} = $name;
- $self->{vlans} = {}; # hash of VLAN objects indexed by VLAN number
- $self->{ports} = {}; # hash of port obects indexed by ifindex number
- $self->{arp} = {}; # arp cache
- # Open SNMP connection. Don't translate octet strings - Net::SNMP doesn't always get it right - we'll do it ourself
- my $community = "public";
- my $timeout = 60;
- ($self->{snmp}, my $err) = Net::SNMP->session( -hostname => $name, -version => '2c', -community => $community, -timeout => $timeout, -port => "161", -translate=>[-octetstring => 0]);
- if ( !defined($self->{snmp}) ) {
- print STDERR "$name: SNMP open error: $err\n";
- return undef;
- }
- return $self;
- }
- sub LoadPorts { # Grab ports, create port objects, populate, and index by ifindex
- my $self = shift;
- # get the port names
- my $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.31.1.1.1.1", 1); # ifName
- foreach my $ifindex ( keys %{$res} ) { # entries will look like '234' => 'Gi6/39'
- my $ifname = $res->{$ifindex};
- $self->{ports}{$ifindex} = port->new($ifname);
- }
- # port descriptions
- $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.31.1.1.1.18", 1); # ifAlias
- foreach my $ifindex ( keys %{$res} ) { # entries will look like '236' => 'Uplink to E54APG06'
- my $desc = $res->{$ifindex};
- $self->{ports}->{$ifindex}->desc($desc);
- }
- # port mode (trunk or access)
- $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.46.1.6.1.1.14", 1); # vlanTrunkPortDynamicStatus
- foreach my $ifindex ( keys %{$res} ) {
- my $mode = $res->{$ifindex};
- $self->{ports}->{$ifindex}->mode('Access') if $mode == 2;
- $self->{ports}->{$ifindex}->mode('Trunk') if $mode == 1;
- }
- # get port status
- # ( 1=>'up', 2=>'down', 3=>'testing', 4=>'unknown', 5=>'dormant', 6=>'notPresent', 7=>'lowerLayerDown');
- $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.2.2.1.8", 1);
- foreach my $ifindex ( keys %{$res} ) {
- my $mode = $res->{$ifindex};
- $self->{ports}->{$ifindex}->status('up') if $mode == 1;
- $self->{ports}->{$ifindex}->status('down') if $mode == 2;
- $self->{ports}->{$ifindex}->status('testing') if $mode == 3;
- $self->{ports}->{$ifindex}->status('unknown') if $mode == 4;
- $self->{ports}->{$ifindex}->status('dormant') if $mode == 5;
- $self->{ports}->{$ifindex}->status('notPresent') if $mode == 6;
- $self->{ports}->{$ifindex}->status('lowerLayerDown') if $mode == 7;
- }
- # CDP neighbor
- $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.23.1.2.1.1.6", 2);
- foreach my $x ( keys %{$res} ) {
- my ($ifIndex, $junk) = split /\./, $x;
- $self->{ports}->{$ifIndex}->cdpn($res->{$x});
- }
- # CDP neighbor port
- $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.23.1.2.1.1.7", 2);
- foreach my $x ( keys %{$res} ) {
- my ($ifIndex, $junk) = split /\./, $x;
- $self->{ports}->{$ifIndex}->cdpnport($res->{$x});
- }
- # Get corresponding VLANs
- $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.68.1.2.2.1.2", 1);
- foreach my $ifindex ( keys %{$res} ) {
- my $vlan = $res->{$ifindex};
- $self->{ports}->{$ifindex}->vlan($vlan);
- }
- # Get any PAGP ports that this port is a member of
- $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.98.1.1.1.1.8", 1);
- foreach my $ifindex ( keys %{$res} ) {
- my $po = $res->{$ifindex};
- $self->{ports}->{$ifindex}->pagp($po) unless $po == $ifindex; # the ifindex of the port channel this interface is a member of
- }
- return keys %{$self->{ports}};
- }
- sub LoadArpCache {
- my $self = shift;
- my $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.3.1.1.2", 4);
- foreach my $ip ( keys %{$res} ) { # entries will look like '1.2.3.4' => '00112233445566'
- my $mac = $res->{$ip};
- $self->{arp}{$ip} = $mac;
- }
- foreach my $i (keys %{$self->{arp}}) {
- $self->{arp}->{$i} = unpack "H*", $self->{arp}->{$i};
- }
- return keys %{$self->{arp}};
- }
- sub ArpEntry {
- my $self = shift;
- my $ip = shift;
- return $self->{arp}->{$ip} if exists $self->{arp}->{$ip};
- return undef;
- }
- sub GetPort { # Given an ifindex value, return port object
- my $self = shift;
- my $p = shift;
- if( ! $self->{ports}->{$p} ) { # No port exists for this ifindex?
- $self->{ports}->{$p} = port->new($p); # create one - use the ifindex as the name
- }
- return $self->{ports}->{$p};
- }
- sub GetVlan { # Given a VLAN number, return VLAN object
- my $self = shift;
- my $p = shift;
- return $self->{vlans}->{$p};
- }
- sub LoadVlans { # Get a list of VLANs, create VLAN objects, populate, index by VLAN number
- my $self = shift;
- # Get a list of the VLANs present on this switch and create a VLAN object
- my $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.46.1.3.1.1.2", 1); # vtpVlanState
- foreach my $vlan ( keys %{$res} ) {
- next unless $res->{$vlan}; # VLAN is not operational
- $self->{vlans}->{$vlan} = vlan->new(($self->{name}, $vlan)); # Create a VLAN object
- }
- # get VLAN descriptions
- $res = util::tablehash($self->{snmp}, "1.3.6.1.4.1.9.9.46.1.3.1.1.4.1", 1); # vtpVlanName
- foreach my $vlan ( keys %{$res} ) { # entries will look like '3' => '85175_wire_room_E'
- $self->{vlans}->{$vlan}->desc($res->{$vlan});
- }
- return keys %{$self->{vlans}};
- }
- package vlan;
- sub new {
- my $class = shift;
- my $switch = shift;
- my $vlan = shift;
- my $self = {};
- bless ($self, $class);
- $self->{vlan} = $vlan;
- $self->{switch} = $switch;
- $self->{bpindex} = undef; # ifindex indexed by bridge port number
- $self->{macindex} = undef; # MAC addresses indexed by mac index
- $self->{desc} = ''; # description
- $self->{cam} = {}; # CAM table - ifIndexes indexed by MAC address
- # Open SNMP connection. Don't translate octet strings - Net::SNMP doesn't always get it right - we'll do it ourself
- my $community = "public";
- my $timeout = 5;
- ($self->{snmp}, my $err) = Net::SNMP->session( -hostname => $switch, -version => '2c', -community => $community . '@' . $vlan, -timeout => $timeout, -port => "161", -translate=>[-octetstring => 0]);
- if (!defined($self->{snmp})) {
- print STDERR "swtich $switch VLAN $vlan SNMP open error: $err\n";
- return undef;
- }
- return $self;
- }
- sub LoadCam { # Grab the CAM table, translate bridge ports to ifindex, and translate MAC indexes to MAC addresses
- my $self = shift;
- $self->{bpindex} = util::tablehash($self->{snmp}, "1.3.6.1.2.1.17.1.4.1.2", 1); # dot1dBasePortIfIndex bridgeport to ifindex xref
- $self->{macindex} = util::tablehash($self->{snmp}, "1.3.6.1.2.1.17.4.3.1.1", 6); # dot1dTpFdbAddress MAC index to MAC
- # MAC addresses in this table are in binary because we told Net::SNMP not to translate. Translate them now
- foreach my $i (keys %{$self->{macindex}}) {
- $self->{macindex}->{$i} = unpack "H*", $self->{macindex}->{$i};
- }
- # Grab the CAM table for this VLAN
- my $res = util::tablehash($self->{snmp}, "1.3.6.1.2.1.17.4.3.1.2", 6); # dot1dTpFdbPort
- foreach my $macindex ( keys %{$res} ) {
- if( ! $self->{macindex}->{$macindex} ) { # No entry for this MAC index
- my @tmp = split /\./, $macindex; # Build a MAC address
- $self->{macindex}->{$macindex} = join "", @tmp; # save it
- }
- my $mac = $self->{macindex}->{$macindex};
- my $bp = $res->{$macindex};
- 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
- $self->{bpindex}->{$bp} = '(other)'; # build it
- }
- my $ifindex = $self->{bpindex}->{$bp};
- $self->{cam}->{$mac} = $ifindex;
- }
- return keys %{$self->{cam}};
- }
- sub GetCam { # Given a MAC address, return the ifindex for the port it was learned on
- my $self = shift;
- my $p = shift;
- return $self->{cam}->{$p};
- }
- sub desc { # for a given VLAN number, return the description
- my $self = shift;
- my $p = shift;
- $self->{desc} = $p if defined $p;
- return $self->{desc};
- }
- package port;
- sub new {
- my $class = shift;
- my $name = shift;
- my $self = {};
- bless ($self, $class);
- $self->{name} = $name;
- $self->{desc} = '';
- $self->{mode} = '';
- $self->{cdpn} = '';
- $self->{status} = '';
- $self->{vlan} = '';
- $self->{pagp} = '';
- return $self;
- }
- sub pagp {
- my $self = shift;
- my $p = shift;
- $self->{pagp} = $p if defined $p;
- return $self->{pagp};
- }
- sub status {
- my $self = shift;
- my $p = shift;
- $self->{status} = $p if defined $p;
- return $self->{status};
- }
- sub cdpn {
- my $self = shift;
- my $p = shift;
- $self->{cdpn} = $p if defined $p;
- return $self->{cdpn};
- }
- sub cdpnport {
- my $self = shift;
- my $p = shift;
- $self->{cdpnport} = $p if defined $p;
- return $self->{cdpnport};
- }
- sub vlan {
- my $self = shift;
- my $p = shift;
- $self->{vlan} = $p if defined $p;
- return $self->{vlan};
- }
- sub mode {
- my $self = shift;
- my $p = shift;
- $self->{mode} = $p if defined $p;
- return $self->{mode};
- }
- sub desc {
- my $self = shift;
- my $p = shift;
- $self->{desc} = $p if defined $p;
- return $self->{desc};
- }
- sub name {
- my $self = shift;
- my $p = shift;
- $self->{name} = $p if defined $p;
- return $self->{name};
- }
- package util;
- ################################################################################
- #
- # Build a hash from an SNMP table. The key is created by taking the last $n
- # elements of the returned OID value.
- #
- ################################################################################
- sub tablehash {
- my $snmp = shift;
- my $oid = shift;
- my $n = shift;
- my $ret = ();
- my $res = $snmp->get_table(-baseoid => $oid, -maxrepetitions => 30);
- foreach my $x ( keys %{$res} ) {
- my @tmp = split /\./, $x;
- my $key = join ".", splice(@tmp, -1 * $n);
- $ret->{$key} = $res->{$x};
- }
- return $ret;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement