#!/usr/local/bin/perl -w # -*- perl -*- # Cricket: a configuration, polling and data display wrapper for RRD files # # Copyright (C) 1998 Jeff R. Allen and WebTV Networks, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # listSwitchInterfaces: a utility to assist in the Cricket configuration of Cisco switches # # Copyright (C) 1999 Todd A. Green # This was based on the listInterfaces utility supplied with Cricket # # See the above disclaimers # # If you have any questions about this program email slartibartfast@awardsforfjords.com # BEGIN { $gInstallRoot = (($0 =~ m:^(.*/):)[0] || "./") . ".."; } use lib "$gInstallRoot/lib"; use strict; use Getopt::Std; use Data::Dumper; $Data::Dumper::Indent = 1; use snmpUtils; use Common::Log; my $options_ref = {}; getopts('Hdh:c:l:', $options_ref); if ($options_ref->{H} || !$options_ref->{h}) { print STDERR "usage: $0 -H -d -h hostname [-c community] [-l interface_list] \n", "\t-H: This page\n", "\t-d: This will pull the description from the user-configurable variable\n", "\t\tin the swtich. For 'Catalyst' switches this is the 'port name (portName)'\n", "\t\tand 'interface description (ifAlias)' in the 2924 switches.\n", "\t-l: Use specified file to find interface descriptions\n", "\t\tFormat is 'hostname:interface-name:description'\n", "\n\tcommunity will default to public if you don't give it.\n"; exit 1; } # Now expand the options for more readability $options_ref->{hostname} = $options_ref->{h}; if (defined $options_ref->{c}) { $options_ref->{community} = $options_ref->{c}; } else { $options_ref->{community} = 'public'; } my $snmp = "$options_ref->{community}\@$options_ref->{hostname}"; # Load the interface_list if present my $hosts_ref = {}; if (defined $options_ref->{l}) { if (open(LIST, $options_ref->{l})) { while() { chomp; if (!/^#/) { my ($hostname, $interface_name, $description) = split(':', $_); $hosts_ref->{$hostname}->{$interface_name} = $description; } } close(LIST); } else { print STDERR "Unable to open interface file '$options_ref->{l}'\n"; exit 1; } } print "target --default--\n"; print "\tswitch\t\t=\t$options_ref->{hostname}\n"; print "\n"; # First we determine if this is a 'Catalyst' switch or and 'IOS' switch # AFAIK only the 2900 series switches are 'IOS' switches # The difference being that the 'Catalyst' switches use the Cisco-Stack-MIB # for access to port names/descriptions, etc where-as the 'IOS' switches # act like the routers do using ifAlias # However both types do use the ifName variable for the interface-name # # Easiest way to tell the difference is look for the existence of a Stack MIB variable my ($sysMgmtType) = '.1.3.6.1.4.1.9.5.1.1.1'; my($is_Catalyst) = snmpUtils::get($snmp, "$sysMgmtType.0" ); my($ifName) = '.1.3.6.1.2.1.31.1.1.1.1'; my $interfaces_ref = {}; # Pull all the interface information foreach my $row (snmpUtils::walk($snmp, $ifName)) { my($oid, $value) = split(/:/, $row, 2); $oid =~ s/$ifName//; my $interface_ref = {}; $interface_ref->{ifName} = $interface_ref->{target} = $value; $interface_ref->{target} =~ s/[\/\s:]/\_/g; # Interface Status $interface_ref->{status} = snmpUtils::get($snmp, "1.3.6.1.2.1.2.2.1.7.$oid"); # Try to find the description from the list they specified if (defined $options_ref->{l}) { $interface_ref->{description} = $hosts_ref->{$options_ref->{hostname}}->{$value}; } # They want to pull it from the switch (if possible) elsif ($options_ref->{d}) { if ($is_Catalyst) { # portname is only available for the module/port interfaces # We'll the ifDescr for the rest (VLANs, etc) if ($value =~ /^\d+\/\d+/) { my $port_oid = $value; $port_oid =~ s/\//\./g; ($interface_ref->{description}) = snmpUtils::get($snmp, "1.3.6.1.4.1.9.5.1.4.1.1.4.$port_oid"); } else { ($interface_ref->{description}) = snmpUtils::get($snmp, "1.3.6.1.2.1.2.2.1.2.$oid"); } } else { # They want the ifAlias field ($interface_ref->{description}) = snmpUtils::get($snmp, "1.3.6.1.2.1.31.1.1.1.18.$oid"); } } else { # Otherwise pull it from ifDescr ($interface_ref->{description}) = snmpUtils::get($snmp, "1.3.6.1.2.1.2.2.1.2.$oid"); } # interface_key converts the interface name into a sortable field $interfaces_ref->{interface_key($value)} = $interface_ref; } # Now print out the interfaces in order my @interfaces = sort keys % { $interfaces_ref }; # Cricket uses a priority to decide what order to list the interfaces in # The higher the number the farther up the list # We force an order by numbering each interface starting from the highest number and working down my $interface_order = $#interfaces; foreach my $interface_key (@interfaces) { my $interface_ref = $interfaces_ref->{$interface_key}; if ($interface_ref->{status} == 1) { print "target $interface_ref->{target}\n"; output("interface-name", $interface_ref->{ifName}); output("short-desc", $interface_ref->{description}); output("order\t", $interface_order); print "\n"; } $interface_order--; } # The purpose of this routine is to munge the interface name into a form that can be sorted sub interface_key { my $interface_name = shift; # By default we'll force everything to the top my $interface_key = ' ' . $interface_name; # Handle Module/Port e.g. 5/1 if ($interface_name =~ /^(\d+)\/(\d+)/) { $interface_key = sprintf('%03d/%03d', $1, $2); } elsif ($interface_name =~ /^VLAN-(\d+)/i) { # The space is to force the VLAN to the top $interface_key = sprintf(' VLAN-%05d', $1); } elsif ($interface_name =~ /^VL(\d+)/i) { # The space is to force the VLAN to the top $interface_key = sprintf(' VL%05d', $1); } # Handle a Fa0/0 type elsif ($interface_name =~ /^(\w+)(\d+)\/(\d+)/) { $interface_key = sprintf('%s%03d/%03d', $1, $2, $3); } # Handle a FEC-4/1-4 type elsif ($interface_name =~ /^(\w+)-(\d+)\/(\d+)-(\d+)/) { # The space is to force the FEC to the top $interface_key = sprintf(' %s-%03d/%03d-%03d', $1, $2, $3, $4); } return $interface_key; } sub output { my($name, $value) = @_; my($quote) = ''; if (defined($value)) { # quote empty (or white-space only) lines, or lines which # will have embedded spaces. $quote = '"' if ($value =~ /^\s*$/ || $value =~ /\s/); print "\t$name = $quote$value$quote\n"; } }