diff --git a/Changes b/Changes index 4c0547ef..bc3819de 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,14 @@ +Version 3.58 + + [ENHANCEMENTS] + + * Add fan and power supply status to L3::Huawei + * Override ifMTU with max frame size when applicable in L3::Huawei + + [BUG FIXES] + + * Correct POE power module to port mapping in L3::Huawei + Version 3.57 (2018-04-26) [ENHANCEMENTS] diff --git a/lib/SNMP/Info/Layer3/Huawei.pm b/lib/SNMP/Info/Layer3/Huawei.pm index 4889d44a..5679157e 100644 --- a/lib/SNMP/Info/Layer3/Huawei.pm +++ b/lib/SNMP/Info/Layer3/Huawei.pm @@ -48,11 +48,12 @@ $VERSION = '3.57'; %MIBS = ( %SNMP::Info::Layer3::MIBS, %SNMP::Info::IEEE802dot3ad::MIBS, - 'HUAWEI-MIB' => 'quidway', - 'HUAWEI-PORT-MIB' => 'hwEthernetDuplex', - 'HUAWEI-IF-EXT-MIB' => 'hwTrunkIfIndex', - 'HUAWEI-L2IF-MIB' => 'hwL2IfPortIfIndex', - 'HUAWEI-POE-MIB' => 'hwPoePower', + 'HUAWEI-MIB' => 'quidway', + 'HUAWEI-PORT-MIB' => 'hwEthernetDuplex', + 'HUAWEI-IF-EXT-MIB' => 'hwTrunkIfIndex', + 'HUAWEI-L2IF-MIB' => 'hwL2IfPortIfIndex', + 'HUAWEI-POE-MIB' => 'hwPoePower', + 'HUAWEI-ENTITY-EXTENT-MIB' => 'hwEntityFanState', ); %GLOBALS = ( %SNMP::Info::Layer3::GLOBALS, ); @@ -61,12 +62,13 @@ $VERSION = '3.57'; %SNMP::Info::Layer3::FUNCS, %SNMP::Info::IEEE802dot3ad::FUNCS, - # HUAWEI-PORT-MIB:: + # HUAWEI-PORT-MIB::hwEthernetTable 'hw_eth_speed_admin' => 'hwEthernetSpeedSet', 'hw_eth_duplex' => 'hwEthernetDuplex', 'hw_eth_auto' => 'hwEthernetNegotiation', + 'hw_eth_frame_len' => 'hwEthernetJumboframeMaxLength', - # HUAWEI-PORT-MIB:: + # HUAWEI-PORT-MIB::hwPhysicalPortTable 'hw_phy_port_slot' => 'hwPhysicalPortInSlot', # HUAWEI-IF-EXT-MIB::hwTrunkIfTable @@ -87,6 +89,13 @@ $VERSION = '3.57'; 'peth_power_consumption' => 'hwPoeSlotConsumingPower', 'peth_power_threshold' => 'hwPoeSlotPowerUtilizationThreshold', + # HUAWEI-ENTITY-EXTENT-MIB::hwFanStatusTable + 'hw_fan_state' => 'hwEntityFanState', + 'hw_fan_descr' => 'hwEntityFanDesc', + + # HUAWEI-ENTITY-EXTENT-MIB::hwPwrStatusTable + 'hw_pwr_state' => 'hwEntityPwrState', + 'hw_pwr_descr' => 'hwEntityPwrDesc', ); %MUNGE = ( @@ -109,7 +118,9 @@ sub os { my $huawei = shift; my $descr = $huawei->description(); - return $1 if ( $descr =~ /\b(VRP)\b/ ); + if ( $descr =~ /\b(VRP)\b/ ) { + return $1; + } return "huawei"; } @@ -151,7 +162,7 @@ sub i_ignore { foreach my $if ( keys %$interfaces ) { # lo0 etc - if ( $interfaces->{$if} =~ /\b(inloopback|console)\d*\b/i ) { + if ( $interfaces->{$if} =~ /\b(inloopback|console)\d*\b/ix ) { $i_ignore{$if}++; } } @@ -238,14 +249,12 @@ sub agg_ports { # 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 $i_descr = $huawei->i_description() || {}; my $peth_port_ifindex = {}; @@ -256,6 +265,11 @@ sub peth_port_ifindex { { $slot = $peth_port_slot->{$i}; } + elsif ( exists $i_descr->{$i} + && $i_descr->{$i} =~ /(\d+)(?:\/\d+){2,3}$/x ) + { + $slot = $1; + } $peth_port_ifindex->{"$slot.$i"} = $i; } return $peth_port_ifindex; @@ -359,6 +373,79 @@ sub peth_port_neg_power { return $peth_port_neg_power; } +sub fan { + my $huawei = shift; + + my $fan = $huawei->hw_fan_descr() || {}; + my $state = $huawei->hw_fan_state() || {}; + + if ( scalar keys %$fan ) { + my @messages = (); + + foreach my $k ( keys %$fan ) { + next if $state->{$k} and $state->{$k} eq 'normal'; + push @messages, "$fan->{$k}: $state->{$k}"; + } + + push @messages, ( ( scalar keys %$fan ) . " fans OK" ) + if scalar @messages == 0; + + return ( join ", ", @messages ); + } + return; +} + +sub ps1_status { + my $huawei = shift; + + my $pwr_state = $huawei->hw_pwr_state() || {}; + my $pwr_descr = $huawei->hw_pwr_descr() || {}; + + my $ret = ""; + my $s = ""; + foreach my $i ( sort keys %$pwr_state ) { + my ( $slot, $num ) = split( /\./, $i ); + next unless $num == 1; + $ret .= $s . $pwr_descr->{$i} . ": " . $pwr_state->{$i}; + $s = ", "; + } + return if ( $s eq "" ); + return $ret; +} + +sub ps2_status { + my $huawei = shift; + + my $pwr_state = $huawei->hw_pwr_state() || {}; + my $pwr_descr = $huawei->hw_pwr_descr() || {}; + + my $ret = ""; + my $s = ""; + foreach my $i ( sort keys %$pwr_state ) { + my ( $slot, $num ) = split( /\./, $i ); + next unless $num == 2; + $ret .= $s . $pwr_descr->{$i} . ": " . $pwr_state->{$i}; + $s = ", "; + } + return if ( $s eq "" ); + return $ret; +} + +sub i_mtu { + my $huawei = shift; + + my $mtus = $huawei->SUPER::i_mtu() || {}; + my $frames = $huawei->hw_eth_frame_len() || {}; + + foreach my $idx ( keys %$mtus ) { + my $frame_sz = $frames->{$idx}; + next unless $frame_sz; + + $mtus->{$idx} = $frame_sz; + } + return $mtus; +} + sub munge_hw_peth_admin { my $admin = shift; @@ -387,7 +474,7 @@ sub munge_hw_peth_status { # 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 = 'searching' if $pwr =~ /(Powering|Power-ready|Detecting)/ix; $pwr = 'deliveringPower' if $pwr =~ /Powered/i; $pwr = 'fault' if $pwr =~ /fault/i; @@ -449,6 +536,8 @@ Subclass for Huawei switches =item F +=item F + =item Inherited Classes' MIBs See L for its own MIB requirements. @@ -476,6 +565,22 @@ Returns 'VRP' if contained in C, 'huawei' otherwise. Returns the software version derived from the C or extracted from C. +=item $huawei->fan() + +Return the status of all fans from the F. Returns +a string indicating the number of fans 'OK' or identification of any fan without +a 'normal' operating status + +=item $huawei->ps1_status() + +Return the status of the first power supply in each chassis or switch from +the F + +=item $huawei->ps2_status() + +Return the status of the second power supply in each chassis or switch from +the F + =back =head2 Globals imported from SNMP::Info::Layer3 @@ -548,6 +653,11 @@ a port bundle on the device. Keys are ifIndex of the slave ports, Values are ifIndex of the corresponding master ports. Attempts to use C first and then C if that is unavailable. +=item C + +Interface MTU value. Overriden with corresponding frame size entry from +C if one exists. + =back =head2 Power Port Table diff --git a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm index aed330b1..2a0d855a 100644 --- a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm +++ b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm @@ -50,6 +50,7 @@ sub setup : Tests(setup) { '_id' => '.1.3.6.1.4.1.2011.2.239.12', '_i_index' => 1, '_i_description' => 1, + '_i_mtu' => 1, '_hw_eth_duplex' => 1, '_hw_eth_auto' => 1, '_el_index' => 1, @@ -65,31 +66,47 @@ sub setup : Tests(setup) { '_hw_peth_port_status' => 1, '_hw_peth_port_class' => 1, '_hw_peth_port_power' => 1, + '_hw_fan_state' => 1, + '_hw_fan_descr' => 1, + '_hw_pwr_state' => 1, + '_hw_pwr_descr' => 1, 'store' => { - 'i_index' => {1 => 1, 6 => 6, 7 => 7, 8 => 8}, + 'i_index' => {1 => 1, 6 => 6, 7 => 7, 8 => 8, 108 => 108}, 'i_description' => { - 1 => 'InLoopBack0', - 6 => 'GigabitEthernet0/0/1', - 7 => 'GigabitEthernet0/0/2', - 8 => 'GigabitEthernet0/0/3' + 1 => 'InLoopBack0', + 6 => 'GigabitEthernet0/0/1', + 7 => 'GigabitEthernet0/0/2', + 8 => 'GigabitEthernet0/0/3', + 108 => 'GigabitEthernet1/0/1' }, - '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}, + 'i_mtu' => {6 => 1500, 7 => 1500, 8 => 1500}, + 'hw_eth_frame_len' => {6 => 2000, 7 => 2000}, + '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_phy_port_slot' => {6 => 0, 7 => 0, 8 => 0, 108 => 1}, + 'hw_peth_power_watts' => {0 => 369600, 1 => 739200}, + 'hw_peth_port_admin' => + {6 => 'enabled', 7 => 'disabled', 8 => 'enabled', 108 => '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}, + {6 => 'Powered', 7 => 'Disabled', 8 => 'Detecting', 108 => 'Powered'}, + 'hw_peth_port_class' => {6 => 3, 7 => 0, 8 => 0, 108 => 4}, + 'hw_peth_port_power' => {6 => 3763, 7 => 0, 8 => 0, 108 => 8374}, + 'hw_fan_state' => + {'1.1' => 'normal', '1.2' => 'abnormal', '2.1' => 'normal'}, + 'hw_fan_descr' => + {'1.1' => 'slot1,FAN1', '1.2' => 'slot1,FAN2', '2.1' => 'slot2,FAN1'}, + 'hw_pwr_state' => + {'1.1' => 'supply', '1.2' => 'notSupply', '2.1' => 'unknown'}, + 'hw_pwr_descr' => + {'1.1' => 'slot1,PWR1', '1.2' => 'slot1,PWR2', '2.1' => 'slot2,PWR1'}, }, }; $test->{info}->cache($cache_data); @@ -254,7 +271,7 @@ sub peth_port_ifindex : Tests(3) { can_ok($test->{info}, 'peth_port_ifindex'); - my $expected = {'0.6' => 6, '0.7' => 7, '0.8' => 8},; + my $expected = {'0.6' => 6, '0.7' => 7, '0.8' => 8, '1.108' => 108}; cmp_deeply($test->{info}->peth_port_ifindex(), $expected, q(POE port 'ifIndex' mapping returns expected values)); @@ -269,7 +286,8 @@ sub peth_port_admin : Tests(3) { can_ok($test->{info}, 'peth_port_admin'); - my $expected = {'0.6' => 'true', '0.7' => 'false', '0.8' => 'true'}; + my $expected + = {'0.6' => 'true', '0.7' => 'false', '0.8' => 'true', '1.108' => 'true'}; cmp_deeply($test->{info}->peth_port_admin(), $expected, q(POE port admin status returns expected values)); @@ -284,8 +302,12 @@ sub peth_port_status : Tests(3) { can_ok($test->{info}, 'peth_port_status'); - my $expected - = {'0.6' => 'deliveringPower', '0.7' => 'disabled', '0.8' => 'searching'}; + my $expected = { + '0.6' => 'deliveringPower', + '0.7' => 'disabled', + '0.8' => 'searching', + '1.108' => 'deliveringPower' + }; cmp_deeply($test->{info}->peth_port_status(), $expected, q(POE port status returns expected values)); @@ -300,7 +322,12 @@ sub peth_port_class : Tests(3) { can_ok($test->{info}, 'peth_port_class'); - my $expected = {'0.6' => 'class3', '0.7' => 'class0', '0.8' => 'class0'}; + my $expected = { + '0.6' => 'class3', + '0.7' => 'class0', + '0.8' => 'class0', + '1.108' => 'class4' + }; cmp_deeply($test->{info}->peth_port_class(), $expected, q(POE port class returns expected values)); @@ -315,7 +342,7 @@ sub peth_port_power : Tests(3) { can_ok($test->{info}, 'peth_port_power'); - my $expected = {'0.6' => 3763, '0.7' => 0, '0.8' => 0}; + my $expected = {'0.6' => 3763, '0.7' => 0, '0.8' => 0, '1.108' => 8374}; cmp_deeply($test->{info}->peth_port_power(), $expected, q(POE port power returns expected values)); @@ -330,7 +357,7 @@ sub peth_port_neg_power : Tests(3) { can_ok($test->{info}, 'peth_port_neg_power'); - my $expected = {'0.6' => 12950}; + my $expected = {'0.6' => 12950, '1.108' => 25500}; cmp_deeply($test->{info}->peth_port_neg_power(), $expected, q(POE port negotiated power returns expected values)); @@ -340,6 +367,70 @@ sub peth_port_neg_power : Tests(3) { {}, q(No data returns empty hash)); } +sub fan : Tests(4) { + my $test = shift; + + can_ok($test->{info}, 'fan'); + + my $expected = 'slot1,FAN2: abnormal'; + + is($test->{info}->fan(), $expected, q(Fan returns expected value)); + + # Change abnormal fan state to normal to test alternate message + $test->{info}{store}{hw_fan_state}{'1.2'} = 'normal'; + $expected = '3 fans OK'; + + is($test->{info}->fan(), $expected, q(Fans OK returns expected value)); + + $test->{info}->clear_cache(); + is($test->{info}->fan(), undef, q(No data returns undef)); +} + +sub ps1_status : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'ps1_status'); + + my $expected = 'slot1,PWR1: supply, slot2,PWR1: unknown'; + + is($test->{info}->ps1_status(), $expected, q(PS1 returns expected value)); + + $test->{info}->clear_cache(); + is($test->{info}->ps1_status(), undef, q(No data returns undef)); +} + +sub ps2_status : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'ps2_status'); + + my $expected = 'slot1,PWR2: notSupply'; + + is($test->{info}->ps2_status(), $expected, q(PS2 returns expected value)); + + $test->{info}->clear_cache(); + is($test->{info}->ps2_status(), undef, q(No data returns undef)); +} + +sub i_mtu : Tests(4) { + my $test = shift; + + can_ok($test->{info}, 'i_mtu'); + + # Test with 'ifMtu' first, so we don't overwrite the i_mtu cache values + my $expected = {6 => 1500, 7 => 1500, 8 => 1500}; + cmp_deeply($test->{info}->i_mtu(), $expected, q(MTU values using 'ifMtu')); + + # Make the 'hw_eth_frame_len' cahce values visible + $test->{info}{'_hw_eth_frame_len'} = 1; + $expected = {6 => 2000, 7 => 2000, 8 => 1500}; + cmp_deeply($test->{info}->i_mtu(), + $expected, q(MTU values using 'hwEthernetJumboframeMaxLength')); + + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->i_mtu(), {}, q(No mapping returns empty hash)); +} + sub munge_hw_peth_admin : Tests(4) { my $test = shift;