2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-28 08:10:53 +00:00

Accept MIB labels for genpdu enumerated values

With PDU2-MIB loaded, SNMP.pm returns an enumerated INTEGER as its label
rather than its number: a PX4 answers inletSensorUnits with "amp" rather
than "2". That failed the numeric test, fell back to none(-1) and
dropped the unit suffix from every rvitals reading.

Add pdu2_enum, which accepts the number, the label, or the label(value)
form Net-SNMP produces with quick printing off, and use it for the
sensor units and for outletSwitchingState.

Also stop requiring the switching probe to read on or off. An outlet can
be in any SensorStateEnumeration state, and pdu2_get has already ruled
out an absent instance, so any answer proves the outlet is switchable.

Signed-off-by: Kilian Cavalotti <kilian@stanford.edu>
This commit is contained in:
Kilian Cavalotti
2026-08-25 17:44:23 -07:00
parent daef2d7bae
commit 0b4695eef2
2 changed files with 85 additions and 11 deletions
+47 -11
View File
@@ -126,6 +126,16 @@ my %PDU2_SENSOR_UNIT = (
4 => "VA", 5 => "Wh", 6 => "VAh", 7 => "C", 8 => "Hz",
9 => "%", 20 => "deg", 23 => "var",
);
#Net-SNMP returns an enumerated INTEGER as its MIB label once the MIB is loaded
#(a PX4 answers "amp" rather than "2"), so both forms have to be accepted. Keys
#are lower cased, since the MIB labels are not.
my %PDU2_UNIT_CODE = (
none => -1, other => 0, volt => 1, amp => 2, watt => 3,
voltamp => 4, watthour => 5, voltamphour => 6, degreec => 7,
hertz => 8, percent => 9, degrees => 20, var => 23,
);
my %PDU2_STATE_CODE = (on => 7, off => 8);
#Ordered for readability rather than by enum value.
my @PDU2_INLET_SENSORS = (4, 1, 5, 6, 7, 23, 3, 29, 8, 9, 10);
my @PDU2_OUTLET_SENSORS = (1, 5, 8);
@@ -660,16 +670,15 @@ sub outletstat {
my $session = shift;
my $outlet = shift;
#genpdu: PDU2-MIB outletSwitchingState uses SensorStateEnumeration, of which
#on(7) and off(8) are the switching states. The value may come back as the
#integer or as the MIB label depending on which MIBs the Net-SNMP perl
#module has loaded, so both forms are accepted.
#genpdu: outletSwitchingState uses SensorStateEnumeration, of which on(7)
#and off(8) are the switching states.
if ($session->{genpdu}) {
my $val = $session->get("$PDU2_OUTLETSTATE.$PDU2_PDUID.$outlet");
return "unknown state" unless (defined $val);
if ($val eq "7" or $val eq "on") { return "on"; }
elsif ($val eq "8" or $val eq "off") { return "off"; }
else { return "$val(unknown state)"; }
my $state = pdu2_enum($val, \%PDU2_STATE_CODE);
return "on" if (defined $state and $state == 7);
return "off" if (defined $state and $state == 8);
return "$val(unknown state)";
}
my $oid = ".1.3.6.1.4.1.2.6.223.8.2.2.1.11";
@@ -841,6 +850,31 @@ sub pdu2_session_args {
#-------------------------------------------------------
=head3 pdu2_enum
Numeric value of an enumerated INTEGER. Net-SNMP returns those as the MIB
label once the MIB is loaded, as label(value) when quick printing is off, and
as the number when it is not. Returns undef for anything else.
=cut
#-------------------------------------------------------
sub pdu2_enum {
my ($val, $codes) = @_;
return undef unless (defined $val);
$val =~ s/^\s+|\s+$//g;
return $val if ($val =~ /^-?\d+$/);
#Trust the label over the number in label(value), so a mislabelled agent
#cannot turn volts into amperes.
my ($label, $num) = ($val =~ /^(\w+)\((-?\d+)\)$/) ? ($1, $2) : ($val, undef);
return $codes->{ lc $label } if (defined $codes->{ lc $label });
return $num;
}
#-------------------------------------------------------
=head3 pdu2_get
Read one object. Returns its value and one of 'ok', 'absent' or 'failed'.
@@ -900,10 +934,11 @@ sub pdu2_session_probe {
}
#Metered-only models such as PX2-1901U answer the measurement tables but
#have no outletSwitchControlTable instances.
#have no outletSwitchControlTable instances. Any state proves the instance
#exists, and an outlet is not always on or off, so this only asks whether
#the read produced one: pdu2_get has already ruled out absent and failed.
my ($probe) = pdu2_get($session, "$PDU2_OUTLETSTATE.$PDU2_PDUID.1");
$session->{genpdu_switchable} =
((defined $probe) and ($probe =~ /^\d+$/ or $probe =~ /^(on|off)$/i)) ? 1 : 0;
$session->{genpdu_switchable} = (defined $probe) ? 1 : 0;
return 1;
}
@@ -949,7 +984,8 @@ sub pdu2_sensor_fmt {
my ($u, $ustate) = pdu2_get($session, "$oids->{units}.$idx");
my ($d, $dstate) = pdu2_get($session, "$oids->{dec}.$idx");
return undef if ($ustate eq 'failed' or $dstate eq 'failed');
$u = -1 unless (defined $u and $u =~ /^-?\d+$/);
$u = pdu2_enum($u, \%PDU2_UNIT_CODE);
$u = -1 unless (defined $u);
$d = 0 unless (defined $d and $d =~ /^\d+$/);
$cache->{$st} = [ $u, $d ];
}
+38
View File
@@ -187,6 +187,36 @@ ok(
'outlet sensors are read for every outlet in the count'
);
#-- rvitals: units as MIB labels ----------------------------------------------
#With PDU2-MIB loaded, Net-SNMP returns SensorUnitsEnumeration as its label:
#a PX4 answers "amp" rather than "2", and "amp(2)" when quick printing is off.
sub units_as {
my ($units) = @_;
my %resp = ("$UNITCONF.2.1" => 1);
inlet_sensor(\%resp, 1, $PX4_INLET[0]);
$resp{"$INLET_UNITS.1.1.1"} = $units;
my ($m) = vitals_messages(\%resp, 0);
return $m;
}
ok(
(grep { $_ eq 'pdu1|inlet 1 RMS Current: 9.503 A' } @{ units_as('amp') }),
'a unit returned as its MIB label still gets its suffix'
);
ok(
(grep { $_ eq 'pdu1|inlet 1 RMS Current: 9.503 A' } @{ units_as('amp(2)') }),
'a unit returned as label(value) still gets its suffix'
);
ok(
(grep { $_ eq 'pdu1|inlet 1 RMS Current: 9.503 Wh' } @{ units_as('wattHour') }),
'a mixed case MIB label is recognised'
);
ok(
(grep { $_ eq 'pdu1|inlet 1 RMS Current: 9.503' } @{ units_as('lux') }),
'a unit with no suffix defined is reported without one'
);
#-- rvitals: metered-only PDUs ------------------------------------------------
my %metered = ("$UNITCONF.2.1" => 1);
@@ -329,6 +359,12 @@ is(scalar(@$probe_msgs), 0, 'a pduCount of 0 does not warn about linked units');
ok($ok, 'a PDU that answers pduCount is usable');
is($probed->{genpdu_switchable}, 1, 'an answered outlet state marks the PDU switchable');
($ok, $probed, $probe_msgs) = probe({
$PDUCOUNT => 1,
"$OUTLETSTATE.1.1" => 'standby',
});
is($probed->{genpdu_switchable}, 1, 'an outlet in a state other than on or off is still switchable');
($ok, $probed, $probe_msgs) = probe({
$PDUCOUNT => 1,
"$OUTLETSTATE.1.1" => $NOSUCH,
@@ -399,6 +435,8 @@ sub outlet_state {
is(outlet_state(7), 'on', 'outletSwitchingState on(7) reads as on');
is(outlet_state(8), 'off', 'outletSwitchingState off(8) reads as off');
is(outlet_state('on'), 'on', 'an outlet state returned as a MIB label reads as on');
is(outlet_state('off(8)'), 'off', 'an outlet state returned as label(value) reads as off');
is(outlet_state(undef), 'unknown state', 'an unanswered outlet state is unknown');
#-- rinv ----------------------------------------------------------------------