1# SNMP::Info::Layer3::Extreme - SNMP Interface to Extreme devices 2# 3# Copyright (c) 2012 Eric Miller 4# 5# Copyright (c) 2002,2003 Regents of the University of California 6# All rights reserved. 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions are met: 10# 11# * Redistributions of source code must retain the above copyright notice, 12# this list of conditions and the following disclaimer. 13# * Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# * Neither the name of the University of California, Santa Cruz nor the 17# names of its contributors may be used to endorse or promote products 18# derived from this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24# LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30# POSSIBILITY OF SUCH DAMAGE. 31 32package SNMP::Info::Layer3::Extreme; 33 34use strict; 35use warnings; 36use Exporter; 37use SNMP::Info::Layer3; 38use SNMP::Info::MAU; 39use SNMP::Info::EDP; 40 41@SNMP::Info::Layer3::Extreme::ISA 42 = qw/SNMP::Info::Layer3 SNMP::Info::MAU 43 SNMP::Info::EDP Exporter/; 44@SNMP::Info::Layer3::Extreme::EXPORT_OK = qw//; 45 46our ($VERSION, %GLOBALS, %FUNCS, %MIBS, %MUNGE); 47 48$VERSION = '3.81'; 49 50%MIBS = ( 51 %SNMP::Info::Layer3::MIBS, 52 %SNMP::Info::MAU::MIBS, 53 %SNMP::Info::EDP::MIBS, 54 'EXTREME-BASE-MIB' => 'extremeAgent', 55 'EXTREME-SYSTEM-MIB' => 'extremeSystem', 56 'EXTREME-FDB-MIB' => 'extremeFdbMacFdbMacAddress', 57 'EXTREME-VLAN-MIB' => 'extremeVlan', 58 'EXTREME-POE-MIB' => 'extremePethSystemAdminEnable', 59 'EXTREME-STP-EXTENSIONS-MIB' => 'extremeStpDomainBridgeId', 60); 61 62%GLOBALS = ( 63 %SNMP::Info::Layer3::GLOBALS, 64 %SNMP::Info::MAU::GLOBALS, 65 %SNMP::Info::EDP::GLOBALS, 66 'serial1' => 'extremeSystemID.0', 67 'temp' => 'extremeCurrentTemperature', 68 'ps1_status_old' => 'extremePrimaryPowerOperational.0', 69 'ps1_status_new' => 'extremePowerSupplyStatus.1', 70 'ps2_status_old' => 'extremeRedundantPowerStatus.0', 71 'ps2_status_new' => 'extremePowerSupplyStatus.2', 72 'mac' => 'dot1dBaseBridgeAddress', 73); 74 75%FUNCS = ( 76 %SNMP::Info::Layer3::FUNCS, 77 %SNMP::Info::MAU::FUNCS, 78 %SNMP::Info::EDP::FUNCS, 79 'fan_state' => 'extremeFanOperational', 80 # EXTREME-FDB-MIB:extremeFdbMacFdbTable 81 'ex_fw_mac' => 'extremeFdbMacFdbMacAddress', 82 'ex_fw_port' => 'extremeFdbMacFdbPortIfIndex', 83 'ex_fw_status' => 'extremeFdbMacFdbStatus', 84 # EXTREME-VLAN-MIB:extremeVlanIfTable 85 'ex_vlan_descr' => 'extremeVlanIfDescr', 86 'ex_vlan_global_id' => 'extremeVlanIfGlobalIdentifier', 87 'ex_vlan_id' => 'extremeVlanIfVlanId', 88 # EXTREME-VLAN-MIB:extremeVlanEncapsIfTable 89 'ex_vlan_encap_tag' => 'extremeVlanEncapsIfTag', 90 # EXTREME-VLAN-MIB:extremeVlanOpaqueTable 91 'ex_vlan_untagged' => 'extremeVlanOpaqueUntaggedPorts', 92 'ex_vlan_tagged' => 'extremeVlanOpaqueTaggedPorts', 93 # EXTREME-POE-MIB::extremePethPseSlotTable 94 'peth_power_watts' => 'extremePethSlotPowerLimit', 95 # EXTREME-POE-MIB::extremePethPsePortTable 96 'peth_port_power' => 'extremePethPortMeasuredPower', 97 # EXTREME-STP-EXTENSIONS-MIB::extremeStpDomainTable 98 'stp_i_time' => 'extremeStpDomainTimeSinceTopologyChange', 99 'stp_i_ntop' => 'extremeStpDomainTopChanges', 100 'stp_i_root' => 'extremeStpDomainDesignatedRoot', 101 'stp_i_root_port' => 'extremeStpDomainRootPortIfIndex', 102 'stp_i_priority' => 'extremeStpDomainBridgePriority', 103 'ex_stp_i_mac' => 'extremeStpDomainBridgeId', 104 # EXTREME-STP-EXTENSIONS-MIB::extremeStpPortTable 105 'stp_p_priority' => 'extremeStpPortPortPriority', 106 'stp_p_state' => 'extremeStpPortPortState', 107 'stp_p_cost' => 'extremeStpPortPathCost', 108 'stp_p_root' => 'extremeStpPortDesignatedRoot', 109 'stp_p_bridge' => 'extremeStpPortDesignatedBridge', 110 'stp_p_port' => 'extremeStpPortDesignatedPort', 111); 112 113%MUNGE = ( 114 115 # Inherit all the built in munging 116 %SNMP::Info::Layer3::MUNGE, 117 %SNMP::Info::MAU::MUNGE, 118 %SNMP::Info::EDP::MUNGE, 119 'ex_fw_mac' => \&SNMP::Info::munge_mac, 120 'ps1_status_old' => \&munge_true_ok, 121 'ps1_status_new' => \&munge_power_stat, 122 'ps2_status_old' => \&munge_power_stat, 123 'ps2_status_new' => \&munge_power_stat, 124 'fan_state' => \&munge_true_ok, 125 'ex_vlan_untagged' => \&SNMP::Info::munge_port_list, 126 'ex_vlan_tagged' => \&SNMP::Info::munge_port_list, 127 'ex_stp_i_mac' => \&SNMP::Info::munge_prio_mac, 128 'stp_i_root' => \&SNMP::Info::munge_prio_mac, 129 'stp_p_root' => \&SNMP::Info::munge_prio_mac, 130 'stp_p_bridge' => \&SNMP::Info::munge_prio_mac, 131 'stp_p_port' => \&SNMP::Info::munge_prio_port, 132); 133 134# Method OverRides 135 136*SNMP::Info::Layer3::Extreme::i_duplex = \&SNMP::Info::MAU::mau_i_duplex; 137*SNMP::Info::Layer3::Extreme::i_duplex_admin 138 = \&SNMP::Info::MAU::mau_i_duplex_admin; 139 140sub model { 141 my $extreme = shift; 142 my $id = $extreme->id(); 143 144 unless ( defined $id ) { 145 print 146 " SNMP::Info::Layer3::Extreme::model() - Device does not support sysObjectID\n" 147 if $extreme->debug(); 148 return; 149 } 150 151 my $model = &SNMP::translateObj($id); 152 153 return $id unless defined $model; 154 155 return $model; 156} 157 158sub vendor { 159 return 'extreme'; 160} 161 162sub os { 163 my $extreme = shift; 164 my $desc = $extreme->description(); 165 166 if ( $desc =~ /xos/i ) { 167 return 'xos'; 168 } 169 170 return 'extremeware'; 171} 172 173 174sub os_ver { 175 my $extreme = shift; 176 my $descr = $extreme->description(); 177 return unless defined $descr; 178 179 if ( $descr =~ m/Version\s+([^ ]+)/i ) { 180 return $1; 181 } 182 183 return; 184} 185 186# 187# ifName is a nice concise port name on Extreme devices. 188# Layer3.pm defaults to i_description, which is verbose 189# and has spaces. However, ifName has the IP address 190# assigned for router interfaces, so we use ifDescr 191# for those. 192sub interfaces { 193 my $extreme = shift; 194 my $partial = shift; 195 my $i_name = $extreme->orig_i_name($partial); 196 my $i_description = $extreme->orig_i_description($partial); 197 my $interfaces = {}; 198 foreach my $idx ( keys %$i_name ) { 199 if ( $i_name->{$idx} =~ /\([0-9.]+\)/ ) { 200 $interfaces->{$idx} = $i_description->{$idx}; 201 } 202 else { 203 $interfaces->{$idx} = $i_name->{$idx}; 204 } 205 } 206 return $interfaces; 207} 208 209# 210# Ignore VLAN meta-interfaces and loopback 211sub i_ignore { 212 my $extreme = shift; 213 my $partial = shift; 214 215 my $i_description = $extreme->i_description($partial) || {}; 216 217 my %i_ignore; 218 foreach my $if ( keys %$i_description ) { 219 if ( $i_description->{$if} 220 =~ /^(802.1Q Encapsulation Tag \d+|VLAN \d+|lo\d+|VirtualRouter\d+)/i ) 221 { 222 $i_ignore{$if}++; 223 } 224 } 225 return \%i_ignore; 226} 227 228# When we use the extreme_fw_* objects, we're not using BRIDGE-MIB. 229# Either way, Extreme uses a 1:1 mapping of bridge interface ID to 230# ifIndex. 231sub bp_index { 232 my $extreme = shift; 233 234 my $bindex = $extreme->SUPER::bp_index(); 235 return $bindex if (keys %$bindex); 236 237 my $if_index = $extreme->i_index(); 238 239 my %bp_index; 240 foreach my $iid ( keys %$if_index ) { 241 $bp_index{$iid} = $iid; 242 } 243 return \%bp_index; 244} 245 246sub munge_true_ok { 247 my $val = shift; 248 return unless defined($val); 249 return "OK" if ( $val eq 'true' ); 250 return "Not OK" if ( $val eq 'false' ); 251 return $val; 252} 253 254sub munge_power_stat { 255 my $val = shift; 256 return unless defined($val); 257 $val =~ s/^present//; 258 $val =~ s/^not/Not /i; 259 return $val; 260} 261 262sub ps1_status { 263 my $extreme = shift; 264 my $ps1_status = $extreme->ps1_status_new(); 265 return $ps1_status || $extreme->ps1_status_old(); 266} 267 268sub ps2_status { 269 my $extreme = shift; 270 my $ps2_status = $extreme->ps2_status_new(); 271 return $ps2_status || $extreme->ps2_status_old(); 272} 273 274sub fan { 275 my $extreme = shift; 276 my $fan_state = $extreme->fan_state(); 277 my $ret = ""; 278 my $s = ""; 279 foreach my $i ( sort { $a <=> $b } keys %$fan_state ) { 280 $ret .= $s . $i . ": " . $fan_state->{$i}; 281 $s = ", "; 282 } 283 return if ( $s eq "" ); 284 return $ret; 285} 286 287# For xos based VLAN functions we need to know how the ports are indexed 288# default is slot * 1000, but some older switches start at 1 289sub _slot_factor { 290 my $extreme = shift; 291 292 my $index = $extreme->i_index(); 293 294 return 1 if (exists $index->{1} && $index->{1} == 1); 295 return 1000; 296} 297 298# Some versions of the Extreme firmware have vendor-specific tables 299# for this; those are ex_fw_*(). Some don't have these tables, 300# we use the BRIDGE-MIB tables if available then the ex_fw_*() methods. 301sub fw_mac { 302 my $extreme = shift; 303 304 my $fw_m = $extreme->SUPER::fw_mac(); 305 return $fw_m if (keys %$fw_m); 306 307 return $extreme->ex_fw_mac(); 308} 309 310sub fw_port { 311 my $extreme = shift; 312 313 my $fw_p = $extreme->SUPER::fw_port(); 314 return $fw_p if (keys %$fw_p); 315 316 return $extreme->ex_fw_port(); 317} 318 319sub fw_status { 320 my $extreme = shift; 321 322 my $fw_s = $extreme->SUPER::fw_status(); 323 return $fw_s if (keys %$fw_s); 324 325 return $extreme->ex_fw_status(); 326} 327 328# Mapping the virtual VLAN interfaces: 329# The virtual VLAN interfaces in extremeVlanIfTable 330# are the higher layer above the interfaces that are 331# untagged, and also above an interface in 332# extremeVlanEncapsIfTable that does the encapsulation. 333# Note that it's possible to have a VLAN defined that 334# does not have a tag, if it has all native interfaces. 335# To represent this, we use a negative version of the 336# internal VLAN ID (the deprecated extremeVlanIfGlobalIdentifier) 337sub _if2tag { 338 my $extreme = shift; 339 my $partial = shift; 340 my $stack = shift || $extreme->ifStackStatus($partial); 341 my $encap_tag = $extreme->ex_vlan_encap_tag(); 342 my $vlan_descr = $extreme->ex_vlan_descr(); 343 344 my $stackmap = {}; 345 foreach my $idx ( keys %$stack ) { 346 my ( $higher, $lower ) = split( /\./, $idx ); 347 $stackmap->{$higher}->{$lower} = $stack->{$idx}; 348 } 349 350 my %if2tag = (); 351 my $missed = 0; 352 foreach my $if ( keys %$vlan_descr ) { 353 $if2tag{$if} = -1; 354 foreach my $tagif ( keys %$encap_tag ) { 355 if ( defined( $stackmap->{$if}->{$tagif} ) 356 && $stackmap->{$if}->{$tagif} eq 'active' ) 357 { 358 $if2tag{$if} = $encap_tag->{$tagif}; 359 } 360 } 361 if ( $if2tag{$if} == -1 ) { 362 $missed++; 363 } 364 } 365 if ($missed) { 366 my $global_id = $extreme->ex_vlan_id(); 367 foreach my $if ( keys %if2tag ) { 368 $if2tag{$if} = -$global_id->{$if} 369 if ( $if2tag{$if} == -1 && defined( $global_id->{$if} ) ); 370 } 371 } 372 return \%if2tag; 373} 374 375# No partial support in v_name or v_index, because the obvious partial 376# is the VLAN ID and the index here is the ifIndex of 377# the VLAN interface. 378sub v_name { 379 my $extreme = shift; 380 return $extreme->ex_vlan_descr(); 381} 382 383sub v_index { 384 my $extreme = shift; 385 return $extreme->ex_vlan_id || $extreme->_if2tag(); 386} 387 388sub i_vlan { 389 my $extreme = shift; 390 my $partial = shift; 391 392 # Some devices support Q-Bridge, if so short circuit and return it 393 my $q_bridge = $extreme->SUPER::i_vlan($partial); 394 return $q_bridge if (keys %$q_bridge); 395 396 # Next we try extremeVlanOpaqueTable 397 my $xos = $extreme->_xos_i_vlan($partial); 398 return $xos if (keys %$xos); 399 400 # Try older ifStack method 401 my $extremeware = $extreme->_extremeware_i_vlan($partial); 402 return $extremeware if (keys %$extremeware); 403 404 return; 405} 406 407sub _xos_i_vlan { 408 my $extreme = shift; 409 my $partial = shift; 410 411 my $index = $extreme->i_index(); 412 my $vlans = $extreme->ex_vlan_id() || {}; 413 my $slotx = $extreme->_slot_factor() || 1000; 414 my $u_ports = $extreme->ex_vlan_untagged() || {}; 415 416 my $i_vlan = {}; 417 foreach my $idx ( keys %$u_ports ) { 418 next unless ( defined $u_ports->{$idx} ); 419 my $portlist = $u_ports->{$idx}; 420 my $ret = []; 421 422 my ( $vlan_if, $slot ) = $idx =~ /^(\d+)\.(\d+)/; 423 my $vlan = $vlans->{$vlan_if} || ''; 424 425 # Convert portlist bit array to bp_index array 426 for ( my $i = 0; $i <= $#$portlist; $i++ ) { 427 push( @{$ret}, ( $slotx * $slot + $i + 1 ) ) 428 if ( @$portlist[$i] ); 429 } 430 431 #Create HoA ifIndex -> VLAN array 432 foreach my $port ( @{$ret} ) { 433 my $ifindex = $index->{$port}; 434 next unless ( defined($ifindex) ); # shouldn't happen 435 next if ( defined $partial and $ifindex !~ /^$partial$/ ); 436 $i_vlan->{$ifindex} = $vlan; 437 } 438 } 439 return $i_vlan; 440} 441 442sub _extremeware_i_vlan { 443 my $extreme = shift; 444 my $partial = shift; 445 my $stack = $extreme->ifStackStatus($partial); 446 my $encap_tag = $extreme->ex_vlan_encap_tag(); 447 my $vlan_descr = $extreme->ex_vlan_descr(); 448 my $stackmap = {}; 449 foreach my $idx ( keys %$stack ) { 450 my ( $higher, $lower ) = split( /\./, $idx ); 451 $stackmap->{$higher}->{$lower} = $stack->{$idx}; 452 } 453 my $if2tag = $extreme->_if2tag( $partial, $stack ); 454 455 # 456 # Now that we've done all that mapping work, we can map the 457 # ifStack indexes. 458 my %i_vlan = (); 459 foreach my $if ( keys %$if2tag ) { 460 foreach my $lowif ( keys %{ $stackmap->{$if} } ) { 461 $i_vlan{$lowif} = $if2tag->{$if}; 462 } 463 } 464 return \%i_vlan; 465} 466 467sub i_vlan_membership { 468 my $extreme = shift; 469 my $partial = shift; 470 471 # Some devices support Q-Bridge, if so short circuit and return it 472 my $q_bridge = $extreme->SUPER::i_vlan_membership($partial); 473 return $q_bridge if (ref {} eq ref $q_bridge and scalar keys %$q_bridge); 474 475 # Next we try extremeVlanOpaqueTable 476 my $xos = $extreme->_xos_i_vlan_membership($partial); 477 return $xos if (ref {} eq ref $xos and scalar keys %$xos); 478 479 # Try older ifStack method 480 my $extremeware = $extreme->_extremeware_i_vlan_membership($partial); 481 return $extremeware if (ref {} eq ref $extremeware and scalar keys %$extremeware); 482 483 return; 484} 485 486sub _xos_i_vlan_membership { 487 my $extreme = shift; 488 my $partial = shift; 489 490 my $index = $extreme->i_index(); 491 my $vlans = $extreme->ex_vlan_id(); 492 my $slotx = $extreme->_slot_factor() || 1000; 493 my $u_ports = $extreme->ex_vlan_untagged() || {}; 494 my $t_ports = $extreme->ex_vlan_tagged() || {}; 495 496 my $i_vlan_membership = {}; 497 foreach my $idx ( keys %$u_ports ) { 498 next unless ( defined $u_ports->{$idx} ); 499 my $u_portlist = $u_ports->{$idx}; 500 my $t_portlist = $t_ports->{$idx}; 501 my $ret = []; 502 503 my ( $vlan_if, $slot ) = $idx =~ /^(\d+)\.(\d+)/; 504 my $vlan = $vlans->{$vlan_if} || ''; 505 506 foreach my $portlist ( $u_portlist, $t_portlist ) { 507 508 # Convert portlist bit array to bp_index array 509 for ( my $i = 0; $i <= $#$portlist; $i++ ) { 510 push( @{$ret}, ( $slotx * $slot + $i + 1 ) ) 511 if ( @$portlist[$i] ); 512 } 513 } 514 515 #Create HoA ifIndex -> VLAN array 516 foreach my $port ( @{$ret} ) { 517 my $ifindex = $index->{$port}; 518 next unless ( defined($ifindex) ); # shouldn't happen 519 next if ( defined $partial and $ifindex !~ /^$partial$/ ); 520 push( @{ $i_vlan_membership->{$ifindex} }, $vlan ); 521 } 522 } 523 return $i_vlan_membership; 524} 525 526sub _extremeware_i_vlan_membership { 527 my $extreme = shift; 528 my $partial = shift; 529 my $stack = $extreme->ifStackStatus($partial); 530 my $encap_tag = $extreme->ex_vlan_encap_tag(); 531 my $vlan_descr = $extreme->ex_vlan_descr(); 532 my $stackmap = {}; 533 foreach my $idx ( keys %$stack ) { 534 my ( $higher, $lower ) = split( /\./, $idx ); 535 $stackmap->{$higher}->{$lower} = $stack->{$idx}; 536 } 537 my $if2tag = $extreme->_if2tag( $partial, $stack ); 538 539 # 540 # Now that we've done all that mapping work, we can map the 541 # ifStack indexes. 542 my %i_vlan_membership = (); 543 foreach my $if ( keys %$if2tag ) { 544 foreach my $lowif ( keys %{ $stackmap->{$if} } ) { 545 push( @{ $i_vlan_membership{$lowif} }, $if2tag->{$if} ); 546 } 547 } 548 549 # 550 # Now add all the tagged ports. 551 foreach my $if ( keys %$encap_tag ) { 552 foreach my $lowif ( keys %{ $stackmap->{$if} } ) { 553 push( @{ $i_vlan_membership{$lowif} }, $encap_tag->{$if} ); 554 } 555 } 556 return \%i_vlan_membership; 557} 558 559sub i_vlan_membership_untagged { 560 my $extreme = shift; 561 my $partial = shift; 562 563 # Some devices support Q-Bridge, if so short circuit and return it 564 my $q_bridge = $extreme->SUPER::i_vlan_membership_untagged($partial); 565 return $q_bridge if (ref {} eq ref $q_bridge and scalar keys %$q_bridge); 566 567 # Next we try extremeVlanOpaqueTable 568 my $xos = $extreme->_xos_i_vlan_membership_untagged($partial); 569 return $xos if (ref {} eq ref $xos and scalar keys %$xos); 570 571 # Try older ifStack method 572 my $extremeware = $extreme->_extremeware_i_vlan_membership_untagged($partial); 573 return $extremeware if (ref {} eq ref $extremeware and scalar keys %$extremeware); 574 575 return; 576} 577 578sub _xos_i_vlan_membership_untagged { 579 my $extreme = shift; 580 my $partial = shift; 581 582 my $index = $extreme->i_index(); 583 my $vlans = $extreme->ex_vlan_id(); 584 my $slotx = $extreme->_slot_factor() || 1000; 585 my $u_ports = $extreme->ex_vlan_untagged() || {}; 586 587 my $i_vlan_membership = {}; 588 foreach my $idx ( keys %$u_ports ) { 589 next unless ( defined $u_ports->{$idx} ); 590 my $u_portlist = $u_ports->{$idx}; 591 my $ret = []; 592 593 my ( $vlan_if, $slot ) = $idx =~ /^(\d+)\.(\d+)/; 594 my $vlan = $vlans->{$vlan_if} || ''; 595 596 foreach my $portlist ( $u_portlist ) { 597 598 # Convert portlist bit array to bp_index array 599 for ( my $i = 0; $i <= $#$portlist; $i++ ) { 600 push( @{$ret}, ( $slotx * $slot + $i + 1 ) ) 601 if ( @$portlist[$i] ); 602 } 603 } 604 605 #Create HoA ifIndex -> VLAN array 606 foreach my $port ( @{$ret} ) { 607 my $ifindex = $index->{$port}; 608 next unless ( defined($ifindex) ); # shouldn't happen 609 next if ( defined $partial and $ifindex !~ /^$partial$/ ); 610 push( @{ $i_vlan_membership->{$ifindex} }, $vlan ); 611 } 612 } 613 return $i_vlan_membership; 614} 615 616# Assuming Cisco-like trunk behavior that native VLAN is transmitted untagged 617sub _extremeware_i_vlan_membership_untagged { 618 my $extreme = shift; 619 my $partial = shift; 620 621 my $vlans = $extreme->_extremeware_i_vlan($partial); 622 my $i_vlan_membership = {}; 623 foreach my $port (keys %$vlans) { 624 my $vlan = $vlans->{$port}; 625 push( @{ $i_vlan_membership->{$port} }, $vlan ); 626 } 627 628 return $i_vlan_membership; 629} 630 631# VLAN management. 632# See extreme-vlan.mib for a detailed description of 633# Extreme's use of ifStackTable and EXTREME-VLAN-MIB. 634 635sub set_i_vlan { 636 my $extreme = shift; 637 return $extreme->_extreme_set_i_vlan( 0, @_ ); 638} 639 640sub set_i_pvid { 641 my $extreme = shift; 642 return $extreme->_extreme_set_i_vlan( 1, @_ ); 643} 644 645# set_i_vlan implicitly turns off any encapsulation 646# set_i_pvid retains any encapsulation 647# otherwise they do the same: set the unencapsulated 648# vlan ID. 649# First arg to _set_i_vlan is whether or not to turn 650# off any encapsulation. 651sub _extreme_set_i_vlan { 652 my $extreme = shift; 653 my ( $is_pvid, $vlan_id, $ifindex ) = @_; 654 my $encap_tag = $extreme->ex_vlan_encap_tag(); 655 656 # The inverted stack MIB would make this easier, since 657 # we need to find the vlan interface 658 # that's stacked above $ifindex. 659 my $cur_stack = $extreme->ifStackStatus(); 660 661 # 662 # create inverted stack 663 my $invstack; 664 foreach my $idx ( keys %$cur_stack ) { 665 my ( $higher, $lower ) = split( /\./, $idx ); 666 $invstack->{$lower}->{$higher} = $cur_stack->{$idx}; 667 } 668 669 # create vlan tag -> encap interface map 670 my %encapif = reverse %$encap_tag; 671 672 # now find encap interface from tag 673 my $encapidx = $encapif{$vlan_id}; 674 if ( !defined($encapidx) ) { 675 $extreme->error_throw( 676 "can't map $vlan_id to encapsulation interface"); 677 return; 678 } 679 680 # now find vlan interface stacked above encap 681 my @abovevlan = keys %{ $invstack->{$encapidx} }; 682 if ( @abovevlan != 1 ) { 683 $extreme->error_throw( 684 "can't map encap interface $encapidx for $vlan_id to encapsulation interface" 685 ); 686 return; 687 } 688 my $vlanidx = $abovevlan[0]; 689 my $rv; 690 691 # Delete old VLAN mapping 692 foreach my $oldidx ( keys %{ $invstack->{$ifindex} } ) { 693 if ( $is_pvid && defined( $encap_tag->{$oldidx} ) ) { 694 next; # Don't delete tagged mappings 695 } 696 $rv = $extreme->set_ifStackStatus( "destroy", 697 $oldidx . "." . $ifindex ); 698 unless ($rv) { 699 $extreme->error_throw( 700 "Unable to remove $ifindex from old VLAN index $oldidx"); 701 return; 702 } 703 } 704 705 # Add new VLAN mapping 706 $rv = $extreme->set_ifStackStatus( "createAndGo", 707 $vlanidx . "." . $ifindex ); 708 unless ($rv) { 709 $extreme->error_throw( 710 "Unable to add new VLAN index $vlanidx to ifIndex $ifindex"); 711 return; 712 } 713 714# XXX invalidate cache of ifstack? 715# XXX Info.pm library function for this? 716# XXX set_ should do invalidation? 717# $store = $extreme->store(); delete $store->{ifStackStatus}; $extreme->store($store); 718# $extreme->{_ifStackStatus} = 0; 719 return $rv; 720} 721 722sub set_remove_i_vlan_tagged { 723 my $extreme = shift; 724 my ( $vlan_id, $ifindex ) = @_; 725 my $encap_tag = $extreme->ex_vlan_encap_tag(); 726 727 # create vlan tag -> encap interface map 728 my %encapif = reverse %$encap_tag; 729 730 # now find encap interface from tag 731 my $encapidx = $encapif{$vlan_id}; 732 if ( !defined($encapidx) ) { 733 $extreme->error_throw( 734 "can't map $vlan_id to encapsulation interface"); 735 return; 736 } 737 my $rv = $extreme->set_ifStackStatus( "destroy", 738 $encapidx . "." . $ifindex ); 739 unless ($rv) { 740 $extreme->error_throw( 741 "Unable to delete VLAN encap ifIndex $encapidx for VLAN $vlan_id from ifIndex $ifindex" 742 ); 743 return; 744 } 745 746 # invalidate cache of ifstack? 747 return $rv; 748} 749 750sub set_add_i_vlan_tagged { 751 my $extreme = shift; 752 my ( $vlan_id, $ifindex ) = @_; 753 my $encap_tag = $extreme->ex_vlan_encap_tag(); 754 755 # create vlan tag -> encap interface map 756 my %encapif = reverse %$encap_tag; 757 758 # now find encap interface from tag 759 my $encapidx = $encapif{$vlan_id}; 760 if ( !defined($encapidx) ) { 761 $extreme->error_throw( 762 "can't map $vlan_id to encapsulation interface"); 763 return; 764 } 765 my $rv = $extreme->set_ifStackStatus( "createAndGo", 766 $encapidx . "." . $ifindex ); 767 unless ($rv) { 768 $extreme->error_throw( 769 "Unable to add VLAN encap ifIndex $encapidx for VLAN $vlan_id to ifIndex $ifindex" 770 ); 771 return; 772 } 773 774 # invalidate cache of ifstack? 775 return $rv; 776} 777 778# LLDP uses the bridge index rather than ifIndex 779sub lldp_if { 780 my $extreme = shift; 781 my $partial = shift; 782 783 my $addr = $extreme->lldp_rem_pid($partial) || {}; 784 my $b_index = $extreme->bp_index() || {}; 785 #my %r_i_descr = reverse %$i_descr; 786 787 my %lldp_if; 788 foreach my $key ( keys %$addr ) { 789 my @aOID = split( '\.', $key ); 790 my $port = $aOID[1]; 791 next unless $port; 792 793 my $idx = $b_index->{$port}; 794 795 $lldp_if{$key} = $idx; 796 } 797 return \%lldp_if; 798} 799 800# extremeStpDomainStpdInstance not accessible, so we need to extract from iid 801sub stp_i_id { 802 my $extreme = shift; 803 my $partial = shift; 804 805 my $stp_i_roots = $extreme->stp_i_root($partial); 806 807 my %stp_i_id; 808 foreach my $iid ( keys %$stp_i_roots ) { 809 $stp_i_id{$iid} = $iid; 810 } 811 return \%stp_i_id; 812} 813 814# extremeStpDomainBridgeId returns priority and mac, 815# for cross class compatibility we just need mac 816sub stp_i_mac { 817 my $extreme = shift; 818 my $partial = shift; 819 820 my $stp_i_bids = $extreme->ex_stp_i_mac($partial); 821 822 my %stp_i_mac; 823 foreach my $iid ( keys %$stp_i_bids ) { 824 my $mac = $stp_i_bids->{$iid}; 825 next unless $mac; 826 827 $mac =~ s/^([0-9A-F][0-9A-F]:){2}//; 828 829 $stp_i_mac{$iid} = $mac; 830 } 831 return \%stp_i_mac; 832} 833 834# Break up the extremeStpPortEntry INDEX into Stpd Instance and IfIndex. 835sub _ex_stpport_index { 836 my $idx = shift; 837 my ( $id, $ifindex ) = split( /\./, $idx); 838 return ($id, $ifindex); 839} 840 841# extremeStpPortPortIfIndex not-accessible, extract from iid 842sub stp_p_id { 843 my $extreme = shift; 844 my $partial = shift; 845 846 my $stp_port = $extreme->stp_p_root($partial); 847 my $stp_p_id = {}; 848 foreach my $idx ( keys %$stp_port ) { 849 my ( $id, $ifindex ) = _ex_stpport_index($idx); 850 $stp_p_id->{$idx} = $ifindex; 851 } 852 return $stp_p_id; 853} 854 855# extremeStpDomainStpdInstance not-accessible, extract from iid 856sub stp_p_stg_id { 857 my $extreme = shift; 858 my $partial = shift; 859 860 my $stp_port = $extreme->stp_p_root($partial); 861 my $stp_p_stg_id = {}; 862 foreach my $idx ( keys %$stp_port ) { 863 my ( $id, $ifindex ) = _ex_stpport_index($idx); 864 $stp_p_stg_id->{$idx} = $id; 865 } 866 return $stp_p_stg_id; 867} 868 8691; 870 871__END__ 872 873=head1 NAME 874 875SNMP::Info::Layer3::Extreme - Perl5 Interface to Extreme Network Devices 876 877=head1 AUTHOR 878 879Eric Miller, Bill Fenner 880 881=head1 SYNOPSIS 882 883 # Let SNMP::Info determine the correct subclass for you. 884 my $extreme = new SNMP::Info( 885 AutoSpecify => 1, 886 Debug => 1, 887 DestHost => 'myswitch', 888 Community => 'public', 889 Version => 2 890 ) 891 or die "Can't connect to DestHost.\n"; 892 893 my $class = $extreme->class(); 894 895 print "SNMP::Info determined this device to fall under subclass : $class\n"; 896 897=head1 DESCRIPTION 898 899Provides abstraction to the configuration information obtainable from an 900Extreme device through SNMP. 901 902=head2 Inherited Classes 903 904=over 905 906=item SNMP::Info::Layer3 907 908=item SNMP::Info::MAU 909 910=item SNMP::Info::EDP 911 912=back 913 914=head2 Required MIBs 915 916=over 917 918=item F<EXTREME-BASE-MIB> 919 920=item F<EXTREME-SYSTEM-MIB> 921 922=item F<EXTREME-FDB-MIB> 923 924=item F<EXTREME-VLAN-MIB> 925 926=item F<EXTREME-POE-MIB> 927 928=item F<EXTREME-STP-EXTENSIONS-MIB> 929 930=item Inherited Classes' MIBs 931 932See classes listed above for their required MIBs. 933 934=back 935 936=head1 GLOBALS 937 938These are methods that return scalar value from SNMP 939 940=over 941 942=item $extreme->model() 943 944Returns model type. Checks $extreme->id() against the F<EXTREME-BASE-MIB>. 945 946=item $extreme->vendor() 947 948Returns C<extreme> 949 950=item $extreme->os() 951 952Returns C<xos> or C<extremeware> depending on description() 953 954=item $extreme->os_ver() 955 956Parses device operating system version from description() 957 958=item $extreme->serial() 959 960Returns serial number 961 962(C<extremeSystemID>) 963 964=item $extreme->temp() 965 966Returns system temperature 967 968(C<extremeCurrentTemperature>) 969 970=item $extreme->ps1_status() 971 972Returns status of power supply 1 973 974(C<extremePowerSupplyStatus.1>) 975 976=item $extreme->ps2_status() 977 978Returns status of power supply 2 979 980(C<extremePowerSupplyStatus.2>) 981 982=item $extreme->fan() 983 984Returns fan status 985 986(C<extremeFanOperational.1>) 987 988=item $extreme->mac() 989 990Returns base mac 991 992(C<dot1dBaseBridgeAddress>) 993 994=back 995 996=head2 Globals imported from SNMP::Info::Layer3 997 998See documentation in L<SNMP::Info::Layer3/"GLOBALS"> for details. 999 1000=head2 Globals imported from SNMP::Info::MAU 1001 1002See documentation in L<SNMP::Info::MAU/"GLOBALS"> for details. 1003 1004=head2 Globals imported from SNMP::Info::EDP 1005 1006See documentation in L<SNMP::Info::EDP/"GLOBALS"> for details. 1007 1008=head1 TABLE METHODS 1009 1010These are methods that return tables of information in the form of a reference 1011to a hash. 1012 1013=head2 Overrides 1014 1015=over 1016 1017=item $extreme->interfaces() 1018 1019Returns a mapping between the Interface Table Index (iid) and the physical 1020port name. 1021 1022=item $extreme->i_duplex() 1023 1024Parses mau_index and mau_link to return the duplex information for 1025interfaces. 1026 1027=item $extreme->i_duplex_admin() 1028 1029Parses C<mac_index>,C<mau_autostat>,C<mau_type_admin> in 1030order to find the admin duplex setting for all the interfaces. 1031 1032Returns either (auto,full,half). 1033 1034=item $extreme->i_ignore() 1035 1036Returns reference to hash. Increments value of IID if port is to be ignored. 1037 1038Ignores VLAN meta interfaces and loopback 1039 1040=item $extreme->fw_mac() 1041 1042(C<extremeFdbMacFdbMacAddress>) 1043 1044=item $extreme->fw_port() 1045 1046(C<extremeFdbMacFdbPortIfIndex>) 1047 1048=item $extreme->fw_status() 1049 1050(C<extremeFdbMacFdbStatus>) 1051 1052=item $extreme->lldp_if() 1053 1054Returns the mapping to the SNMP Interface Table. Extreme LLDP uses the 1055bridge index rather than ifIndex. 1056 1057=item $extreme->i_vlan() 1058 1059Returns a mapping between C<ifIndex> and the VLAN. 1060 1061=item $extreme->i_vlan_membership() 1062 1063Returns reference to hash of arrays: key = C<ifIndex>, value = array of VLAN 1064IDs. These are the VLANs which are members of the egress list for the port. 1065 1066 Example: 1067 my $interfaces = $extreme->interfaces(); 1068 my $vlans = $extreme->i_vlan_membership(); 1069 1070 foreach my $iid (sort keys %$interfaces) { 1071 my $port = $interfaces->{$iid}; 1072 my $vlan = join(',', sort(@{$vlans->{$iid}})); 1073 print "Port: $port VLAN: $vlan\n"; 1074 } 1075 1076=item $extreme->i_vlan_membership_untagged() 1077 1078Returns reference to hash of arrays: key = C<ifIndex>, value = array of VLAN 1079IDs. These are the VLANs which are members of the untagged egress list for 1080the port. 1081 1082=item $extreme->v_index() 1083 1084Returns VLAN IDs 1085 1086=item $extreme->v_name() 1087 1088Returns VLAN names 1089 1090(C<extremeVlanIfDescr>) 1091 1092=item $extreme->bp_index() 1093 1094Returns reference to hash of bridge port table entries map back to interface 1095identifier (iid) 1096 1097Returns (C<ifIndex>) for both key and value since we're using 1098F<EXTREME-FDB-MIB> rather than F<BRIDGE-MIB>. 1099 1100=item $extreme->peth_port_power() 1101 1102Power supplied by PoE ports, in milliwatts 1103 1104(C<extremePethPortMeasuredPower>) 1105 1106=item $extreme->peth_power_watts() 1107 1108The configured maximum amount of in-line power available to the slot. 1109 1110(C<extremePethSlotPowerLimit>) 1111 1112=back 1113 1114=head2 Spanning Tree Instance Globals 1115 1116=over 1117 1118=item $extreme->stp_i_mac() 1119 1120Returns the MAC extracted from (C<extremeStpDomainBridgeId>). 1121 1122=item $extreme->stp_i_id() 1123 1124Returns the unique identifier of the STP domain. 1125 1126(C<extremeStpDomainStpdInstance>) 1127 1128=item $extreme->stp_i_time() 1129 1130Returns time since last topology change detected. (100ths/second) 1131 1132(C<extremeStpDomainTimeSinceTopologyChange>) 1133 1134=item $extreme->stp_i_time() 1135 1136Returns time since last topology change detected. (100ths/second) 1137 1138(C<extremeStpDomainTimeSinceTopologyChange>) 1139 1140=item $extreme->stp_i_time() 1141 1142Returns the total number of topology changes detected. 1143 1144(C<extremeStpDomainTopChanges>) 1145 1146=item $extreme->stp_i_root() 1147 1148Returns root of STP. 1149 1150(C<extremeStpDomainDesignatedRoot>) 1151 1152=item $extreme->stp_i_root_port() 1153 1154Returns the port number of the port that offers the lowest cost path 1155to the root bridge. 1156 1157(C<extremeStpDomainRootPortIfIndex>) 1158 1159=item $extreme->stp_i_priority() 1160 1161Returns the port number of the port that offers the lowest cost path 1162to the root bridge. 1163 1164(C<extremeStpDomainBridgePriority>) 1165 1166=back 1167 1168=head2 Spanning Tree Protocol Port Table 1169 1170=over 1171 1172=item $extreme->stp_p_id() 1173 1174(C<extremeStpPortPortIfIndex>) 1175 1176=item $extreme->stp_p_stg_id() 1177 1178(C<extremeStpDomainStpdInstance>) 1179 1180=item $extreme->stp_p_priority() 1181 1182(C<extremeStpPortPortPriority>) 1183 1184=item $extreme->stp_p_state() 1185 1186(C<extremeStpPortPortState>) 1187 1188=item $extreme->stp_p_cost() 1189 1190(C<extremeStpPortPathCost>) 1191 1192=item $extreme->stp_p_root() 1193 1194(C<extremeStpPortDesignatedRoot>) 1195 1196=item $extreme->stp_p_bridge() 1197 1198(C<extremeStpPortDesignatedBridge>) 1199 1200=item $extreme->stp_p_port() 1201 1202(C<extremeStpPortDesignatedPort>) 1203 1204=back 1205 1206=head2 Table Methods imported from SNMP::Info::Layer3 1207 1208See documentation in L<SNMP::Info::Layer3/"TABLE METHODS"> for details. 1209 1210=head2 Table Methods imported from SNMP::Info::MAU 1211 1212See documentation in L<SNMP::Info::MAU/"TABLE METHODS"> for details. 1213 1214=head2 Table Methods imported from SNMP::Info::EDP 1215 1216See documentation in L<SNMP::Info::EDP/"TABLE METHODS"> for details. 1217 1218=head1 SET METHODS 1219 1220These are methods that provide SNMP set functionality for overridden methods 1221or provide a simpler interface to complex set operations. See 1222L<SNMP::Info/"SETTING DATA VIA SNMP"> for general information on set 1223operations. 1224 1225=over 1226 1227=item $extreme->set_i_vlan ( vlan, ifIndex ) 1228 1229Changes an access (untagged) port VLAN, must be supplied with the numeric 1230VLAN ID and port C<ifIndex>. This method should only be used on end station 1231(non-trunk) ports. 1232 1233 Example: 1234 my %if_map = reverse %{$extreme->interfaces()}; 1235 $extreme->set_i_vlan('2', $if_map{'FastEthernet0/1'}) 1236 or die "Couldn't change port VLAN. ",$extreme->error(1); 1237 1238=item $extreme->set_i_pvid ( pvid, ifIndex ) 1239 1240Sets port default VLAN, must be supplied with the numeric VLAN ID and 1241port C<ifIndex>. This method should only be used on trunk ports. 1242 1243 Example: 1244 my %if_map = reverse %{$extreme->interfaces()}; 1245 $extreme->set_i_pvid('2', $if_map{'FastEthernet0/1'}) 1246 or die "Couldn't change port default VLAN. ",$extreme->error(1); 1247 1248=item $extreme->set_add_i_vlan_tagged ( vlan, ifIndex ) 1249 1250Adds the VLAN to the enabled VLANs list of the port, must be supplied with the 1251numeric VLAN ID and port C<ifIndex>. 1252 1253 Example: 1254 my %if_map = reverse %{$extreme->interfaces()}; 1255 $extreme->set_add_i_vlan_tagged('2', $if_map{'FastEthernet0/1'}) 1256 or die "Couldn't add port to egress list. ",$extreme->error(1); 1257 1258=item $extreme->set_remove_i_vlan_tagged ( vlan, ifIndex ) 1259 1260Removes the VLAN from the enabled VLANs list of the port, must be supplied 1261with the numeric VLAN ID and port C<ifIndex>. 1262 1263 Example: 1264 my %if_map = reverse %{$extreme->interfaces()}; 1265 $extreme->set_remove_i_vlan_tagged('2', $if_map{'FastEthernet0/1'}) 1266 or die "Couldn't add port to egress list. ",$extreme->error(1); 1267 1268=back 1269 1270=head1 Data Munging Callback Subroutines 1271 1272=over 1273 1274=item $extreme->munge_power_stat() 1275 1276Removes 'present' and changes 'not' to 'Not' in the front of a string. 1277 1278=item $extreme->munge_true_ok() 1279 1280Replaces 'true' with "OK" and 'false' with "Not OK". 1281 1282=back 1283 1284=cut 1285