#196 support for powerconnect 8164F

Refactor Layer3::Dell for better support of newer models - requires updated MIBs
This commit is contained in:
Eric A. Miller
2018-04-15 23:38:05 -04:00
parent 62823e3d83
commit 82755ab8db
5 changed files with 389 additions and 59 deletions

View File

@@ -12,7 +12,7 @@ before_install:
- mkdir ~/netdisco-mibs - mkdir ~/netdisco-mibs
- cd ~/netdisco-mibs - cd ~/netdisco-mibs
install: install:
- curl -sL https://github.com/netdisco/netdisco-mibs/releases/download/4.007/netdisco-mibs.tar.gz | tar --strip-components=1 -zxf - - curl -sL https://github.com/netdisco/netdisco-mibs/releases/download/4.008/netdisco-mibs.tar.gz | tar --strip-components=1 -zxf -
- cpanm --quiet --notest PkgConfig Test::CChecker Alien::zlib::Static Alien::OpenSSL::Static Alien::SNMP::MAXTC - cpanm --quiet --notest PkgConfig Test::CChecker Alien::zlib::Static Alien::OpenSSL::Static Alien::SNMP::MAXTC
before_script: before_script:
- 'cd ${TRAVIS_BUILD_DIR}' - 'cd ${TRAVIS_BUILD_DIR}'

View File

@@ -6,7 +6,13 @@ Version 3.55
[ENHANCEMENTS] [ENHANCEMENTS]
* #196 Support for powerconnect 8164F
* Refactor Layer3::Dell for better support of newer models
* Increase capture of i_vlan on router interfaces in L3::Cisco * Increase capture of i_vlan on router interfaces in L3::Cisco
* Factor out logic to determine serial number from ENTITY-MIB in Layer2 and
Layer3 to new method entity_derived_serial in Entity class and added new
method entity_derived_os_ver in Entity class to determine OS version
in a similar manner
[BUG FIXES] [BUG FIXES]

View File

@@ -792,7 +792,7 @@ See documentation in L<SNMP::Info::Layer3::DLink> for details.
=item SNMP::Info::Layer3::Dell =item SNMP::Info::Layer3::Dell
Subclass for Dell PowerConnect switches. D-Link, the IBM BladeCenter Subclass for Dell PowerConnect switches. The IBM BladeCenter
Gigabit Ethernet Switch Module and some Linksys switches Gigabit Ethernet Switch Module and some Linksys switches
also use this module based upon MIB support. also use this module based upon MIB support.

View File

