# SNMP::Info::Layer3::Huawei # # Copyright (c) 2018 Jeroen van Ingen and Eric Miller # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the University of California, Santa Cruz nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. package SNMP::Info::Layer3::Huawei; use strict; use warnings; use Exporter; use SNMP::Info::Layer3; use SNMP::Info::IEEE802dot3ad; @SNMP::Info::Layer3::Huawei::ISA = qw/ SNMP::Info::IEEE802dot3ad SNMP::Info::Layer3 Exporter /; @SNMP::Info::Layer3::Huawei::EXPORT_OK = qw//; our ($VERSION, %GLOBALS, %MIBS, %FUNCS, %MUNGE); $VERSION = '3.70'; %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-ENTITY-EXTENT-MIB' => 'hwEntityFanState', ); %GLOBALS = ( %SNMP::Info::Layer3::GLOBALS, ); %FUNCS = ( %SNMP::Info::Layer3::FUNCS, %SNMP::Info::IEEE802dot3ad::FUNCS, # 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::hwPhysicalPortTable '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', # 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 = ( %SNMP::Info::Layer3::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 { return "huawei"; } sub os { my $huawei = shift; my $descr = $huawei->description(); if ( defined ($descr) && $descr =~ /\b(VRP)\b/ ) { return $1; } return "huawei"; } 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; if (defined ($descr) && $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 mac { my $huawei = shift; return $huawei->b_mac(); } sub i_ignore { my $huawei = shift; my $partial = shift; my $interfaces = $huawei->interfaces($partial) || {}; my %i_ignore; foreach my $if ( keys %$interfaces ) { # lo0 etc if ( $interfaces->{$if} =~ /\b(inloopback|console)\d*\b/ix ) { $i_ignore{$if}++; } } return \%i_ignore; } 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 # 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 = {}; 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}; } 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; } 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 fan { my $huawei = shift; my $fan = $huawei->hw_fan_descr() || {}; my $state = $huawei->hw_fan_state() || {}; if ( scalar keys %$state ) { my @messages = (); foreach my $k ( keys %$state ) { next if $state->{$k} and $state->{$k} eq 'normal'; my ($slot, $num) = split(/\./, $k); my $descr = "Slot $slot,Fan $num"; $descr = $fan->{$k} if ($fan->{$k}); push @messages, "$descr: $state->{$k}"; } push @messages, ( ( scalar keys %$state ) . " 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; my $descr = "Slot $slot,PS $num"; $descr = $pwr_descr->{$i} if ($pwr_descr->{$i}); $ret .= $s . $descr . ": " . $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; my $descr = "Slot $slot,PS $num"; $descr = $pwr_descr->{$i} if ($pwr_descr->{$i}); $ret .= $s . $descr . ": " . $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; $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)/ix; $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 switches and routers. =head1 AUTHORS Jeroen van Ingen and Eric Miller =head1 SYNOPSIS # Let SNMP::Info determine the correct subclass for you. my $huawei = new SNMP::Info( AutoSpecify => 1, Debug => 1, DestHost => 'myrouter', Community => 'public', Version => 2 ) or die "Can't connect to DestHost.\n"; my $class = $huawei->class(); print "SNMP::Info determined this device to fall under subclass : $class\n"; =head1 DESCRIPTION Subclass for Huawei switches =head2 Inherited Classes =over =item SNMP::Info::Layer3 =item SNMP::Info::IEEE802dot3ad =back =head2 Required MIBs =over =item F =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. =back =head1 GLOBALS These are methods that return scalar value from SNMP =over =item $huawei->vendor() Returns 'huawei'. =item $huawei->os() Returns 'VRP' if contained in C, 'huawei' otherwise. =item $huawei->os_ver() Returns the software version derived from the C or extracted from C. =item $huawei->mac() Base MAC of the device. (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 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 =item $huawei->i_ignore() 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. Attempts to use C first and then C if that is unavailable. =item C Interface MTU value. Overridden with corresponding frame size entry from C if one exists. =back =head2 Power Port Table The index of these methods have been normalized to slot.port and values munged to provide compatibility 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 compatibility 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 =head2 Table Methods imported from SNMP::Info::Layer3 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