diff --git a/.travis.yml b/.travis.yml index 4f36d747..d4eaf172 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ before_install: - mkdir ~/netdisco-mibs - cd ~/netdisco-mibs install: - - curl -sL https://github.com/netdisco/netdisco-mibs/releases/download/4.008/netdisco-mibs.tar.gz | tar --strip-components=1 -zxf - + - curl -sL https://github.com/netdisco/netdisco-mibs/releases/download/4.009/netdisco-mibs.tar.gz | tar --strip-components=1 -zxf - - cpanm --quiet --notest PkgConfig Test::CChecker Alien::zlib::Static Alien::OpenSSL::Static Alien::SNMP::MAXTC before_script: - 'cd ${TRAVIS_BUILD_DIR}' diff --git a/Changes b/Changes index 7fb67327..3d683405 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,11 @@ Version 3.57 + [ENHANCEMENTS] + + * #145 Patch for Huawei (robwwd) + * #228 Huawei aggregate link support + * POE and duplex admin support added to L3::Huawei + [BUG FIXES] * IEEE802dot3ad portlist is indexed with a dot1dBasePort, cross reference diff --git a/lib/SNMP/Info/Layer3/Huawei.pm b/lib/SNMP/Info/Layer3/Huawei.pm index a2dc2cad..a807adeb 100644 --- a/lib/SNMP/Info/Layer3/Huawei.pm +++ b/lib/SNMP/Info/Layer3/Huawei.pm @@ -1,6 +1,6 @@ # SNMP::Info::Layer3::Huawei # -# Copyright (c) 2018 Jeroen van Ingen +# Copyright (c) 2018 Jeroen van Ingen and Eric Miller # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,18 +32,14 @@ package SNMP::Info::Layer3::Huawei; use strict; use Exporter; use SNMP::Info::Layer3; -use SNMP::Info::LLDP; -use SNMP::Info::IEEE802dot3ad 'agg_ports_lag'; +use SNMP::Info::IEEE802dot3ad; @SNMP::Info::Layer3::Huawei::ISA = qw/ - SNMP::Info::IEEE802dot3ad - SNMP::Info::LLDP - SNMP::Info::Layer3 - Exporter -/; -@SNMP::Info::Layer3::Huawei::EXPORT_OK = qw/ - agg_ports -/; + SNMP::Info::IEEE802dot3ad + SNMP::Info::Layer3 + Exporter + /; +@SNMP::Info::Layer3::Huawei::EXPORT_OK = qw//; use vars qw/$VERSION %GLOBALS %MIBS %FUNCS %MUNGE/; @@ -51,26 +47,58 @@ $VERSION = '3.56'; %MIBS = ( %SNMP::Info::Layer3::MIBS, - %SNMP::Info::LLDP::MIBS, %SNMP::Info::IEEE802dot3ad::MIBS, - 'HUAWEI-MIB' => 'quidway', + 'HUAWEI-MIB' => 'quidway', + 'HUAWEI-PORT-MIB' => 'hwEthernetDuplex', + 'HUAWEI-IF-EXT-MIB' => 'hwTrunkIfIndex', + 'HUAWEI-L2IF-MIB' => 'hwL2IfPortIfIndex', + 'HUAWEI-POE-MIB' => 'hwPoePower', ); -%GLOBALS = ( - %SNMP::Info::Layer3::GLOBALS, - %SNMP::Info::LLDP::GLOBALS, -); +%GLOBALS = ( %SNMP::Info::Layer3::GLOBALS, ); %FUNCS = ( %SNMP::Info::Layer3::FUNCS, - %SNMP::Info::LLDP::FUNCS, %SNMP::Info::IEEE802dot3ad::FUNCS, + + # HUAWEI-PORT-MIB:: + 'hw_eth_speed_admin' => 'hwEthernetSpeedSet', + 'hw_eth_duplex' => 'hwEthernetDuplex', + 'hw_eth_auto' => 'hwEthernetNegotiation', + + # HUAWEI-PORT-MIB:: + 'hw_phy_port_slot' => 'hwPhysicalPortInSlot', + + # HUAWEI-IF-EXT-MIB::hwTrunkIfTable + 'hw_trunk_if_idx' => 'hwTrunkIfIndex', + 'hw_trunk_entry' => 'hwTrunkValidEntry', + + # HUAWEI-L2IF-MIB::hwL2IfTable + 'hw_l2if_port_idx' => 'hwL2IfPortIfIndex', + + # HUAWEI-POE-MIB::hwPoePortTable + 'hw_peth_port_admin' => 'hwPoePortEnable', + 'hw_peth_port_status' => 'hwPoePortPowerStatus', + 'hw_peth_port_class' => 'hwPoePortPdClass', + 'hw_peth_port_power' => 'hwPoePortConsumingPower', + + # HUAWEI-POE-MIB::hwPoeSlotTable + 'peth_power_watts' => 'hwPoeSlotMaximumPower', + 'peth_power_consumption' => 'hwPoeSlotConsumingPower', + 'peth_power_threshold' => 'hwPoeSlotPowerUtilizationThreshold', + ); %MUNGE = ( %SNMP::Info::Layer3::MUNGE, - %SNMP::Info::LLDP::MUNGE, %SNMP::Info::IEEE802dot3ad::MUNGE, + 'hw_peth_port_admin' => \&SNMP::Info::Layer3::Huawei::munge_hw_peth_admin, + 'peth_power_watts' => \&SNMP::Info::Layer3::Huawei::munge_hw_peth_power, + 'peth_power_consumption' => + \&SNMP::Info::Layer3::Huawei::munge_hw_peth_power, + 'hw_peth_port_status' => + \&SNMP::Info::Layer3::Huawei::munge_hw_peth_status, + 'hw_peth_port_class' => \&SNMP::Info::Layer3::Huawei::munge_hw_peth_class, ); sub vendor { @@ -87,19 +115,37 @@ sub os { sub os_ver { my $huawei = shift; + + my $entity_os = $huawei->entity_derived_os_ver(); + if ( defined $entity_os and $entity_os !~ /^\s*$/ ) { + return $entity_os; + } + my $descr = $huawei->description(); my $os_ver = undef; - $os_ver = "$1" if ( $descr =~ /\bVersion ([0-9.]+)/i ); + if ($descr =~ /Version\s # Start match on Version string + ([\d\.]+) # Capture the primary version in 1 + ,? # There may be a comma + \s # Always a space + (?:Release|Feature)? # Don't capture stanza if present + (?:\(\w+)? # If paren & model don't capture + \s # Always a space + (\w+) # If 2nd part of version capture in 2 + /xi + ) + { + $os_ver = $2 ? "$1 $2" : $1; + } return $os_ver; } sub i_ignore { - my $l3 = shift; + my $huawei = shift; my $partial = shift; - my $interfaces = $l3->interfaces($partial) || {}; + my $interfaces = $huawei->interfaces($partial) || {}; my %i_ignore; foreach my $if ( keys %$interfaces ) { @@ -112,18 +158,253 @@ sub i_ignore { return \%i_ignore; } -sub agg_ports { return agg_ports_lag(@_) } +sub bp_index { + my $huawei = shift; + + my $hw_index = $huawei->hw_l2if_port_idx(); + return $hw_index + if ( ref {} eq ref $hw_index and scalar keys %$hw_index ); + + return $huawei->SUPER::bp_index(); +} + +sub i_duplex { + my $huawei = shift; + my $partial = shift; + + my $hw_duplex = $huawei->hw_eth_duplex($partial); + return $hw_duplex + if ( ref {} eq ref $hw_duplex and scalar keys %$hw_duplex ); + + return $huawei->SUPER::i_duplex($partial); +} + +sub i_duplex_admin { + my $huawei = shift; + my $partial = shift; + + my $hw_duplex_admin = $huawei->hw_eth_duplex($partial) || {}; + my $hw_auto = $huawei->hw_eth_auto($partial) || {}; + + my %i_duplex_admin; + foreach my $if ( keys %$hw_duplex_admin ) { + my $duplex = $hw_duplex_admin->{$if}; + next unless defined $duplex; + my $auto = $hw_auto->{$if} || 'disabled'; + + my $string = 'other'; + $string = 'half' if ( $duplex =~ /half/i and $auto =~ /disabled/i ); + $string = 'full' if ( $duplex =~ /full/i and $auto =~ /disabled/i ); + $string = 'auto' if $auto =~ /enabled/i; + + $i_duplex_admin{$if} = $string; + } + return \%i_duplex_admin; +} + +sub agg_ports { + my $huawei = shift; + + # First use proprietary MIB for broader implementation across + # devices type / os and no xref of hwL2IfPortIfIndex + my $masters = $huawei->hw_trunk_if_idx(); + my $slaves = $huawei->hw_trunk_entry(); + + my $ret = {}; + + if ( ref {} eq ref $masters + and scalar keys %$masters + and ref {} eq ref $slaves + and scalar keys %$slaves ) + { + foreach my $s ( keys %$slaves ) { + next if $slaves->{$s} ne 'valid'; + my ( $trunk, $sl_idx ) = split( /\./, $s ); + foreach my $m ( keys %$masters ) { + next unless $m == $trunk; + next unless defined $masters->{$m}; + $ret->{$sl_idx} = $masters->{$m}; + last; + } + } + return $ret; + } + + # If for some reason we don't get the info, try IEEE8023-LAG-MIB + return $huawei->agg_ports_lag(); +} + +# The standard IEEE 802.af POWER-ETHERNET-MIB has an index of module.port +# The HUAWEI-POE-MIB only indexes by ifIndex, we need to match the standard +# for so method calls across classes work the same +# +# Note: Only snmpwalks with single chassis / box with single power source +# in slot zero. This may need to be revisited +# +sub peth_port_ifindex { + my $huawei = shift; + + my $peth_port_status = $huawei->hw_peth_port_status() || {}; + my $peth_port_slot = $huawei->hw_phy_port_slot() || {}; + + my $peth_port_ifindex = {}; + + foreach my $i ( keys %$peth_port_status ) { + my $slot = 0; + if ( exists $peth_port_slot->{$i} + && defined $peth_port_slot->{$i} ) + { + $slot = $peth_port_slot->{$i}; + } + $peth_port_ifindex->{"$slot.$i"} = $i; + } + return $peth_port_ifindex; +} + +sub peth_port_admin { + my $huawei = shift; + + my $port_admin = $huawei->hw_peth_port_admin() || {}; + my $port_ifindex = $huawei->peth_port_ifindex() || {}; + + my $peth_port_admin = {}; + + foreach my $idx ( keys %$port_ifindex ) { + my $ifindex = $port_ifindex->{$idx}; + my $admin = $port_admin->{$ifindex}; + next unless $admin; + + $peth_port_admin->{$idx} = $admin; + } + return $peth_port_admin; +} + +sub peth_port_status { + my $huawei = shift; + + my $port_status = $huawei->hw_peth_port_status() || {}; + my $port_ifindex = $huawei->peth_port_ifindex() || {}; + + my $peth_port_status = {}; + + foreach my $idx ( keys %$port_ifindex ) { + my $ifindex = $port_ifindex->{$idx}; + my $status = $port_status->{$ifindex}; + next unless $status; + + $peth_port_status->{$idx} = $status; + } + return $peth_port_status; +} + +sub peth_port_class { + my $huawei = shift; + + my $port_class = $huawei->hw_peth_port_class() || {}; + my $port_ifindex = $huawei->peth_port_ifindex() || {}; + + my $peth_port_class = {}; + + foreach my $idx ( keys %$port_ifindex ) { + my $ifindex = $port_ifindex->{$idx}; + my $class = $port_class->{$ifindex}; + next unless $class; + + $peth_port_class->{$idx} = $class; + } + return $peth_port_class; +} + +sub peth_port_power { + my $huawei = shift; + + my $port_power = $huawei->hw_peth_port_power() || {}; + my $port_ifindex = $huawei->peth_port_ifindex() || {}; + + my $peth_port_power = {}; + + foreach my $idx ( keys %$port_ifindex ) { + my $ifindex = $port_ifindex->{$idx}; + my $power = $port_power->{$ifindex}; + next unless defined $power; + + $peth_port_power->{$idx} = $power; + } + return $peth_port_power; +} + +sub peth_port_neg_power { + my $huawei = shift; + + my $peth_port_status = $huawei->peth_port_status() || {}; + my $peth_port_class = $huawei->peth_port_class() || {}; + my $port_ifindex = $huawei->peth_port_ifindex() || {}; + + my $huaweimax = { + 'class0' => 12950, + 'class1' => 3840, + 'class2' => 6490, + 'class3' => 12950, + 'class4' => 25500 + }; + + my $peth_port_neg_power = {}; + + foreach my $idx ( keys %$port_ifindex ) { + if ( $peth_port_status->{$idx} eq 'deliveringPower' ) { + $peth_port_neg_power->{$idx} + = $huaweimax->{ $peth_port_class->{$idx} }; + } + } + return $peth_port_neg_power; +} + +sub munge_hw_peth_admin { + my $admin = shift; + + $admin =~ s/enabled/true/; + $admin =~ s/disabled/false/; + return $admin; +} + +sub munge_hw_peth_power { + my $pwr = shift; + + $pwr = $pwr / 1000; + return sprintf( "%.0f", $pwr ); +} + +sub munge_hw_peth_class { + my $pwr = shift; + + return "class$pwr"; +} + +# The values are from the MIB reference guide +sub munge_hw_peth_status { + my $pwr = shift; + + # The status is an octet string rather than enum + # so use regex rather than hash lookup + $pwr = 'disabled' if $pwr =~ /Disabled/i; + $pwr = 'searching' if $pwr =~ /(Powering|Power-ready|Detecting)/i; + $pwr = 'deliveringPower' if $pwr =~ /Powered/i; + $pwr = 'fault' if $pwr =~ /fault/i; + + return $pwr; +} 1; + __END__ =head1 NAME -SNMP::Info::Layer3::Huawei - SNMP Interface to Huawei Layer 3 switches and routers. +SNMP::Info::Layer3::Huawei - SNMP Interface to Huawei switches and routers. =head1 AUTHORS -Jeroen van Ingen +Jeroen van Ingen and Eric Miller =head1 SYNOPSIS @@ -142,7 +423,7 @@ Jeroen van Ingen =head1 DESCRIPTION -Subclass for Huawei Quidway switches +Subclass for Huawei switches =head2 Inherited Classes @@ -150,8 +431,6 @@ Subclass for Huawei Quidway switches =item SNMP::Info::Layer3 -=item SNMP::Info::LLDP - =item SNMP::Info::IEEE802dot3ad =back @@ -162,12 +441,18 @@ Subclass for Huawei Quidway switches =item F +=item F + +=item F + +=item F + +=item F + =item Inherited Classes' MIBs See L for its own MIB requirements. -See L for its own MIB requirements. - See L for its own MIB requirements. =back @@ -188,7 +473,8 @@ Returns 'VRP' if contained in C, 'huawei' otherwise. =item $huawei->os_ver() -Returns the software version extracted from C. +Returns the software version derived from the C or +extracted from C. =back @@ -196,15 +482,49 @@ Returns the software version extracted from C. See documentation in L for details. -=head2 Globals imported from SNMP::Info::LLDP - -See documentation in L for details. - =head1 TABLE ENTRIES These are methods that return tables of information in the form of a reference to a hash. +=over + +=item $huawei->i_duplex() + +Returns reference to map of IIDs to current link duplex. + +=item $huawei->i_duplex_admin() + +Returns reference to hash of IIDs to admin duplex setting. + +=back + +=head2 POE Slot Table + +=over + +=item $huawei->peth_power_watts() + +The slot's power supply's capacity, in watts. + +C + +=item $huawei->peth_power_consumption() + +How much power, in watts, this power supply has been committed to +deliver. + +C + +=item $huawei->peth_power_threshold() + +The threshold (in percent) of consumption required to raise an +alarm. + +C + +=back + =head2 Overrides =over @@ -215,11 +535,58 @@ Returns reference to hash. Increments value of IID if port is to be ignored. Ignores InLoopback and Console interfaces +=item $huawei->bp_index() + +Returns a mapping between C and the Bridge Table. Uses +C for the most complete mapping and falls back to +C if not available. + =item C Returns a HASH reference mapping from slave to master port for each member of a port bundle on the device. Keys are ifIndex of the slave ports, Values are -ifIndex of the corresponding master ports. +ifIndex of the corresponding master ports. Attempts to use C +first and then C if that is unavailable. + +=back + +=head2 Power Port Table + +The index of these methods have been normalized to slot.port and values +munged to provide compatability with the IEEE 802.3af F +and equivalent L methods. + +=over + +=item $huawei->peth_port_admin() + +Administrative status: is this port permitted to deliver power? + +=item $huawei->peth_port_status() + +Current status: is this port delivering power, searching, disabled, etc? + +=item $huawei->peth_port_class() + +Device class: if status is delivering power, this represents the 802.3af +class of the device being powered. + +=item $huawei->peth_port_power() + +Power supplied the port, in milliwatts + +=item $huawei->peth_port_ifindex() + +Returns an index of slot.port to an C. Slot defaults to zero +meaning chassis or box if there is no C to slot mapping available in +C. Mapping the index to slot.port is a normalization +function to provide compatability with the IEEE 802.3af F. + +=item $huawei->peth_port_neg_power() + +The power, in milliwatts, that has been committed to this port. +This value is derived from the 802.3af class of the device being +powered. =back @@ -227,12 +594,31 @@ ifIndex of the corresponding master ports. See documentation in L for details. -=head2 Table Methods imported from SNMP::Info::LLDP - -See documentation in L for details. - =head2 Table Methods imported from SNMP::Info::IEEE802dot3ad See documentation in L for details. +=head1 Data Munging Callback Subroutines + +=over + +=item $huawei->munge_hw_peth_admin() + +Normalizes C values to 'true' or 'false'. + +=item $huawei->munge_hw_peth_class() + +Normalizes C values by prepending 'class'. + +=item $huawei->munge_hw_peth_power() + +Converts and rounds to a whole number milliwatts to watts. + +=item $huawei->munge_hw_peth_status() + +Normalizes C values to those that would be returned by +the the IEEE 802.3af F. + +=back + =cut diff --git a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm index fe6d763c..aed330b1 100644 --- a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm +++ b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm @@ -33,30 +33,373 @@ use Test::Class::Most parent => 'My::Test::Class'; use SNMP::Info::Layer3::Huawei; -# Remove this startup override once we have full method coverage -sub startup : Tests(startup => 1) { - my $test = shift; - $test->SUPER::startup(); - - $test->todo_methods(1); -} - sub setup : Tests(setup) { my $test = shift; $test->SUPER::setup; # Start with a common cache that will serve most tests - my $d_string = 'Huawei Versatile Routing Platform Software VRP (R) software, '; + my $d_string + = 'Huawei Versatile Routing Platform Software VRP (R) software, '; $d_string .= 'Version 8.100 (CE6810EI V100R005C10SPC200) '; + my $cache_data = { - '_layers' => 4, + '_layers' => 4, '_description' => $d_string, # HUAWEI-MIB::ce6810-48S4Q-EI - '_id' => '.1.3.6.1.4.1.2011.2.239.12', - 'store' => {}, + '_id' => '.1.3.6.1.4.1.2011.2.239.12', + '_i_index' => 1, + '_i_description' => 1, + '_hw_eth_duplex' => 1, + '_hw_eth_auto' => 1, + '_el_index' => 1, + '_el_duplex' => 1, + '_hw_trunk_if_idx' => 1, + '_hw_trunk_entry' => 1, + '_ad_lag_ports' => 1, + '_hw_l2if_port_idx' => 1, + '_bp_index' => 1, + '_hw_phy_port_slot' => 1, + '_hw_peth_power_watts' => 1, + '_hw_peth_port_admin' => 1, + '_hw_peth_port_status' => 1, + '_hw_peth_port_class' => 1, + '_hw_peth_port_power' => 1, + + 'store' => { + 'i_index' => {1 => 1, 6 => 6, 7 => 7, 8 => 8}, + 'i_description' => { + 1 => 'InLoopBack0', + 6 => 'GigabitEthernet0/0/1', + 7 => 'GigabitEthernet0/0/2', + 8 => 'GigabitEthernet0/0/3' + }, + 'hw_eth_duplex' => {6 => 'full', 7 => 'full', 8 => 'half'}, + 'hw_eth_auto' => {6 => 'enabled', 7 => 'disabled', 8 => 'disabled'}, + 'el_index' => {9 => 9, 10 => 10}, + 'el_duplex' => {9 => 'full', 10 => 'half'}, + 'hw_trunk_if_idx' => {0 => 121}, + 'hw_trunk_entry' => {'0.55' => 'valid', '0.110' => 'valid'}, + 'ad_lag_ports' => {34 => pack("H*", '00000060')}, + 'hw_l2if_port_idx' => {26 => 30, 27 => 31}, + 'bp_index' => {2 => 1, 7 => 3}, + 'hw_phy_port_slot' => {6 => 0, 7 => 0, 8 => 0}, + 'hw_peth_power_watts' => {0 => 369600}, + 'hw_peth_port_admin' => {6 => 'enabled', 7 => 'disabled', 8 => 'enabled'}, + 'hw_peth_port_status' => + {6 => 'Powered', 7 => 'Disabled', 8 => 'Detecting'}, + 'hw_peth_port_class' => {6 => 3, 7 => 0, 8 => 0}, + 'hw_peth_port_power' => {6 => 3763, 7 => 0, 8 => 0}, + }, }; $test->{info}->cache($cache_data); } +sub vendor : Tests(2) { + my $test = shift; + + can_ok($test->{info}, 'vendor'); + is($test->{info}->vendor(), 'Huawei', q(Vendor returns 'Huawei')); +} + +sub os : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'os'); + is($test->{info}->os(), 'VRP', q(OS returns 'VRP' when description matches)); + + $test->{info}->clear_cache(); + is($test->{info}->os(), 'huawei', q(... and 'huawei' when it doesn't)); +} + +sub os_ver : Tests(7) { + my $test = shift; + + can_ok($test->{info}, 'os_ver'); + + is( + $test->{info}->os_ver(), + '8.100 V100R005C10SPC200', + q(OS version returned from 'sysDescr' example 1) + ); + + my $descr = 'Version 3.40, Release 0311P07 Quidway Series Router AR28-31 '; + $test->{info}{_description} = $descr; + + is($test->{info}->os_ver(), + '3.40 0311P07', q(OS version returned from 'sysDescr'example 2)); + + $descr = 'Version 3.40, Feature 0308 Quidway Series Router AR46-40 '; + $test->{info}{_description} = $descr; + + is($test->{info}->os_ver(), + '3.40 0308', q(OS version returned from 'sysDescr'example 3)); + + $descr = 'Version 3.40, Feature 0121L01.Quidway Router AR18-34E.'; + $test->{info}{_description} = $descr; + + is($test->{info}->os_ver(), + '3.40 0121L01', q(OS version returned from 'sysDescr'example 4)); + + $descr = 'software,Version 5.120 (AR151 V200R003C01SPC100) '; + $test->{info}{_description} = $descr; + + is( + $test->{info}->os_ver(), + '5.120 V200R003C01SPC100', + q(OS version returned from 'sysDescr'example 5) + ); + + $test->{info}->clear_cache(); + is($test->{info}->os_ver(), undef, q(No data returns undef OS version)); +} + +# Not overriden in class, but tested anyway +sub model : Tests(2) { + my $test = shift; + + can_ok($test->{info}, 'model'); + is($test->{info}->model(), 'ce6810-48S4Q-EI', q(Model translates id)); +} + +sub i_ignore : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'i_ignore'); + + my $expected = {1 => 1}; + cmp_deeply($test->{info}->i_ignore(), + $expected, q(Loopback interface ignored)); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->i_ignore(), {}, q(No matches returns empty hash)); +} + +sub bp_index : Tests(4) { + my $test = shift; + + can_ok($test->{info}, 'bp_index'); + + my $expected = {26 => 30, 27 => 31}; + cmp_deeply($test->{info}->bp_index(), + $expected, q(Bridge to interface index mapping using 'hwL2IfPortIfIndex')); + + delete $test->{info}{'_hw_l2if_port_idx'}; + $expected = {2 => 1, 7 => 3}; + cmp_deeply($test->{info}->bp_index(), + $expected, + q(Bridge to interface index mapping using 'dot1dBasePortIfIndex')); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->bp_index(), undef, + q(No mapping returns empty hash)); +} + +sub i_duplex : Tests(4) { + my $test = shift; + + can_ok($test->{info}, 'i_duplex'); + + my $expected = {6 => 'full', 7 => 'full', 8 => 'half'}; + cmp_deeply($test->{info}->i_duplex(), + $expected, q(Duplex values using 'hwEthernetDuplex')); + + delete $test->{info}{'_hw_eth_duplex'}; + $expected = {9 => 'full', 10 => 'half'}; + cmp_deeply($test->{info}->i_duplex(), + $expected, q(Duplex values using 'EtherLike-MIB')); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->i_duplex(), {}, q(No mapping returns empty hash)); +} + +sub i_duplex_admin : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'i_duplex_admin'); + + my $expected = {6 => 'auto', 7 => 'full', 8 => 'half'}; + cmp_deeply($test->{info}->i_duplex_admin(), + $expected, q(Duplex admin values using 'hwEthernetDuplex')); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->i_duplex_admin(), + {}, q(No mapping returns empty hash)); +} + +sub agg_ports : Tests(4) { + my $test = shift; + + can_ok($test->{info}, 'agg_ports'); + + my $expected = {55 => 121, 110 => 121}; + + cmp_deeply($test->{info}->agg_ports(), + $expected, + q(Aggregated links have expected values using 'HUAWEI-IF-EXT-MIB')); + + delete $test->{info}{_hw_trunk_if_idx}; + $expected = {30 => 34, 31 => 34}; + + cmp_deeply($test->{info}->agg_ports(), + $expected, + q(Aggregated links have expected values using 'IEEE8023-LAG-MIB')); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->agg_ports(), {}, q(No data returns empty hash)); +} + +sub peth_port_ifindex : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'peth_port_ifindex'); + + my $expected = {'0.6' => 6, '0.7' => 7, '0.8' => 8},; + + cmp_deeply($test->{info}->peth_port_ifindex(), + $expected, q(POE port 'ifIndex' mapping returns expected values)); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->peth_port_ifindex(), + {}, q(No data returns empty hash)); +} + +sub peth_port_admin : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'peth_port_admin'); + + my $expected = {'0.6' => 'true', '0.7' => 'false', '0.8' => 'true'}; + + cmp_deeply($test->{info}->peth_port_admin(), + $expected, q(POE port admin status returns expected values)); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->peth_port_admin(), + {}, q(No data returns empty hash)); +} + +sub peth_port_status : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'peth_port_status'); + + my $expected + = {'0.6' => 'deliveringPower', '0.7' => 'disabled', '0.8' => 'searching'}; + + cmp_deeply($test->{info}->peth_port_status(), + $expected, q(POE port status returns expected values)); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->peth_port_status(), + {}, q(No data returns empty hash)); +} + +sub peth_port_class : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'peth_port_class'); + + my $expected = {'0.6' => 'class3', '0.7' => 'class0', '0.8' => 'class0'}; + + cmp_deeply($test->{info}->peth_port_class(), + $expected, q(POE port class returns expected values)); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->peth_port_class(), + {}, q(No data returns empty hash)); +} + +sub peth_port_power : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'peth_port_power'); + + my $expected = {'0.6' => 3763, '0.7' => 0, '0.8' => 0}; + + cmp_deeply($test->{info}->peth_port_power(), + $expected, q(POE port power returns expected values)); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->peth_port_power(), + {}, q(No data returns empty hash)); +} + +sub peth_port_neg_power : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'peth_port_neg_power'); + + my $expected = {'0.6' => 12950}; + + cmp_deeply($test->{info}->peth_port_neg_power(), + $expected, q(POE port negotiated power returns expected values)); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->peth_port_neg_power(), + {}, q(No data returns empty hash)); +} + +sub munge_hw_peth_admin : Tests(4) { + my $test = shift; + + can_ok($test->{info}, 'munge_hw_peth_admin'); + + my $expected = 'true'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_admin('enabled'), + $expected, q(... enabled munges to true)); + + $expected = 'false'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_admin('disabled'), + $expected, q(... disabled munges to false)); + + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_admin('huh'), + 'huh', q(... anything else not munged)); +} + +sub munge_hw_peth_power : Tests(2) { + my $test = shift; + + can_ok($test->{info}, 'munge_hw_peth_power'); + + my $expected = '370'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_power('369600'), + $expected, q(... mW converted/rounded to W)); +} + +sub munge_hw_peth_class : Tests(2) { + my $test = shift; + + can_ok($test->{info}, 'munge_hw_peth_class'); + + my $expected = 'class3'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_class(3), + $expected, q(... 'class' text added to numeric class)); +} + +sub munge_hw_peth_status : Tests(6) { + my $test = shift; + + can_ok($test->{info}, 'munge_hw_peth_status'); + + my $expected = 'disabled'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_status('Disabled'), + $expected, q(... Disabled munges to disabled)); + + $expected = 'searching'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_status('Detecting'), + $expected, q(... Detecting munges to searching)); + + $expected = 'deliveringPower'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_status('Powered'), + $expected, q(... Powered munges to deliveringPower)); + + $expected = 'fault'; + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_status('Other-fault'), + $expected, q(... Other-fault munges to fault)); + + is(SNMP::Info::Layer3::Huawei::munge_hw_peth_status('huh'), + 'huh', q(... anything else not munged)); +} + 1;