Commit c946abb8 authored by Mike Hibler's avatar Mike Hibler

Add "status" command that can be applied to power controllers

(eg. "power status rpc14-1" or "power status all").

In rpc module, add a "status" function that is called to collect,
well..status.  This is used by the generic power code, but more importantly,
by the new "powermon" script (which could have just called "power status",
but no point in parsing output twice).
parent 68051c77
......@@ -105,13 +105,6 @@ if ($op =~ /^(on|off|cycle|status)$/) {
@machines = @ARGV;
foreach my $n (0..$#ARGV) {
$machines[$n] =~ s/^([-\@\w.]+)$/$1/;
# Shark hack
if ($machines[$n] =~ /^(sh\d+)-[1-8]$/) {
print "WARNING: Rebooting $machines[$n] will reboot all of shelf $1!\n";
$machines[$n] = $1;
}
# End shark hack
}
#
......@@ -128,6 +121,16 @@ foreach my $n (0..$#machines) {
#
print "do \"$op\" to @machines\n" if $verbose > 1;
#
# Handle the status command which is not a per-node operation and not
# allowed by anyone except admins.
#
if ($op eq "status") {
die("Only admins are allowed to query status\n")
if ($UID != 0 && !TBAdmin($UID));
exit(dostatus(@machines));
}
#
# ElabinElab is special; Do local permission checks, build up a node list
# and then contact the proxy to do the actual work. No perl bindings yet,
......@@ -331,3 +334,92 @@ foreach my $power_id (keys %outlets) {
# Return 0 on success. Return non-zero number of nodes that failed.
exit $exitval;
sub byname() {
my ($as, $an, $bs, $bn);
if ($a =~ /(.*[^\d])(\d+)$/) {
$as = $1; $an = $2;
} else {
$as = $a;
}
if ($b =~ /(.*[^\d])(\d+)$/) {
$bs = $1; $bn = $2;
} else {
$bs = $b;
}
$as cmp $bs || $an <=> $bn;
}
#
# Query the given controllers for their status
#
sub dostatus(@) {
my @wanted = @_;
my %ctrls = ();
my $errors = 0;
if ($ELABINELAB) {
warn "Cannot get status from inner elab\n";
return 1;
}
my $doall = (@wanted == 1 && $wanted[0] eq "all");
#
# Fetch all possible power controllers
#
my $result = DBQueryFatal("select n.node_id,t.type ".
"from nodes as n " .
"left join node_types as t on n.type=t.type " .
"where n.role='powerctrl'");
while (my ($ctrl, $type) = $result->fetchrow()) {
$ctrls{$ctrl} = $type;
}
@wanted = sort byname keys(%ctrls)
if ($doall);
#
# Loop through desired controllers getting status
#
for my $ctrl (@wanted) {
my %status;
if (!defined($ctrls{$ctrl})) {
warn "No such power controller '$ctrl', ignored\n";
$errors++;
next;
}
if ($ctrls{$ctrl} =~ /^RPC/) {
if (rpc27status($ctrl,\%status)) {
print "Could not get status for $ctrl.\n";
$errors++;
next;
}
print "$ctrl: ";
print $status{current}, " Amps, "
if $status{current};
print $status{power}, " Watts, "
if $status{power};
if (defined($status{tempF}) || defined($status{tempC})) {
my $temp = $status{tempF};
if (!defined($temp)) {
$temp = $status{tempC} * 9 / 5 + 32;
}
printf "%.1f F", $temp;
}
print "\n";
for my $outlet (1..20) {
my $ostr = "outlet$outlet";
print " Outlet $outlet: ", $status{$ostr}, "\n"
if (defined($status{$ostr}));
}
} elsif (!$doall) {
warn "Cannot get status for $ctrl (type " .
$ctrls{$ctrl} . ") yet\n";
$errors++;
}
}
return $errors;
}
......@@ -11,7 +11,7 @@ package power_rpc27;
use Exporter;
@ISA = ("Exporter");
@EXPORT = qw( rpc27ctrl );
@EXPORT = qw( rpc27status rpc27ctrl );
use Socket;
use IO::Handle;
......@@ -37,14 +37,40 @@ my $debug = 0;
# RPC27 Prompt string
my $RPC27_PROMPT = 'RPC-\d+>';
# RPC help message. Used as terminator for status fetch.
my $RPC27_HELPMSG = 'Type "Help" for a list of commands';
my %RPC27_CMD =
("cycle"=>"reboot",
"on" =>"on",
"off" =>"off");
("cycle" => "reboot",
"on" => "on",
"off" => "off");
sub rpc27status {
my ($controller, $statusp) = @_;
my($TIP, $i, $insync);
#
# Form the connection to the controller via a "tip" line to the
# capture process. Once we have that, we can just talk to the
# controller directly.
#
if (!($TIP = tipconnect($controller))) {
print STDERR "*** Could not form TIP connection to $controller\n";
return 1;
}
if (syncandsend($controller, $TIP, "status", $statusp)) {
return 1;
}
return 0;
}
# Main routine.
# usage: rpc27ctrl(cmd, controller, outlet)
# cmd = { "cycle" | "on" | "off" }
# cmd = { "cycle" | "on" | "off" | "status" }
# controller = <node_id>
# outlet = int, 1 <= outlet <= 20
#
......@@ -52,7 +78,6 @@ my %RPC27_CMD =
#
sub rpc27ctrl {
my($cmd, $controller, @outlets) = @_;
my($TIP, $i, $insync);
#
# Check parameters
......@@ -95,7 +120,7 @@ sub rpc27ctrl {
foreach my $outlet (@outlet_strings) {
my $command = "$RPC27_CMD{$cmd} $outlet";
if (syncandsend($controller,$TIP,$command)) {
if (syncandsend($controller,$TIP,$command,undef)) {
#
# On failure, syncandsend has already closed $TIP
#
......@@ -114,8 +139,8 @@ sub rpc27ctrl {
# the controller opened with tipconnect, and $command is the whole command
# (ie. 'reboot 20,40') to send.
#
sub syncandsend($$) {
my ($controller,$TIP,$cmd) = @_;
sub syncandsend($$$) {
my ($controller,$TIP,$cmd,$statusp) = @_;
#
# Send a couple of newlines to get the command prompt, and then wait
......@@ -135,17 +160,13 @@ sub syncandsend($$) {
return 1;
}
my $cc = 0;
while (1) {
if (sysread($TIP, $line, 1, $cc) == 0) {
print STDERR
"*** Power control sync read failed ".
"($controller/$outlet)\n";
close($TIP);
return 1;
}
$cc++;
last if ($line =~ /\n/ || $cc > 1023);
$line = rpc_readline($TIP);
if (!defined($line)) {
print STDERR
"*** Power control sync read failed ".
"($controller/$outlet)\n";
close($TIP);
return 1;
}
if ($debug) {
print "Read: $line";
......@@ -187,6 +208,42 @@ sub syncandsend($$) {
return 1;
}
# If status is desired, slurp and parse everything up to the next prompt
if ($statusp) {
my %status = ();
print "Getting status\n"
if ($debug);
while (my $line = rpc_readline($TIP)) {
print "Read: $line"
if ($debug);
# skip echoed prompt+command
if ($line =~ /status/) {
next;
}
# XXX cannot look for final prompt because there is no newline
if ($line =~ /$RPC27_HELPMSG/) {
last;
}
if ($line =~ /Temperature:\s+(\d+\.\d+) C/) {
$status{tempC} = $1;
} elsif ($line =~ /Average Power:\s+(\d+) Watts/) {
$status{power} = $1;
} elsif ($line =~ /True RMS Current:\s+(\d+\.\d+) Amps/) {
$status{current} = $1;
}
# note the /g, controllers with 20 ports put two outlets per line
elsif (my %ohash = $line =~ /Outlet\s+(\d+)\s+:\s+(On|Off)/g) {
for my $o (keys(%ohash)) {
my $outlet = "outlet$o";
$status{$outlet} = $ohash{$o};
}
}
}
%$statusp = %status;
print "Returning status\n"
if ($debug);
}
return 0;
}
......@@ -294,4 +351,20 @@ sub tipconnect($) {
return 0;
}
sub rpc_readline($)
{
my ($TIP) = @_;
my $line;
my $cc = 0;
while (1) {
if (sysread($TIP, $line, 1, $cc) == 0) {
return undef;
}
$cc++;
last if ($line =~ /\n/ || $cc > 1023);
}
return $line;
}
1;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment