From 0896bd881c891fa71db746854f5bd451b4b5586a Mon Sep 17 00:00:00 2001 From: nick n <39005454+inphobia@users.noreply.github.com> Date: Mon, 15 Apr 2019 22:47:03 +0200 Subject: [PATCH] lenovo / cnos os support (#323) while it's not yet complete is already is quite functional. things like vlan, lldp, connected nodes, etc all work test coverage also included. features to be added: link aggregation master/slave (some strange difference with standard 802.3ad i think) spanning tree will most likely work too fan/psu status modules / entity-mib seems to be strangly formatted --- Changes | 3 + MANIFEST | 2 + META.json | 8 +- META.yml | 5 +- lib/SNMP/Info.pm | 7 + lib/SNMP/Info/Layer3/Lenovo.pm | 322 +++++++++++++++++++++++++ xt/lib/Test/SNMP/Info/Layer3/Lenovo.pm | 147 +++++++++++ 7 files changed, 491 insertions(+), 3 deletions(-) create mode 100644 lib/SNMP/Info/Layer3/Lenovo.pm create mode 100644 xt/lib/Test/SNMP/Info/Layer3/Lenovo.pm diff --git a/Changes b/Changes index 308f82e8..d90f159d 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,8 @@ Version 3.67 (2019-xx-xx) + [NEW FEATURES] + * initial lenovo / cnos support (inphobia) + [BUG FIXES] * #319 make fortinet return a useful interface name (inphobia) diff --git a/MANIFEST b/MANIFEST index c31e7079..b6f5a6d1 100644 --- a/MANIFEST +++ b/MANIFEST @@ -104,6 +104,7 @@ lib/SNMP/Info/Layer3/Huawei.pm lib/SNMP/Info/Layer3/IBMGbTor.pm lib/SNMP/Info/Layer3/Juniper.pm lib/SNMP/Info/Layer3/Lantronix.pm +lib/SNMP/Info/Layer3/Lenovo.pm lib/SNMP/Info/Layer3/Microsoft.pm lib/SNMP/Info/Layer3/Mikrotik.pm lib/SNMP/Info/Layer3/N1600.pm @@ -248,6 +249,7 @@ xt/lib/Test/SNMP/Info/Layer3/Huawei.pm xt/lib/Test/SNMP/Info/Layer3/IBMGbTor.pm xt/lib/Test/SNMP/Info/Layer3/Juniper.pm xt/lib/Test/SNMP/Info/Layer3/Lantronix.pm +xt/lib/Test/SNMP/Info/Layer3/Lenovo.pm xt/lib/Test/SNMP/Info/Layer3/Microsoft.pm xt/lib/Test/SNMP/Info/Layer3/Mikrotik.pm xt/lib/Test/SNMP/Info/Layer3/N1600.pm diff --git a/META.json b/META.json index f891085c..a641178e 100644 --- a/META.json +++ b/META.json @@ -434,6 +434,10 @@ "file" : "lib/SNMP/Info/Layer3/Lantronix.pm", "version" : "3.66" }, + "SNMP::Info::Layer3::Lenovo" : { + "file" : "lib/SNMP/Info/Layer3/Lenovo.pm", + "version" : "3.66" + }, "SNMP::Info::Layer3::Microsoft" : { "file" : "lib/SNMP/Info/Layer3/Microsoft.pm", "version" : "3.66" @@ -574,7 +578,7 @@ }, "homepage" : "http://netdisco.org/", "license" : [ - "http://opensource.org/licenses/BSD-3-Clause" + "http://opensource.org/licenses/bsd-license.php" ], "repository" : { "url" : "https://github.com/netdisco/snmp-info" @@ -583,5 +587,5 @@ "x_MailingList" : "https://lists.sourceforge.net/lists/listinfo/snmp-info-users" }, "version" : "3.66", - "x_serialization_backend" : "JSON::PP version 2.97001" + "x_serialization_backend" : "JSON::PP version 4.02" } diff --git a/META.yml b/META.yml index 81070d21..2e1e6073 100644 --- a/META.yml +++ b/META.yml @@ -312,6 +312,9 @@ provides: SNMP::Info::Layer3::Lantronix: file: lib/SNMP/Info/Layer3/Lantronix.pm version: '3.66' + SNMP::Info::Layer3::Lenovo: + file: lib/SNMP/Info/Layer3/Lenovo.pm + version: '3.66' SNMP::Info::Layer3::Microsoft: file: lib/SNMP/Info/Layer3/Microsoft.pm version: '3.66' @@ -426,7 +429,7 @@ resources: MailingList: https://lists.sourceforge.net/lists/listinfo/snmp-info-users bugtracker: https://github.com/netdisco/snmp-info/issues homepage: http://netdisco.org/ - license: http://opensource.org/licenses/BSD-3-Clause + license: http://opensource.org/licenses/bsd-license.php repository: https://github.com/netdisco/snmp-info version: '3.66' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff --git a/lib/SNMP/Info.pm b/lib/SNMP/Info.pm index 5749b28c..88747822 100644 --- a/lib/SNMP/Info.pm +++ b/lib/SNMP/Info.pm @@ -906,6 +906,12 @@ Subclass for Lantronix devices. See documentation in L for details. +=item SNMP::Info::Layer3::Lenovo + +Subclass for Lenovo switches running CNOS. + +See documentation in L for details. + =item SNMP::Info::Layer3::Microsoft Subclass for Generic Microsoft Routers running Microsoft Windows OS. @@ -1665,6 +1671,7 @@ sub device_type { 14823 => 'SNMP::Info::Layer3::Aruba', 14988 => 'SNMP::Info::Layer3::Mikrotik', 17163 => 'SNMP::Info::Layer3::Steelhead', + 19046 => 'SNMP::Info::Layer3::Lenovo', 21091 => 'SNMP::Info::Layer2::Exinda', 25506 => 'SNMP::Info::Layer3::H3C', 25461 => 'SNMP::Info::Layer3::PaloAlto', diff --git a/lib/SNMP/Info/Layer3/Lenovo.pm b/lib/SNMP/Info/Layer3/Lenovo.pm new file mode 100644 index 00000000..8f0080fc --- /dev/null +++ b/lib/SNMP/Info/Layer3/Lenovo.pm @@ -0,0 +1,322 @@ +# SNMP::Info::Layer3::Lenovo +# +# Copyright (c) 2019 nick nauwelaerts +# 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. + +# TODO +# fallback to super::i_speed needed? +# lag members (no ez way to map master<->slaves) +# psu & fan info should be possible +# spanning tree info is avail too +# modules list could use more work + +package SNMP::Info::Layer3::Lenovo; + +use strict; +use warnings; +use Exporter; +use SNMP::Info::Layer3; +use SNMP::Info::IEEE802dot3ad; + +@SNMP::Info::Layer3::Lenovo::ISA = qw/ + SNMP::Info::Layer3 + SNMP::Info::IEEE802dot3ad + Exporter +/; +@SNMP::Info::Layer3::Lenovo::EXPORT_OK = qw//; + +use vars qw/$VERSION %GLOBALS %MIBS %FUNCS %MUNGE/; + +$VERSION = '3.66'; + +%MIBS = ( + %SNMP::Info::Layer3::MIBS, + %SNMP::Info::IEEE802dot3ad::MIBS, + 'LENOVO-ENV-MIB' => 'lenovoEnvMibPowerSupplyIndex', + 'LENOVO-PRODUCTS-MIB' => 'tor', +); + +%GLOBALS = ( + %SNMP::Info::Layer3::GLOBALS, + %SNMP::Info::IEEE802dot3ad::GLOBALS, + # no way to get os version and other device details + # ENTITY-MIB however can help out + 'os_ver' => 'entPhysicalSoftwareRev.1', + 'mac' => 'dot1dBaseBridgeAddress', +); + +%FUNCS = ( + %SNMP::Info::Layer3::FUNCS, + %SNMP::Info::IEEE802dot3ad::FUNCS, + # perhaps we should honor what the device returns, but it's just + # the opposite of what most other's do, so overwrite + 'i_name' => 'ifDescr', + 'i_description' => 'ifName', +); + +%MUNGE = ( + %SNMP::Info::Layer3::MUNGE, + %SNMP::Info::IEEE802dot3ad::MUNGE, +); + +# lenovo does not set ifSpeed to 4294967295 for highspeed links, instead +# it substracts 4294967296 from the value until the remainder fits, so +# 10gbit interfaces are presented as: +# 10000000000 - 4294967296 - 4294967296 = 1410065408 +# so just always return if_speed_high +# +# forcing the use of ifhighspeed would be preferred but not possible atm +# so copy both functions from Info.pm & overwrite + +# is there any way to just overwrite the whole function? +# (overwrite i_speed with i_speed_high). would be more elegant. +sub i_speed { + my $cnos = shift; + my $partial = shift; + + return $cnos->orig_i_speed_high($partial); +} + +# also need to overwrite i_speed_raw, netdisco uses this in some +# instances +sub i_speed_raw { + my $info = shift; + my $partial = shift; + + # remove the speed formating + my $munge_i_speed = delete $info->{munge}{i_speed}; + # also for highspeed interfaces e.g. TenGigabitEthernet + my $munge_i_speed_high = delete $info->{munge}{i_speed_high}; + + my $i_speed_raw = $info->orig_i_speed($partial); +# my $i_speed_high = undef; + + # just overwrite if interface speed is over 2.5gbps + foreach my $i ( keys %$i_speed_raw ) { + my $i_speed_high = undef; + + $i_speed_high = $info->i_speed_high($partial); + if (defined($i_speed_high) and ($i_speed_high->{$i} > 2500)) { + $i_speed_raw->{$i} = ( $i_speed_high->{$i} * 1_000_000 ); + } + } + + # restore the speed formating + $info->{munge}{i_speed} = $munge_i_speed; + $info->{munge}{i_speed_high} = $munge_i_speed_high; + + return $i_speed_raw; +} + +sub vendor { + return 'lenovo'; +} + +sub os { + return 'cnos'; +} + +# work in progress, there seems to be no standardized way to map +# lag members to the master. +sub agg_ports_cnos { + my $dev = shift; + + # TODO: implement partial + my $ports = $dev->ad_lag_ports(); + my $index = $dev->bp_index() || {}; + + return {} unless ref {} eq ref $ports and scalar keys %$ports; + + my $ret = {}; + foreach my $m ( keys %$ports ) { +print "m $m\n"; + my $idx = $m; + my $portlist = $ports->{$m}; +printf "p %d\n", scalar(@$portlist); + + next unless $portlist; + + # While dot3adAggTable is indexed by ifIndex, the portlist is indexed + # with a dot1dBasePort, so we need to use dot1dBasePortIfIndex to map to + # the ifIndex. If we don't have dot1dBasePortIfIndex assume + # dot1dBasePort = ifIndex + for ( my $i = 0; $i <= scalar(@$portlist); $i++ ) { + my $ifindex = $i+1; + if ( exists($index->{$i+1}) and defined($index->{$i+1}) ) { + $ifindex = $index->{$i+1}; +print "ifi $ifindex\n"; + } + $ret->{$ifindex} = $idx if ( @$portlist[$i] ); + } + } + + return $ret; +} + +#sub agg_ports { return agg_ports_cnos(@_) } + +1; + +__END__ + +=head1 NAME + +SNMP::Info::Layer3::Lenovo - SNMP Interface to Lenovo switches running CNOS. + +=head1 AUTHORS + +Nick Nauwelaerts + +=head1 SYNOPSIS + + # Let SNMP::Info determine the correct subclass for you. + use SNMP::Info; + my $cnos = new SNMP::Info( + AutoSpecify => 1, + Debug => 1, + DestHost => 'myrouter', + Community => 'public', + Version => 2 + ) + or die "Can't connect to DestHost.\n"; + my $class = $cnos->class(); + print "SNMP::Info determined this device to fall under subclass : $class\n"; + +=head1 DESCRIPTION + +Subclass for Lenovo switches running CNOS. + +=head2 Inherited Classes + +=over + +=item SNMP::Info::IEEE802dot3ad + +=item SNMP::Info::Layer3 + +=back + +=head2 Required MIBs + +=over + +=item F + +=item F + +=back + +=head2 Inherited Classes' MIBs + +See L for its own MIB requirements. + +See L for its own MIB requirements. + +=head1 GLOBALS + +These are methods that return scalar value from SNMP. + +=over + +=item $cnos->mac() + +Returns base mac based on C. + +=item $cnos->os_ver() + +Returns the OS version extracted from C. + +=back + +=head2 Overrides + +=over + +=item $cnos->vendor() + +Returns 'lenovo'. + +=item $cnos->os() + +Returns 'cnos'. + +=back + +=head2 Globals imported from SNMP::Info::IEEE802dot3ad + +See documentation in L for details. + +=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 $cnos->agg_ports_cnos() + +placeholder function, will return agg_ports mapping once implemented. + +=back + +=head2 Overrides + +=over + +=item $cnos->i_description() + +Uses C to match most other devices. + +=item $cnos->i_name() + +Uses C to match most other devices. + +=item $cnos->i_speed() + +CNOS does not set C to 4294967295 for high speed links, return +C instead. SNMP::Info will handle this correctly. + +=item $cnos->i_speed_raw() + +If C > 2500 we overwrite C, using the +formula: C * 1_000_000. + +=back + +=head2 Table Methods imported from SNMP::Info::IEEE802dot3ad + +See documentation in L for details. + +=head2 Table Methods imported from SNMP::Info::Layer3 + +See documentation in L for details. + +=cut diff --git a/xt/lib/Test/SNMP/Info/Layer3/Lenovo.pm b/xt/lib/Test/SNMP/Info/Layer3/Lenovo.pm new file mode 100644 index 00000000..28abdd6d --- /dev/null +++ b/xt/lib/Test/SNMP/Info/Layer3/Lenovo.pm @@ -0,0 +1,147 @@ +# Test::SNMP::Info::Layer3::Lenovo +# +# Copyright (c) 2019 nick nauwelaerts +# 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 Test::SNMP::Info::Layer3::Lenovo; + +use Test::Class::Most parent => 'My::Test::Class'; + +use SNMP::Info::Layer3::Lenovo; + +# XXX can be removed when agg_ports_cnos is implemented +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 $cache_data = { + '_id' => '.1.3.6.1.4.1.19046.1.7.32', + '_layers' => 14, + '_description' => 'Lenovo ThinkSystem NE1032 RackSwitch', + + '_i_index' => 1, + '_i_speed_high' => 1, + 'store' => { + 'i_index' => { + 2 => 2, + 10310 => 10310, + 103999 => 103999, + 410001 => 410001, + }, + 'i_speed_high' => { + 2 => 1000, + 10310 => 0, + 103999 => 20000, + 410001 => 10000, + }, + }, + }; + $test->{info}->cache($cache_data); +} + +sub os : Tests(2) { + my $test = shift; + + can_ok($test->{info}, 'os'); + is($test->{info}->os(), 'cnos', q(OS returns 'cnos')); +} + +sub vendor : Tests(2) { + my $test = shift; + + can_ok($test->{info}, 'vendor'); + is($test->{info}->vendor(), 'lenovo', q(Vendor returns 'lenovo')); +} + +sub i_speed : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'i_speed'); + + my $expected = { + 2 => "1.0 Gbps", + 10310 => "0 Mbps", + 103999 => "20 Gbps", + 410001 => "10 Gbps", + }; + + cmp_deeply($test->{info}->i_speed(), + $expected, q(i_speed data has expected values)); + + # do we want undef or empty hash? + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->i_speed(), undef, q(i_speed no data returns empty undef)); +} + +sub i_speed_raw : Tests(3) { + my $test = shift; + + can_ok($test->{info}, 'i_speed_raw'); + + # set original mib data to make sure we simulate everything correctly + my $data = { + 'IF-MIB::ifSpeed' => { + 2 => 1000000000, + 10310 => 8000, + 103999 => 2820130816, + 410001 => 1410065408, + }, + 'IF-MIB::ifHighSpeed' => { + 2 => 1000, + 10310 => 0, + 103999 => 20000, + 410001 => 10000, + }, + }; + $test->{info}{sess}{Data} = $data; + + my $expected = { + 2 => 1000000000, + 10310 => 8000, + 103999 => 20000000000, + 410001 => 10000000000, + }; + + cmp_deeply($test->{info}->i_speed_raw(), + $expected, q(i_speed_raw data has expected values)); + + delete $test->{info}{_i_speed_raw}; + delete $test->{info}{store}{i_speed_raw}; + $test->{info}{sess}{Data} = {}; + $test->{info}->clear_cache(); + cmp_deeply($test->{info}->i_speed_raw(), {}, q(i_speed_raw no data returns empty hash)); +} + +1;