Commit 7bca0640 authored by David Johnson's avatar David Johnson

Add iLO power control. Uses passwd/pubkey auth, depending on what's in

the db, to slogin to the node's ilo interface (which has to be specified
in the interfaces table), then uses a simple expect-like sequence over a
pty to control power.
parent bc307111
......@@ -32,6 +32,7 @@ use power_sgmote;
use power_mail;
use power_whol;
use power_rmcp;
use power_ilo;
use snmpit_apc;
use libtestbed;
use User;
......@@ -289,7 +290,8 @@ foreach my $power_id (keys %outlets) {
my $class;
if ($power_id eq "mail" || $power_id =~ /^whol-/
|| $power_id=~ /^rmcp-/) {
|| $power_id=~ /^rmcp-/
|| $power_id eq 'ilo' || $power_id eq 'ilo2') {
$type = $power_id;
$IP = "";
$class = "";
......@@ -352,6 +354,11 @@ foreach my $power_id (keys %outlets) {
print "Control of $nodestr failed.\n"; ++$exitval;
++$errors;
}
} elsif ($type eq 'ilo2' || $type eq 'ilo') {
if (iloctrl($type,$op,@nodes)) {
print "Control of $nodestr failed.\n"; ++$exitval;
++$errors;
}
} elsif ($type eq "mail") {
if (mailctrl($op,@nodes)) {
print "Control of $nodestr failed.\n"; $exitval++;
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
#
# Handle iLO or iLO 2 remote power control.
# Node must have an interface such that role='other' and
# interface_type='ilo2' or 'ilo'.
#
# Supports either pubkey or passwd auth, depending on what's in db.
#
package power_ilo;
use Exporter;
@ISA = ("Exporter");
@EXPORT = qw( iloctrl );
use lib "@prefix@/lib";
use libdb;
use IO::Pty;
use POSIX qw(setsid);
my $debug = 0;
my $parallelize = 0;
# Turn off line buffering on output
$| = 1;
my %portinfo = ();
#
# usage: iloctrl(type, cmd, nodes)
# type = { "ilo" | "ilo2" }
# cmd = { "cycle" | "on" | "off" }
# nodes = list of one or more physical node names
#
# Returns 0 on success. Non-zero on failure.
#
sub iloctrl($$@) {
my ($type,$cmd,@nodes) = @_;
my $exitval = 0;
if ($debug) {
print "iloctrl called with $type,$cmd,(" . join(',',@nodes) . ")\n";
}
if ($cmd ne "cycle" && $cmd ne "on" && $cmd ne "off") {
warn "invalid power command '$cmd'; \n" .
" valid commands are 'cycle, 'off', and 'on'.\n";
return scalar(@nodes);
}
my %ilo_nodeinfo = ();
# grab ilo IP and auth info
foreach my $n (@nodes) {
my $res = DBQueryFatal("select IP from interfaces" .
" where node_id='$n' and role='other'" .
" and interface_type='$type'");
if (!defined($res) || !$res || $res->num_rows() == 0) {
warn "No $type interface for $n; cannot find iLO IP!\n";
++$exitval;
next;
}
my ($IP) = $res->fetchrow();
$res = DBQueryFatal("select key_role,key_uid,mykey" .
" from outlets_remoteauth" .
" where node_id='$n' and key_type='$type'");
if (!defined($res) || !$res || $res->num_rows() == 0) {
warn "No $type remote auth info for $n!\n";
++$exitval;
next;
}
my ($krole,$kuid,$kkey) = $res->fetchrow();
$ilo_nodeinfo{$n} = [ $IP,$krole,$kuid,$kkey ];
}
for my $n (keys(%ilo_nodeinfo)) {
my ($IP,$krole,$kuid,$kkey) = @{$ilo_nodeinfo{$n}};
if ($parallelize) {
if (my $pid = fork()) {
push @kids, $pid;
}
else {
my $tret = iloexec($n,$type,$cmd,$IP,$krole,$kuid,$kkey);
exit $tret;
}
}
else {
if (iloexec($n,$type,$cmd,$IP,$krole,$kuid,$kkey)) {
++$exitval;
}
}
# grab child exit vals
if ($parallelize) {
while (wait() > 0) {
if ($?) {
++$exitval;
}
}
}
}
return $exitval;
}
#
# Arguments: $node_id,$type,$cmd,$IP,$key_role,$key_uid,$key
#
sub iloexec($$$$$$$) {
my ($node_id,$type,$cmd,$IP,$key_role,$key_uid,$key) = @_;
if ($debug) {
print "iloexec called with (" . join(',',@_) . ")\n";
}
if (!defined($type) || !defined($cmd) || !defined($IP)
|| !defined($key_role) || !defined($key_uid) || !defined($key)) {
warn "Incomplete argument list, skipping node" .
(defined($node_id)?" $node_id":"");
return -1;
}
my $power_cmd;
if ($cmd eq 'cycle') {
$power_cmd = 'power reset';
}
elsif ($cmd eq 'reset') {
$power_cmd = 'power warm';
}
elsif ($cmd eq 'on') {
$power_cmd = 'power on';
}
elsif ($cmd eq 'off') {
$power_cmd = 'power off';
}
else {
warn "Bad iLO power command $cmd";
return -11;
}
if ($type ne 'ilo' && $type ne 'ilo2') {
warn "Unsupported iLO type $type!";
return -7;
}
my @expect_seq;
my $ssh_cmd = "ssh -l '$key_uid'";
if ($key_role eq 'ssh-key') {
if ($key ne '') {
$ssh_cmd .= " -i '$key'";
}
@expect_seq = (['hpiLO-> ',$power_cmd],['hpiLO-> ','exit']);
}
elsif ($key_role eq 'ssh-passwd') {
$ssh_cmd .= " -o PubkeyAuthentication=no";
$ssh_cmd .= " -o PasswordAuthentication=yes";
if ($key eq '') {
warn "iLO key_role ssh-passwd specified, but no passwd!";
return -13;
}
@expect_seq = (['password: ',$key],['hpiLO-> ',$power_cmd],
['hpiLO-> ','exit']);
}
else {
warn "Unsupported key_role $key_role!";
return -14;
}
$ssh_cmd .= " $IP";
my $pty = IO::Pty->new() || die "can't make pty: $!";
defined (my $pid = fork()) || die "fork: $!";
if (!$pid) {
# Flip to UID 0 to ensure we can read whatever private key we need
$EUID = 0;
if ($debug) {
print "Flipped to root: $EUID\n";
}
# Connect our kid to the tty so the parent can chat through the pty
POSIX::setsid();
$pty->make_slave_controlling_terminal();
my $tty = $pty->slave();
my $tty_fd = $tty->fileno();
close($pty);
open(STDIN,"<&$tty_fd");
open(STDOUT,">&$tty_fd");
open(STDERR,">&STDOUT");
close($tty);
# Don't want ssh to prompt us via ssh-askpass!
delete $ENV{DISPLAY};
exec("$ssh_cmd") || die "exec: $!";
}
#
# Talk to ssh over the pty: wait for expected output and send responses
#
my @lines = ();
foreach $es (@expect_seq) {
my ($rval,$sval) = @$es;
my $found = 0;
my $line = '';
while (1) {
my $char;
if (read($pty,$char,1) != 1) {
warn "Error in read in iLO pseudo expect loop!\n";
print "Had read the following lines:\n";
foreach my $ln (@lines) {
print " $ln\n";
}
last;
}
if ($char eq "\r" || $char eq "\n") {
push @lines,$line;
if ($debug) {
print "read '$line' while looking for '$rval'\n";
}
$line = '';
}
else {
$line .= $char;
}
if ($line =~ /$rval$/) {
print $pty "$sval\r\n";
if ($debug) {
print "sent '$sval'\n";
}
$found = 1;
last;
}
}
if (!$found) {
# some sort of error; try to kill off ssh
kill(15,$pid);
return -16;
}
}
# if we get here, things probably went ok...
return 0;
}
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