mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-05 20:47:55 +00:00
0b4695eef2
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>
2071 lines
67 KiB
Perl
2071 lines
67 KiB
Perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
#-------------------------------------------------------
|
|
|
|
=head1
|
|
xCAT plugin package to handle pdu
|
|
|
|
Supported command:
|
|
rpower
|
|
rinv
|
|
rvitals
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
package xCAT_plugin::pdu;
|
|
|
|
BEGIN {
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
|
|
}
|
|
use lib "$::XCATROOT/lib/perl";
|
|
|
|
use xCAT::Table;
|
|
use xCAT::Utils;
|
|
use xCAT::FifoPipe;
|
|
use xCAT::MsgUtils;
|
|
use xCAT::State;
|
|
use xCAT::SvrUtils;
|
|
use xCAT::Usage;
|
|
use xCAT::NodeRange;
|
|
use Data::Dumper;
|
|
use Getopt::Long;
|
|
use File::Path;
|
|
use Term::ANSIColor;
|
|
use Time::Local;
|
|
use strict;
|
|
use Class::Struct;
|
|
use XML::Simple;
|
|
use Storable qw(dclone);
|
|
use SNMP;
|
|
use Expect;
|
|
use Net::Ping;
|
|
|
|
my $VERBOSE = 0;
|
|
my %allerrornodes = ();
|
|
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. pduCount reports how many units sit behind one agent, 1 when
|
|
# standalone.
|
|
#-------------------------------------------------------
|
|
my $PDU2_PDUCOUNT = ".1.3.6.1.4.1.13742.6.3.1.0"; #pduCount, ro, scalar
|
|
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;
|
|
|
|
#PDUs whose linked-unit warning has already been printed.
|
|
my %genpdu_linked;
|
|
|
|
#rinv / rvitals sources. The nameplate and unitConfiguration entries are
|
|
#INDEX { pduId }; the measurement and sensor-configuration entries are
|
|
#INDEX { pduId, <inlet|outlet>Id, sensorType }.
|
|
#SensorSignedMinimum selects the value column. On a PX4 the unsigned column
|
|
#returns 0 for reactive power, and the signed column returns 0 for active
|
|
#energy, whose range exceeds Integer32.
|
|
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_INLETSIGNEDVAL = ".1.3.6.1.4.1.13742.6.5.2.3.1.6"; #measurementsInletSensorSignedValue
|
|
my $PDU2_INLETSIGNEDMIN = ".1.3.6.1.4.1.13742.6.3.3.4.1.27"; #inletSensorSignedMinimum
|
|
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_OUTLETSIGNEDVAL = ".1.3.6.1.4.1.13742.6.5.4.3.1.6"; #measurementsOutletSensorSignedValue
|
|
my $PDU2_OUTLETSIGNEDMIN = ".1.3.6.1.4.1.13742.6.3.5.4.1.27"; #outletSensorSignedMinimum
|
|
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
|
|
|
|
#The five columns pdu2_sensor_fmt reads, grouped per entity type.
|
|
my $PDU2_INLET_SENSOR = {
|
|
val => $PDU2_INLETVAL,
|
|
signed => $PDU2_INLETSIGNEDVAL,
|
|
min => $PDU2_INLETSIGNEDMIN,
|
|
units => $PDU2_INLETUNITS,
|
|
dec => $PDU2_INLETDEC,
|
|
};
|
|
my $PDU2_OUTLET_SENSOR = {
|
|
val => $PDU2_OUTLETVAL,
|
|
signed => $PDU2_OUTLETSIGNEDVAL,
|
|
min => $PDU2_OUTLETSIGNEDMIN,
|
|
units => $PDU2_OUTLETUNITS,
|
|
dec => $PDU2_OUTLETDEC,
|
|
};
|
|
|
|
#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",
|
|
);
|
|
|
|
#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);
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 handled_commands
|
|
|
|
Return list of commands handled by this plugin
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
|
|
sub handled_commands
|
|
{
|
|
return {
|
|
rpower => ["nodehm:mgt","pduoutlet:pdu=\.\*"],
|
|
rinv => ["nodehm:mgt"],
|
|
rvitals => ["nodehm:mgt"],
|
|
nodeset => ["nodehm:mgt"],
|
|
rspconfig => ["nodehm:mgt"],
|
|
pdudiscover => "pdu",
|
|
};
|
|
}
|
|
|
|
sub pdu_usage
|
|
{
|
|
my ($callback, $command) = @_;
|
|
my $usagemsg =
|
|
"Usage:
|
|
The following commands support both type of PDUs :
|
|
pdudiscover [<noderange>|--range ipranges] [-r|-x|-z] [-w] [-V|--verbose] [--setup]
|
|
rpower pdunodes [off|on|stat|reset]
|
|
rinv pdunodes
|
|
rvitals pdunodes
|
|
|
|
The following commands support IR PDU with pdutype=irpdu :
|
|
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
|
|
rspconfig pdunodes snmpcfg
|
|
rspconfig pdunode [hostname=<NAME>|ip=<IP>|mask=<MASK>]
|
|
\n";
|
|
|
|
if ($callback)
|
|
{
|
|
my $rsp = {};
|
|
$rsp->{data}->[0] = $usagemsg;
|
|
xCAT::MsgUtils->message("I", $rsp, $callback);
|
|
}
|
|
else
|
|
{
|
|
xCAT::MsgUtils->message("I", $usagemsg);
|
|
}
|
|
return;
|
|
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
=head3 preprocess_request
|
|
|
|
Parse the arguments and display the usage or the version string.
|
|
|
|
=cut
|
|
#--------------------------------------------------------------------------------
|
|
sub preprocess_request {
|
|
my $req = shift;
|
|
if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
|
|
my $callback=shift;
|
|
my @requests;
|
|
|
|
my $command = $req->{command}->[0];
|
|
my $noderange = $req->{node}; #Should be arrayref
|
|
my $extrargs = $req->{arg};
|
|
my @exargs=($req->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs=@$extrargs;
|
|
}
|
|
|
|
my $usage_string=xCAT::Usage->parseCommand($command, @exargs);
|
|
if ($usage_string) {
|
|
&pdu_usage($callback, $command);
|
|
return 1;
|
|
}
|
|
|
|
if ((!$noderange) && ($command ne "pdudiscover") ){
|
|
&pdu_usage($callback, $command);
|
|
return;
|
|
}
|
|
|
|
my @result = ();
|
|
my $mncopy = {%$req};
|
|
push @result, $mncopy;
|
|
return \@result;
|
|
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 process_request
|
|
|
|
Process the command.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub process_request
|
|
{
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my $subreq = shift;
|
|
my $command = $request->{command}->[0];
|
|
my $noderange = $request->{node}; #Should be arrayref
|
|
my $extrargs = $request->{arg};
|
|
my @exargs = ($request->{arg});
|
|
|
|
if (ref($extrargs)) {
|
|
@exargs = @$extrargs;
|
|
}
|
|
|
|
# get all entries from pdu table
|
|
my @attrs=();
|
|
my $schema = xCAT::Table->getTableSchema('pdu');
|
|
my $desc = $schema->{descriptions};
|
|
foreach my $c (@{ $schema->{cols} }) {
|
|
push @attrs, $c;
|
|
}
|
|
|
|
$pdutab = xCAT::Table->new('pdu');
|
|
if ($pdutab) {
|
|
$pduhash = $pdutab->getAllNodeAttribs(\@attrs, 1);
|
|
}
|
|
|
|
if( $command eq "rinv") {
|
|
#for higher performance, handle node in batch
|
|
return showMFR($noderange, $callback);
|
|
}elsif ($command eq "rvitals") {
|
|
return showMonitorData($noderange, $callback);
|
|
}elsif ($command eq "rpower") {
|
|
my $subcmd = $exargs[0];
|
|
my $subcmd2 = $exargs[1];
|
|
if (($subcmd eq 'pduoff') || ($subcmd eq 'pduon') || ($subcmd eq 'pdustat')|| ($subcmd eq 'pdureset') ){
|
|
#if one day, pdu node have pdu attribute, handle in this section too
|
|
return powerpduoutlet($noderange, $subcmd, $callback);
|
|
} else {
|
|
#-------------------------------------------
|
|
#there are 2 cases will enter this block
|
|
#one is if node's mgt is pdu
|
|
#another is if node has pdu attribute but mgt isn't pdu
|
|
#if the node has pdu attribute but mgt isn't pdu,
|
|
#should do nothing for this node, let other plugin to hanle this node
|
|
#-------------------------------------------
|
|
my @allpdunodes=();
|
|
my $nodehm = xCAT::Table->new('nodehm');
|
|
my $nodehmhash = $nodehm->getNodesAttribs($noderange, ['mgt']);
|
|
foreach my $node (@$noderange) {
|
|
if($nodehmhash->{$node}->[0]->{mgt} eq 'pdu'){
|
|
push @allpdunodes, $node;
|
|
}
|
|
}
|
|
if(@allpdunodes) {
|
|
if ( ($subcmd =~ /relay/) || ($subcmd2 =~ /relay/) ) {
|
|
process_powerrelay($request,$subreq,\@allpdunodes,$callback);
|
|
} elsif(($subcmd eq 'on') || ($subcmd eq 'off') || ($subcmd eq 'stat') || ($subcmd eq 'state') || ($subcmd eq 'reset') ){
|
|
return powerpdu(\@allpdunodes, $subcmd, $callback);
|
|
} else {
|
|
my $pdunode = join (",", @allpdunodes);
|
|
$callback->({ errorcode => [1],error => "The option $subcmd is not support for pdu node(s) $pdunode."});
|
|
&pdu_usage($callback, $command);
|
|
}
|
|
}
|
|
}
|
|
}elsif($command eq "rspconfig") {
|
|
my $subcmd = $exargs[0];
|
|
if ($subcmd eq 'sshcfg') {
|
|
process_sshcfg($noderange, $subcmd, $callback);
|
|
}elsif ($subcmd eq 'snmpcfg') {
|
|
process_snmpcfg($noderange, $subcmd, $callback);
|
|
}elsif ($subcmd =~ /ip|gateway|netmask|hostname/) {
|
|
process_netcfg($request, $subreq, $subcmd, $callback);
|
|
} else {
|
|
$callback->({ errorcode => [1],error => "The input $command $subcmd is not support for pdu"});
|
|
&pdu_usage($callback, $command);
|
|
}
|
|
}elsif($command eq "pdudiscover") {
|
|
process_pdudiscover($request, $subreq, $callback);
|
|
}elsif($command eq "nodeset") {
|
|
$callback->({ errorcode => [1],error => "The input $command is not support for pdu"});
|
|
&pdu_usage($callback, $command);
|
|
}else{
|
|
#reserve for other new command in future
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 fill_outletcount
|
|
|
|
Get outlet count for IR PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
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');
|
|
|
|
my $count = $session->get("$outletoid");
|
|
if ($count) {
|
|
$pdutab->setNodeAttribs($pdu, {outlet => $count});
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("Invalid Outlet number ", $callback,$pdu);
|
|
}
|
|
|
|
return $count;
|
|
|
|
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 powerpdu
|
|
|
|
Process power command (stat/off/on) for pdu/pdus
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub powerpdu {
|
|
my $noderange = shift;
|
|
my $subcmd = shift;
|
|
my $callback = shift;
|
|
|
|
if (($subcmd eq "stat") || ($subcmd eq "state")){
|
|
return powerstat($noderange, $callback);
|
|
}
|
|
|
|
foreach my $node (@$noderange) {
|
|
if ($pduhash->{$node}->[0]->{pdutype} eq 'crpdu') {
|
|
process_relay($node,$subcmd,$callback,1,3);
|
|
next;
|
|
}
|
|
|
|
my $session = connectTopdu($node,$callback);
|
|
if (!$session) {
|
|
$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);
|
|
}
|
|
|
|
my $value;
|
|
my $statstr;
|
|
if ($subcmd eq "off") {
|
|
$value = 0;
|
|
$statstr = "off";
|
|
} elsif ( $subcmd eq "on") {
|
|
$value = 1;
|
|
$statstr = "on";
|
|
} else {
|
|
$value = 2;
|
|
$statstr = "reset";
|
|
}
|
|
|
|
for (my $outlet =1; $outlet <= $count; $outlet++)
|
|
{
|
|
outletpower($session, $outlet, $value);
|
|
if ($session->{ErrorStr}) {
|
|
$callback->({ errorcode => [1],error => "Failed to get outlet status for $node"});
|
|
} else {
|
|
my $output = " outlet $outlet is $statstr";
|
|
xCAT::SvrUtils::sendmsg($output, $callback, $node, %allerrornodes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 powerpduoutlet
|
|
|
|
Process power command (pdustat/pduoff/pduon) for compute nodes,
|
|
the pdu attribute needs to be set
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub powerpduoutlet {
|
|
my $noderange = shift;
|
|
my $subcmd = shift;
|
|
my $callback = shift;
|
|
my $output;
|
|
my $value;
|
|
my $statstr;
|
|
|
|
my $tmpnodestr = join(",", @$noderange);
|
|
|
|
my $nodetab = xCAT::Table->new('pduoutlet');
|
|
my $nodepdu = $nodetab->getNodesAttribs($noderange,['pdu']);
|
|
|
|
foreach my $node (@$noderange) {
|
|
# the pdu attribute needs to be set
|
|
if(! $nodepdu->{$node}->[0]->{pdu}){
|
|
$callback->({ error => "$node: without pdu attribute"});
|
|
next;
|
|
}
|
|
|
|
my @pdus = split /,/, $nodepdu->{$node}->[0]->{pdu};
|
|
foreach my $pdu_outlet (@pdus) {
|
|
my ($pdu, $outlet) = split /:/, $pdu_outlet;
|
|
if ($pduhash->{$pdu}->[0]->{pdutype} eq 'crpdu') {
|
|
$callback->({ error => "$node: This command doesn't supports CONSTELLATION PDU with pdutype=crpdu for $pdu"});
|
|
next;
|
|
}
|
|
my $session = connectTopdu($pdu,$callback);
|
|
if (!$session) {
|
|
$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);
|
|
}
|
|
if ($outlet > $count ) {
|
|
$callback->({ error => "$node: $pdu outlet number $outlet is invalid"});
|
|
next;
|
|
}
|
|
my $cmd;
|
|
if ($subcmd eq "pdustat") {
|
|
$statstr=outletstat($session, $outlet);
|
|
} elsif ($subcmd eq "pduoff") {
|
|
$value = 0;
|
|
$statstr = "off";
|
|
outletpower($session, $outlet, $value);
|
|
} elsif ($subcmd eq "pduon") {
|
|
$value = 1;
|
|
$statstr = "on";
|
|
outletpower($session, $outlet, $value);
|
|
} elsif ($subcmd eq "pdureset") {
|
|
$value = 2;
|
|
$statstr = "reset";
|
|
outletpower($session, $outlet, $value);
|
|
} else {
|
|
$callback->({ error => "$subcmd is not support"});
|
|
}
|
|
|
|
if ($session->{ErrorStr}) {
|
|
$callback->({ errorcode => [1],error => "$node: $pdu outlet $outlet has error = $session->{ErrorStr}"});
|
|
} else {
|
|
$output = "$pdu operational state for outlet $outlet is $statstr";
|
|
xCAT::SvrUtils::sendmsg($output, $callback, $node, %allerrornodes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 outletpower
|
|
|
|
Process power command for one pdu outlet,
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub outletpower {
|
|
my $session = shift;
|
|
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}) {
|
|
$oid = ".1.3.6.1.4.1.2.6.223.8.2.2.1.13";
|
|
}
|
|
|
|
|
|
my $varbind = new SNMP::Varbind([ $oid, $outlet, $value, $type ]);
|
|
return $session->set($varbind);
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 powerstat
|
|
|
|
Process command to query status of pdu
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub powerstat {
|
|
my $noderange = shift;
|
|
my $callback = shift;
|
|
my $output;
|
|
|
|
foreach my $pdu (@$noderange) {
|
|
if ($pduhash->{$pdu}->[0]->{pdutype} eq 'crpdu') {
|
|
my $snmpversion = $pduhash->{$pdu}->[0]->{snmpversion};
|
|
my $snmpcmd;
|
|
if ($snmpversion =~ /3/) {
|
|
my $snmpuser = $pduhash->{$pdu}->[0]->{snmpuser};
|
|
my $seclevel = $pduhash->{$pdu}->[0]->{seclevel};
|
|
if ((defined $snmpuser) && (defined $seclevel)) {
|
|
my $authtype = $pduhash->{$pdu}->[0]->{authtype};
|
|
if (!defined $authtype) {
|
|
$authtype="MD5";
|
|
}
|
|
my $authkey = $pduhash->{$pdu}->[0]->{authkey};
|
|
my $privtype = $pduhash->{$pdu}->[0]->{privtype};
|
|
if (!defined $privtype) {
|
|
$privtype="DES";
|
|
}
|
|
my $privkey = $pduhash->{$pdu}->[0]->{privkey};
|
|
if (!defined $privkey) {
|
|
if (defined $authkey) {
|
|
$privkey=$authkey;
|
|
}
|
|
}
|
|
if ($seclevel eq "authNoPriv") {
|
|
$snmpcmd = "snmpwalk -v3 -u $snmpuser -a $authtype -A $authkey -l $seclevel";
|
|
} elsif ($seclevel eq "authPriv") {
|
|
$snmpcmd = "snmpwalk -v3 -u $snmpuser -a $authtype -A $authkey -l $seclevel -x $privtype -X $privkey";
|
|
} else { #default to notAuthNoPriv
|
|
$snmpcmd = "snmpwalk -v3 -u $snmpuser -l $seclevel";
|
|
}
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("ERROR: No snmpuser or Security level defined for snmpV3 configuration", $callback,$pdu);
|
|
xCAT::SvrUtils::sendmsg(" use chdef command to add pdu snmpV3 attributes to pdu table", $callback,$pdu);
|
|
xCAT::SvrUtils::sendmsg(" ex: chdef coral-pdu snmpversion=3, snmpuser=admin, authtype=MD5 authkey=password1 privtype=DES privkey=password2 seclevel=authPriv", $callback,$pdu);
|
|
xCAT::SvrUtils::sendmsg(" then run 'rspconfig $pdu snmpcfg' command ", $callback,$pdu);
|
|
next;
|
|
}
|
|
} else {
|
|
# use default value
|
|
$snmpcmd = "snmpwalk -v3 -u admin -a MD5 -A password1 -l authPriv -x DES -X password2";
|
|
}
|
|
for (my $relay = 1; $relay <= 3; $relay++) {
|
|
relaystat($pdu, $relay, $snmpcmd, $callback);
|
|
}
|
|
next;
|
|
}
|
|
my $session = connectTopdu($pdu,$callback);
|
|
if (!$session) {
|
|
$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);
|
|
}
|
|
for (my $outlet =1; $outlet <= $count; $outlet++)
|
|
{
|
|
my $statstr = outletstat($session, $outlet);
|
|
my $msg = " operational state for the outlet $outlet is $statstr";
|
|
xCAT::SvrUtils::sendmsg($msg, $callback, $pdu, %allerrornodes);
|
|
}
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 outletstat
|
|
|
|
Process command to query status of one pdu outlet
|
|
ibmPduOutletState defined from mib file
|
|
off(0)
|
|
on(1)
|
|
cycling(2)
|
|
delaySwitch10(3)
|
|
delaySwitch30(4)
|
|
delaySwitch60(5)
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub outletstat {
|
|
my $session = shift;
|
|
my $outlet = shift;
|
|
|
|
#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);
|
|
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";
|
|
my $output;
|
|
my $statstr;
|
|
if ($session->{newmib}) {
|
|
$oid = ".1.3.6.1.4.1.2.6.223.8.2.2.1.13";
|
|
}
|
|
|
|
$output = $session->get("$oid.$outlet");
|
|
if ($output eq 0) {
|
|
$statstr = "off";
|
|
} elsif ($output eq 1) {
|
|
$statstr = "on";
|
|
} elsif ($output eq 2) {
|
|
$statstr = "cycling";
|
|
} elsif ($output eq 3) {
|
|
$statstr = "delaySwitch10";
|
|
} elsif ($output eq 4) {
|
|
$statstr = "delaySwitch30";
|
|
} elsif ($output eq 5) {
|
|
$statstr = "delaySwitch60";
|
|
} else {
|
|
$statstr = "$output(unknown state)" ;
|
|
}
|
|
return $statstr;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 connectTopdu
|
|
|
|
connect pdu via snmp session
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
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;
|
|
if ($pduhash->{$pdu}->[0]->{community}) {
|
|
$community = $pduhash->{$pdu}->[0]->{community};
|
|
} else {
|
|
$community = "public";
|
|
}
|
|
|
|
my $snmpver = "1";
|
|
my $session;
|
|
my $msg = "connectTopdu";
|
|
my $versionoid = ".1.3.6.1.4.1.2.6.223.7.3.0";
|
|
|
|
$session = new SNMP::Session(
|
|
DestHost => $pdu,
|
|
Version => $snmpver,
|
|
Community => $community,
|
|
UseSprintValue => 1,
|
|
);
|
|
unless ($session) {
|
|
return;
|
|
}
|
|
|
|
my $varbind = new SNMP::Varbind([ $versionoid, '' ]);
|
|
my $pduversion = $session->get($varbind);
|
|
if ($session->{ErrorStr}) {
|
|
return;
|
|
}
|
|
|
|
$session->{newmib} = 0;
|
|
if ($pduversion =~ /sLEN/) {
|
|
$session->{newmib} = 1;
|
|
}
|
|
|
|
return $session;
|
|
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=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 $session = new SNMP::Session(pdu2_session_args($pdu, $pduhash->{$pdu}->[0]));
|
|
unless ($session) {
|
|
return;
|
|
}
|
|
$session->{genpdu} = 1;
|
|
|
|
unless (pdu2_session_probe($session, $pdu, $callback)) {
|
|
return;
|
|
}
|
|
|
|
return $session;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 pdu2_session_args
|
|
|
|
Map a 'pdu' table row onto the SNMP::Session arguments for that PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub pdu2_session_args {
|
|
my ($pdu, $ent) = @_;
|
|
|
|
my $snmpver = $ent->{snmpversion};
|
|
if (!defined $snmpver) { $snmpver = "1"; }
|
|
elsif ($snmpver =~ /3/) { $snmpver = "3"; }
|
|
elsif ($snmpver =~ /2/) { $snmpver = "2"; }
|
|
else { $snmpver = "1"; }
|
|
|
|
if ($snmpver ne "3") {
|
|
return (
|
|
DestHost => $pdu,
|
|
Version => $snmpver,
|
|
Community => $ent->{community} || "public",
|
|
UseSprintValue => 1,
|
|
);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return %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'.
|
|
A missing object is exception text under v2c/v3 and an error under v1, so
|
|
both count as absent rather than as a failure.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub pdu2_get {
|
|
my ($session, $oid) = @_;
|
|
|
|
#Cleared first so the error below belongs to this GET.
|
|
$session->{ErrorStr} = '';
|
|
my $val = $session->get($oid);
|
|
my $err = $session->{ErrorStr} || '';
|
|
|
|
return ($val, 'ok') if (defined $val and $val ne '' and $val !~ /^No Such/i);
|
|
return (undef, 'failed') if ($err ne '' and $err !~ /no such/i);
|
|
return (undef, 'absent');
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 pdu2_session_probe
|
|
|
|
Returns false when the PDU does not answer, so callers report a connection
|
|
failure, and records whether its outlets can be switched.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub pdu2_session_probe {
|
|
my ($session, $pdu, $callback) = @_;
|
|
|
|
#SNMP::Session->new does not contact the device, so pduCount is the first
|
|
#exchange and the liveness check: a silent PDU otherwise looks like one
|
|
#with no switched outlets.
|
|
my ($pducount, $state) = pdu2_get($session, "$PDU2_PDUCOUNT");
|
|
return 0 if ($state eq 'failed');
|
|
|
|
unless (defined $pducount and $pducount =~ /^\d+$/ and $pducount >= 1) {
|
|
#Some MIB implementations may not provide pduCount. Fall back to a
|
|
#nameplate object, so credentials without PDU2 access are rejected.
|
|
my ($model) = pdu2_get($session, "$PDU2_NAMEPLATE.3.$PDU2_PDUID");
|
|
return 0 unless (defined $model);
|
|
$pducount = 1;
|
|
}
|
|
|
|
#pduCount includes the primary and its link units (for BCM2 and PMC, the
|
|
#meters and the controller). genpdu drives pduId 1, still the primary when
|
|
#linked, so warn rather than refuse. Once per PDU: node ranges connect per
|
|
#node.
|
|
if ($pducount != 1 and !$genpdu_linked{$pdu}) {
|
|
$genpdu_linked{$pdu} = 1;
|
|
xCAT::SvrUtils::sendmsg("pduCount is $pducount, only the primary unit (pduId $PDU2_PDUID) is managed", $callback, $pdu);
|
|
}
|
|
|
|
#Metered-only models such as PX2-1901U answer the measurement tables but
|
|
#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) ? 1 : 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=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.
|
|
|
|
$oids is one of the PDU2_*_SENSOR column sets and $idx is the full instance
|
|
suffix (pduId.entityId.sensorType). The value column, scaling factor and unit
|
|
all come from the matching sensor-configuration table, so no unit, precision
|
|
or column choice is hardcoded.
|
|
|
|
Unit and decimal digits are cached per sensorType, and only once a sensor is
|
|
known to exist, so an entity that lacks one cannot seed the cache for those
|
|
behind it. The signed minimum is read per entity instead: it decides which
|
|
column is read, and a stale one reports a wrong number, not no number.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub pdu2_sensor_fmt {
|
|
my ($session, $oids, $idx, $cache) = @_;
|
|
|
|
#Use the signed column when the minimum is below zero. An absent minimum
|
|
#means firmware without the column, so keep the unsigned reading; a failed
|
|
#GET means neither column can be trusted.
|
|
my ($min, $state) = pdu2_get($session, "$oids->{min}.$idx");
|
|
return undef if ($state eq 'failed');
|
|
$min = 0 unless (defined $min and $min =~ /^-?\d+$/);
|
|
|
|
my $valoid = ($min < 0) ? $oids->{signed} : $oids->{val};
|
|
my ($raw) = pdu2_get($session, "$valoid.$idx");
|
|
return undef unless (defined $raw and $raw =~ /^-?\d+$/);
|
|
|
|
my $st = (split /\./, $idx)[-1];
|
|
unless (exists $cache->{$st}) {
|
|
#A failed read here would cache the wrong scaling for every entity
|
|
#behind this one.
|
|
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 = pdu2_enum($u, \%PDU2_UNIT_CODE);
|
|
$u = -1 unless (defined $u);
|
|
$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) {
|
|
#Not every model populates every nameplate field.
|
|
my ($output) = pdu2_get($session, "$f->[0].$f->[1].$PDU2_PDUID");
|
|
next unless (defined $output);
|
|
#UseSprintValue returns DisplayStrings quoted; strip for readability.
|
|
$output =~ s/^"(.*)"$/$1/;
|
|
next if ($output eq '');
|
|
xCAT::SvrUtils::sendmsg("$f->[2]: $output", $callback, $pdu);
|
|
}
|
|
|
|
my ($output) = pdu2_get($session, "$SYSDESCR");
|
|
if (defined $output) {
|
|
$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_INLET_SENSOR,
|
|
"$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;
|
|
|
|
#PX2-1901U and PX3-1901U have no per-outlet sensors: probing avoids 192
|
|
#failed GETs on a 48-outlet unit. rmsCurrent, the first outlet sensor, is
|
|
#unsigned.
|
|
my ($probe) = pdu2_get($session, "$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_OUTLET_SENSOR,
|
|
"$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
|
|
Config ip/netmask of PDU via PduManager command
|
|
PduManager is a tool for CoralPdu to manager the PDU.
|
|
* /dev/shm/bin/PduManager -h
|
|
'-i' set PDU system IP
|
|
'-n' set system ip netmask. e.g.:PduManager -i xxx.xxx.xxx.xxx -n xxx.xxx.xxx.xxx
|
|
|
|
example: rspconfig coralpdu hostname=coralpdu
|
|
rspconfig coralpdu ip=1.1.1.1 netmask=255.0.0.0
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub process_netcfg {
|
|
my $request = shift;
|
|
my $subreq = shift;
|
|
my $subcmd = shift;
|
|
my $callback = shift;
|
|
my $hostname;
|
|
my $ip;
|
|
my $netmask;
|
|
my $args;
|
|
my $exp;
|
|
my $errstr;
|
|
|
|
my $extrargs = $request->{arg};
|
|
my @exargs = ($request->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs = @$extrargs;
|
|
}
|
|
|
|
my $nodes = $request->{node};
|
|
my $node_number = @$nodes;
|
|
if ($node_number gt "1") {
|
|
xCAT::SvrUtils::sendmsg("Can not configure more than 1 nodes", $callback);
|
|
return;
|
|
}
|
|
|
|
my $pdu = @$nodes[0];
|
|
my $rsp = {};
|
|
|
|
my $nodetab = xCAT::Table->new('hosts');
|
|
my $nodehash = $nodetab->getNodesAttribs($nodes,['ip','otherinterfaces']);
|
|
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;
|
|
}
|
|
|
|
# connect to PDU
|
|
my $username = $pduhash->{$pdu}->[0]->{username};
|
|
my $password = $pduhash->{$pdu}->[0]->{password};
|
|
($exp, $errstr) = session_connect($static_ip, $discover_ip,$username,$password);
|
|
if (defined $errstr) {
|
|
xCAT::SvrUtils::sendmsg("Failed to connect", $callback,$pdu);
|
|
return;
|
|
}
|
|
|
|
foreach my $cmd (@exargs) {
|
|
my ($key, $value) = split(/=/, $cmd);
|
|
if ($key =~ /hostname/) {
|
|
$hostname = $value;
|
|
my ($ret, $err) = session_exec($exp, "echo $hostname > /etc/hostname;/etc/init.d/hostname.sh");
|
|
if (defined $err) {
|
|
xCAT::SvrUtils::sendmsg("Failed to set hostname", $callback);
|
|
}
|
|
}elsif ($key =~ /ip/) {
|
|
$ip = $value;
|
|
} elsif ($key =~ /netmask/) {
|
|
$netmask = $value;
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("rspconfig $cmd is not support yet, ignored", $callback);
|
|
}
|
|
}
|
|
|
|
$args = "/dev/shm/bin/PduManager ";
|
|
my $opt;
|
|
if ($ip) {
|
|
$opt = "-i $ip ";
|
|
}
|
|
if ($netmask) {
|
|
$opt = $opt . "-n $netmask";
|
|
}
|
|
if ($opt) {
|
|
my $dshcmd = $args . $opt ;
|
|
my ($ret, $err) = session_exec($exp, $dshcmd);
|
|
if (defined $err) {
|
|
#session will be hung if ip address changed
|
|
my $p = Net::Ping->new();
|
|
if ( ($p->ping($ip)) && ($err =~ /TIMEOUT/) ) {
|
|
xCAT::SvrUtils::sendmsg("$ip is reachable", $callback);
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("Failed to run $dshcmd, error=$err", $callback);
|
|
return;
|
|
}
|
|
}
|
|
xCAT::SvrUtils::sendmsg("$dshcmd ran successfully", $callback);
|
|
xCAT::Utils->runxcmd({ command => ['chdef'], arg => ['-t','node','-o',$pdu,"ip=$ip","otherinterfaces="] }, $subreq, 0, 1);
|
|
xCAT::Utils->runxcmd({ command => ['makehosts'], node => [$pdu] }, $subreq, 0, 1);
|
|
}
|
|
if (defined $exp) {
|
|
$exp->hard_close();
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 process_sshcfg
|
|
|
|
Config passwordless for coralpdu
|
|
|
|
example: rspconfig coralpdu sshcfg
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub process_sshcfg {
|
|
my $noderange = shift;
|
|
my $subcmd = shift;
|
|
my $callback = shift;
|
|
|
|
my $keyfile = "/root/.ssh/id_rsa.pub";
|
|
my $rootkey = `cat /root/.ssh/id_rsa.pub`;
|
|
my $cmd;
|
|
|
|
my $nodetab = xCAT::Table->new('hosts');
|
|
my $nodehash = $nodetab->getNodesAttribs($noderange,['ip','otherinterfaces']);
|
|
|
|
foreach my $pdu (@$noderange) {
|
|
unless ($pduhash->{$pdu}->[0]->{pdutype} eq "crpdu") {
|
|
xCAT::SvrUtils::sendmsg("This command only supports CONSTELLATION PDU with pdutype=crpdu", $callback,$pdu);
|
|
next;
|
|
}
|
|
|
|
my $msg = " process_sshcfg";
|
|
xCAT::SvrUtils::sendmsg($msg, $callback, $pdu, %allerrornodes);
|
|
|
|
#remove old host key from /root/.ssh/known_hosts
|
|
$cmd = "ssh-keygen -R $pdu";
|
|
xCAT::Utils->runcmd($cmd, 0);
|
|
|
|
my $static_ip = $nodehash->{$pdu}->[0]->{ip};
|
|
my $discover_ip = $nodehash->{$pdu}->[0]->{otherinterfaces};
|
|
my $username = $pduhash->{$pdu}->[0]->{username};
|
|
my $password = $pduhash->{$pdu}->[0]->{password};
|
|
|
|
my ($exp, $errstr) = session_connect($static_ip, $discover_ip,$username,$password);
|
|
if (!defined $exp) {
|
|
$msg = " Failed to connect $errstr";
|
|
xCAT::SvrUtils::sendmsg($msg, $callback, $pdu, %allerrornodes);
|
|
next;
|
|
}
|
|
|
|
my $ret;
|
|
my $err;
|
|
|
|
($ret, $err) = session_exec($exp, "mkdir -p /home/root/.ssh");
|
|
($ret, $err) = session_exec($exp, "chmod 700 /home/root/.ssh");
|
|
($ret, $err) = session_exec($exp, "echo \"$rootkey\" >/home/root/.ssh/authorized_keys");
|
|
($ret, $err) = session_exec($exp, "chmod 644 /home/root/.ssh/authorized_keys");
|
|
|
|
$exp->hard_close();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 session_connect
|
|
|
|
open a expect session and connect to CR PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub session_connect {
|
|
my $static_ip = shift;
|
|
my $discover_ip = shift;
|
|
my $userid = shift;
|
|
my $password = shift;
|
|
|
|
#default password for coral pdu
|
|
if (!defined $userid) {
|
|
$userid = "root";
|
|
}
|
|
if (!defined $password) {
|
|
$password = "password8";
|
|
}
|
|
|
|
my $timeout = 30;
|
|
|
|
my $ssh_ip;
|
|
my $p = Net::Ping->new();
|
|
if ($p->ping($static_ip)) {
|
|
$ssh_ip = $static_ip;
|
|
} elsif ($p->ping($discover_ip)) {
|
|
$ssh_ip = $discover_ip;
|
|
} else {
|
|
return(undef, " is not reachable\n");
|
|
}
|
|
|
|
my $ssh = Expect->new;
|
|
my $command = 'ssh';
|
|
my @parameters = ($userid . "@" . $ssh_ip);
|
|
|
|
$ssh->debug(0);
|
|
$ssh->log_stdout(0); # suppress stdout output..
|
|
|
|
unless ($ssh->spawn($command, @parameters))
|
|
{
|
|
my $err = $!;
|
|
$ssh->soft_close();
|
|
my $rsp;
|
|
return(undef, "unable to run command $command $err\n");
|
|
}
|
|
|
|
$ssh->expect($timeout,
|
|
[ "-re", qr/WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED/, sub {die "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n"; } ],
|
|
[ "-re", qr/\(yes\/no\)\?\s*$/, sub { $ssh->send("yes\n"); exp_continue; } ],
|
|
[ "-re", qr/ password:/, sub {$ssh->send("$password\n"); exp_continue; } ],
|
|
[ "-re", qr/:~\$/, sub { $ssh->send("sudo su\n"); exp_continue; } ],
|
|
[ "-re", qr/.*#/, sub { $ssh->clear_accum(); } ],
|
|
[ timeout => sub { die "No login.\n"; } ]
|
|
);
|
|
$ssh->clear_accum();
|
|
return ($ssh);
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 session_exec
|
|
|
|
execute command to CR PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub session_exec {
|
|
my $exp = shift;
|
|
my $cmd = shift;
|
|
my $timeout = shift;
|
|
my $prompt = shift;
|
|
|
|
$timeout = 30 unless defined $timeout;
|
|
$prompt = qr/.*#/ unless defined $prompt;
|
|
|
|
|
|
$exp->clear_accum();
|
|
$exp->send("$cmd\n");
|
|
my ($mpos, $merr, $mstr, $mbmatch, $mamatch) = $exp->expect(6, "-re", $prompt);
|
|
|
|
if (defined $merr) {
|
|
return(undef,$merr);
|
|
}
|
|
return($mbmatch);
|
|
}
|
|
|
|
#-----------------------------------------------------------------
|
|
|
|
=head3 process_pdudiscover
|
|
|
|
Discover the pdu for a given range of DHCP ip address
|
|
it will call switchdiscover command with -s snmp --pdu options
|
|
|
|
example: pdudiscover --range iprange -w
|
|
|
|
=cut
|
|
|
|
#------------------------------------------------------------------
|
|
sub process_pdudiscover {
|
|
my $request = shift;
|
|
my $sub_req = shift;
|
|
my $callback = shift;
|
|
my $extrargs = $request->{arg};
|
|
my @exargs = ($request->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs=@$extrargs;
|
|
}
|
|
|
|
#check case in GetOptions
|
|
$Getopt::Long::ignorecase = 0;
|
|
Getopt::Long::Configure( "bundling" );
|
|
Getopt::Long::Configure("no_pass_through");
|
|
my %opt;
|
|
if (!GetOptions( \%opt,
|
|
qw(h|help V|verbose x z w r range=s setup))) {
|
|
my $usage_string = xCAT::Usage->getUsage($request->{command}->[0]);
|
|
$callback->({ data => $usage_string });
|
|
return;
|
|
|
|
}
|
|
|
|
push @exargs, "-s snmp --pdu";
|
|
my $cmd = "switchdiscover @exargs";
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
|
|
my $rsp = {};
|
|
push @{ $rsp->{data} }, "$result";
|
|
xCAT::MsgUtils->message("I", $rsp, $callback);
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 showMFR
|
|
|
|
show MFR information of PDU via PduManager command
|
|
PduManager is a tool for CoralPdu to manager the PDU.
|
|
* /dev/shm/bin/PduManager -h
|
|
'-m' show MFR info
|
|
|
|
example: rinv coralpdu
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
|
|
sub showMFR {
|
|
my $noderange = shift;
|
|
my $callback = shift;
|
|
my $output;
|
|
|
|
my $nodetab = xCAT::Table->new('hosts');
|
|
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;
|
|
}
|
|
|
|
# connect to PDU
|
|
my $static_ip = $nodehash->{$pdu}->[0]->{ip};
|
|
my $discover_ip = $nodehash->{$pdu}->[0]->{otherinterfaces};
|
|
my $username = $pduhash->{$pdu}->[0]->{username};
|
|
my $password = $pduhash->{$pdu}->[0]->{password};
|
|
|
|
my ($exp, $errstr) = session_connect($static_ip, $discover_ip,$username,$password);
|
|
if (defined $errstr) {
|
|
xCAT::SvrUtils::sendmsg("Failed to connect: $errstr", $callback);
|
|
}
|
|
|
|
my ($ret, $err) = session_exec($exp, "/dev/shm/bin/PduManager -m");
|
|
if (defined $err) {
|
|
xCAT::SvrUtils::sendmsg("Failed to list MFR information: $err", $callback);
|
|
}
|
|
if (defined $ret) {
|
|
foreach my $line (split /[\r\n]+/, $ret) {
|
|
if ($line) {
|
|
$line = join(' ',split(' ',$line));
|
|
xCAT::SvrUtils::sendmsg("$line", $callback,$pdu);
|
|
}
|
|
}
|
|
}
|
|
|
|
$exp->hard_close();
|
|
}
|
|
}
|
|
|
|
sub rinv_for_irpdu
|
|
{
|
|
my $pdu = shift;
|
|
my $callback = shift;
|
|
my $output;
|
|
|
|
my $session = connectTopdu($pdu,$callback);
|
|
if (!$session) {
|
|
$callback->({ errorcode => [1],error => "Couldn't connect to $pdu"});
|
|
next;
|
|
}
|
|
#ibmPduSoftwareVersion
|
|
$output = $session->get(".1.3.6.1.4.1.2.6.223.7.3.0");
|
|
if ($output) {
|
|
xCAT::SvrUtils::sendmsg("PDU Software Version: $output", $callback,$pdu);
|
|
}
|
|
#ibmPduMachineType
|
|
$output = $session->get(".1.3.6.1.4.1.2.6.223.7.4.0");
|
|
if ($output) {
|
|
xCAT::SvrUtils::sendmsg("PDU Machine Type: $output", $callback,$pdu);
|
|
}
|
|
#ibmPduModelNumber
|
|
$output = $session->get(".1.3.6.1.4.1.2.6.223.7.5.0");
|
|
if ($output) {
|
|
xCAT::SvrUtils::sendmsg("PDU Model Number: $output", $callback,$pdu);
|
|
}
|
|
#ibmPduPartNumber
|
|
$output = $session->get(".1.3.6.1.4.1.2.6.223.7.6.0");
|
|
if ($output) {
|
|
xCAT::SvrUtils::sendmsg("PDU Part Number: $output", $callback,$pdu);
|
|
}
|
|
#ibmPduName
|
|
$output = $session->get(".1.3.6.1.4.1.2.6.223.7.7.0");
|
|
if ($output) {
|
|
xCAT::SvrUtils::sendmsg("PDU Name: $output", $callback,$pdu);
|
|
}
|
|
#ibmPduSerialNumber
|
|
$output = $session->get(".1.3.6.1.4.1.2.6.223.7.9.0");
|
|
if ($output) {
|
|
xCAT::SvrUtils::sendmsg("PDU Serial Number: $output", $callback,$pdu);
|
|
}
|
|
#ibmPduDescription
|
|
$output = $session->get(".1.3.6.1.4.1.2.6.223.7.10.0");
|
|
if ($output) {
|
|
xCAT::SvrUtils::sendmsg("PDU Description: $output", $callback,$pdu);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 showMonitorData
|
|
|
|
Show realtime monitor data(input voltage, current, power)
|
|
of PDU via PduManager command
|
|
PduManager is a tool for CoralPdu to manager the PDU.
|
|
* /dev/shm/bin/PduManager -h
|
|
'-d' show realtime monitor data(input voltage, current, power)
|
|
|
|
example: rvitals coralpdu
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub showMonitorData {
|
|
my $noderange = shift;
|
|
my $callback = shift;
|
|
my $output;
|
|
|
|
my $nodetab = xCAT::Table->new('hosts');
|
|
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) {
|
|
$callback->({ errorcode => [1],error => "Couldn't connect to $pdu"});
|
|
next;
|
|
}
|
|
my $count = $pduhash->{$pdu}->[0]->{outlet};
|
|
unless ($count) {
|
|
$count = fill_outletCount($session, $pdu, $callback);
|
|
}
|
|
if ($count > 0) {
|
|
rvitals_for_irpdu($pdu, $count, $session, $callback);
|
|
}
|
|
next;
|
|
}
|
|
|
|
# connect to PDU
|
|
my $static_ip = $nodehash->{$pdu}->[0]->{ip};
|
|
my $discover_ip = $nodehash->{$pdu}->[0]->{otherinterfaces};
|
|
my $username = $pduhash->{$pdu}->[0]->{username};
|
|
my $password = $pduhash->{$pdu}->[0]->{password};
|
|
|
|
my ($exp, $errstr) = session_connect($static_ip, $discover_ip,$username,$password);
|
|
|
|
my $ret;
|
|
my $err;
|
|
|
|
($ret, $err) = session_exec($exp, "/dev/shm/bin/PduManager -d");
|
|
if (defined $err) {
|
|
xCAT::SvrUtils::sendmsg("Failed to show monitor data: $err", $callback);
|
|
}
|
|
if (defined $ret) {
|
|
foreach my $line (split /[\r\n]+/, $ret) {
|
|
if ($line) {
|
|
$line = join(' ',split(' ',$line));
|
|
xCAT::SvrUtils::sendmsg("$line", $callback,$pdu);
|
|
}
|
|
}
|
|
}
|
|
|
|
$exp->hard_close();
|
|
}
|
|
}
|
|
|
|
|
|
sub rvitals_for_irpdu
|
|
{
|
|
my $pdu = shift;
|
|
my $count = shift;
|
|
my $session = shift;
|
|
my $callback = shift;
|
|
my $output;
|
|
|
|
#ibmPduVoltageWarning: (voltageNormal(0),voltageOutOfRange(1))
|
|
my $voltagewarning = ".1.3.6.1.4.1.2.6.223.0.1.1.7.0";
|
|
$output = $session->get("$voltagewarning");
|
|
xCAT::SvrUtils::sendmsg("Voltage Warning: $output", $callback,$pdu);
|
|
|
|
# get power info for each outlet
|
|
# starts oid .2.6.223.8.2.2.1.7 to .2.6.223.8.2.2.1.14
|
|
#ibmPduOutletCurrent
|
|
my $outletcurrent = ".1.3.6.1.4.1.2.6.223.8.2.2.1.7";
|
|
#ibmPduOutletMaxCapacity
|
|
my $outletmaxcap = ".1.3.6.1.4.1.2.6.223.8.2.2.1.8";
|
|
#ibmPduOutletCurrentThresholdWarning
|
|
my $currentthrewarning = ".1.3.6.1.4.1.2.6.223.8.2.2.1.9";
|
|
#ibmPduOutletCurrentThresholdCritical
|
|
my $currentthrecrit = ".1.3.6.1.4.1.2.6.223.8.2.2.1.10";
|
|
#ibmPduOutletLastPowerReading
|
|
my $lastpowerreading = ".1.3.6.1.4.1.2.6.223.8.2.2.1.13";
|
|
for (my $outlet = 1; $outlet <= $count; $outlet++) {
|
|
$output = $session->get("$outletcurrent.$outlet");
|
|
xCAT::SvrUtils::sendmsg("outlet $outlet Current: $output mA", $callback,$pdu);
|
|
$output = $session->get("$outletmaxcap.$outlet");
|
|
xCAT::SvrUtils::sendmsg("outlet $outlet Max Capacity of the current: $output mA", $callback,$pdu);
|
|
$output = $session->get("$currentthrewarning.$outlet");
|
|
xCAT::SvrUtils::sendmsg("outlet $outlet Current Threshold Warning: $output mA", $callback,$pdu);
|
|
$output = $session->get("$currentthrecrit.$outlet");
|
|
xCAT::SvrUtils::sendmsg("outlet $outlet Current Threshold Critical: $output mA", $callback,$pdu);
|
|
$output = $session->get("$lastpowerreading.$outlet");
|
|
xCAT::SvrUtils::sendmsg("outlet $outlet Last Power Reading: $output Watts", $callback,$pdu);
|
|
}
|
|
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 relaystat
|
|
|
|
process individual relay stat for CR PDU.
|
|
The OID for 3 relay:
|
|
1.3.6.1.4.1.2.6.262.15.2.13
|
|
1.3.6.1.4.1.2.6.262.15.2.14
|
|
1.3.6.1.4.1.2.6.262.15.2.15
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub relaystat {
|
|
my $pdu = shift;
|
|
my $relay = shift;
|
|
my $snmpcmd = shift;
|
|
my $callback = shift;
|
|
|
|
my $relayoid = $relay + 12;
|
|
|
|
#default pdu snmpv3, won't show up for snmpv1
|
|
my $cmd = "$snmpcmd $pdu 1.3.6.1.4.1.2.6.262.15.2.$relayoid";
|
|
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
my ($msg,$stat) = split /: /, $result;
|
|
if ($stat eq "1" ) {
|
|
xCAT::SvrUtils::sendmsg(" relay $relay is on", $callback, $pdu, %allerrornodes);
|
|
} elsif ( $stat eq "0" ) {
|
|
xCAT::SvrUtils::sendmsg(" relay $relay is off", $callback, $pdu, %allerrornodes);
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg(" relay $relay is $stat=unknown", $callback, $pdu, %allerrornodes);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 process_powerrelay
|
|
|
|
process relay action for CR PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub process_powerrelay {
|
|
my $request = shift;
|
|
my $subreq = shift;
|
|
my $subcmd = shift;
|
|
my $callback = shift;
|
|
|
|
my $relay;
|
|
my $action;
|
|
|
|
my $extrargs = $request->{arg};
|
|
my @exargs = ($request->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs = @$extrargs;
|
|
}
|
|
|
|
my $nodes = $request->{node};
|
|
|
|
foreach my $cmd (@exargs) {
|
|
if ($cmd =~ /=/ ) {
|
|
my ($key, $value) = split(/=/, $cmd);
|
|
$relay = $value;
|
|
} else {
|
|
$action = $cmd;
|
|
}
|
|
}
|
|
if ( (defined $relay) && (defined $action) ) {
|
|
my $relay_count = 1;
|
|
foreach my $pdu (@$nodes) {
|
|
process_relay($pdu, $action, $callback, $relay, $relay_count);
|
|
}
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg(" This command is not support, please define relay number and action", $callback);
|
|
}
|
|
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 process_relay
|
|
|
|
process relay action for CR PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub process_relay {
|
|
my $pdu = shift;
|
|
my $subcmd = shift;
|
|
my $callback = shift;
|
|
my $relay_num = shift;
|
|
my $relay_count = shift;
|
|
|
|
if ( !defined $relay_count ) {
|
|
$relay_num = 1;
|
|
$relay_count = 3;
|
|
}
|
|
|
|
my $nodetab = xCAT::Table->new('hosts');
|
|
my $nodehash = $nodetab->getNodeAttribs($pdu,['ip','otherinterfaces']);
|
|
my $username = $pduhash->{$pdu}->[0]->{username};
|
|
my $passwd = $pduhash->{$pdu}->[0]->{password};
|
|
|
|
# connect to PDU
|
|
my $static_ip = $nodehash->{$pdu}->[0]->{ip};
|
|
my $discover_ip = $nodehash->{$pdu}->[0]->{otherinterfaces};
|
|
my ($session, $errstr) = session_connect($static_ip, $discover_ip,$username,$passwd);
|
|
|
|
my $ret;
|
|
my $err;
|
|
my $statestr;
|
|
|
|
|
|
for (my $i = 0; $i < $relay_count; $i++) {
|
|
my $relay = $relay_num;
|
|
xCAT::SvrUtils::sendmsg(" power $subcmd for relay $relay_num", $callback,$pdu);
|
|
if ($subcmd eq "off") {
|
|
relay_action($session, $pdu, $relay, "OFF", $callback);
|
|
} elsif ( $subcmd eq "on") {
|
|
relay_action($session, $pdu, $relay, "ON", $callback);
|
|
} elsif ( $subcmd eq "reset") {
|
|
relay_action($session, $pdu, $relay, "OFF", $callback);
|
|
relay_action($session, $pdu, $relay, "ON", $callback);
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg(" subcmd $subcmd is not support", $callback,$pdu);
|
|
}
|
|
$relay_num++;
|
|
}
|
|
$session->hard_close();
|
|
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 realy_action
|
|
|
|
process individual relay action for CR PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub relay_action {
|
|
my $session = shift;
|
|
my $pdu = shift;
|
|
my $relay = shift;
|
|
my $action = shift;
|
|
my $callback = shift;
|
|
|
|
my ($ret, $err) = session_exec($session, "/dev/shm/bin/PduManager -r $relay -v $action");
|
|
if (defined $err) {
|
|
xCAT::SvrUtils::sendmsg("Failed to process relay action: $err", $callback);
|
|
}
|
|
if (defined $ret) {
|
|
xCAT::SvrUtils::sendmsg("$ret", $callback,$pdu);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 process_snmpcfg
|
|
|
|
config snmp and snmpv3 for CR PDU.
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub process_snmpcfg {
|
|
my $noderange = shift;
|
|
my $subcmd = shift;
|
|
my $callback = shift;
|
|
my $snmp_conf="/etc/snmp/snmpd.conf";
|
|
my $xCATSettingsSTART="xCAT settings START";
|
|
my $xCATSettingsEND="xCAT settings END";
|
|
my $xCATSettingsInfo="Entries between the START and END lines will be replaced each time by xCAT command";
|
|
|
|
|
|
my $nodetab = xCAT::Table->new('hosts');
|
|
my $nodehash = $nodetab->getNodesAttribs($noderange,['ip','otherinterfaces']);
|
|
|
|
foreach my $pdu (@$noderange) {
|
|
unless ($pduhash->{$pdu}->[0]->{pdutype} eq "crpdu") {
|
|
xCAT::SvrUtils::sendmsg("This command only supports CONSTELLATION PDU with pdutype=crpdu", $callback,$pdu);
|
|
next;
|
|
}
|
|
|
|
my $community = $pduhash->{$pdu}->[0]->{community};
|
|
my $snmpversion = $pduhash->{$pdu}->[0]->{snmpversion};
|
|
my $snmpuser = $pduhash->{$pdu}->[0]->{snmpuser};
|
|
my $authtype = $pduhash->{$pdu}->[0]->{authtype};
|
|
if (!defined $authtype) {
|
|
$authtype="MD5";
|
|
}
|
|
my $authkey = $pduhash->{$pdu}->[0]->{authkey};
|
|
my $privtype = $pduhash->{$pdu}->[0]->{privtype};
|
|
if (!defined $privtype) {
|
|
$privtype="DES";
|
|
}
|
|
my $privkey = $pduhash->{$pdu}->[0]->{privkey};
|
|
if (!defined $privkey) {
|
|
if (defined $authkey) {
|
|
$privkey=$authkey;
|
|
}
|
|
}
|
|
my $seclevel = $pduhash->{$pdu}->[0]->{seclevel};
|
|
|
|
# connect to PDU
|
|
my $static_ip = $nodehash->{$pdu}->[0]->{ip};
|
|
my $discover_ip = $nodehash->{$pdu}->[0]->{otherinterfaces};
|
|
my $username = $pduhash->{$pdu}->[0]->{username};
|
|
my $password = $pduhash->{$pdu}->[0]->{password};
|
|
|
|
my ($exp, $errstr) = session_connect($static_ip, $discover_ip,$username,$password);
|
|
|
|
my $ret;
|
|
my $err;
|
|
|
|
($ret, $err) = session_exec($exp, "sed -i '/$xCATSettingsSTART/,/$xCATSettingsEND/ d' $snmp_conf");
|
|
($ret, $err) = session_exec($exp, "echo '# $xCATSettingsSTART' >> $snmp_conf");
|
|
($ret, $err) = session_exec($exp, "echo '# $xCATSettingsInfo' >> $snmp_conf");
|
|
if (defined $community) {
|
|
($ret, $err) = session_exec($exp, "echo 'com2sec readwrite default $community' >> $snmp_conf");
|
|
}
|
|
#set snmpv3 configuration
|
|
if ($snmpversion =~ /3/) {
|
|
if ((defined $snmpuser) && (defined $seclevel)) {
|
|
my $msg1;
|
|
if ($seclevel eq "authNoPriv") {
|
|
$msg1 = "createUser $snmpuser $authtype $authkey";
|
|
} elsif ($seclevel eq "authPriv") {
|
|
$msg1 = "createUser $snmpuser $authtype $authkey $privtype $privkey";
|
|
} else { #default to notAuthNoPriv
|
|
$msg1 = "createUser $snmpuser";
|
|
}
|
|
my $msg2 = "rwuser $snmpuser $seclevel .1.3.6.1.4.1.2.6.262";
|
|
($ret, $err) = session_exec($exp, "sed -i '/\"$snmpuser\"/ d' /var/lib/net-snmp/snmpd.conf");
|
|
($ret, $err) = session_exec($exp, "echo $msg1 >> $snmp_conf");
|
|
($ret, $err) = session_exec($exp, "echo $msg2 >> $snmp_conf");
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("Need to define user name and security level for snmpv3 configuration", $callback);
|
|
}
|
|
}
|
|
($ret, $err) = session_exec($exp, "echo '# $xCATSettingsEND' >> $snmp_conf");
|
|
|
|
#need to restart snmpd after config file changes
|
|
($ret, $err) = session_exec($exp, "ps | grep snmpd | grep -v grep | awk '{ print $1}' | xargs kill -9");
|
|
($ret, $err) = session_exec($exp, "/usr/sbin/snmpd -Lsd -Lf /dev/null -p /var/run/snmpd");
|
|
if (defined $err) {
|
|
xCAT::SvrUtils::sendmsg("Failed to configure snmp : $err", $callback);
|
|
}
|
|
|
|
|
|
$exp->hard_close();
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 netcfg_for_irpdu
|
|
|
|
change hostname and network setting for IR PDU.
|
|
|
|
=cut
|
|
#-------------------------------------------------------
|
|
sub netcfg_for_irpdu {
|
|
my $pdu = shift;
|
|
my $static_ip = shift;
|
|
my $discover_ip = shift;
|
|
my $request = shift;
|
|
my $subreq = shift;
|
|
my $callback = shift;
|
|
my $hostname;
|
|
my $ip;
|
|
my $gateway;
|
|
my $netmask;
|
|
|
|
my $extrargs = $request->{arg};
|
|
my @exargs = ($request->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs = @$extrargs;
|
|
}
|
|
|
|
#get user/password from pdu table if defined
|
|
#default password for irpdu
|
|
my $passwd = "1001";
|
|
my $username = "ADMIN";
|
|
|
|
if ($pduhash->{$pdu}->[0]->{username}) {
|
|
$username = $pduhash->{$pdu}->[0]->{username};
|
|
}
|
|
if ($pduhash->{$pdu}->[0]->{password}) {
|
|
$passwd = $pduhash->{$pdu}->[0]->{password};
|
|
}
|
|
|
|
my $timeout = 10;
|
|
my $send_change = "N";
|
|
|
|
my $login_ip;
|
|
|
|
# somehow, only system command works for checking if irpdu is pingable
|
|
# Net::Ping Module and xCAT::NetworkUtils::isPingable both are not working
|
|
if (system("ping -c 2 $static_ip") == 0 ) {
|
|
$login_ip = $static_ip;
|
|
} elsif (system("ping -c 2 $discover_ip") == 0) {
|
|
$login_ip = $discover_ip;
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg(" is not reachable", $callback,$pdu);
|
|
return;
|
|
}
|
|
|
|
foreach my $cmd (@exargs) {
|
|
my ($key, $value) = split(/=/, $cmd);
|
|
if ($key =~ /hostname/) {
|
|
$hostname = $value;
|
|
xCAT::SvrUtils::sendmsg("change pdu hostname to $hostname", $callback);
|
|
}
|
|
if ($key =~ /ip/) {
|
|
$ip = $value;
|
|
$send_change = "Y";
|
|
xCAT::SvrUtils::sendmsg("change ip address for $pdu to $ip", $callback);
|
|
}
|
|
if ($key =~ /gateway/) {
|
|
$gateway = $value;
|
|
$send_change = "Y";
|
|
xCAT::SvrUtils::sendmsg("change gateway for $pdu to $gateway", $callback);
|
|
}
|
|
if ($key =~ /netmask/) {
|
|
$netmask = $value;
|
|
$send_change = "Y";
|
|
xCAT::SvrUtils::sendmsg("change netmask for $pdu to $netmask", $callback);
|
|
}
|
|
|
|
}
|
|
|
|
my $login_cmd = "telnet $login_ip\r";
|
|
my $user_prompt = " Login: ";
|
|
my $pwd_prompt = "Password: ";
|
|
my $pdu_prompt = "Please Enter Your Selection => ";
|
|
my $send_zero = 0;
|
|
my $send_one = 1;
|
|
my $send_two = 2;
|
|
|
|
my $mypdu = new Expect;
|
|
|
|
$mypdu->debug(0);
|
|
$mypdu->log_stdout(0); # suppress stdout output..
|
|
|
|
unless ($mypdu->spawn($login_cmd))
|
|
{
|
|
$mypdu->soft_close();
|
|
xCAT::SvrUtils::sendmsg("Unable to run $login_cmd", $callback);
|
|
return;
|
|
}
|
|
my @result = $mypdu->expect(
|
|
$timeout,
|
|
[
|
|
$user_prompt,
|
|
sub {
|
|
$mypdu->clear_accum();
|
|
$mypdu->send("$username\r");
|
|
$mypdu->clear_accum();
|
|
$mypdu->exp_continue();
|
|
}
|
|
],
|
|
[
|
|
$pwd_prompt,
|
|
sub {
|
|
$mypdu->clear_accum();
|
|
$mypdu->send("$passwd\r");
|
|
$mypdu->clear_accum();
|
|
$mypdu->exp_continue();
|
|
}
|
|
],
|
|
[
|
|
$pdu_prompt,
|
|
sub {
|
|
$mypdu->clear_accum();
|
|
$mypdu->send("$send_one\r");
|
|
$mypdu->send("$send_one\r");
|
|
#change hostname
|
|
$mypdu->send("$send_one\r");
|
|
$mypdu->send("$hostname\r");
|
|
$mypdu->send("$send_zero\r");
|
|
#change network setting
|
|
$mypdu->send("$send_two\r");
|
|
$mypdu->send("$send_one\r");
|
|
$mypdu->send("$ip\r");
|
|
$mypdu->send("$gateway\r");
|
|
$mypdu->send("$netmask\r");
|
|
$mypdu->send("$send_change\r");
|
|
# go back Previous Menu
|
|
$mypdu->send("$send_zero\r");
|
|
$mypdu->send("$send_zero\r");
|
|
}
|
|
],
|
|
);
|
|
|
|
if (defined($result[1]))
|
|
{
|
|
my $errmsg = $result[1];
|
|
$mypdu->soft_close();
|
|
xCAT::SvrUtils::sendmsg("Failed expect command: $errmsg", $callback,$pdu);
|
|
return;
|
|
}
|
|
$mypdu->soft_close();
|
|
|
|
xCAT::SvrUtils::sendmsg("hostname or network setting changed, update node definition ", $callback);
|
|
xCAT::Utils->runxcmd({ command => ['chdef'], arg => ['-t','node','-o',$pdu,"otherinterfaces="] }, $subreq, 0, 1);
|
|
if ( (defined $ip) and ($static_ip ne $ip) ) {
|
|
xCAT::Utils->runxcmd({ command => ['chdef'], arg => ['-t','node','-o',$pdu,"ip=$ip",'status=configured'] }, $subreq, 0, 1);
|
|
xCAT::Utils->runxcmd({ command => ['makehosts'], node => [$pdu] }, $subreq, 0, 1);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
1;
|