@@ -50,12 +50,13 @@ $VERSION = '3.54';
'RADLAN-rlInterfaces' => 'rlIfNumOfLoopbackPorts', 'RADLAN-rlInterfaces' => 'rlIfNumOfLoopbackPorts',
'RADLAN-HWENVIROMENT' => 'rlEnvPhysicalDescription', 'RADLAN-HWENVIROMENT' => 'rlEnvPhysicalDescription',
'Dell-Vendor-MIB' => 'productIdentificationVersion', 'Dell-Vendor-MIB' => 'productIdentificationVersion',
'DELL-REF-MIB' => 'dell6224Switch',
); );
%GLOBALS = ( %GLOBALS = (
%SNMP::Info::Layer3::GLOBALS, %SNMP::Info::Layer3::GLOBALS,
%SNMP::Info::LLDP::GLOBALS, %SNMP::Info::LLDP::GLOBALS,
'os_ver' => 'productIdentificationVersion', 'dell_os_ver' => 'productIdentificationVersion',
'dell_id_name' => 'productIdentificationDisplayName', 'dell_id_name' => 'productIdentificationDisplayName',
); );
@@ -115,16 +116,22 @@ sub model {
my $dell = shift; my $dell = shift;
my $name = $dell->dell_id_name(); my $name = $dell->dell_id_name();
my $descr = $dell->description(); my $id = $dell->id();
if ( defined $name and $name =~ m/(\d+)/ ) { if ( defined $name and $name =~ m/(\d+)/ ) {
return $1; return $1;
} }
# Don't have a vendor MIB for D-Link return unless defined $id;
else {
return $descr; my $model = SNMP::translateObj($id);
if (defined $model) {
$model =~ s/^dell//;
$model =~ s/Switch$//;
return $model;
} }
return $id;
} }
sub vendor { sub vendor {
@@ -139,19 +146,19 @@ sub os {
return $dell->_vendor(); return $dell->_vendor();
} }
sub serial { sub os_ver {
my $dell = shift; my $dell = shift;
my $numbers = $dell->dell_serial_no(); my $entity_os = $dell->entity_derived_os_ver();
if ( defined $entity_os and $entity_os !~ /^\s*$/ ){
foreach my $key ( keys %$numbers ) { return $entity_os;
my $serial = $numbers->{$key};
return $serial if ( defined $serial and $serial !~ /^\s*$/ );
next;
} }
# Last resort my $dell_os = $dell->dell_os_ver();
return $dell->SUPER::serial(); if ( defined $dell_os and $dell_os !~ /^\s*$/ ) {
return $dell_os;
}
return;
} }
# check all fans, and report overall status # check all fans, and report overall status
@@ -160,6 +167,8 @@ sub fan {
my $fan = $dell->dell_fan_desc() || {}; my $fan = $dell->dell_fan_desc() || {};
my $state = $dell->dell_fan_state() || {}; my $state = $dell->dell_fan_state() || {};
if (scalar keys %$fan) {
my @messages = (); my @messages = ();
foreach my $k (keys %$fan) { foreach my $k (keys %$fan) {
@@ -172,29 +181,60 @@ sub fan {
return (join ", ", @messages); return (join ", ", @messages);
} }
return;
sub _ps_status {
my ($dell, $unit) = @_;
my $status = 'unknown';
return $status if !defined $unit;
my $desc = $dell->dell_pwr_desc() || {};
my $state = $dell->dell_pwr_state() || {};
foreach my $k (keys %$desc) {
next unless $desc->{$k} and $desc->{$k} eq "ps1_unit$unit";
return ($state->{$k} || $status);
} }
return $status; sub ps1_type {
my $dell = shift;
my $src = $dell->dell_pwr_src() || {};
foreach my $k (sort keys %$src) {
next unless $src->{$k};
return $src->{$k};
}
return;
} }
sub ps1_type { return 'internalRedundant' } sub ps2_type {
sub ps2_type { return 'internalRedundant' } my $dell = shift;
sub ps1_status { return (shift)->_ps_status(1) } my $src = $dell->dell_pwr_src() || {};
sub ps2_status { return (shift)->_ps_status(2) }
my $i = 0;
foreach my $k (sort keys %$src) {
$i++;
next unless $src->{$k} and $i == 2;
return $src->{$k};
}
return;
}
sub ps1_status {
my $dell = shift;
my $status = $dell->dell_pwr_state() || {};
foreach my $k (sort keys %$status) {
next unless $status->{$k};
return $status->{$k};
}
return;
}
sub ps2_status {
my $dell = shift;
my $status = $dell->dell_pwr_state() || {};
my $i = 0;
foreach my $k (sort keys %$status) {
$i++;
next unless $status->{$k} and $i == 2;
return $status->{$k};
}
return;
}
sub interfaces { sub interfaces {
my $dell = shift; my $dell = shift;
@@ -214,6 +254,23 @@ sub interfaces {
return $i_descr; return $i_descr;
} }
sub i_duplex {
my $dell = shift;
my $partial = shift;
my $dell_duplex = $dell->dell_duplex($partial) || {};
my %i_duplex;
foreach my $if ( keys %$dell_duplex ) {
my $duplex = $dell_duplex->{$if};
next unless defined $duplex;
next if $duplex eq 'unknown';
$i_duplex{$if} = $duplex;
}
return \%i_duplex;
}
sub i_duplex_admin { sub i_duplex_admin {
my $dell = shift; my $dell = shift;
my $partial = shift; my $partial = shift;
@@ -226,11 +283,11 @@ sub i_duplex_admin {
foreach my $if ( keys %$interfaces ) { foreach my $if ( keys %$interfaces ) {
my $duplex = $dell_duplex->{$if}; my $duplex = $dell_duplex->{$if};
next unless defined $duplex; next unless defined $duplex;
my $auto = $dell_auto->{$if} || 'false'; my $auto = $dell_auto->{$if} || 'disabled';
$duplex = 'half' if ( $duplex =~ /half/i and $auto =~ /false/i ); $duplex = 'half' if ( $duplex eq 'half' and $auto eq 'disabled' );
$duplex = 'full' if ( $duplex =~ /half/i and $auto =~ /false/i ); $duplex = 'full' if ( $duplex eq 'full' and $auto eq 'disabled' );
$duplex = 'auto' if $auto =~ /true/i; $duplex = 'auto' if $auto eq 'enabled';
$i_duplex_admin{$if} = $duplex; $i_duplex_admin{$if} = $duplex;
} }
return \%i_duplex_admin; return \%i_duplex_admin;
@@ -242,7 +299,6 @@ sub _vendor {
my $id = $dell->id() || 'undef'; my $id = $dell->id() || 'undef';
my %oidmap = ( my %oidmap = (
2 => 'ibm', 2 => 'ibm',
171 => 'dlink',
674 => 'dell', 674 => 'dell',
3955 => 'linksys', 3955 => 'linksys',
); );
@@ -252,7 +308,7 @@ sub _vendor {
return $oidmap{$id}; return $oidmap{$id};
} }
else { else {
return 'dlink'; return $id;
} }
} }
@@ -449,6 +505,10 @@ Returns the map between SNMP Interface Identifier (iid) and physical port
name. Uses name if available instead of description since descriptions are name. Uses name if available instead of description since descriptions are
sometimes not unique. sometimes not unique.
=item $dell->i_duplex()
Returns reference to map of IIDs to current link duplex.
=item $dell->i_duplex_admin() =item $dell->i_duplex_admin()
Returns reference to hash of iid to current link administrative duplex Returns reference to hash of iid to current link administrative duplex

View File

@@ -33,28 +33,292 @@ use Test::Class::Most parent => 'My::Test::Class';
use SNMP::Info::Layer3::Dell; use SNMP::Info::Layer3::Dell;
# 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) { sub setup : Tests(setup) {
my $test = shift; my $test = shift;
$test->SUPER::setup; $test->SUPER::setup;
# Start with a common cache that will serve most tests # Start with a common cache that will serve most tests
my $cache_data = { my $cache_data = {
'_layers' => 3, '_layers' => 4,
'_description' => 'Powerconnect 8024F, 3.1.4.5, VxWorks 6.5', '_description' => 'Powerconnect 8024F, 3.1.4.5, VxWorks 6.5',
# DELL-WLAN-MIB::powerConnect.3024 # DELL-REF-MIB::dell8024FSwitch
'_id' => '.1.3.6.1.4.1.674.10895.3024', '_id' => '.1.3.6.1.4.1.674.10895.3024',
'store' => {}, '_i_description' => 1,
'_i_name' => 1,
# These don't exist in the 8024F, but pretend they do to simplify fan,
# ps*, and duplex_admin test code, there is no equivalent in newer models
'_dell_fan_desc' => 1,
'_dell_fan_state' => 1,
'_dell_pwr_src' => 1,
'_dell_pwr_state' => 1,
'_dell_pwr_desc' => 1,
'_dell_duplex' => 1,
'_dell_duplex_admin' => 1,
'_dell_auto' => 1,
# ENTITY-MIB used for coverage of entity_derived_os_ver in os_ver
'_e_parent' => 1,
'_e_class' => 1,
'store' => {
'i_description' => {
1 => 'Ethernet Interface',
2 => 'Ethernet Interface',
3 => 'Ethernet Interface'
},
'i_name' => {1 => 'g1', 2 => 'g2'},
'dell_fan_desc' => {67109249 => 'fan1', 67109250 => 'fan2'},
'dell_fan_state' => {67109249 => 'normal', 67109250 => 'warning'},
'dell_pwr_src' => {67109185 => 'ac', 67109186 => 'unknown'},
'dell_pwr_state' => {67109185 => 'normal', 67109186 => 'notPresent'},
'dell_pwr_desc' => {67109185 => 'ps1', 67109186 => 'ps2'},
'dell_duplex' => {1 => 'full', 2 => 'half', 3 => 'unknown'},
'dell_duplex_admin' => {1 => 'full', 2 => 'half', 3 => 'none'},
'dell_auto' => {1 => 'disabled', 2 => 'disabled', 3 => 'enabled'},
'e_parent' => {1 => 0, 2 => 1, 3 => 2, 54 => 1, 55 => 54},
'e_class' => {
1 => 'stack',
2 => 'chassis',
3 => 'module',
54 => 'chassis',
55 => 'module'
},
},
}; };
$test->{info}->cache($cache_data); $test->{info}->cache($cache_data);
} }
# This class is also used for devices that don't have the Dell IANA private
# enterprise number (674). If we ever create a new class to cover these
# devices these tests should serve as a reminder to remove the applicable
# from this class
sub device_type : Tests(+2) {
my $test = shift;
$test->SUPER::device_type();
# IBM BladeCenter 4-Port GB Ethernet Switch Module
my $cache_data = {
'_layers' => 2,
'_description' => 'IBM Gigabit Ethernet Switch Module',
'_id' => '.1.3.6.1.4.1.2'
};
$test->{info}->cache($cache_data);
is($test->{info}->device_type,
'SNMP::Info::Layer3::Dell',
'IBM BladeCenter 4-Port GB Ethernet Switch Module');
$test->{info}->clear_cache();
# Linksys 2024/2048
$cache_data = {
'_layers' => 2,
'_description' => '48-Port 10/100/1000 Gigabit Switch w/WebView',
'_id' => '.1.3.6.1.4.1.3955.6.1.2024.1'
};
$test->{info}->cache($cache_data);
is($test->{info}->device_type,
'SNMP::Info::Layer3::Dell', 'Linksys 2024/2048');
$test->{info}->clear_cache();
}
sub model : Tests(5) {
my $test = shift;
can_ok($test->{info}, 'model');
is($test->{info}->model(),
'8024F', q(Model has expected value using 'sysObjectID'));
# Non Dell sysObjectID's won't resolve and should return a partially
# resolved OID
$test->{info}{_id} = '.1.3.6.1.4.1.3955.6.1.2024.1';
is($test->{info}->model(),
'enterprises.3955.6.1.2024.1',
q(Non Dell returns partially resolved 'sysObjectID'));
# On older switches, sysObjectID will not resolve, this is from
# Dell-Vendor-MIB::productIdentificationDisplayName which is not populated
# on newer models such as the dell8024FSwitch which snmp data is in the
# test setup method to populate the default cache
$test->{info}{_dell_id_name} = 'PowerConnect 5324';
is($test->{info}->model(),
'5324', q(Older 'productIdentificationDisplayName' returns expected model));
$test->{info}->clear_cache();
is($test->{info}->model(), undef, q(No id returns undef model));
}
sub vendor : Tests(5) {
my $test = shift;
can_ok($test->{info}, 'vendor');
is($test->{info}->vendor(), 'dell', q(Vendor returns 'dell'));
$test->{info}{_id} = '.1.3.6.1.4.1.3955.6.1.2024.1';
is($test->{info}->vendor(), 'linksys', q(Vendor returns 'linksys'));
$test->{info}{_id} = '.1.3.6.1.4.1.2';
is($test->{info}->vendor(), 'ibm', q(Vendor returns 'ibm'));
$test->{info}->clear_cache();
is($test->{info}->vendor(),
'undef', q(No 'sysObjectID' returns 'undef' string));
}
sub os : Tests(5) {
my $test = shift;
can_ok($test->{info}, 'os');
is($test->{info}->os(), 'dell', q(OS returns 'dell'));
$test->{info}{_id} = '.1.3.6.1.4.1.3955.6.1.2024.1';
is($test->{info}->os(), 'linksys', q(OS returns 'linksys'));
$test->{info}{_id} = '.1.3.6.1.4.1.2';
is($test->{info}->os(), 'ibm', q(OS returns 'ibm'));
$test->{info}->clear_cache();
is($test->{info}->os(), 'undef', q(No 'sysObjectID' returns 'undef' string));
}
sub os_ver : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'os_ver');
# The call to SUPER::serial() will use the entity_derived_serial method
# which uses a partial fetch for e_serial which ignores the cache
# and reloads data therefore we must use the mocked session.
my $data
= {'ENTITY-MIB::entPhysicalSoftwareRev' =>
{1 => undef, 2 => '5.1.2.3', 3 => undef, 54 => '6.1.2.3', 55 => undef},
};
$test->{info}{sess}{Data} = $data;
is($test->{info}->os_ver(),
'5.1.2.3', q(OS version has expected value using 'ENTITY-MIB'));
$test->{info}->clear_cache();
# Manually populate cache entry
$test->{info}{_dell_os_ver} = '1.0.0.45';
is($test->{info}->os_ver(),
'1.0.0.45',
q(OS version has expected value using 'productIdentificationVersion'));
$test->{info}->clear_cache();
is($test->{info}->os_ver(), undef, q(No data retruns undef));
}
sub fan : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'fan');
is($test->{info}->fan(), 'fan2: warning', q(Returns fan not in normal state));
# All fans normal returns a distinct string for this class
$test->{info}{store}{dell_fan_state}
= {67109249 => 'normal', 67109250 => 'normal'};
is($test->{info}->fan(), '2 fans OK', q(All fans ok));
$test->{info}->clear_cache();
is($test->{info}->fan(), undef, q(No fan data returns undef));
}
sub ps1_type : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'ps1_type');
is($test->{info}->ps1_type(), 'ac');
$test->{info}->clear_cache();
is($test->{info}->ps1_type(),
undef, q(No power supply data returns type undef));
}
sub ps2_type : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'ps2_type');
is($test->{info}->ps2_type(), 'unknown');
$test->{info}->clear_cache();
is($test->{info}->ps2_type(),
undef, q(No power supply data returns type undef));
}
sub ps1_status : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'ps1_status');
is($test->{info}->ps1_status(), 'normal');
$test->{info}->clear_cache();
is($test->{info}->ps1_status(),
undef, q(No power supply data returns status undef));
}
sub ps2_status : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'ps2_status');
is($test->{info}->ps2_status(), 'notPresent');
$test->{info}->clear_cache();
is($test->{info}->ps2_status(),
undef, q(No power supply data returns status undef));
}
sub interfaces : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'interfaces');
my $expected = {1 => 'g1', 2 => 'g2', 3 => 'Ethernet Interface'};
cmp_deeply($test->{info}->interfaces(),
$expected, q(Interfaces have expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->interfaces(), {}, q(No data returns empty hash));
}
sub i_duplex : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'i_duplex');
my $expected = {1 => 'full', 2 => 'half'};
cmp_deeply($test->{info}->i_duplex(),
$expected, q(Interfaces have expected duplex values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->i_duplex(),
{}, q(No duplex admin data returns empty hash));
}
sub i_duplex_admin : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'i_duplex_admin');
my $expected = {1 => 'full', 2 => 'half', 3 => 'auto'};
cmp_deeply($test->{info}->i_duplex_admin(),
$expected, q(Interfaces have expected duplex admin values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->i_duplex_admin(),
{}, q(No duplex admin data returns empty hash));
}
sub qb_fdb_index : Tests(2) {
my $test = shift;
can_ok($test->{info}, 'qb_fdb_index');
is($test->{info}->qb_fdb_index(), undef);
}
1; 1;