2007-10-26 22:44:33 +00:00
#!/usr/bin/perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT::MacMap ;
2011-06-30 19:18:37 +00:00
require Exporter ;
our @ ISA = qw/Exporter/ ;
our @ EXPORT_OK = qw/walkoid/ ;
2010-10-01 13:07:03 +00:00
use strict ;
2007-10-26 22:44:33 +00:00
use xCAT::Table ;
2008-01-21 19:49:59 +00:00
use xCAT::Utils ;
2008-08-22 16:22:58 +00:00
use xCAT::MsgUtils ;
2007-10-26 22:44:33 +00:00
use IO::Select ;
use IO::Handle ;
use Sys::Syslog ;
2011-04-11 13:49:13 +00:00
#use Data::Dumper;
2010-10-01 13:07:03 +00:00
use POSIX qw/WNOHANG/ ;
2009-03-25 14:39:38 +00:00
use SNMP ;
2007-10-26 22:44:33 +00:00
my % cisco_vlans ; #Special hash structure to reflect discovered VLANS on Cisco equip
#use IF-MIB (1.3.6.1.2.1.2) for all switches
# 1.3.6.1.2.1.31.1.1 - ifXtable
# 1.3.6.1.2.1.31.1.1.1.1.N = name - ifName
#Using BRIDGE-MIB for most switches( 1.3.6.1.2.1.17 )
# 1.3.6.1.2.1.17.1.4 - dot1dBasePortTable
# 1.3.6.1.2.1.17.1.4.1.1.X = N - dot1dBasePort
# 1.3.6.1.2.1.17.4.3 - dot1dTpFdbTable #FAILS on FORCE10,
#
#If particular result fails, fallback to Q-BRIDGE-MIB for Force10 (1.3.6.1.2.1.17.7)
# 1.3.6.1.2.1.17.7.1.2.2 - dot1qTpFdbTable
2010-10-13 20:01:37 +00:00
#now for the lldp fun. lldp mib uses yet another index. The mib states
#that the index should correlate to dot1dbaseport, however
#limits the index to 4096 while dot1dbaseport can go much higher
#confirmed on various switches that this index cannot be numerically correlated
#to if-mib in a reliable fashion immediately for all switches
#LldpPortIdSubtype dictates the format
#in order of preference on subtype:
#if 5, then portid==ifName (my favorite, least work, no further lookups)
#if 3, then may be able to link into IF-MIB via ifPhysAddress matching more reliably
#if 7, then it may be anything at all, portDesc may be best option when encounterd, though occasionally looks like a 5. In the cases where it looks like a 5,
#portdesc seems usable too.
#detailed switch by switch results below
#on Force10, the following happens:
# -index violates mib by going over max value
# -subtype is 5, meaning portid should be == ifName, usable
# -lldpPortDesc is blank, cannot be used
#on juniper:
# -index violates mib by not matching dot1dbaseport
# -lldpPortId is 'helpfully', the index in ascii form (gee thanks), useless example of type 7
# -lldpPortDesc looks like "ge-1/0/43.0",only hope.
#bigiron, fcx, turboiron, :
# -lldpPortId is a 3 mac address
# --lldpportdesc looks useful 10GigabitEthernet6/6
#netiron ces: no support
#cisco ios:
# -the portid is == ifname
# subtype is 5 or 7, but either way it acts like 5
# -portdesc == ifdesc, useful for when 7 is seen for fallback
#
#voltaire 10ge: no support
#
#bnt g8124 and 8052
# -subtype of 7
# -the index, portid, and portdesc are all the same (i.e. 18="18"="18")
#smc 8848:
# -subtype of 3, hex mac string
# -portdesc matches ifDesc, no mapping to ifName
#smc 8126: no support for lldp mib
#ibm b32l: no support
#
2007-10-26 22:44:33 +00:00
sub namesmatch {
= pod
MacMap attempts to do it ' s best to determine whether or not a particular SNMP description of
a port matches the user specified value in the configuration . Generally , if the configuration
consists of non - stacked switches without line cards , the user should only need to specify the
port number without any characters or / characters . If the configuration contains line cards
or stacked switches , use of that particular switch ' s appropriate / syntax in generally called
for . The exception being stacked SMC 8848 switches , in which all ports are still single
numbers , and the ports on the second switch begin at 57 .
If difficulty is encountered , or a switch is attempted with a format that doesn ' t match any
existing rule , it is recommended to use snmpwalk on the switch with the .1 .3 .6 .1 .2 .1 .31 .1 .1 .1 .1
OID , and have the switch table port value match exactly the format suggested by that OID .
= cut
my $ namepercfg = shift ;
my $ namepersnmp = shift ;
if ( $ namepercfg eq $ namepersnmp ) {
return 1 ; # They matched perfectly
}
#Begin guessing, first off, all tested scenarios have likely correct guesses ending
#in the cfg string, with some non-numeric prefix before it.
2008-10-14 22:56:43 +00:00
#3com convention, contributed by Aaron Knister
if ( $ namepersnmp =~ /^RMON Port (0?)(\d+) on unit \d+/ ) {
if ( $ 2 =~ $ namepercfg ) {
return 1 ;
}
}
2010-07-21 14:48:47 +00:00
# dell 6248 convention
if ( $ namepersnmp =~ /^Unit \d Port (\d+)$/ ) {
if ( $ 1 eq $ namepercfg ) {
return 1 ;
}
}
2011-03-01 15:43:34 +00:00
unless ( $ namepersnmp =~ /[^0123456789]$namepercfg(\.0)?\z/ ) { #ensure name from user exists in the string without being preceeded immediately by a number, and allowing a .0 to exist after the cfg for juniper
2009-03-27 03:33:59 +00:00
#Most common case, won't match at all
return 0 ;
}
2011-03-01 15:43:34 +00:00
#at this point we know the string the user wanted does exist on this port, now we move on to non-ethernet ports that may ambiguously match the user request as well
2008-10-14 22:56:43 +00:00
2008-11-12 19:19:13 +00:00
#stop contemplating vlan, Nu, stacking ports, and console interfaces
2012-01-27 15:12:06 +00:00
if ( ( $ namepersnmp =~ /vl/i ) or ( $ namepersnmp =~ /Nu/ ) or ( $ namepersnmp =~ /onsole/ ) or ( $ namepersnmp =~ /Stack/ ) or ( $ namepersnmp =~ /Trunk/ ) ) {
2007-10-26 22:44:33 +00:00
return 0 ;
}
#broken up for code readablitiy, don't check port channel numbers or CPU
#have to distinguish betweer Port and Po and PortChannel
2009-03-27 03:33:59 +00:00
if ( ( $ namepersnmp !~ /Port #/ ) and ( $ namepersnmp !~ /Port\d/ ) and ( $ namepersnmp =~ /Po/ ) or ( $ namepersnmp =~ /po\d/ ) or ( $ namepersnmp =~ /XGE/ ) or ( $ namepersnmp =~ /LAG/ ) or ( $ namepersnmp =~ /CPU/ ) ) {
2007-10-26 22:44:33 +00:00
return 0 ;
}
#don't contemplate ManagementEthernet
if ( ( $ namepersnmp =~ /Management/ ) ) {
return 0 ;
}
#The blacklist approach has been exhausted. For now, assuming that means good,
#if something ambiguous happens, the whitelist would have been:
#'Port','Port #','/' (if namepercfg has no /, then / would be...),
#'Gi','Te','GigabitEthernet','TenGigabitEthernet'
return 1 ;
}
sub new {
my $ self = { } ;
2009-03-25 14:39:38 +00:00
# Since switch.pm and lsslp.pm both create a MacMap object, SNMP is still required at xcatd start up.
# So we are going back to "use SNMP;" at the top of this file so RPM will automatically generate a prereq.
#eval { require SNMP; };
#if ($@) { die "SNMP support required to use MacMAP"; }
2007-10-26 22:44:33 +00:00
my $ proto = shift ;
my $ class = ref ( $ proto ) || $ proto ;
bless ( $ self , $ class ) ;
return $ self ;
}
2010-09-28 20:30:38 +00:00
sub rvlan {
#The Q-BRIDGE way:
#IF-MIB for ifName<->ifIndex (much like the find_mac code)
#BRIDGE-MIB for ifIndex<->BridgeIndex (again, familiar)
#Q-BRIDGE-MIB for vlanId<->vlanIndex
# and vlanIndex<->dot1qVlanStaticUntaggedPorts
# and vlanIndex<->dot1qVlanStaticEgressPorts (tagged allowed ports)
# for changing the PVID of a port, the current bitfields must be read, the offset into the bitfield of the correct bridge index must be zero everywhere but vlan 1, then must be 1 in the target vlan. If it is zeroed in a vlan other than 1 without being 'oned' elsewhere, it reverts to vlan 1
#that is the documented steps for brocade
#some switches support vlan creation via qbridge, either via writing to the table or write to the row. SMC has write to non-existent row and vlanId==vlanIndex, which is logical. If a switches vlanIndex!=vlanId, QBridge doesnet' offer a clean injection point.
2010-09-29 14:16:31 +00:00
#QBridge also has dot1qPvid, but wasn't writable in Brocade.. it is readable though, so can guide the read, mask out, mask in activity above
#argument specification:
# nodes => [ list reference of nodes to query/set ]
2010-10-01 13:07:03 +00:00
# operation => "pvid=<vid> or vlan=<vid>" for now, addvlan= and delvlan= for tagged vlans, 'pvid', vlan, or stat without = checks current value
my $ self = shift ;
my $ community = "public" ;
$ self - > { sitetab } = xCAT::Table - > new ( 'site' ) ;
my $ tmp = $ self - > { sitetab } - > getAttribs ( { key = > 'snmpc' } , 'value' ) ;
if ( $ tmp and $ tmp - > { value } ) { $ community = $ tmp - > { value } }
my % args = @ _ ;
my $ op = $ args { operation } ;
my $ nodes = $ args { nodes } ;
#first order of business is to identify the target switches
my $ switchtab = xCAT::Table - > new ( 'switch' , - create = > 0 ) ;
unless ( $ switchtab ) { return ; }
my $ switchents = $ switchtab - > getNodesAttribs ( $ nodes , [ qw/switch port/ ] ) ;
my $ node ;
foreach $ node ( keys %$ switchents ) {
my $ entry ;
foreach $ entry ( @ { $ switchents - > { $ node } } ) {
$ self - > { switches } - > { $ entry - > { switch } } - > { $ entry - > { port } } = $ node ;
}
}
my $ switches = [ keys % { $ self - > { switches } } ] ;
my $ switchestab = xCAT::Table - > new ( 'switches' , - create = > 0 ) ;
my @ switchesents ;
if ( $ switchestab ) {
foreach ( values % { $ switchestab - > getNodesAttribs ( $ switches , [ qw( switch snmpversion username password privacy auth ) ] ) } ) {
push @ switchesents , @$ _ ;
}
}
$ self - > fill_switchparms ( community = > $ community , switchesents = > \ @ switchesents ) ;
my $ switch ;
foreach $ switch ( keys % { $ self - > { switches } } ) { #first we'll extract the lay of the land...
$ self - > refresh_switch ( undef , $ community , $ switch ) ;
unless ( $ self - > { switchinfo } - > { $ switch } - > { vlanidtoindex } ) { #need vlan id to vlanindex map for qbridge unless cisco
$ self - > scan_qbridge_vlans ( switch = > $ switch , community = > $ community ) ;
}
}
#print Dumper($self->{switchinfo});
# $self->{switchinfo}->{$switch}->{bridgeidxtoifname}->{$boid}=$portname;
# $self->{switchinfo}->{$switch}->{ifnametobridgeidx}->{$portname}=$boid;
$ op =~ s/stat/pvid/ ;
$ op =~ s/vlan/pvid/ ;
if ( $ op =~ /^addpvid/ ) { # add tagged vlan
} elsif ( $ op =~ /delpvid/ ) { #remove tagged vlan
} else { #native vlan query or set
}
}
sub scan_qbridge_vlans {
my $ self = shift ;
my % args = @ _ ;
my $ switch = $ args { switch } ;
my $ session = $ self - > { switchsessions } - > { $ switch } ;
$ self - > { switchinfo } - > { vlanindextoid } = walkoid ( $ session , '.1.3.6.1.2.1.17.7.1.4.2.1.3' ) ;
foreach ( keys % { $ self - > { switchinfo } - > { vlanindextoid } } ) {
#TODO: try to scan
}
2010-09-28 20:30:38 +00:00
}
2007-10-26 22:44:33 +00:00
sub find_mac {
# This function is given a mac address, checks for given mac address
# and returns undef if unable to find the node, and the nodename otherwise
my $ self = shift ;
my $ mac = shift ;
2008-01-11 20:55:28 +00:00
my $ cachedonly = shift ;
2007-10-26 22:44:33 +00:00
# For now HARDCODE (TODO, configurable?) a cache as stale after five minutes
# Also, if things are changed in the config, our cache could be wrong,
# invalidate on switch table write?
if ( $ self - > { mactable } - > { lc ( $ mac ) } and ( $ self - > { timestamp } > ( time ( ) - 300 ) ) ) {
my $ reftbl = 0 ;
foreach ( keys % { $ self - > { mactable } } ) {
if ( ( lc ( $ mac ) ne $ _ ) and ( $ self - > { mactable } - > { lc ( $ mac ) } eq $ self - > { mactable } - > { $ _ } ) ) {
2008-01-11 20:55:28 +00:00
#$reftbl = 1;
#Delete *possibly* stale data, without being heavy handed..
2009-02-24 11:02:34 +00:00
#But if this mac indicates multiple nodes, leave it there
if ( $ self - > { mactable } - > { lc ( $ mac ) } !~ /,/ )
{
delete $ self - > { mactable } - > { $ _ } ;
}
2007-10-26 22:44:33 +00:00
}
}
unless ( $ reftbl ) { return $ self - > { mactable } - > { lc ( $ mac ) } ; }
}
2008-01-11 20:55:28 +00:00
#If requesting a cache only check or the cache is a mere 20 seconds old
#don't bother querying switches
if ( $ cachedonly or ( $ self - > { timestamp } > ( time ( ) - 20 ) ) ) { return undef ; }
2007-10-26 22:44:33 +00:00
$ self - > refresh_table ; #not cached or stale cache, refresh
if ( $ self - > { mactable } - > { lc ( $ mac ) } ) {
return $ self - > { mactable } - > { lc ( $ mac ) } ;
}
return undef ;
}
2010-10-01 13:07:03 +00:00
sub fill_switchparms {
my $ self = shift ;
my % args = @ _ ;
my $ community = $ args { community } ;
$ self - > { switchparmhash } = { } ;
my @ switchentries = @ { $ args { switchesents } } ;
foreach ( @ switchentries ) {
my $ curswitch = $ _ - > { switch } ;
$ self - > { switchparmhash } - > { $ curswitch } = $ _ ;
if ( $ _ - > { snmpversion } ) {
if ( $ _ - > { snmpversion } =~ /3/ ) { #clean up to accept things like v3 or ver3 or 3, whatever.
$ self - > { switchparmhash } - > { $ curswitch } - > { snmpversion } = 3 ;
unless ( $ _ - > { auth } ) {
$ self - > { switchparmhash } - > { $ curswitch } - > { auth } = 'md5' ; #Default to md5 auth if not specified but using v3
}
} elsif ( $ _ - > { snmpversion } =~ /2/ ) {
$ self - > { switchparmhash } - > { $ curswitch } - > { snmpversion } = 2 ;
} else {
$ self - > { switchparmhash } - > { $ curswitch } - > { snmpversion } = 1 ; #Default to lowest common denominator, snmpv1
}
}
unless ( defined $ _ - > { password } ) { #if no password set, inherit the community
$ self - > { switchparmhash } - > { $ curswitch } - > { password } = $ community ;
}
}
}
2007-10-26 22:44:33 +00:00
sub refresh_table {
my $ self = shift ;
2008-10-29 03:08:34 +00:00
my $ curswitch ;
2008-02-01 16:23:20 +00:00
$ self - > { mactable } = { } ;
$ self - > { switchtab } = xCAT::Table - > new ( 'switch' , - create = > 1 ) ;
2008-10-29 03:08:34 +00:00
$ self - > { switchestab } = xCAT::Table - > new ( 'switches' , - create = > 1 ) ;
2010-09-13 18:54:26 +00:00
my @ switchentries = $ self - > { switchestab } - > getAllNodeAttribs ( [ qw( switch snmpversion username password privacy auth ) ] ) ;
2007-10-26 22:44:33 +00:00
my $ community = "public" ;
2008-10-29 03:08:34 +00:00
$ self - > { sitetab } = xCAT::Table - > new ( 'site' ) ;
2007-10-26 22:44:33 +00:00
my $ tmp = $ self - > { sitetab } - > getAttribs ( { key = > 'snmpc' } , 'value' ) ;
if ( $ tmp and $ tmp - > { value } ) { $ community = $ tmp - > { value } }
else { #Would warn here..
}
2010-10-01 13:07:03 +00:00
$ self - > { switchparmhash } = { } ;
2008-10-29 03:08:34 +00:00
foreach ( @ switchentries ) {
$ curswitch = $ _ - > { switch } ;
$ self - > { switchparmhash } - > { $ curswitch } = $ _ ;
if ( $ _ - > { snmpversion } ) {
if ( $ _ - > { snmpversion } =~ /3/ ) { #clean up to accept things like v3 or ver3 or 3, whatever.
$ self - > { switchparmhash } - > { $ curswitch } - > { snmpversion } = 3 ;
unless ( $ _ - > { auth } ) {
$ self - > { switchparmhash } - > { $ curswitch } - > { auth } = 'md5' ; #Default to md5 auth if not specified but using v3
}
} elsif ( $ _ - > { snmpversion } =~ /2/ ) {
$ self - > { switchparmhash } - > { $ curswitch } - > { snmpversion } = 2 ;
} else {
$ self - > { switchparmhash } - > { $ curswitch } - > { snmpversion } = 1 ; #Default to lowest common denominator, snmpv1
}
}
unless ( defined $ _ - > { password } ) { #if no password set, inherit the community
$ self - > { switchparmhash } - > { $ curswitch } - > { password } = $ community ;
}
}
2007-10-26 22:44:33 +00:00
my % checked_pairs ;
2008-01-14 22:19:17 +00:00
my @ entries = $ self - > { switchtab } - > getAllNodeAttribs ( [ 'node' , 'port' , 'switch' ] ) ;
2007-10-26 22:44:33 +00:00
#Build hash of switch port names per switch
$ self - > { switches } = { } ;
2010-10-01 13:07:03 +00:00
foreach my $ entry ( @ entries ) {
2008-01-19 16:45:29 +00:00
if ( defined ( $ entry - > { switch } ) and $ entry - > { switch } ne "" and defined ( $ entry - > { port } ) and $ entry - > { port } ne "" ) {
2009-01-19 08:58:29 +00:00
if ( ! $ self - > { switches } - > { $ entry - > { switch } } - > { $ entry - > { port } } )
{
$ self - > { switches } - > { $ entry - > { switch } } - > { $ entry - > { port } } = $ entry - > { node } ;
}
else
{
$ self - > { switches } - > { $ entry - > { switch } } - > { $ entry - > { port } } . = ",$entry->{node}" ;
}
2008-01-19 16:45:29 +00:00
} else {
2008-08-22 16:22:58 +00:00
xCAT::MsgUtils - > message ( "S" , "xCAT Table error:" . $ entry - > { node } . "Has missing or invalid switch.switch and/or switch.port fields" ) ;
2008-01-19 16:45:29 +00:00
}
}
2007-10-26 22:44:33 +00:00
my $ children = 0 ;
my $ inputs = new IO:: Select ;
$ SIG { CHLD } = sub { while ( waitpid ( - 1 , WNOHANG ) > 0 ) { $ children - - } } ;
2010-10-01 13:07:03 +00:00
foreach my $ entry ( @ entries ) {
2007-10-26 22:44:33 +00:00
if ( $ checked_pairs { $ entry - > { switch } } ) {
next ;
}
$ checked_pairs { $ entry - > { switch } } = 1 ;
pipe my $ child , my $ parent ;
$ child - > autoflush ( 1 ) ;
$ parent - > autoflush ( 1 ) ;
$ children + + ;
2010-10-01 13:07:03 +00:00
my $ cpid = xCAT::Utils - > xfork ;
2007-10-26 22:44:33 +00:00
unless ( defined $ cpid ) { die "Cannot fork" } ;
if ( $ cpid == 0 ) {
close ( $ child ) ;
$ self - > refresh_switch ( $ parent , $ community , $ entry - > { switch } ) ;
exit ( 0 ) ;
}
close ( $ parent ) ;
$ inputs - > add ( $ child ) ;
}
while ( $ children ) {
$ self - > handle_output ( $ inputs ) ;
}
while ( $ self - > handle_output ( $ inputs ) ) { } ; #Drain the pipes
$ self - > { timestamp } = time ;
}
sub handle_output {
my $ self = shift ;
my $ inputs = shift ;
my @ readied = $ inputs - > can_read ( 1 ) ;
my $ rc = @ readied ;
my $ ready ;
foreach $ ready ( @ readied ) {
my $ line = <$ready> ;
unless ( $ line ) {
$ inputs - > remove ( $ ready ) ;
close ( $ ready ) ;
next ;
}
$ line =~ m/^([^|]*)\|(.*)/ ;
$ self - > { mactable } - > { $ 1 } = $ 2 ;
}
return $ rc ;
}
sub walkoid {
my $ session = shift ;
my $ oid = shift ;
2009-01-30 16:25:55 +00:00
my % namedargs = @ _ ;
2007-10-26 22:44:33 +00:00
my $ retmap = undef ;
my $ varbind = new SNMP:: Varbind ( [ $ oid , '' ] ) ;
$ session - > getnext ( $ varbind ) ;
if ( $ session - > { ErrorStr } ) {
2009-01-30 16:25:55 +00:00
unless ( $ namedargs { silentfail } ) {
2010-10-01 13:07:03 +00:00
if ( $ namedargs { ciscowarn } ) {
2009-01-30 16:25:55 +00:00
xCAT::MsgUtils - > message ( "S" , "Error communicating with " . $ session - > { DestHost } . " (First attempt at indexing by VLAN failed, ensure that the switch has the vlan configured such that it appears in 'show vlan'): " . $ session - > { ErrorStr } ) ;
} else {
xCAT::MsgUtils - > message ( "S" , "Error communicating with " . $ session - > { DestHost } . ": " . $ session - > { ErrorStr } ) ;
}
}
2007-10-26 22:44:33 +00:00
return undef ;
}
my $ count = 0 ;
while ( $ varbind - > [ 0 ] =~ /^$oid\.?(.*)/ ) {
$ count + + ;
if ( $ 1 ) {
$ retmap - > { $ 1 . "." . $ varbind - > [ 1 ] } = $ varbind - > [ 2 ] ; #If $1 is set, means key should
} else {
$ retmap - > { $ varbind - > [ 1 ] } = $ varbind - > [ 2 ] ; #If $1 is set, means key should
}
$ session - > getnext ( $ varbind ) ;
}
return $ retmap ;
}
2010-09-29 14:16:31 +00:00
sub getsnmpsession {
#gets an snmp v3 session appropriate for a switch using the switches table for guidance on the hows
#arguments: switch => $switchname and optionally vlan=> $vid if needed for community string indexing
2010-09-29 15:11:26 +00:00
my $ self = shift ;
2010-09-29 14:16:31 +00:00
my % args = @ _ ;
my $ switch = $ args { 'switch' } ;
my $ vlanid = $ args { 'vlanid' } ;
my $ community = $ args { 'community' } ;
my $ session ;
my $ snmpver = '1' ;
my $ swent = $ self - > { switchparmhash } - > { $ switch } ;
2008-10-29 03:08:34 +00:00
if ( $ swent ) {
$ snmpver = $ swent - > { snmpversion } ;
$ community = $ swent - > { password } ;
2008-09-22 20:03:02 +00:00
}
2008-10-29 03:08:34 +00:00
if ( $ snmpver ne '3' ) {
2010-09-29 14:16:31 +00:00
if ( $ vlanid ) { $ community . = '@' . $ vlanid ; }
2008-10-29 03:08:34 +00:00
$ session = new SNMP:: Session (
DestHost = > $ switch ,
Version = > $ snmpver ,
Community = > $ community ,
UseNumeric = > 1
) ;
} else { #we have snmp3
2010-09-29 14:16:31 +00:00
my % args = (
2008-10-29 03:08:34 +00:00
DestHost = > $ switch ,
SecName = > $ swent - > { username } ,
AuthProto = > uc ( $ swent - > { auth } ) ,
AuthPass = > $ community ,
Version = > $ snmpver ,
SecLevel = > 'authNoPriv' ,
UseNumeric = > 1
2010-09-29 14:16:31 +00:00
) ;
if ( $ vlanid ) { $ args { Context } = "vlan-" . $ vlanid ; }
if ( $ swent - > { privacy } ) {
$ args { SecLevel } = 'authPriv' ;
$ args { PrivProto } = uc ( $ swent - > { privacy } ) ;
$ args { PrivPass } = $ community ;
2008-10-29 03:08:34 +00:00
}
2010-09-29 14:16:31 +00:00
$ session = new SNMP:: Session ( % args ) ;
2008-10-29 03:08:34 +00:00
}
2010-09-29 14:16:31 +00:00
return $ session ;
}
sub refresh_switch {
my $ self = shift ;
my $ output = shift ;
my $ community = shift ;
my $ switch = shift ;
2008-10-29 03:08:34 +00:00
2007-10-26 22:44:33 +00:00
#if ($error) { die $error; }
2010-10-01 13:07:03 +00:00
my $ session = $ self - > getsnmpsession ( 'community' = > $ community , 'switch' = > $ switch ) ;
2008-08-22 16:22:58 +00:00
unless ( $ session ) { xCAT::MsgUtils - > message ( "S" , "Failed to communicate with $switch" ) ; return ; }
2007-10-26 22:44:33 +00:00
my $ namemap = walkoid ( $ session , '.1.3.6.1.2.1.31.1.1.1.1' ) ;
2010-09-29 15:11:26 +00:00
#namemap is the mapping of ifIndex->(human readable name)
2008-04-24 20:51:06 +00:00
if ( $ namemap ) {
my $ ifnamesupport = 0 ; #Assume broken ifnamesupport until proven good... (Nortel switch)
foreach ( keys % { $ namemap } ) {
if ( $ namemap - > { $ _ } ) {
$ ifnamesupport = 1 ;
last ;
}
}
unless ( $ ifnamesupport ) {
$ namemap = 0 ;
}
}
unless ( $ namemap ) { #Failback to ifDescr. ifDescr is close, but not perfect on some switches
$ namemap = walkoid ( $ session , '.1.3.6.1.2.1.2.2.1.2' ) ;
}
2007-10-26 22:44:33 +00:00
unless ( $ namemap ) {
return ;
}
#Above is valid without community string indexing, on cisco, we need it on the next one and onward
2010-09-29 15:11:26 +00:00
my $ iftovlanmap = walkoid ( $ session , '.1.3.6.1.4.1.9.9.68.1.2.2.1.2' , silentfail = > 1 ) ; #use cisco vlan membership mib to ascertain vlan
2010-02-25 20:18:17 +00:00
my $ trunktovlanmap = walkoid ( $ session , '.1.3.6.1.4.1.9.9.46.1.6.1.1.5' , silentfail = > 1 ) ; #for trunk ports, we are interested in the native vlan
2010-09-29 15:11:26 +00:00
#so we need cisco vtp mib too
2007-10-26 22:44:33 +00:00
my % vlans_to_check ;
2010-02-25 20:18:17 +00:00
if ( defined ( $ iftovlanmap ) or defined ( $ trunktovlanmap ) ) { #We have a cisco, the intelligent thing is to do SNMP gets on the ports
2010-10-01 13:07:03 +00:00
$ self - > { switchinfo } - > { $ switch } - > { vlanidtoindex } = "NA" ; #mark this switch to ignore for qbridge scans
2007-10-26 22:44:33 +00:00
# that we can verify are populated per switch table
my $ portid ;
foreach $ portid ( keys % { $ namemap } ) {
my $ portname ;
my $ switchport = $ namemap - > { $ portid } ;
foreach $ portname ( keys % { $ self - > { switches } - > { $ switch } } ) {
2010-02-25 20:18:17 +00:00
unless ( namesmatch ( $ portname , $ switchport ) ) {
next ;
}
if ( not defined $ iftovlanmap - > { $ portid } and not defined $ trunktovlanmap - > { $ portid } ) {
xCAT::MsgUtils - > message ( "S" , "$portid missing from switch" ) ;
next ;
}
if ( defined $ iftovlanmap - > { $ portid } ) {
$ vlans_to_check { "" . $ iftovlanmap - > { $ portid } } = 1 ; #cast to string, may not be needed
2010-09-29 15:11:26 +00:00
$ self - > { nodeinfo } - > { $ self - > { switches } - > { $ switch } - > { $ portname } } - > { vlans } - > { $ portname } = $ iftovlanmap - > { $ portid } ;
2010-02-25 20:18:17 +00:00
} else { #given above if statement, brigetovlanmap *must* be defined*
$ vlans_to_check { "" . $ trunktovlanmap - > { $ portid } } = 1 ; #cast to string, may not be needed
2010-09-29 15:11:26 +00:00
$ self - > { nodeinfo } - > { $ self - > { switches } - > { $ switch } - > { $ portname } } - > { vlans } - > { $ portname } = $ trunktovlanmap - > { $ portid } ;
2010-02-25 20:18:17 +00:00
}
2007-10-26 22:44:33 +00:00
}
}
} else {
$ vlans_to_check { 'NA' } = 1 ;
}
my $ vlan ;
2009-01-30 16:25:55 +00:00
my $ iscisco = 0 ;
2010-10-01 13:07:03 +00:00
foreach $ vlan ( sort keys % vlans_to_check ) { #Sort, because if numbers, we want 1 first, because that vlan should not get communiy string indexed query
2008-11-12 19:19:13 +00:00
unless ( not $ vlan or $ vlan eq 'NA' or $ vlan eq '1' ) { #don't subject users to the context pain unless needed
2009-01-30 16:25:55 +00:00
$ iscisco = 1 ;
2010-09-29 15:11:26 +00:00
$ session = $ self - > getsnmpsession ( 'switch' = > $ switch , 'community' = > $ community , 'vlanid' = > $ vlan ) ;
2007-10-26 22:44:33 +00:00
}
unless ( $ session ) { return ; }
2009-01-30 16:25:55 +00:00
my $ bridgetoifmap = walkoid ( $ session , '.1.3.6.1.2.1.17.1.4.1.2' , ciscowarn = > $ iscisco ) ; # Good for all switches
2012-02-05 17:37:47 +00:00
if ( not ref $ bridgetoifmap or ! keys % { $ bridgetoifmap } ) {
xCAT::MsgUtils - > message ( "S" , "Error communicating with " . $ session - > { DestHost } . ": failed to get a valid response to BRIDGE-MIB request" ) ;
return ;
}
2010-10-01 13:07:03 +00:00
2007-10-26 22:44:33 +00:00
# my $mactoindexmap = walkoid($session,'.1.3.6.1.2.1.17.4.3.1.2');
2009-01-30 16:25:55 +00:00
my $ mactoindexmap = walkoid ( $ session , '.1.3.6.1.2.1.17.7.1.2.2.1.2' , silentfail = > 1 ) ;
2007-10-26 22:44:33 +00:00
unless ( defined ( $ mactoindexmap ) ) { #if no qbridge defined, try bridge mib, probably cisco
#$mactoindexmap = walkoid($session,'.1.3.6.1.2.1.17.7.1.2.2.1.2');
2009-01-30 16:25:55 +00:00
$ mactoindexmap = walkoid ( $ session , '.1.3.6.1.2.1.17.4.3.1.2' , ciscowarn = > $ iscisco ) ;
2007-10-26 22:44:33 +00:00
} #Ok, time to process the data
2012-02-05 17:37:47 +00:00
if ( not ref $ mactoindexmap or ! keys % { $ mactoindexmap } ) {
xCAT::MsgUtils - > message ( "S" , "Error communicating with " . $ session - > { DestHost } . ": Unable to get MAC entries via either BRIDGE or Q-BRIDE MIB" ) ;
return ;
}
2007-10-26 22:44:33 +00:00
foreach my $ oid ( keys %$ namemap ) {
#$oid =~ m/1.3.6.1.2.1.31.1.1.1.1.(.*)/;
my $ ifindex = $ oid ;
my $ portname ;
my $ switchport = $ namemap - > { $ oid } ;
foreach $ portname ( keys % { $ self - > { switches } - > { $ switch } } ) { # a little redundant, but
# computationally trivial
unless ( namesmatch ( $ portname , $ switchport ) ) { next }
#if still running, we have match
foreach my $ boid ( keys %$ bridgetoifmap ) {
unless ( $ bridgetoifmap - > { $ boid } == $ ifindex ) { next ; }
2010-10-01 13:07:03 +00:00
$ self - > { switchinfo } - > { $ switch } - > { bridgeidxtoifname } - > { $ boid } = $ portname ;
$ self - > { switchinfo } - > { $ switch } - > { ifnametobridgeidx } - > { $ portname } = $ boid ;
$ self - > { nodeinfo } - > { $ self - > { switches } - > { $ switch } - > { $ portname } } - > { portnametobridgeindex } - > { $ portname } = $ boid ;
$ self - > { nodeinfo } - > { $ self - > { switches } - > { $ switch } - > { $ portname } } - > { bridgeindextoportname } - > { $ boid } = $ portname ;
2007-10-26 22:44:33 +00:00
my $ bridgeport = $ boid ;
foreach ( keys %$ mactoindexmap ) {
if ( $ mactoindexmap - > { $ _ } == $ bridgeport ) {
my @ tmp = split /\./ , $ _ ;
my @ mac = @ tmp [ - 6 .. - 1 ] ;
2010-09-29 15:11:26 +00:00
my $ macstring = sprintf ( "%02x:%02x:%02x:%02x:%02x:%02x" , @ mac ) ;
if ( $ output ) {
2010-09-29 15:18:45 +00:00
printf $ output "$macstring|%s\n" , $ self - > { switches } - > { $ switch } - > { $ portname } ;
2010-09-29 15:11:26 +00:00
}
push @ { $ self - > { nodeinfo } - > { $ self - > { switches } - > { $ switch } - > { $ portname } } - > { macs } - > { $ portname } } , $ macstring ; #this could be used as getmacs sort of deal
2007-10-26 22:44:33 +00:00
}
}
}
}
}
}
2010-10-01 13:07:03 +00:00
$ self - > { switchsessions } - > { $ switch } = $ session ; #save session for future use
2007-10-26 22:44:33 +00:00
}
1 ;