# SNMP::Info::NortelStack # $Id$ # # Copyright (c) 2008 Eric Miller # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the University of California, Santa Cruz nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. package SNMP::Info::NortelStack; use strict; use Exporter; use SNMP::Info; @SNMP::Info::NortelStack::ISA = qw/SNMP::Info Exporter/; @SNMP::Info::NortelStack::EXPORT_OK = qw//; use vars qw/$VERSION %FUNCS %GLOBALS %MIBS %MUNGE/; $VERSION = '2.09'; %MIBS = ( # S5-ROOT-MIB and S5-TCS-MIB required by the MIBs below 'S5-AGENT-MIB' => 's5AgMyGrpIndx', 'S5-CHASSIS-MIB' => 's5ChasType', 'S5-REG-MIB' => 's5ChasTypeVal', ); %GLOBALS = ( # From S5-AGENT-MIB 'ns_ag_ver' => 's5AgInfoVer', 'ns_op_mode' => 's5AgSysCurrentOperationalMode', 'ns_auto_pvid' => 's5AgSysAutoPvid', 'tftp_host' => 's5AgSysTftpServerAddress', 'tftp_file' => 's5AgSysBinaryConfigFilename', 'tftp_action' => 's5AgInfoFileAction', 'tftp_result' => 's5AgInfoFileStatus', 'vlan' => 's5AgSysManagementVlanId', # From S5-CHASSIS-MIB 'ns_serial' => 's5ChasSerNum', 'ns_ch_type' => 's5ChasType', 'ns_cfg_chg' => 's5ChasGblConfChngs', 'ns_cfg_time' => 's5ChasGblConfLstChng', ); %FUNCS = ( # From S5-AGENT-MIB::s5AgMyIfTable 'i_cfg_file' => 's5AgMyIfCfgFname', 'i_cfg_host' => 's5AgMyIfLdSvrAddr', # From S5-CHASSIS-MIB::s5ChasGrpTable 'ns_grp_type' => 's5ChasGrpType', # From S5-CHASSIS-MIB::s5ChasComTable 'ns_com_grp_idx' => 's5ChasComGrpIndx', 'ns_com_idx' => 's5ChasComIndx', 'ns_com_sub_idx' => 's5ChasComSubIndx', 'ns_com_type' => 's5ChasComType', 'ns_com_descr' => 's5ChasComDescr', 'ns_com_ver' => 's5ChasComVer', 'ns_com_serial' => 's5ChasComSerNum', # From S5-CHASSIS-MIB::s5ChasStoreTable 'ns_store_grp_idx' => 's5ChasStoreGrpIndx', 'ns_store_com_idx' => 's5ChasStoreComIndx', 'ns_store_sub_idx' => 's5ChasStoreSubIndx', 'ns_store_idx' => 's5ChasStoreIndx', 'ns_store_type' => 's5ChasStoreType', 'ns_store_size' => 's5ChasStoreCurSize', 'ns_store_ver' => 's5ChasStoreCntntVer', ); %MUNGE = ( 'ns_ch_type' => \&SNMP::Info::munge_e_type, 'ns_grp_type' => \&munge_ns_grp_type, 'ns_com_type' => \&SNMP::Info::munge_e_type, 'ns_store_type' => \&SNMP::Info::munge_e_type, ); sub os_ver { my $stack = shift; my $ver = $stack->ns_ag_ver(); return unless defined $ver; if ( $ver =~ m/(\d+\.\d+\.\d+\.\d+)/ ) { return $1; } if ( $ver =~ m/V(\d+\.\d+\.\d+)/i ) { return $1; } return; } sub os_bin { my $stack = shift; my $ver = $stack->ns_ag_ver(); return unless defined $ver; if ( $ver =~ m/(\d+\.\d+\.\d+\.\d+)/i ) { return $1; } if ( $ver =~ m/V(\d+\.\d+.\d+)/i ) { return $1; } return; } # Need to override here since overridden in Layer2 and Layer3 classes sub serial { my $stack = shift; my $ver = $stack->ns_serial(); return $ver unless !defined $ver; return; } # Pseudo ENTITY-MIB methods for older switches with don't support ENTITY-MIB # This class supports both stackable and chassis based switches, identify if # we have a stackable so that we return appropriate entPhysicalClass sub _ns_e_is_virtual { my $stack = shift; # We really only need one value, but we want this cached since most # methods call it at least via ns_e_index() my $v_test = $stack->s5ChasComRelPos() || {}; return $v_test->{'8.1.0'}; } # Identify is the stackable is actually a stack vs. single switch sub _ns_e_is_stack { my $stack = shift; my $s_test = $stack->ns_e_class() || {}; foreach my $iid ( keys %$s_test ) { my $class = $s_test->{$iid}; next unless defined $class; return 1 if ( $class eq 'stack' ); } return 0; } sub ns_e_index { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_com_grp_idx($partial) || {}; my $is_virtual = $stack->_ns_e_is_virtual(); my %ns_e_index; foreach my $iid ( keys %$ns_e_idx ) { # Skip backplane, power, sensor, fan, clock - these aren't in the # newer devices ENTITY-MIB we're emulating next if ( $iid =~ /^[24567]/ ); next if ( ($is_virtual) and ( $iid =~ /^8/ or $iid eq '1.0.0' ) ); # Format into consistent integer format so that numeric sorting works my $index = join( '', map { sprintf "%02d", $_ } split /\./, $iid ); $ns_e_index{$iid} = $index; } return \%ns_e_index; } sub ns_e_class { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $classes = $stack->ns_grp_type(); my $ns_grp_enc = $stack->s5ChasGrpEncodeFactor($partial) || {}; my $is_virtual = $stack->_ns_e_is_virtual(); my %ns_e_class; foreach my $iid ( keys %$ns_e_idx ) { my ( $grp, $idx, $sub ) = split( /\./, $iid ); next unless defined $grp; my $class = $classes->{$grp}; next unless defined $class; my $enc = $ns_grp_enc->{$grp}; # Handle quirks of dealing with both stacks and chassis if ( ( !$is_virtual ) and ( $grp == 1 ) ) { $class = 'module'; } if ( ($is_virtual) and ( $grp == 3 ) and !( $idx % $enc ) ) { $class = 'chassis'; } $ns_e_class{$iid} = $class; } return \%ns_e_class; } sub ns_e_descr { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_e_descr = $stack->ns_com_descr($partial) || {}; my %ns_e_descr; foreach my $iid ( keys %$ns_e_idx ) { my $descr = $ns_e_descr->{$iid}; next unless defined $descr; $ns_e_descr{$iid} = $descr; } return \%ns_e_descr; } sub ns_e_name { my $stack = shift; my $partial = shift; my $ns_class = $stack->ns_e_class() || {}; my $ns_e_idx = $stack->ns_e_index() || {}; my $ns_grp_enc = $stack->s5ChasGrpEncodeFactor($partial) || {}; my $is_virtual = $stack->_ns_e_is_virtual(); my %ns_e_name; foreach my $iid ( keys %$ns_e_idx ) { my ( $grp, $idx, $sub ) = split( /\./, $iid ); my $class = $ns_class->{$iid}; next unless defined $class; my $enc = $ns_grp_enc->{$grp}; if ( ( !$is_virtual ) and ( $grp == 1 ) ) { $ns_e_name{$iid} = 'Supervisory Module'; } elsif ( $class eq 'stack' ) { $ns_e_name{$iid} = 'Stack Master Unit'; } elsif ( $class eq 'chassis' ) { if ($is_virtual) { my $unit = $idx / $enc; $ns_e_name{$iid} = "Switch Unit $unit"; } else { $ns_e_name{$iid} = "Chassis"; } } elsif ( $class eq 'module' ) { if ($is_virtual) { my $unit = int( $idx / $enc ); my $mda = $idx % $enc; $ns_e_name{$iid} = "Switch Unit $unit, MDA $mda"; } elsif ( $sub != 0 ) { $ns_e_name{$iid} = "Module Slot $idx, Subcomponent $sub"; } else { $ns_e_name{$iid} = "Module Slot $idx"; } } } return \%ns_e_name; } sub ns_e_hwver { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_e_ver = $stack->ns_com_ver($partial) || {}; my %ns_e_hwver; foreach my $iid ( keys %$ns_e_idx ) { my $ver = $ns_e_ver->{$iid}; next unless defined $ver; $ns_e_hwver{$iid} = $ver; } return \%ns_e_hwver; } sub ns_e_vendor { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my %ns_e_vendor; foreach my $iid ( keys %$ns_e_idx ) { my $vendor = 'nortel'; $ns_e_vendor{$iid} = $vendor; } return \%ns_e_vendor; } sub ns_e_serial { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_e_serial = $stack->ns_com_serial($partial) || {}; my %ns_e_serial; foreach my $iid ( keys %$ns_e_idx ) { my $serial = $ns_e_serial->{$iid}; next unless defined $serial; $ns_e_serial{$iid} = $serial; } return \%ns_e_serial; } sub ns_e_type { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_e_type = $stack->ns_com_type($partial) || {}; my $is_stack = $stack->_ns_e_is_stack(); my $ch_type = $stack->ns_ch_type(); my %ns_e_type; foreach my $iid ( keys %$ns_e_idx ) { my $type = $ns_e_type->{$iid}; next unless defined $type; if ( $is_stack and $iid =~ /^1/ ) { $type = $ch_type; } $ns_e_type{$iid} = $type; } return \%ns_e_type; } sub ns_e_pos { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_grp_enc = $stack->s5ChasGrpEncodeFactor($partial) || {}; my $is_stack = $stack->_ns_e_is_stack(); my $is_virtual = $stack->_ns_e_is_virtual(); my %ns_e_pos; foreach my $iid ( keys %$ns_e_idx ) { my ( $grp, $pos, $idx ) = split( /\./, $iid ); next unless defined $grp; next unless defined $pos; if ( $grp == 1 ) { if ($is_stack) { $pos = -1; } else { $pos = 99; } } elsif ( $grp == 3 and $idx == 0 ) { my $enc = $ns_grp_enc->{$grp}; if ( $is_virtual and ( $pos % $enc ) ) { $pos = int( $pos % $enc ); } elsif ( $is_virtual and !$is_stack and !( $pos % $enc ) ) { $pos = -1; } elsif ( $is_virtual and !( $pos % $enc ) ) { $pos = ( $pos / $enc ); } } elsif ( !$is_stack and $grp == 3 ) { $pos = $idx; } elsif ( $grp == 8 ) { $pos = -1; } $ns_e_pos{$iid} = $pos; } return \%ns_e_pos; } sub ns_e_fwver { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_e_ver = $stack->ns_store_ver($partial) || {}; my $ns_e_type = $stack->ns_store_type($partial) || {}; my $ns_grp_enc = $stack->s5ChasGrpEncodeFactor($partial) || {}; my $is_virt = $stack->_ns_e_is_virtual(); my %ns_e_fwver; foreach my $iid ( keys %$ns_e_type ) { my $type = $ns_e_type->{$iid}; next unless defined $type; next unless $type =~ /(rom|boot|fw)/i; my $ver = $ns_e_ver->{$iid}; next unless defined $ver; $iid =~ s/\.\d+$//; if ($is_virt) { my ( $grp, $idx, $pos ) = split( /\./, $iid ); my $enc = $ns_grp_enc->{$grp}; $idx = $idx * $enc; $iid = "3.$idx.$pos"; } $ns_e_fwver{$iid} = $ver; } return \%ns_e_fwver; } sub ns_e_swver { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_e_ver = $stack->ns_store_ver($partial) || {}; my $ns_e_type = $stack->ns_store_type($partial) || {}; my $ns_grp_enc = $stack->s5ChasGrpEncodeFactor($partial) || {}; my $is_virt = $stack->_ns_e_is_virtual(); my %ns_e_swver; foreach my $iid ( keys %$ns_e_type ) { my $type = $ns_e_type->{$iid}; next unless defined $type; next unless $type =~ /(flash)/i; my $ver = $ns_e_ver->{$iid}; next unless defined $ver; $iid =~ s/\.\d+$//; if ($is_virt) { my ( $grp, $idx, $pos ) = split( /\./, $iid ); my $enc = $ns_grp_enc->{$grp}; $idx = $idx * $enc; $iid = "3.$idx.$pos"; } $ns_e_swver{$iid} = $ver; } return \%ns_e_swver; } sub ns_e_parent { my $stack = shift; my $partial = shift; my $ns_e_idx = $stack->ns_e_index($partial) || {}; my $ns_grp_enc = $stack->s5ChasGrpEncodeFactor($partial) || {}; my $is_stack = $stack->_ns_e_is_stack(); my $is_virtual = $stack->_ns_e_is_virtual(); my %ns_e_parent; foreach my $iid ( keys %$ns_e_idx ) { my $index = $ns_e_idx->{$iid}; my ( $grp, $idx, $pos ) = split( /\./, $iid ); next unless defined $grp; if ( $grp == 8 ) { $ns_e_parent{$iid} = '0'; } if ( $grp == 1 ) { if ($is_stack) { $ns_e_parent{$iid} = '0'; } else { $ns_e_parent{$iid} = '080100'; } } if ( $grp == 3 ) { my $enc = $ns_grp_enc->{$grp}; if ( $idx % $enc ) { my $npos = ( $idx % $enc ) * $enc; my @parent = ( $grp, $npos, $pos ); my $parent = join( '', map { sprintf "%02d", $_ } @parent ); $ns_e_parent{$iid} = $parent; } elsif ($is_stack) { $ns_e_parent{$iid} = '010100'; } elsif ( $is_virtual and !$is_stack ) { $ns_e_parent{$iid} = 0; } elsif ( $pos == 0 ) { $ns_e_parent{$iid} = '080100'; } else { my $parent = $iid; $parent =~ s/\.\d+$/\.00/; $parent = join( '', map { sprintf "%02d", $_ } split /\./, $parent ); $ns_e_parent{$iid} = $parent; } } next; } return \%ns_e_parent; } sub munge_ns_grp_type { my $oid = shift; my %e_class = ( Sup => 'stack', Bkpl => 'backplane', Brd => 'module', Pwr => 'powerSupply', TmpSnr => 'sensor', Fan => 'fan', Clk => 'other', Unit => 'chassis', ); my $name = &SNMP::translateObj($oid); $name =~ s/s5ChasGrp//; if ( ( defined($name) ) and ( exists( $e_class{$name} ) ) ) { $name = $e_class{$name}; } return $name if defined($name); return $oid; } 1; __END__ =head1 NAME SNMP::Info::NortelStack - SNMP Interface to the Nortel F and F =head1 AUTHOR Eric Miller =head1 SYNOPSIS # Let SNMP::Info determine the correct subclass for you. my $stack = new SNMP::Info( AutoSpecify => 1, Debug => 1, # These arguments are passed directly on to SNMP::Session DestHost => 'myswitch', Community => 'public', Version => 2 ) or die "Can't connect to DestHost.\n"; my $class = $stack->class(); print "SNMP::Info determined this device to fall under subclass : $class\n"; =head1 DESCRIPTION SNMP::Info::NortelStack is a subclass of SNMP::Info that provides an interface to F and F. These MIBs are used across the Nortel Stackable Ethernet Switches (BayStack), as well as, older Nortel devices such as the Centillion family of ATM switches. Use or create in a subclass of SNMP::Info. Do not use directly. =head2 Inherited Classes None. =head2 Required MIBs =over =item F =item F =item F and F are required by the other MIBs. =back =head1 GLOBAL METHODS These are methods that return scalar values from SNMP =over =item $stack->os_ver() Returns the software version extracted from (C) =item $stack->os_bin() Returns the firmware version extracted from (C) =item $stack->serial() Returns serial number of the chassis (C) =item $stack->ns_ag_ver() Returns the version of the agent in the form 'major.minor.maintenance[letters]'. (C) =item $stack->ns_op_mode() Returns the stacking mode. (C) =item $stack->tftp_action() This object is used to download or upload a config file or an image file. (C) =item $stack->tftp_result() Returns the status of the latest action as shown by $stack->tftp_action(). (C) =item $stack->ns_auto_pvid() Returns the value indicating whether adding a port as a member of a VLAN automatically results in its PVID being set to be the same as that VLAN ID. (C) =item $stack->tftp_file() Name of the binary configuration file that will be downloaded/uploaded when the $stack->tftp_action() object is set. (C) =item $stack->tftp_host() The IP address of the TFTP server for all TFTP operations. (C) =item $stack->vlan() Returns the VLAN ID of the system's management VLAN. (C) =item $stack->ch_ser() Returns the serial number of the chassis. (C) =item $stack->ns_cfg_chg() Returns the total number of configuration changes (other than attachment changes, or physical additions or removals) in the chassis that have been detected since cold/warm start. (C) =item $stack->ns_cfg_time() Returns the value of C when the last configuration change (other than attachment changes, or physical additions or removals) in the chassis was detected. (C) =back =head1 TABLE METHODS These are methods that return tables of information in the form of a reference to a hash. =head2 Agent Interface Table (C) =over =item $stack->i_cfg_file() Returns reference to hash. Key: Table entry, Value: Name of the file (C) =item $stack->i_cfg_host() Returns reference to hash. Key: Table entry, Value: IP address of the load server (C) =back =head2 Chassis Components Table (C) =over =item $stack->ns_com_grp_idx() Returns reference to hash. Key: Table entry, Value: Index of the chassis level group which contains this component. (C) =item $stack->ns_com_idx() Returns reference to hash. Key: Table entry, Value: Index of the component in the group. For modules in the 'board' group, this is the slot number. (C) =item $stack->ns_com_sub_idx() Returns reference to hash. Key: Table entry, Value: Index of the sub-component in the component. (C) =item $stack->ns_com_type() Returns reference to hash. Key: Table entry, Value: Type (C) =item $stack->ns_com_descr() Returns reference to hash. Key: Table entry, Value: Description (C) =item $stack->ns_com_ver() Returns reference to hash. Key: Table entry, Value: Version (C) =item $stack->ns_com_serial() Returns reference to hash. Key: Table entry, Value: Serial Number (C) =back =head2 Storage Area Table (C) =over =item $stack->ns_store_grp_idx() Returns reference to hash. Key: Table entry, Value: Index of the chassis level group. (C) =item $stack->ns_store_idx() Returns reference to hash. Key: Table entry, Value: Index of the group. (C) =item $stack->ns_store_sub_idx() Returns reference to hash. Key: Table entry, Value: Index of the sub-component. (C) =item $stack->ns_store_idx() Returns reference to hash. Key: Table entry, Value: Index of the storage area. (C) =item $stack->ns_store_type() Returns reference to hash. Key: Table entry, Value: Type (C) =item $stack->ns_store_size() Returns reference to hash. Key: Table entry, Value: Size (C) =item $stack->ns_store_ver() Returns reference to hash. Key: Table entry, Value: Version (C) =back =head2 Pseudo F information These methods emulate F Physical Table methods using F. =over =item $stack->ns_e_index() Returns reference to hash. Key: IID, Value: Integer, Indices are combined into a six digit integer, each index is two digits padded with leading zero if required. =item $stack->ns_e_class() Returns reference to hash. Key: IID, Value: General hardware type (C). Group is stripped from the string. Values may be Supervisory Module, Back Plane, Board, Power Supply, Sensor, Fan, Clock, Unit. =item $stack->ns_e_descr() Returns reference to hash. Key: IID, Value: Human friendly name (C) =item $stack->ns_e_name() Returns reference to hash. Key: IID, Value: Human friendly name =item $stack->ns_e_hwver() Returns reference to hash. Key: IID, Value: Hardware version (C) =item $stack->ns_e_vendor() Returns reference to hash. Key: IID, Value: nortel =item $stack->ns_e_serial() Returns reference to hash. Key: IID, Value: Serial number (C) =item $stack->ns_e_pos() Returns reference to hash. Key: IID, Value: The relative position among all entities sharing the same parent. (C) =item $stack->ns_e_type() Returns reference to hash. Key: IID, Value: Type of component/sub-component as defined under C in F. =item $stack->ns_e_fwver() Returns reference to hash. Key: IID, Value: Firmware revision. Value of C for entries with rom, boot, or firmware in C. =item $stack->ns_e_swver() Returns reference to hash. Key: IID, Value: Software revision. Value of C for entries with "flash" in C. =item $stack->ns_e_parent() Returns reference to hash. Key: IID, Value: The value of ns_e_index() for the entity which 'contains' this entity. A value of zero indicates this entity is not contained in any other entity. =back =head1 Data Munging Callback Subroutines =over =item $stack->munge_ns_grp_type() Munges C into an C equivalent. =back =cut