From 083c600997afe1f0f8b54610832f13032112d547 Mon Sep 17 00:00:00 2001 From: Christian Ramseyer Date: Mon, 8 Sep 2025 22:18:10 +0200 Subject: [PATCH 1/5] Huawei: unify os_ver and os, fetch hwPatchFileVersion --- lib/SNMP/Info/Layer3/Huawei.pm | 47 +++++++++++++++++- xt/lib/Test/SNMP/Info/Layer3/Huawei.pm | 68 ++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 11 deletions(-) diff --git a/lib/SNMP/Info/Layer3/Huawei.pm b/lib/SNMP/Info/Layer3/Huawei.pm index ff84b444..e59d4efe 100644 --- a/lib/SNMP/Info/Layer3/Huawei.pm +++ b/lib/SNMP/Info/Layer3/Huawei.pm @@ -54,6 +54,7 @@ $VERSION = '3.973000'; 'HUAWEI-IF-EXT-MIB' => 'hwTrunkIfIndex', 'HUAWEI-L2IF-MIB' => 'hwL2IfPortIfIndex', 'HUAWEI-POE-MIB' => 'hwPoePower', + 'HUAWEI-SYS-MAN-MIB' => 'hwPatchFileVersion', 'HUAWEI-ENTITY-EXTENT-MIB' => 'hwEntityFanState', ); @@ -97,6 +98,9 @@ $VERSION = '3.973000'; # HUAWEI-ENTITY-EXTENT-MIB::hwPwrStatusTable 'hw_pwr_state' => 'hwEntityPwrState', 'hw_pwr_descr' => 'hwEntityPwrDesc', + + # HUAWEI-SYS-MAN-MIB::hwPatchFileTable + 'hw_patch_version' => 'hwPatchFileVersion', ); %MUNGE = ( @@ -121,8 +125,11 @@ sub os { if ( defined ($descr) && $descr =~ /\b(VRP)\b/ ) { return $1; + }elsif ( defined ($descr) && $descr =~ /\b(YunShan OS)\b/i ) { + return "YunShan OS"; + } else { + return "huawei"; } - return "huawei"; } sub os_ver { @@ -133,6 +140,44 @@ sub os_ver { return $entity_os; } + my $patchstr = $huawei->_os_patch(); + print STDERR "PPPPP $patchstr\n"; + # Prefer extracting uniform V600R023C10SPC500-style version strings from sysDescr first + # we trim the base kernel version like 5.170 to make the netdisco inventory page nicer + my $descr = $huawei->description(); + if ( defined $descr && $descr =~ /(V\d{3}[A-Za-z0-9]+)/ ) { + return $patchstr ? ($1 . " ".$patchstr) : $1; + #return $1 + $patchstr; + }else{ + # if there is no version in the V\d{3}-style, use the old approach of returning more + return $huawei->_os_ver_legacy(); + } +} + +sub _os_patch { + my $huawei = shift; + + # Patches are based on the release, e.g. + # V600R023C10SPC500 -> V600R023HP1501 -> Hot Patch 1501 + # V200R011C10SPC600 -> V200R011SPC102 -> Service Pack Collection 102 + # V200R011C10SPC600 -> V200R011SPH033 -> Special Patch Hotfix 033 + my $patches = $huawei->hw_patch_version(); + #use Data::Dumper; print Dumper $patches; + + print STDERR "xxx $patches\n"; + my $patchstr = join ' ', map { /V\d{3}R\d{3}(\S+)/ ? $1 : () } values %$patches; + return $patchstr; +} + + +sub _os_ver_legacy { + 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; diff --git a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm index 347a7ad9..f90d782f 100644 --- a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm +++ b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm @@ -73,6 +73,7 @@ sub setup : Tests(setup) { '_hw_pwr_descr' => 1, 'store' => { + 'hw_patch_version' => { '1' => 'HP777', 2 => 'HP123'}, 'i_index' => {1 => 1, 6 => 6, 7 => 7, 8 => 8, 108 => 108}, 'i_description' => { 1 => 'InLoopBack0', @@ -120,54 +121,101 @@ sub vendor : Tests(2) { is($test->{info}->vendor(), 'huawei', q(Vendor returns 'huawei')); } -sub os : Tests(3) { +sub os : Tests(5) { my $test = shift; can_ok($test->{info}, 'os'); - is($test->{info}->os(), 'VRP', q(OS returns 'VRP' when description matches)); + is($test->{info}->os(), 'VRP', q(OS returns 'VRP' when V100 description matches)); + + my $descr = "Huawei YunShan OS\nVersion 1.23.1.1 (S5700 V600R023C10SPC500)\n" + ."Copyright (C) 2021-2024 Huawei Technologies Co., Ltd."; + $test->{info}{_description} = $descr; + is($test->{info}->os(), 'YunShan OS', q(OS returns 'YunShan OS' when V600 description matches)); + + $descr = "Huawei Versatile Routing Platform Software\r\nVRP (R) software,Version 5.170 (S5700 V200R011C10SPC600)\r\n" + ."Copyright (C) 2007 Huawei Technologies Co., Ltd."; + $test->{info}{_description} = $descr; + is($test->{info}->os(), 'VRP', q(OS returns 'VRP' when V200 description matches)); $test->{info}->clear_cache(); - is($test->{info}->os(), 'huawei', q(... and 'huawei' when it doesn't)); + is($test->{info}->os(), 'huawei', q(... and 'huawei' as fallback if nothing else matches)); } -sub os_ver : Tests(7) { +=pod +sub os_ver_with_patch : Tests(2) { my $test = shift; can_ok($test->{info}, 'os_ver'); + my $descr = "Huawei YunShan OS\nVersion 1.23.1.1 (S5700 V600R023C10SPC500)\n" + ."Copyright (C) 2021-2024 Huawei Technologies Co., Ltd."; + $test->{info}{_description} = $descr; + $test->{info}{store}{hw_patch_version}{'1'} = 'V600R023HP1501'; + #$test->{info}{store}{hw_fan_state}{'1.2'} = 'normal'; is( $test->{info}->os_ver(), - '8.100 V100R005C10SPC200', + 'V600R023C10SPC500 HP1501', + q(OS version returned with patch example 1) + ); +} +=cut + +sub os_ver : Tests(9) { + my $test = shift; + + can_ok($test->{info}, 'os_ver'); + + is( + $test->{info}->os_ver(), + 'V100R005C10SPC200', q(OS version returned from 'sysDescr' example 1) ); + # this first batch is ending up in _os_ver_legacy + 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)); + # contemporary os_ver + $descr = 'software,Version 5.120 (AR151 V200R003C01SPC100) '; $test->{info}{_description} = $descr; - is( $test->{info}->os_ver(), - '5.120 V200R003C01SPC100', + 'V200R003C01SPC100', q(OS version returned from 'sysDescr'example 5) ); + $descr = "Huawei YunShan OS\nVersion 1.23.1.1 (S5700 V600R023C10SPC500)\n" + ."Copyright (C) 2021-2024 Huawei Technologies Co., Ltd."; + $test->{info}{_description} = $descr; + is( + $test->{info}->os_ver(), + 'V600R023C10SPC500', + q(OS version returned from 'sysDescr'example 6) + ); + + $descr = "Huawei Versatile Routing Platform Software\r\nVRP (R) software,Version 5.170 (S5700 V200R011C10SPC600) \r\n" + ."Copyright (C) 2007 Huawei Technologies Co., Ltd."; + $test->{info}{_description} = $descr; + is( + $test->{info}->os_ver(), + 'V200R011C10SPC600', + q(OS version returned from 'sysDescr'example 7) + ); + $test->{info}->clear_cache(); is($test->{info}->os_ver(), undef, q(No data returns undef OS version)); } From 86b1b35fa33fca03fade81ad3926aba4cf89c369 Mon Sep 17 00:00:00 2001 From: Christian Ramseyer Date: Mon, 8 Sep 2025 22:23:10 +0200 Subject: [PATCH 2/5] Huawei: fix tests --- lib/SNMP/Info/Layer3/Huawei.pm | 2 -- xt/lib/Test/SNMP/Info/Layer3/Huawei.pm | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/SNMP/Info/Layer3/Huawei.pm b/lib/SNMP/Info/Layer3/Huawei.pm index e59d4efe..798a5f39 100644 --- a/lib/SNMP/Info/Layer3/Huawei.pm +++ b/lib/SNMP/Info/Layer3/Huawei.pm @@ -141,7 +141,6 @@ sub os_ver { } my $patchstr = $huawei->_os_patch(); - print STDERR "PPPPP $patchstr\n"; # Prefer extracting uniform V600R023C10SPC500-style version strings from sysDescr first # we trim the base kernel version like 5.170 to make the netdisco inventory page nicer my $descr = $huawei->description(); @@ -164,7 +163,6 @@ sub _os_patch { my $patches = $huawei->hw_patch_version(); #use Data::Dumper; print Dumper $patches; - print STDERR "xxx $patches\n"; my $patchstr = join ' ', map { /V\d{3}R\d{3}(\S+)/ ? $1 : () } values %$patches; return $patchstr; } diff --git a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm index f90d782f..66f8ff2d 100644 --- a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm +++ b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm @@ -71,9 +71,10 @@ sub setup : Tests(setup) { '_hw_fan_descr' => 1, '_hw_pwr_state' => 1, '_hw_pwr_descr' => 1, + '_hw_patch_version' => 1, 'store' => { - 'hw_patch_version' => { '1' => 'HP777', 2 => 'HP123'}, + 'hw_patch_version' => { }, 'i_index' => {1 => 1, 6 => 6, 7 => 7, 8 => 8, 108 => 108}, 'i_description' => { 1 => 'InLoopBack0', @@ -141,7 +142,6 @@ sub os : Tests(5) { is($test->{info}->os(), 'huawei', q(... and 'huawei' as fallback if nothing else matches)); } -=pod sub os_ver_with_patch : Tests(2) { my $test = shift; @@ -158,7 +158,6 @@ sub os_ver_with_patch : Tests(2) { q(OS version returned with patch example 1) ); } -=cut sub os_ver : Tests(9) { my $test = shift; From 873fefa10309d9263d94515a0269494bbc20cc36 Mon Sep 17 00:00:00 2001 From: Christian Ramseyer Date: Mon, 8 Sep 2025 22:43:19 +0200 Subject: [PATCH 3/5] Huawei: move entity_derived_os_ver to _os_ver_legacy only --- lib/SNMP/Info/Layer3/Huawei.pm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/SNMP/Info/Layer3/Huawei.pm b/lib/SNMP/Info/Layer3/Huawei.pm index 798a5f39..3e56065e 100644 --- a/lib/SNMP/Info/Layer3/Huawei.pm +++ b/lib/SNMP/Info/Layer3/Huawei.pm @@ -135,11 +135,6 @@ 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 $patchstr = $huawei->_os_patch(); # Prefer extracting uniform V600R023C10SPC500-style version strings from sysDescr first # we trim the base kernel version like 5.170 to make the netdisco inventory page nicer From 168b4cde845035a1c0fcae39de13823e1cbeab12 Mon Sep 17 00:00:00 2001 From: Christian Ramseyer Date: Wed, 10 Sep 2025 13:16:49 +0200 Subject: [PATCH 4/5] Huawei: use hwPatchTable.hwPatchEntry instead of hwPatchFileTable to find the current patch --- lib/SNMP/Info/Layer3/Huawei.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/SNMP/Info/Layer3/Huawei.pm b/lib/SNMP/Info/Layer3/Huawei.pm index 3e56065e..4fc05ddf 100644 --- a/lib/SNMP/Info/Layer3/Huawei.pm +++ b/lib/SNMP/Info/Layer3/Huawei.pm @@ -54,7 +54,7 @@ $VERSION = '3.973000'; 'HUAWEI-IF-EXT-MIB' => 'hwTrunkIfIndex', 'HUAWEI-L2IF-MIB' => 'hwL2IfPortIfIndex', 'HUAWEI-POE-MIB' => 'hwPoePower', - 'HUAWEI-SYS-MAN-MIB' => 'hwPatchFileVersion', + 'HUAWEI-SYS-MAN-MIB' => 'hwPatchVersion', 'HUAWEI-ENTITY-EXTENT-MIB' => 'hwEntityFanState', ); @@ -100,7 +100,7 @@ $VERSION = '3.973000'; 'hw_pwr_descr' => 'hwEntityPwrDesc', # HUAWEI-SYS-MAN-MIB::hwPatchFileTable - 'hw_patch_version' => 'hwPatchFileVersion', + 'hw_patch_version' => 'hwPatchVersion', ); %MUNGE = ( @@ -158,6 +158,8 @@ sub _os_patch { my $patches = $huawei->hw_patch_version(); #use Data::Dumper; print Dumper $patches; + # hwPatchTable.hwPatchEntry.hwPatchVersion should only return one row (at a somewhat random-looking index), + # but we join up multiple results just in case my $patchstr = join ' ', map { /V\d{3}R\d{3}(\S+)/ ? $1 : () } values %$patches; return $patchstr; } From 8a461197d6c03d1fcd2b8d648f22f8db532da65a Mon Sep 17 00:00:00 2001 From: Christian Ramseyer Date: Fri, 12 Sep 2025 01:04:39 +0200 Subject: [PATCH 5/5] Huawei: improve _os_patch to work for APs too --- lib/SNMP/Info/Layer3/Huawei.pm | 16 ++++++++++++++-- xt/lib/Test/SNMP/Info/Layer3/Huawei.pm | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/SNMP/Info/Layer3/Huawei.pm b/lib/SNMP/Info/Layer3/Huawei.pm index 4fc05ddf..e63f453d 100644 --- a/lib/SNMP/Info/Layer3/Huawei.pm +++ b/lib/SNMP/Info/Layer3/Huawei.pm @@ -152,15 +152,27 @@ sub _os_patch { my $huawei = shift; # Patches are based on the release, e.g. + # os_ver hw_patch_version # V600R023C10SPC500 -> V600R023HP1501 -> Hot Patch 1501 # V200R011C10SPC600 -> V200R011SPC102 -> Service Pack Collection 102 # V200R011C10SPC600 -> V200R011SPH033 -> Special Patch Hotfix 033 + # V200R022C00SPC100 -> V200R022CO0SPC100B189 -> APs are somehow different, this is AP Build 189 - just use B189 + # V200R023C00SPC100 -> V200R023CO0SPH180 -> APs without baseline in patch name, use SPH180 + # + # we only return the last segment like HP1501 + my $patches = $huawei->hw_patch_version(); - #use Data::Dumper; print Dumper $patches; # hwPatchTable.hwPatchEntry.hwPatchVersion should only return one row (at a somewhat random-looking index), # but we join up multiple results just in case - my $patchstr = join ' ', map { /V\d{3}R\d{3}(\S+)/ ? $1 : () } values %$patches; + my $patchstr = join ' ', + map { + my $s = $_; + $s =~ s/^V\d{3}R\d{3}//; # drop VRP prefix + $s =~ s/.*?([A-Z]+\d+)$/$1/; # only use last suffix to skip the extra bits in APs + $s; + } values %$patches; + return $patchstr; } diff --git a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm index 66f8ff2d..2e93dd92 100644 --- a/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm +++ b/xt/lib/Test/SNMP/Info/Layer3/Huawei.pm @@ -159,6 +159,27 @@ sub os_ver_with_patch : Tests(2) { ); } +sub os_patch : Tests { + my $test = shift; + + can_ok($test->{info}, '_os_patch'); + + # more explanation is in _os_patch + my %cases = ( + 'V600R023HP1501' => 'HP1501', + 'V200R011SPC102' => 'SPC102', + 'V200R011SPH033' => 'SPH033', + 'V200R022C00SPC100B189' => 'B189', + 'V200R023CO0SPH180' => 'SPH180', + ); + + while (my ($input, $expected) = each %cases) { + $test->{info}{store}{hw_patch_version} = { '1' => $input }; + is( $test->{info}->_os_patch(), $expected, "extracting patch from $input" ); + } +} + + sub os_ver : Tests(9) { my $test = shift;