From 8955e25c4fa867a0472292617691cf379080580b Mon Sep 17 00:00:00 2001 From: Bill Fenner <> Date: Wed, 13 Jun 2007 15:11:04 +0000 Subject: [PATCH] Add new VLAN methods to Extreme. Add additional powersupply and fan info. Use the concise interface name (e.g., "1/32"), instead of description (e.g., "RMON Port 32 on Unit 1"). --- Info/Layer3/Extreme.pm | 433 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 402 insertions(+), 31 deletions(-) diff --git a/Info/Layer3/Extreme.pm b/Info/Layer3/Extreme.pm index 0f355e54..92cb04cb 100644 --- a/Info/Layer3/Extreme.pm +++ b/Info/Layer3/Extreme.pm @@ -1,5 +1,6 @@ # SNMP::Info::Layer3::Extreme - SNMP Interface to Extreme devices # Eric Miller +# Bill Fenner # # Copyright (c) 2005 Eric Miller # @@ -40,7 +41,7 @@ use SNMP::Info::MAU; use vars qw/$VERSION $DEBUG %GLOBALS %FUNCS $INIT %MIBS %MUNGE/; -$VERSION = '1.04'; +$VERSION = '1.05'; @SNMP::Info::Layer3::Extreme::ISA = qw/SNMP::Info::Layer3 SNMP::Info::MAU Exporter/; @SNMP::Info::Layer3::Extreme::EXPORT_OK = qw//; @@ -50,37 +51,50 @@ $VERSION = '1.04'; 'EXTREME-BASE-MIB' => 'extremeAgent', 'EXTREME-SYSTEM-MIB' => 'extremeSystem', 'EXTREME-FDB-MIB' => 'extremeSystem', + 'EXTREME-VLAN-MIB' => 'extremeVlan', ); %GLOBALS = ( %SNMP::Info::Layer3::GLOBALS, %SNMP::Info::MAU::GLOBALS, - 'serial' => 'extremeSystemID', - 'temp' => 'extremeCurrentTemperature', - 'ps1_status' => 'extremePowerSupplyStatus.1', - 'fan' => 'extremeFanOperational.1', - 'mac' => 'dot1dBaseBridgeAddress', + 'serial1' => 'extremeSystemID.0', + 'temp' => 'extremeCurrentTemperature', + 'ps1_status_old' => 'extremePrimaryPowerOperational.0', + 'ps1_status_new' => 'extremePowerSupplyStatus.1', + 'ps2_status_old' => 'extremeRedundantPowerStatus.0', + 'ps2_status_new' => 'extremePowerSupplyStatus.2', + 'mac' => 'dot1dBaseBridgeAddress', ); %FUNCS = ( %SNMP::Info::Layer3::FUNCS, %SNMP::Info::MAU::FUNCS, + 'fan_state' => 'extremeFanOperational', # EXTREME-FDB-MIB:extremeFdbMacFdbTable - 'fw_mac' => 'extremeFdbMacFdbMacAddress', - 'fw_port' => 'extremeFdbMacFdbPortIfIndex', - 'fw_status' => 'extremeFdbMacFdbStatus', + 'ex_fw_mac' => 'extremeFdbMacFdbMacAddress', + 'ex_fw_port' => 'extremeFdbMacFdbPortIfIndex', + 'ex_fw_status' => 'extremeFdbMacFdbStatus', + # EXTREME-VLAN-MIB:extremeVlanIfTable + 'ex_vlan_descr' => 'extremeVlanIfDescr', + 'ex_vlan_global_id' => 'extremeVlanIfGlobalIdentifier', + # EXTREME-VLAN-MIB:extremeVlanEncapsIfTable + 'ex_vlan_encap_tag' => 'extremeVlanEncapsIfTag', ); %MUNGE = ( # Inherit all the built in munging %SNMP::Info::Layer3::MUNGE, %SNMP::Info::MAU::MUNGE, + 'ex_fw_mac' => \&SNMP::Info::munge_mac, + 'ps1_status_old' => \&munge_true_ok, + 'ps1_status_new' => \&munge_power_stat, + 'ps2_status_old' => \&munge_power_stat, + 'ps2_status_new' => \&munge_power_stat, + 'fan_state' => \&munge_true_ok, ); # Method OverRides -sub bulkwalk_no { 1; } - *SNMP::Info::Layer3::Extreme::i_duplex = \&SNMP::Info::MAU::mau_i_duplex; *SNMP::Info::Layer3::Extreme::i_duplex_admin = \&SNMP::Info::MAU::mau_i_duplex_admin; @@ -120,7 +134,48 @@ sub os_ver { return undef; } -# We're not using BRIDGE-MIB +# +# ifName is a nice concise port name on Extreme devices. +# Layer3.pm defaults to i_description, which is verbose +# and has spaces. However, ifName has the IP address +# assigned for router interfaces, so we use ifDescr +# for those. +sub interfaces { + my $extreme = shift; + my $partial = shift; + my $i_name = $extreme->orig_i_name($partial); + my $i_description = $extreme->orig_i_description($partial); + my $interfaces = {}; + foreach my $idx (keys %$i_name) { + if ($i_name->{$idx} =~ /\([0-9.]+\)/) { + $interfaces->{$idx} = $i_description->{$idx}; + } else { + $interfaces->{$idx} = $i_name->{$idx}; + } + } + return $interfaces; +} + +# +# Ignore VLAN meta-interfaces and loopback +sub i_ignore { + my $extreme = shift; + my $partial = shift; + + my $i_description = $extreme->i_description($partial) || {}; + + my %i_ignore; + foreach my $if (keys %$i_description) { + if ($i_description->{$if} =~ /^(802.1Q Encapsulation Tag \d+|VLAN \d+|lo\d+)/i){ + $i_ignore{$if}++; + } + } + return \%i_ignore; +} + +# When we use the extreme_fw_* objects, we're not using BRIDGE-MIB. +# Either way, Extreme uses a 1:1 mapping of bridge interface ID to +# ifIndex. sub bp_index { my $extreme = shift; my $if_index = $extreme->i_index(); @@ -132,28 +187,301 @@ sub bp_index { return \%bp_index; } -# Index values in the Q-BRIDGE-MIB are the same -# as in the BRIDGE-MIB and do not match ifIndex. +sub munge_true_ok { + my $val = shift; + return undef unless defined($val); + return "OK" if ($val eq 'true'); + return "Not OK" if ($val eq 'false'); + return $val; +} + +sub munge_power_stat { + my $val = shift; + return undef unless defined($val); + $val =~ s/^present//; + $val =~ s/^not/Not /i; + return $val; +} + +sub ps1_status { + my $extreme = shift; + my $ps1_status = $extreme->ps1_status_new(); + return $ps1_status || $extreme->ps1_status_old(); +} + +sub ps2_status { + my $extreme = shift; + my $ps2_status = $extreme->ps2_status_new(); + return $ps2_status || $extreme->ps2_status_old(); +} + +sub fan { + my $extreme = shift; + my $fan_state = $extreme->fan_state(); + my $ret = ""; + my $s = ""; + foreach my $i (sort {$a <=> $b} keys %$fan_state) { + $ret .= $s . $i . ": " . $fan_state->{$i}; + $s = ", "; + } + return undef if ($s eq ""); + $ret; +} + +# Newer versions of the Extreme firmware have vendor-specific tables +# for this; those are ex_fw_*(). Older firmware versions don't have +# these tables, so we use the BRIDGE-MIB tables. +sub fw_mac { + my $extreme = shift; + my $fw_mac = $extreme->ex_fw_mac; + return $fw_mac if defined($fw_mac); + return $extreme->orig_fw_mac(); +} + +sub fw_port { + my $extreme = shift; + my $fw_port = $extreme->ex_fw_port; + return $fw_port if defined($fw_port); + return $extreme->orig_fw_port(); +} + +sub fw_status { + my $extreme = shift; + my $fw_status = $extreme->ex_fw_status; + return $fw_status if defined($fw_status); + return $extreme->orig_fw_status(); +} + +# Mapping the virtual VLAN interfaces: +# The virtual VLAN interfaces in extremeVlanIfTable +# are the higher layer above the interfaces that are +# untagged, and also above an interface in +# extremeVlanEncapsIfTable that does the encapsulation. +# Note that it's possible to have a VLAN defined that +# does not have a tag, if it has all native interfaces. +# To represent this, we use a negative version of the +# internal VLAN ID (the deprecated extremeVlanIfGlobalIdentifier) +sub _if2tag { + my $extreme = shift; + my $partial = shift; + my $stack = shift || $extreme->ifStackStatus($partial); + my $encap_tag = $extreme->ex_vlan_encap_tag(); + my $vlan_descr = $extreme->ex_vlan_descr(); + + my $stackmap = {}; + foreach my $idx (keys %$stack) { + my ($higher, $lower) = split(/\./, $idx); + $stackmap->{$higher}->{$lower} = $stack->{$idx}; + } + + my %if2tag = (); + my $missed = 0; + foreach my $if (keys %$vlan_descr) { + $if2tag{$if} = -1; + foreach my $tagif (keys %$encap_tag) { + if (defined($stackmap->{$if}->{$tagif}) && $stackmap->{$if}->{$tagif} eq 'active') { + $if2tag{$if} = $encap_tag->{$tagif}; + } + } + if ($if2tag{$if} == -1) { + $missed++; + } + } + if ($missed) { + my $global_id = $extreme->ex_vlan_global_id(); + foreach my $if (keys %if2tag) { + $if2tag{$if} = -$global_id->{$if} if ($if2tag{$if} == -1 && defined($global_id->{$if})); + } + } + \%if2tag; +} + +# No partial support in v_name or v_index, because the obivous partial +# is the VLAN ID and the index here is the ifIndex of +# the VLAN interface. +sub v_name { + my $extreme = shift; + return $extreme->ex_vlan_descr(); +} + +sub v_index { + my $extreme = shift; + return $extreme->_if2tag(); +} + sub i_vlan { my $extreme = shift; - my $qb_i_vlan = $extreme->qb_i_vlan(); - my $bp_index = $extreme->bp_index(); - - my %i_vlan; - foreach my $v_index (keys %$qb_i_vlan){ - my $vlan = $qb_i_vlan->{$v_index}; - my $iid = $bp_index->{$v_index}; - - unless (defined $iid) { - print " Port $v_index has no bp_index mapping. Skipping\n" - if $DEBUG; - next; + my $partial = shift; + my $stack = $extreme->ifStackStatus($partial); + my $encap_tag = $extreme->ex_vlan_encap_tag(); + my $vlan_descr = $extreme->ex_vlan_descr(); + my $stackmap = {}; + foreach my $idx (keys %$stack) { + my ($higher, $lower) = split(/\./, $idx); + $stackmap->{$higher}->{$lower} = $stack->{$idx}; + } + my $if2tag = $extreme->_if2tag($partial, $stack); + # + # Now that we've done all that mapping work, we can map the + # ifStack indexes. + my %i_vlan = (); + foreach my $if (keys %$if2tag) { + foreach my $lowif (keys %{$stackmap->{$if}}) { + $i_vlan{$lowif} = $if2tag->{$if}; } - $i_vlan{$iid}=$vlan; } return \%i_vlan; } +sub i_vlan_membership { + my $extreme = shift; + my $partial = shift; + my $stack = $extreme->ifStackStatus($partial); + my $encap_tag = $extreme->ex_vlan_encap_tag(); + my $vlan_descr = $extreme->ex_vlan_descr(); + my $stackmap = {}; + foreach my $idx (keys %$stack) { + my ($higher, $lower) = split(/\./, $idx); + $stackmap->{$higher}->{$lower} = $stack->{$idx}; + } + my $if2tag = $extreme->_if2tag($partial, $stack); + # + # Now that we've done all that mapping work, we can map the + # ifStack indexes. + my %i_vlan_membership = (); + foreach my $if (keys %$if2tag) { + foreach my $lowif (keys %{$stackmap->{$if}}) { + push(@{$i_vlan_membership{$lowif}}, $if2tag->{$if}); + } + } + # + # Now add all the tagged ports. + foreach my $if (keys %$encap_tag) { + foreach my $lowif (keys %{$stackmap->{$if}}) { + push(@{$i_vlan_membership{$lowif}}, $encap_tag->{$if}); + } + } + return \%i_vlan_membership; +} + +# VLAN management. +# See extreme-vlan.mib for a detailed description of +# Extreme's use of ifStackTable and EXTREME-VLAN-MIB. + +sub set_i_vlan { + my $extreme = shift; + return $extreme->_extreme_set_i_vlan(0, @_); +} + +sub set_i_pvid { + my $extreme = shift; + return $extreme->_extreme_set_i_vlan(1, @_); +} + +# set_i_vlan implicitly turns off any encapsulation +# set_i_pvid retains any encapsulation +# otherwise they do the same: set the unencapsulated +# vlan ID. +# First arg to _set_i_vlan is whether or not to turn +# off any encapsulation. +sub _extreme_set_i_vlan { + my $extreme = shift; + my ($is_pvid, $vlan_id, $ifindex) = @_; + my $encap_tag = $extreme->ex_vlan_encap_tag(); + # The inverted stack MIB would make this easier, since + # we need to find the vlan interface + # that's stacked above $ifindex. + my $cur_stack = $extreme->ifStackStatus(); + # + # create inverted stack + my $invstack; + foreach my $idx (keys %$cur_stack) { + my ($higher, $lower) = split(/\./, $idx); + $invstack->{$lower}->{$higher} = $cur_stack->{$idx}; + } + # create vlan tag -> encap interface map + my %encapif = reverse %$encap_tag; + # now find encap interface from tag + my $encapidx = $encapif{$vlan_id}; + if (!defined($encapidx)) { + $extreme->error_throw("can't map $vlan_id to encapsulation interface"); + return undef; + } + # now find vlan interface stacked above encap + my @abovevlan = keys %{$invstack->{$encapidx}}; + if (@abovevlan != 1) { + $extreme->error_throw("can't map encap interface $encapidx for $vlan_id to encapsulation interface"); + return undef; + } + my $vlanidx = $abovevlan[0]; + my $rv; + # Delete old VLAN mapping + foreach my $oldidx (keys %{$invstack->{$ifindex}}) { + if ($is_pvid && defined($encap_tag->{$oldidx})) { + next; # Don't delete tagged mappings + } + $rv = $extreme->set_ifStackStatus("destroy", $oldidx . "." . $ifindex); + unless ($rv) { + $extreme->error_throw("Unable to remove $ifindex from old VLAN index $oldidx"); + return undef; + } + } + # Add new VLAN mapping + $rv = $extreme->set_ifStackStatus("createAndGo", $vlanidx . "." . $ifindex); + unless ($rv) { + $extreme->error_throw("Unable to add new VLAN index $vlanidx to ifIndex $ifindex"); + return undef; + } + # XXX invalidate cache of ifstack? + # XXX Info.pm library function for this? + # XXX set_ should do invalidation? + # $store = $extreme->store(); delete $store->{ifStackStatus}; $extreme->store($store); + # $extreme->{_ifStackStatus} = 0; + return $rv; +} + +sub set_remove_i_vlan_tagged { + my $extreme = shift; + my ($vlan_id, $ifindex) = @_; + my $encap_tag = $extreme->ex_vlan_encap_tag(); + # create vlan tag -> encap interface map + my %encapif = reverse %$encap_tag; + # now find encap interface from tag + my $encapidx = $encapif{$vlan_id}; + if (!defined($encapidx)) { + $extreme->error_throw("can't map $vlan_id to encapsulation interface"); + return undef; + } + my $rv = $extreme->set_ifStackStatus("destroy", $encapidx . "." . $ifindex); + unless ($rv) { + $extreme->error_throw("Unable to delete VLAN encap ifIndex $encapidx for VLAN $vlan_id from ifIndex $ifindex"); + return undef; + } + # invalidate cache of ifstack? + return $rv; +} + +sub set_add_i_vlan_tagged { + my $extreme = shift; + my ($vlan_id, $ifindex) = @_; + my $encap_tag = $extreme->ex_vlan_encap_tag(); + # create vlan tag -> encap interface map + my %encapif = reverse %$encap_tag; + # now find encap interface from tag + my $encapidx = $encapif{$vlan_id}; + if (!defined($encapidx)) { + $extreme->error_throw("can't map $vlan_id to encapsulation interface"); + return undef; + } + my $rv = $extreme->set_ifStackStatus("createAndGo", $encapidx . "." . $ifindex); + unless ($rv) { + $extreme->error_throw("Unable to add VLAN encap ifIndex $encapidx for VLAN $vlan_id to ifIndex $ifindex"); + return undef; + } + # invalidate cache of ifstack? + return $rv; +} + 1; __END__ @@ -163,7 +491,7 @@ SNMP::Info::Layer3::Extreme - Perl5 Interface to Extreme Network Devices =head1 AUTHOR -Eric Miller +Eric Miller, Bill Fenner =head1 SYNOPSIS @@ -212,6 +540,8 @@ my $extreme = new SNMP::Info::Layer3::Extreme(...); =item EXTREME-FDB-MIB +=item EXTREME-VLAN-MIB + =item Inherited Classes' MIBs See classes listed above for their required MIBs. @@ -224,9 +554,9 @@ These are methods that return scalar value from SNMP =over -=item $extreme->bulkwalk_no - -Return C<1>. Bulkwalk is currently turned off for this class. +#=item $extreme->bulkwalk_no +# +#Return C<1>. Bulkwalk is currently turned off for this class. =item $extreme->model() @@ -328,4 +658,45 @@ See documentation in L for details. See documentation in L for details. +=item $extreme->set_i_vlan ( vlan, ifIndex ) + +Changes an access (untagged) port VLAN, must be supplied with the numeric +VLAN ID and port ifIndex. This method should only be used on end station +(non-trunk) ports. + + Example: + my %if_map = reverse %{$extreme->interfaces()}; + $extreme->set_i_vlan('2', $if_map{'FastEthernet0/1'}) + or die "Couldn't change port VLAN. ",$extreme->error(1); + +=item $extreme->set_i_pvid ( pvid, ifIndex ) + +Sets port default VLAN, must be supplied with the numeric VLAN ID and +port ifIndex. This method should only be used on trunk ports. + + Example: + my %if_map = reverse %{$extreme->interfaces()}; + $extreme->set_i_pvid('2', $if_map{'FastEthernet0/1'}) + or die "Couldn't change port default VLAN. ",$extreme->error(1); + +=item $extreme->set_add_i_vlan_tagged ( vlan, ifIndex ) + +Adds the VLAN to the enabled VLANs list of the port, must be supplied with the +numeric VLAN ID and port ifIndex. + + Example: + my %if_map = reverse %{$extreme->interfaces()}; + $extreme->set_add_i_vlan_tagged('2', $if_map{'FastEthernet0/1'}) + or die "Couldn't add port to egress list. ",$extreme->error(1); + +=item $extreme->set_remove_i_vlan_tagged ( vlan, ifIndex ) + +Removes the VLAN from the enabled VLANs list of the port, must be supplied +with the numeric VLAN ID and port ifIndex. + + Example: + my %if_map = reverse %{$extreme->interfaces()}; + $extreme->set_remove_i_vlan_tagged('2', $if_map{'FastEthernet0/1'}) + or die "Couldn't add port to egress list. ",$extreme->error(1); + =cut