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