mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-10 11:36:25 +00:00
Add genpdu pdutype for generic SNMP PDUs (PDU2-MIB)
Add a new pdutype (genpdu) for PDUs implementing the Raritan PDU2-MIB. A single MIB covers the Raritan PX2/PX3/PX4/PXC/SRC/PXO/BCM series, the Server Technology PRO3X/PRO4X series, the Legrand intelligent PDUs, and all PDUs following the PDU2-MIB. Supports rpower (whole-PDU and per-outlet), rinv and rvitals over SNMP v1, v2c or v3, with credentials read from the pdu table). Unlike the existing types, sensor units and decimal precision are read from the MIB per sensor rather than hardcoded, so readings are correct across models that report differing precision for the same sensor. Outlet switching capability is probed at connect time, so metered-only models report a single "unsupported" message instead of a per-outlet error. rspconfig is not supported for genpdu; PDU linking and BCM2/PMC power meters are out of scope. Tested on Raritan PX4-5851-E7V2 (fw 4.2.10.5-50400, switched), PX3-1901U-N1 and PX3-1901U-N1A6 (fw 4.0.20.5-49038, metered), and PX2-1901U-N1A6 (fw 4.0.20.5-49038, metered). Signed-off-by: Kilian Cavalotti <kilian@stanford.edu>
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
Generic SNMP PDU
|
||||
================
|
||||
|
||||
The generic SNMP PDU support (``pdutype=genpdu``) drives any PDU that implements the Raritan **PDU2-MIB**. A single MIB covers the Raritan PX2, PX3, PX4, PXC, SRC, PXO and BCM series, the Server Technology PRO3X and PRO4X series, and the Legrand intelligent PDUs, so the same code path applies to all of them without per-model configuration.
|
||||
|
||||
xCAT communicates with these PDUs over SNMP v1, v2c or v3, using the credentials in the ``pdu`` table. Unlike the other PDU types, the sensor scaling factors and units are read from the device rather than hardcoded, so readings are correct on any model regardless of the precision it reports.
|
||||
|
||||
|
||||
Defining a PDU
|
||||
--------------
|
||||
|
||||
Create the PDU node definition and set the SNMP credentials in the ``pdu`` table.
|
||||
|
||||
For SNMP v2c: ::
|
||||
|
||||
mkdef mypdu groups=pdu nodetype=pdu mgt=pdu pdutype=genpdu
|
||||
chdef mypdu snmpversion=2 community=<community>
|
||||
|
||||
For SNMP v3: ::
|
||||
|
||||
mkdef mypdu groups=pdu nodetype=pdu mgt=pdu pdutype=genpdu
|
||||
chdef mypdu snmpversion=3 snmpuser=<user> \
|
||||
authtype=SHA authkey=<passphrase> \
|
||||
privtype=AES privkey=<passphrase> seclevel=authPriv
|
||||
|
||||
The ``seclevel`` attribute is optional. When it is not set, ``authPriv`` is used if ``privtype`` is set and ``authNoPriv`` otherwise. When ``privkey`` is not set it falls back to ``authkey``. If ``authtype`` or ``privtype`` are not set they default to ``SHA`` and ``DES``.
|
||||
|
||||
The ``outlet`` attribute is discovered automatically from the MIB on first use and cached in the ``pdu`` table. It can also be set explicitly to skip the discovery query.
|
||||
|
||||
|
||||
Switched and metered PDUs
|
||||
-------------------------
|
||||
|
||||
PDU2-MIB covers both switched models and metered-only models. xCAT detects which it is talking to at connection time, so no configuration is needed to distinguish them.
|
||||
|
||||
On a metered-only PDU, ``rinv`` and ``rvitals`` work normally, and the power control commands report that switching is unavailable rather than failing per outlet: ::
|
||||
|
||||
# rpower mypdu stat
|
||||
mypdu: this PDU does not support outlet switching
|
||||
|
||||
Writing to a switched PDU requires credentials with write access. SNMP agents on these devices commonly grant read-only access by default, in which case xCAT reports ``noAccess`` on power commands while read commands continue to work. Configure a read-write community, or an SNMP v3 user with write permission, on the PDU itself.
|
||||
|
||||
|
||||
PDU Commands
|
||||
------------
|
||||
|
||||
Administrators will need to know the exact mapping of the outlets to each server in the frame. xCAT cannot validate the physical cable is connected to the correct server.
|
||||
|
||||
Add a ``pdu`` attribute to the compute node definition in the form "PDU_Name:outlet": ::
|
||||
|
||||
#
|
||||
# Compute server cn01 has two power supplies
|
||||
# connected to outlet 6 and 7 on pdu=mypdu
|
||||
#
|
||||
chdef cn01 pdu=mypdu:6,mypdu:7
|
||||
|
||||
The following commands are supported against a compute node:
|
||||
|
||||
* Check the pdu status for a compute node: ::
|
||||
|
||||
# rpower cn01 pdustat
|
||||
cn01: mypdu operational state for outlet 6 is on
|
||||
cn01: mypdu operational state for outlet 7 is on
|
||||
|
||||
* Power off the PDU outlets for a compute node: ::
|
||||
|
||||
# rpower cn01 pduoff
|
||||
cn01: mypdu operational state for outlet 6 is off
|
||||
cn01: mypdu operational state for outlet 7 is off
|
||||
|
||||
* Power on the PDU outlets for a compute node: ::
|
||||
|
||||
# rpower cn01 pduon
|
||||
cn01: mypdu operational state for outlet 6 is on
|
||||
cn01: mypdu operational state for outlet 7 is on
|
||||
|
||||
* Power cycling the PDU outlets for a compute node: ::
|
||||
|
||||
# rpower cn01 pdureset
|
||||
cn01: mypdu operational state for outlet 6 is reset
|
||||
cn01: mypdu operational state for outlet 7 is reset
|
||||
|
||||
The following commands are supported against a PDU:
|
||||
|
||||
* Check the status of the full PDU: ::
|
||||
|
||||
# rpower mypdu stat
|
||||
mypdu: operational state for the outlet 1 is on
|
||||
mypdu: operational state for the outlet 2 is on
|
||||
mypdu: operational state for the outlet 3 is on
|
||||
|
||||
* Power off, on or reset the full PDU: ::
|
||||
|
||||
# rpower mypdu off
|
||||
# rpower mypdu on
|
||||
# rpower mypdu reset
|
||||
|
||||
* PDU inventory information: ::
|
||||
|
||||
# rinv mypdu
|
||||
mypdu: PDU Manufacturer: Raritan
|
||||
mypdu: PDU Model: PX4-5851-E7V2
|
||||
mypdu: PDU Serial Number: XXXXXXXXXX
|
||||
mypdu: PDU Rated Voltage: 360-415V
|
||||
mypdu: PDU Rated Current: 24A
|
||||
mypdu: PDU Rated Frequency: 50/60Hz
|
||||
mypdu: PDU Rated VA: 15.0-17.3kVA
|
||||
mypdu: PDU Inlet Count: 1
|
||||
mypdu: PDU Overcurrent Protector Count: 6
|
||||
mypdu: PDU Outlet Count: 36
|
||||
mypdu: PDU Description: Raritan PDU, MD:PX4-5851-E7V2 HW:0x1D FW:4.2.10.5-50400
|
||||
|
||||
* PDU and outlet power information: ::
|
||||
|
||||
# rvitals mypdu
|
||||
mypdu: inlet 1 RMS Voltage: 412 V
|
||||
mypdu: inlet 1 RMS Current: 13.959 A
|
||||
mypdu: inlet 1 Active Power: 8879 W
|
||||
mypdu: inlet 1 Apparent Power: 8950 VA
|
||||
mypdu: inlet 1 Power Factor: 0.99
|
||||
mypdu: inlet 1 Frequency: 60.0 Hz
|
||||
mypdu: inlet 1 Unbalanced Current: 11 %
|
||||
mypdu: inlet 1 Reactive Power: 0 var
|
||||
mypdu: inlet 1 Active Energy: 145702407 Wh
|
||||
mypdu: inlet 1 Apparent Energy: 147093631 VAh
|
||||
mypdu: outlet 1 RMS Current: 0.000 A
|
||||
mypdu: outlet 1 Active Power: 0 W
|
||||
mypdu: outlet 1 Active Energy: 0 Wh
|
||||
mypdu: outlet 2 RMS Current: 2.793 A
|
||||
mypdu: outlet 2 Active Power: 660 W
|
||||
mypdu: outlet 2 Active Energy: 9794434 Wh
|
||||
|
||||
Units and decimal precision are read from the MIB for each sensor rather
|
||||
than being hardcoded, so readings are correct on any model without
|
||||
per-model configuration. The same sensor can therefore be reported at
|
||||
different precision on different models: for example inlet RMS current
|
||||
appears as ``13.959 A`` on a PX4 and ``18.1 A`` on a PX2, because the two
|
||||
devices declare a different number of decimal digits for it.
|
||||
|
||||
Sensors that the device does not report are omitted, so the exact set of
|
||||
lines varies by model. Per-outlet sensors are skipped entirely on models
|
||||
that do not meter individual outlets. The reported sensor types are a
|
||||
fixed list in the plugin and can be extended by adding entries to it;
|
||||
types with no name defined are reported as ``sensor <N>``.
|
||||
|
||||
**Note:** ``rspconfig`` is not supported for ``pdutype=genpdu``. Network and hostname configuration should be done on the PDU itself.
|
||||
|
||||
**Note:** For BMC based compute nodes, turning the PDU outlet power on does not automatically power on the compute side. Users will need to issue ``rpower <node> on`` to power on the compute side after the BMC boots.
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
* PDU linking (cascading multiple PDUs behind one management interface) is not supported. The MIB indexes outlets by link ID, and xCAT assumes the value used by unlinked units. BCM2 and PMC power meters are also out of scope for the same reason.
|
||||
|
||||
* ``authtype``, ``privtype`` and the other SNMP attributes are per-PDU attributes in the ``pdu`` table, so PDUs using different SNMP protocols need their values set individually.
|
||||
|
||||
|
||||
Tested hardware
|
||||
---------------
|
||||
|
||||
* Raritan PX4-5851-E7V2, firmware 4.2.10.5-50400 (switched, 36 outlets)
|
||||
* Raritan PX3-1901U-N1 and PX3-1901U-N1A6, firmware 4.0.20.5-49038 (metered, 48 outlets)
|
||||
* Raritan PX2-1901U-N1A6, firmware 4.0.20.5-49038 (metered, 48 outlets)
|
||||
@@ -1,9 +1,9 @@
|
||||
PDUs
|
||||
====
|
||||
|
||||
Power Distribution Units (PDUs) are devices that distribute power to servers in a frame. They have the capability of monitoring the amount of power that is being used by devices plugged into it and cycle power to individual receptacles. xCAT can support two kinds of PDUs, infrastructure PDU (irpdu) and collaborative PDU (crpdu).
|
||||
Power Distribution Units (PDUs) are devices that distribute power to servers in a frame. They have the capability of monitoring the amount of power that is being used by devices plugged into it and cycle power to individual receptacles. xCAT can support three kinds of PDUs, infrastructure PDU (irpdu), collaborative PDU (crpdu) and generic SNMP PDU (genpdu).
|
||||
|
||||
The Infrastructure rack PDUs are switched and monitored 1U PDU products which can connect up to nine C19 devices or up to 12 C13 devices and an additional three C13 peripheral devices to a single dedicated power source. The Collaborative PDU is on the compute rack and has the 6x IEC 320-C13 receptacles that feed the rack switches. These two types of PDU have different design and implementation. xCAT has different code path to maintains PDU commands via **pdutype**.
|
||||
The Infrastructure rack PDUs are switched and monitored 1U PDU products which can connect up to nine C19 devices or up to 12 C13 devices and an additional three C13 peripheral devices to a single dedicated power source. The Collaborative PDU is on the compute rack and has the 6x IEC 320-C13 receptacles that feed the rack switches. The generic SNMP PDU is any PDU driven by the Raritan PDU2-MIB, which covers the Raritan PX2/PX3/PX4/PXC/SRC/PXO/BCM series, the Server Technology PRO3X/PRO4X series and the Legrand intelligent PDUs. These types of PDU have different design and implementation. xCAT has different code path to maintains PDU commands via **pdutype**.
|
||||
|
||||
|
||||
.. toctree::
|
||||
@@ -12,3 +12,4 @@ The Infrastructure rack PDUs are switched and monitored 1U PDU products which ca
|
||||
pdu.rst
|
||||
irpdu.rst
|
||||
crpdu.rst
|
||||
genpdu.rst
|
||||
|
||||
@@ -50,7 +50,7 @@ pdu Attributes:
|
||||
|
||||
\ **pdutype**\
|
||||
|
||||
The type of pdu
|
||||
The type of pdu: irpdu (infrastructure PDU, SNMP), crpdu (collaborative PDU, ssh) or genpdu (generic SNMP PDU, PDU2-MIB)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -707,7 +707,7 @@ passed as argument rather than by table value',
|
||||
descriptions => {
|
||||
node => 'The hostname/address of the pdu to which the settings apply',
|
||||
nodetype => 'The node type should be pdu ',
|
||||
pdutype => 'The type of pdu ',
|
||||
pdutype => 'The type of pdu: irpdu (infrastructure PDU, SNMP), crpdu (collaborative PDU, ssh) or genpdu (generic SNMP PDU, PDU2-MIB)',
|
||||
outlet => 'The pdu outlet count',
|
||||
username => 'The remote login user name',
|
||||
password => 'The remote login password',
|
||||
|
||||
@@ -46,6 +46,63 @@ my $callback;
|
||||
my $pdutab;
|
||||
my $pduhash;
|
||||
|
||||
#-------------------------------------------------------
|
||||
# PDU2-MIB OIDs, used for pdutype=genpdu (generic SNMP PDU).
|
||||
#
|
||||
# A single MIB covers the Raritan PX2/PX3/PX4/PXC/SRC/PXO/BCM/BCM2 series, the
|
||||
# Server Technology PRO3X/PRO4X series and the Legrand intelligent PDUs, so
|
||||
# these OIDs are model-independent (PDU2-MIB rev 4.3.0, 202410070000Z).
|
||||
#
|
||||
# Both outlet tables are INDEX { pduId, outletId }. Per the pduId DESCRIPTION,
|
||||
# pduId is the PDU's link ID and is 1 when linking is not used; BCM2/PMC use 0
|
||||
# for the main controller. Linked and BCM2/PMC deployments are out of scope for
|
||||
# genpdu, because pduoutlet.pdu is parsed as PDU:outlet and has no field for a
|
||||
# link ID. Read pduCount (.1.3.6.1.4.1.13742.6.3.1.0) to confirm a unit is
|
||||
# unlinked; it returns 1 when it is.
|
||||
#-------------------------------------------------------
|
||||
my $PDU2_SWITCHOP = ".1.3.6.1.4.1.13742.6.4.1.2.1.2"; #switchingOperation, rw, off(0) on(1) cycle(2)
|
||||
my $PDU2_OUTLETSTATE = ".1.3.6.1.4.1.13742.6.4.1.2.1.3"; #outletSwitchingState, ro, on(7) off(8)
|
||||
my $PDU2_OUTLETCOUNT = ".1.3.6.1.4.1.13742.6.3.2.2.1.4"; #outletCount, ro, INDEX { pduId }
|
||||
my $PDU2_PDUID = 1;
|
||||
|
||||
#rinv / rvitals sources. The nameplate and unitConfiguration entries are
|
||||
#INDEX { pduId }; the measurement and sensor-configuration entries are
|
||||
#INDEX { pduId, <inlet|outlet>Id, sensorType }.
|
||||
my $PDU2_NAMEPLATE = ".1.3.6.1.4.1.13742.6.3.2.1.1"; #nameplateEntry
|
||||
my $PDU2_UNITCONF = ".1.3.6.1.4.1.13742.6.3.2.2.1"; #unitConfigurationEntry
|
||||
my $PDU2_INLETVAL = ".1.3.6.1.4.1.13742.6.5.2.3.1.4"; #measurementsInletSensorValue
|
||||
my $PDU2_INLETUNITS = ".1.3.6.1.4.1.13742.6.3.3.4.1.6"; #inletSensorUnits
|
||||
my $PDU2_INLETDEC = ".1.3.6.1.4.1.13742.6.3.3.4.1.7"; #inletSensorDecimalDigits
|
||||
my $PDU2_OUTLETVAL = ".1.3.6.1.4.1.13742.6.5.4.3.1.4"; #measurementsOutletSensorValue
|
||||
my $PDU2_OUTLETUNITS = ".1.3.6.1.4.1.13742.6.3.5.4.1.6"; #outletSensorUnits
|
||||
my $PDU2_OUTLETDEC = ".1.3.6.1.4.1.13742.6.3.5.4.1.7"; #outletSensorDecimalDigits
|
||||
|
||||
#PDU2-MIB has no pollable PDU-level firmware version (boardFirmwareVersion is
|
||||
#per-controller under unit 3, imageVersion is trap payload only), so the
|
||||
#firmware string is taken from sysDescr in SNMPv2-MIB.
|
||||
my $SYSDESCR = ".1.3.6.1.2.1.1.1.0";
|
||||
|
||||
#SensorTypeEnumeration subset reported for inlets and outlets, and the
|
||||
#SensorUnitsEnumeration values those sensors use. Readings are scaled by the
|
||||
#matching ...SensorDecimalDigits and labelled from ...SensorUnits, so this
|
||||
#works unchanged on any PDU2 model regardless of which sensors it populates.
|
||||
my %PDU2_SENSOR_NAME = (
|
||||
1 => "RMS Current", 2 => "Peak Current",
|
||||
3 => "Unbalanced Current", 4 => "RMS Voltage",
|
||||
5 => "Active Power", 6 => "Apparent Power",
|
||||
7 => "Power Factor", 8 => "Active Energy",
|
||||
9 => "Apparent Energy", 10 => "Temperature",
|
||||
23 => "Frequency", 29 => "Reactive Power",
|
||||
);
|
||||
my %PDU2_SENSOR_UNIT = (
|
||||
-1 => "", 0 => "", 1 => "V", 2 => "A", 3 => "W",
|
||||
4 => "VA", 5 => "Wh", 6 => "VAh", 7 => "C", 8 => "Hz",
|
||||
9 => "%", 20 => "deg", 23 => "var",
|
||||
);
|
||||
#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);
|
||||
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
@@ -84,6 +141,13 @@ sub pdu_usage
|
||||
rpower computenodes [pduoff|pduon|pdustat|pdustatus|pdureset]
|
||||
rspconfig irpdunode [hostname=<NAME>|ip=<IP>|gateway=<GATEWAY>|mask=<MASK>]
|
||||
|
||||
The following commands support Generic SNMP PDU with pdutype=genpdu :
|
||||
rpower pdunodes [off|on|stat|reset]
|
||||
rpower computenodes [pduoff|pduon|pdustat|pdureset]
|
||||
rinv pdunodes
|
||||
rvitals pdunodes
|
||||
(rspconfig is not supported for pdutype=genpdu)
|
||||
|
||||
The following commands support CR PDU with pdutype=crpdu :
|
||||
rpower pdunodes relay=[1|2|3] [on|off]
|
||||
rspconfig pdunodes sshcfg
|
||||
@@ -257,6 +321,21 @@ sub fill_outletCount {
|
||||
my $session = shift;
|
||||
my $pdu = shift;
|
||||
my $callback = shift;
|
||||
|
||||
#genpdu: outletCount is the authoritative count; the outletSwitchControlTable
|
||||
#DESCRIPTION states its entry count is given by outletCount. If the 'outlet'
|
||||
#attribute is set on the pdu node, callers use it first and this never runs.
|
||||
if ($session->{genpdu}) {
|
||||
my $count = $session->get("$PDU2_OUTLETCOUNT.$PDU2_PDUID");
|
||||
if ($count) {
|
||||
my $pdutab = xCAT::Table->new('pdu');
|
||||
$pdutab->setNodeAttribs($pdu, { outlet => $count });
|
||||
} else {
|
||||
xCAT::SvrUtils::sendmsg("Could not read outletCount, set the 'outlet' attribute on $pdu", $callback, $pdu);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
my $outletoid = ".1.3.6.1.4.1.2.6.223.8.2.1.0";
|
||||
my $pdutab = xCAT::Table->new('pdu');
|
||||
|
||||
@@ -301,6 +380,10 @@ sub powerpdu {
|
||||
$callback->({ errorcode => [1],error => "Couldn't connect to $node"});
|
||||
next;
|
||||
}
|
||||
if (defined $session->{genpdu_switchable} and !$session->{genpdu_switchable}) {
|
||||
xCAT::SvrUtils::sendmsg("this PDU does not support outlet switching", $callback, $node);
|
||||
next;
|
||||
}
|
||||
my $count = $pduhash->{$node}->[0]->{outlet};
|
||||
unless ($count) {
|
||||
$count = fill_outletCount($session, $node, $callback);
|
||||
@@ -374,6 +457,10 @@ sub powerpduoutlet {
|
||||
$callback->({ errorcode => [1],error => "$node: Couldn't connect to $pdu"});
|
||||
next;
|
||||
}
|
||||
if (defined $session->{genpdu_switchable} and !$session->{genpdu_switchable}) {
|
||||
$callback->({ errorcode => [1],error => "$node: $pdu does not support outlet switching"});
|
||||
next;
|
||||
}
|
||||
my $count = $pduhash->{$pdu}->[0]->{outlet};
|
||||
unless ($count) {
|
||||
$count = fill_outletCount($session, $pdu, $callback);
|
||||
@@ -425,6 +512,14 @@ sub outletpower {
|
||||
my $outlet = shift;
|
||||
my $value = shift;
|
||||
|
||||
#genpdu: PDU2-MIB switchingOperation is off(0) on(1) cycle(2), and the
|
||||
#callers (powerpdu / powerpduoutlet) already pass 0=off, 1=on, 2=reset, so
|
||||
#the value maps directly onto the MIB enum with no translation.
|
||||
if ($session->{genpdu}) {
|
||||
my $varbind = new SNMP::Varbind([ $PDU2_SWITCHOP, "$PDU2_PDUID.$outlet", $value, "INTEGER" ]);
|
||||
return $session->set($varbind);
|
||||
}
|
||||
|
||||
my $oid = ".1.3.6.1.4.1.2.6.223.8.2.2.1.11";
|
||||
my $type = "INTEGER";
|
||||
if ($session->{newmib}) {
|
||||
@@ -501,6 +596,10 @@ sub powerstat {
|
||||
$callback->({ errorcode => [1],error => "Couldn't connect to $pdu"});
|
||||
next;
|
||||
}
|
||||
if (defined $session->{genpdu_switchable} and !$session->{genpdu_switchable}) {
|
||||
xCAT::SvrUtils::sendmsg("this PDU does not support outlet switching", $callback, $pdu);
|
||||
next;
|
||||
}
|
||||
my $count = $pduhash->{$pdu}->[0]->{outlet};
|
||||
unless ($count) {
|
||||
$count = fill_outletCount($session, $pdu, $callback);
|
||||
@@ -534,6 +633,18 @@ 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.
|
||||
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 $oid = ".1.3.6.1.4.1.2.6.223.8.2.2.1.11";
|
||||
my $output;
|
||||
my $statstr;
|
||||
@@ -573,6 +684,12 @@ sub connectTopdu {
|
||||
my $pdu = shift;
|
||||
my $callback = shift;
|
||||
|
||||
#genpdu uses its own v1/v2c/v3 session builder and the PDU2-MIB OIDs; skip
|
||||
#the IBM-specific version probe below, which a non-IBM agent will not answer.
|
||||
if (($pduhash->{$pdu}->[0]->{pdutype} || '') eq 'genpdu') {
|
||||
return connectTopdu_gen($pdu, $callback);
|
||||
}
|
||||
|
||||
#get community string from pdu table if defined,
|
||||
#otherwise, use default
|
||||
my $community;
|
||||
@@ -614,6 +731,215 @@ sub connectTopdu {
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 connectTopdu_gen
|
||||
|
||||
Build an SNMP v1/v2c/v3 session for a generic SNMP PDU (pdutype=genpdu),
|
||||
driven by the PDU2-MIB.
|
||||
|
||||
SNMP parameters come from the 'pdu' table, following the same model xCAT
|
||||
already uses for ethernet switches in xCAT::MacMap::getsnmpsession:
|
||||
- one secret is reused (community for v1/v2c; the auth passphrase for v3),
|
||||
and privkey falls back to authkey when only one is provided.
|
||||
- SecLevel is derived: authNoPriv unless a privacy protocol is set.
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub connectTopdu_gen {
|
||||
my $pdu = shift;
|
||||
my $callback = shift;
|
||||
|
||||
my $ent = $pduhash->{$pdu}->[0];
|
||||
my $snmpver = $ent->{snmpversion};
|
||||
if (!defined $snmpver) { $snmpver = "1"; }
|
||||
elsif ($snmpver =~ /3/) { $snmpver = "3"; }
|
||||
elsif ($snmpver =~ /2/) { $snmpver = "2"; }
|
||||
else { $snmpver = "1"; }
|
||||
|
||||
my $session;
|
||||
if ($snmpver ne "3") {
|
||||
my $community = $ent->{community} || "public";
|
||||
$session = new SNMP::Session(
|
||||
DestHost => $pdu,
|
||||
Version => $snmpver,
|
||||
Community => $community,
|
||||
UseSprintValue => 1,
|
||||
);
|
||||
} else {
|
||||
my $authpass = $ent->{authkey};
|
||||
my $privpass = (defined $ent->{privkey}) ? $ent->{privkey} : $authpass;
|
||||
my $seclevel = $ent->{seclevel};
|
||||
unless ($seclevel) {
|
||||
$seclevel = ($ent->{privtype}) ? "authPriv" : "authNoPriv";
|
||||
}
|
||||
my %args = (
|
||||
DestHost => $pdu,
|
||||
Version => 3,
|
||||
SecName => $ent->{snmpuser},
|
||||
SecLevel => $seclevel,
|
||||
AuthProto => uc($ent->{authtype} || "SHA"),
|
||||
AuthPass => $authpass,
|
||||
UseSprintValue => 1,
|
||||
);
|
||||
if ($seclevel eq "authPriv") {
|
||||
$args{PrivProto} = uc($ent->{privtype} || "DES");
|
||||
$args{PrivPass} = $privpass;
|
||||
}
|
||||
$session = new SNMP::Session(%args);
|
||||
}
|
||||
|
||||
unless ($session) {
|
||||
return;
|
||||
}
|
||||
$session->{genpdu} = 1;
|
||||
|
||||
#Metered-only PDU2 models (for example PX2-1901U) answer the nameplate and
|
||||
#measurement tables normally but have no outletSwitchControlTable instances.
|
||||
#Probe once here, the same way the irpdu path above probes a version OID,
|
||||
#so callers can refuse switching with one clear message instead of leaking
|
||||
#the agent's "No Such Instance" text from every outlet read.
|
||||
my $probe = $session->get("$PDU2_OUTLETSTATE.$PDU2_PDUID.1");
|
||||
$session->{genpdu_switchable} =
|
||||
((defined $probe) and ($probe =~ /^\d+$/ or $probe =~ /^(on|off)$/i)) ? 1 : 0;
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 pdu2_sensor_fmt
|
||||
|
||||
Read one PDU2-MIB sensor and return it formatted with its unit, or undef if
|
||||
the sensor is not present on this device.
|
||||
|
||||
$idx is the full instance suffix (pduId.entityId.sensorType). The scaling
|
||||
factor and unit come from the matching sensor-configuration table, so no
|
||||
unit or precision is hardcoded. They are formally per-entity but are uniform
|
||||
across entities on PDU2 devices, so they are cached per sensorType to keep
|
||||
this to one GET per entity per sensor rather than three.
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub pdu2_sensor_fmt {
|
||||
my ($session, $valoid, $unitoid, $decoid, $idx, $cache) = @_;
|
||||
|
||||
my $raw = $session->get("$valoid.$idx");
|
||||
return undef unless (defined $raw and $raw =~ /^-?\d+$/);
|
||||
|
||||
my $st = (split /\./, $idx)[-1];
|
||||
unless (exists $cache->{$st}) {
|
||||
my $u = $session->get("$unitoid.$idx");
|
||||
my $d = $session->get("$decoid.$idx");
|
||||
$u = -1 unless (defined $u and $u =~ /^-?\d+$/);
|
||||
$d = 0 unless (defined $d and $d =~ /^\d+$/);
|
||||
$cache->{$st} = [ $u, $d ];
|
||||
}
|
||||
my ($units, $digits) = @{ $cache->{$st} };
|
||||
|
||||
my $val = sprintf("%.*f", $digits, $raw / (10 ** $digits));
|
||||
my $sfx = (defined $PDU2_SENSOR_UNIT{$units}) ? $PDU2_SENSOR_UNIT{$units} : "";
|
||||
return ($sfx ne "") ? "$val $sfx" : $val;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 rinv_for_genpdu
|
||||
|
||||
Inventory for a generic SNMP PDU, from the PDU2-MIB nameplate and
|
||||
unitConfiguration entries plus sysDescr for the firmware string.
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub rinv_for_genpdu {
|
||||
my $pdu = shift;
|
||||
my $session = shift;
|
||||
my $callback = shift;
|
||||
|
||||
#The rated* nameplate objects are DisplayStrings with the unit already
|
||||
#included (for example "24A", "15.0-17.3kVA"), so nothing is appended.
|
||||
my @fields = (
|
||||
[ $PDU2_NAMEPLATE, 2, "PDU Manufacturer" ],
|
||||
[ $PDU2_NAMEPLATE, 3, "PDU Model" ],
|
||||
[ $PDU2_NAMEPLATE, 4, "PDU Serial Number" ],
|
||||
[ $PDU2_NAMEPLATE, 5, "PDU Rated Voltage" ],
|
||||
[ $PDU2_NAMEPLATE, 6, "PDU Rated Current" ],
|
||||
[ $PDU2_NAMEPLATE, 7, "PDU Rated Frequency" ],
|
||||
[ $PDU2_NAMEPLATE, 8, "PDU Rated VA" ],
|
||||
[ $PDU2_UNITCONF, 2, "PDU Inlet Count" ],
|
||||
[ $PDU2_UNITCONF, 3, "PDU Overcurrent Protector Count" ],
|
||||
[ $PDU2_UNITCONF, 4, "PDU Outlet Count" ],
|
||||
);
|
||||
|
||||
foreach my $f (@fields) {
|
||||
my $output = $session->get("$f->[0].$f->[1].$PDU2_PDUID");
|
||||
next unless (defined $output and $output ne '');
|
||||
#UseSprintValue returns DisplayStrings quoted; strip for readability.
|
||||
$output =~ s/^"(.*)"$/$1/;
|
||||
next if ($output eq '');
|
||||
xCAT::SvrUtils::sendmsg("$f->[2]: $output", $callback, $pdu);
|
||||
}
|
||||
|
||||
my $output = $session->get("$SYSDESCR");
|
||||
if (defined $output and $output ne '') {
|
||||
$output =~ s/^"(.*)"$/$1/;
|
||||
xCAT::SvrUtils::sendmsg("PDU Description: $output", $callback, $pdu);
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 rvitals_for_genpdu
|
||||
|
||||
Sensor readings for a generic SNMP PDU: every supported inlet sensor the
|
||||
device reports, then current, active power and active energy per outlet.
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub rvitals_for_genpdu {
|
||||
my $pdu = shift;
|
||||
my $count = shift;
|
||||
my $session = shift;
|
||||
my $callback = shift;
|
||||
|
||||
my $inlets = $session->get("$PDU2_UNITCONF.2.$PDU2_PDUID");
|
||||
$inlets = 1 unless (defined $inlets and $inlets =~ /^\d+$/ and $inlets > 0);
|
||||
|
||||
my %inlet_cache;
|
||||
for (my $inlet = 1; $inlet <= $inlets; $inlet++) {
|
||||
foreach my $st (@PDU2_INLET_SENSORS) {
|
||||
my $fmt = pdu2_sensor_fmt($session, $PDU2_INLETVAL, $PDU2_INLETUNITS,
|
||||
$PDU2_INLETDEC, "$PDU2_PDUID.$inlet.$st", \%inlet_cache);
|
||||
next unless (defined $fmt);
|
||||
my $name = $PDU2_SENSOR_NAME{$st} || "sensor $st";
|
||||
xCAT::SvrUtils::sendmsg("inlet $inlet $name: $fmt", $callback, $pdu);
|
||||
}
|
||||
}
|
||||
|
||||
my %outlet_cache;
|
||||
|
||||
#Metered-only models (PX2-1901U, PX3-1901U) populate the inlet tables but
|
||||
#have no per-outlet sensors. Probe one before looping: a 48-outlet unit
|
||||
#otherwise costs 144 GETs that all return No Such Instance and print
|
||||
#nothing, which matters when rvitals is run against a group.
|
||||
my $probe = $session->get("$PDU2_OUTLETVAL.$PDU2_PDUID.1.$PDU2_OUTLET_SENSORS[0]");
|
||||
return unless (defined $probe and $probe =~ /^-?\d+$/);
|
||||
|
||||
for (my $outlet = 1; $outlet <= $count; $outlet++) {
|
||||
foreach my $st (@PDU2_OUTLET_SENSORS) {
|
||||
my $fmt = pdu2_sensor_fmt($session, $PDU2_OUTLETVAL, $PDU2_OUTLETUNITS,
|
||||
$PDU2_OUTLETDEC, "$PDU2_PDUID.$outlet.$st", \%outlet_cache);
|
||||
next unless (defined $fmt);
|
||||
my $name = $PDU2_SENSOR_NAME{$st} || "sensor $st";
|
||||
xCAT::SvrUtils::sendmsg("outlet $outlet $name: $fmt", $callback, $pdu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 process_netcfg
|
||||
|
||||
Config hostname of PDU
|
||||
@@ -662,6 +988,11 @@ sub process_netcfg {
|
||||
my $static_ip = $nodehash->{$pdu}->[0]->{ip};
|
||||
my $discover_ip = $nodehash->{$pdu}->[0]->{otherinterfaces};
|
||||
|
||||
if (($pduhash->{$pdu}->[0]->{pdutype} || '') eq 'genpdu') {
|
||||
xCAT::SvrUtils::sendmsg("rspconfig $subcmd is not supported for pdutype=genpdu", $callback, $pdu);
|
||||
return;
|
||||
}
|
||||
|
||||
unless ($pduhash->{$pdu}->[0]->{pdutype} eq "crpdu") {
|
||||
netcfg_for_irpdu($pdu, $static_ip, $discover_ip, $request, $subreq, $callback);
|
||||
return;
|
||||
@@ -944,6 +1275,15 @@ sub showMFR {
|
||||
my $nodehash = $nodetab->getNodesAttribs($noderange,['ip','otherinterfaces']);
|
||||
|
||||
foreach my $pdu (@$noderange) {
|
||||
if (($pduhash->{$pdu}->[0]->{pdutype} || '') eq 'genpdu') {
|
||||
my $session = connectTopdu($pdu, $callback);
|
||||
if (!$session) {
|
||||
$callback->({ errorcode => [1], error => "Couldn't connect to $pdu" });
|
||||
next;
|
||||
}
|
||||
rinv_for_genpdu($pdu, $session, $callback);
|
||||
next;
|
||||
}
|
||||
unless ($pduhash->{$pdu}->[0]->{pdutype} eq "crpdu") {
|
||||
rinv_for_irpdu($pdu, $callback);
|
||||
next;
|
||||
@@ -1051,6 +1391,19 @@ sub showMonitorData {
|
||||
my $nodehash = $nodetab->getNodesAttribs($noderange,['ip','otherinterfaces']);
|
||||
|
||||
foreach my $pdu (@$noderange) {
|
||||
if (($pduhash->{$pdu}->[0]->{pdutype} || '') eq 'genpdu') {
|
||||
my $session = connectTopdu($pdu, $callback);
|
||||
if (!$session) {
|
||||
$callback->({ errorcode => [1], error => "Couldn't connect to $pdu" });
|
||||
next;
|
||||
}
|
||||
my $count = $pduhash->{$pdu}->[0]->{outlet};
|
||||
unless ($count) {
|
||||
$count = fill_outletCount($session, $pdu, $callback);
|
||||
}
|
||||
rvitals_for_genpdu($pdu, $count, $session, $callback);
|
||||
next;
|
||||
}
|
||||
unless ($pduhash->{$pdu}->[0]->{pdutype} eq "crpdu") {
|
||||
my $session = connectTopdu($pdu,$callback);
|
||||
if (!$session) {
|
||||
|
||||
Reference in New Issue
Block a user