initilo.pl.in 9.4 KB
Newer Older
1
2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2000-2013 University of Utah and the Flux Group.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
use English;
use Getopt::Std;
use Socket;
use IO::Handle;     # thousands of lines just for autoflush :-(
use POSIX ":sys_wait_h";

#
# Setup management interfaces for nodes, given a data file we get
# from HP.
#
sub usage()
{
Leigh B Stoller's avatar
Leigh B Stoller committed
36
    print STDERR "Usage: initilo.pl [-d] [-n] <ilopswd> <nodeid>\n";
37
    print STDERR "Usage: initilo.pl [-d] [-n] -b <nodeid>\n";
38
39
40
41
    print STDERR "Usage: initilo.pl [-d] [-n] -o <nodeid>\n";
    print STDERR "Usage: initilo.pl [-d] [-n] -c <ip> <ilopswd>\n";
    exit(-1);
}
Leigh B Stoller's avatar
Leigh B Stoller committed
42
my $optlist	= "dncobs";
43
44
45
46
my $debug	= 0;
my $impotent    = 0;
my $control     = 0;
my $dooutlet    = 0;
47
my $bootorder   = 0;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
my $tempfile    = "/tmp/$$.xml";

#
# Configure variables
#
my $TB	      = "@prefix@";
my $TBOPS     = "@TBOPSEMAIL@";
my $ILOPSWD   = "$TB/etc/ilo.pswd";
my $SRCDIR    = "@srcdir@";
my $CURL      = "/usr/local/bin/curl";
my $DSAKEY    = "/root/.ssh/id_dsa";
my $SUDO      = "/usr/local/bin/sudo";
my $WAP       = "$TB/sbin/withadminprivs";

# Protos
sub Fatal($);
sub ChangeBootOrder($);
sub SendXML($$);
sub SetupControlNode($$);
Leigh B Stoller's avatar
Leigh B Stoller committed
67
sub DoOutlet($);
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

# un-taint path
$ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/site/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use emdb;
use EmulabConstants;
use emutil;
use User;
use Node;
use Interface;

#
# Turn off line buffering on output
#
$| = 1;

#
# Parse command arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
    $debug = 1;
}
if (defined($options{"n"})) {
    $impotent = 1;
}
if (defined($options{"c"})) {
    $control = 1;
}
105
106
107
if (defined($options{"b"})) {
    $bootorder = 1;
}
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
if (defined($options{"o"})) {
    $dooutlet = 1;
}
Fatal("$ILOPSWD does not exist")
    if (! -e $ILOPSWD);

#
# Must be root if actually doing this.
#
if ($UID && !$impotent) {
    Fatal("This script must be run as root! Maybe use sudo?")
}

# This is the iLo password for the elabman user we create.
my $elabmanpswd = `cat $ILOPSWD`;
chomp($elabmanpswd);

# The XML goo.
my $setupgoo  = `cat $SRCDIR/setupilo.xml`;
my $resetgoo  = `cat $SRCDIR/resetilo.xml`;
my $chpswdgoo = `cat $SRCDIR/chpswd.xml`;
Leigh B Stoller's avatar
Leigh B Stoller committed
129
my $sshkeygoo = `cat $SRCDIR/addsshkey.xml`;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

# The pubkey.
my $pubkey = `cat ${DSAKEY}.pub`;
if ($?) {
    Fatal("Cannot read ${DSAKEY}.pub");
}
chomp($pubkey);

# Need to kill off the comment.
if ($pubkey =~ /^(ssh-dss\s+[^\ ]*)/) {
    $pubkey = $1;
}
if ($control) {
    usage()
	if (@ARGV != 2);

    exit(SetupControlNode($ARGV[0], $ARGV[1]));
}
Leigh B Stoller's avatar
Leigh B Stoller committed
148
149
150
151
152
153
if ($dooutlet) {
    usage()
	if (@ARGV != 1);

    exit(DoOutlet($ARGV[0]));
}
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
if ($bootorder) {
    usage()
	if (@ARGV != 1);

    my $node = Node->Lookup($ARGV[0]);
    if (!defined($node)) {
	Fatal("No such node");
    }
    # This has to be in the interfaces table already.
    my $management_interface = Interface->LookupManagement($node);
    if (!defined($management_interface)) {
	Fatal("No management interface for $node");
    }
    my $iloIP = $management_interface->IP();
    if (ChangeBootOrder($iloIP)) {
	Fatal("Failed to change the boot order on $iloIP")
    }
    exit(0);
}

174
175
176
177
178
179
180
181
182
183
184
185
usage()
    if (@ARGV != 2);

my $ilopswd = $ARGV[0];
my $node    = Node->Lookup($ARGV[1]);
if (!defined($node)) {
    Fatal("No such node");
}
my $node_id = $node->node_id();

# This has to be in the interfaces table already.
my $management_interface = Interface->LookupManagement($node);
Leigh B Stoller's avatar
Leigh B Stoller committed
186
if (!defined($management_interface)) {
187
188
189
190
191
192
193
194
195
196
    Fatal("No management interface for $node");
}
my $iloIP = $management_interface->IP();

#
# Before we create the interface, make sure we can install our
# login/password/key info on the ilo.
#
# Replace the appropriate parts of the XML goo.
#
197
my $xmlgoo = sprintf($setupgoo, $ilopswd, $elabmanpswd);
198
199
200
201
202
203
204
if (SendXML($iloIP, $xmlgoo)) {
    Fatal("Failed to send xmlgoo to $iloIP");
}

#
# The boot order cannot be changed via ribcl. What a pain.
#
205
if (0 && ChangeBootOrder($iloIP)) {
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
    Fatal("Failed to change the boot order on $iloIP")
}

#
# This changes the Administrator password. We do this cause it is
# an 8 digit number, and the login is well known. So eventually it
# can be guessed. 
#
# We set it the same as the elabman password for now, but that
# might change.
#
$xmlgoo = sprintf($chpswdgoo, $ilopswd, $elabmanpswd);
if (SendXML($iloIP, $xmlgoo)) {
    Fatal("Failed to send chpswd xml to $iloIP");
}
Leigh B Stoller's avatar
Leigh B Stoller committed
221
222
223
224
225

$xmlgoo = sprintf($sshkeygoo, $elabmanpswd, $pubkey, $pubkey);
if (SendXML($iloIP, $xmlgoo)) {
    Fatal("Failed to send addsshkey xml to $iloIP");
}
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
    
#
# This resets the ilo.
#
$xmlgoo = sprintf($resetgoo, $elabmanpswd);
if (SendXML($iloIP, $xmlgoo)) {
    Fatal("Failed to send xmlgoo to $iloIP");
}

#
# SSH over to change the boot order,
# The "expect" like stuff copied from power_ilo ...
#
sub ChangeBootOrder($)
{
    my ($ip) = @_;

    my @args = ("ssh", "-tt", "-i", ${DSAKEY}, "elabman\@${ip}");
    print "@args\n";

    return 0
	if ($impotent);
    
    if (! socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC)) {
	Fatal("socketpair failed: $!");
    }
    CHILD->autoflush(1);
    PARENT->autoflush(1);

    my $childpid = fork();
    if (! $childpid) {
	close(CHILD);

	#
	# Dup our descriptors to the parent, and exec the program.
	# The parent then talks to it read/write.
	#
	open(STDIN,  "<&PARENT") || die "Can't redirect stdin";
	open(STDOUT, ">&PARENT") || die "Can't redirect stdout";
	open(STDERR, ">&PARENT") || die "Can't redirect stderr";

	exec(@args);
	die("ssh exec failed\n");
    }
    close(PARENT);

    my @expect_seq = (['hpiLO-> ',"cd system1/bootconfig1"],
		      ['hpiLO-> ','show bootsource5'],
		      ['hpiLO-> ','set bootsource5 bootorder=1'],
		      ['hpiLO-> ','exit']);
    

    #
    # 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(CHILD,$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) {
		    if ($debug > 2) {
			print "read '$line' while looking for '$rval'\n";
		    }
		    elsif ($line ne '') {
			print "$line\n";
		    }
		}
		
		$line = '';
	    }
	    else {
		$line .= $char;
	    }

	    if ($line =~ /$rval$/) {
		print CHILD "$sval\r";
		print "sent '$sval'\n";
		$found = 1;
		last;
	    }
	}

	if (!$found) {
	    # some sort of error; try to kill off ssh
	    kill(15,$childpid);
	    return -16;
	}
    }
    close(CHILD);

    # make sure the local ssh dies:
    my $i = 5;
    my $dead = 0;
    while (--$i) {
	my $ret = waitpid($childpid,WNOHANG);
	if ($ret == -1 || $ret == $childpid) {
	    $dead = 1;
	    last;
	}
	sleep(1);
    }
    kill(KILL,$childpid) if (!$dead);
    
    return 0;
}

#
# Send some XML to the ribcl
#
sub SendXML($$)
{
    my ($ip, $xmlgoo) = @_;
    
    print $xmlgoo
	if ($debug);

    # Stick it into a file for curl.
    open(XML, ">$tempfile")
	or Fatal("Could not create $tempfile");
    print XML $xmlgoo;
    close(XML);

    #
    # Ship this off with curl. 
    #
    my $cmd = "$CURL -k --data-binary \@${tempfile} https://$ip/ribcl";
    print "$cmd\n";
    if (!$impotent) {
	my $output = emutil::ExecQuiet($cmd);
	if ($?) {
	    print $output;
	    Fatal("ribcl failed");
	}
	my @lines = split('\n', $output);
	while (@lines) {
	    my $line = shift(@lines);
	    print $line
		if ($debug);
	    if ($line =~ /^\s*STATUS="(\w*)"/) {
		my $status = hex($1);
		if ($status != 0) {
		    my $line = shift(@lines);
		    $line =~ s/\s*MESSAGE=//;
		    print "$line\n";
		    Fatal("ribcl failed");
		}
	    }
	}
    }
    unlink($tempfile)
	if (!$debug);

    return 0;
}

exit(0);

sub Fatal($)
{
    my ($msg) = @_;

    die("*** $0:\n".
	"    $msg\n");
}

#
# Setup the control node.
#
sub SetupControlNode($$)
{
    my ($ip, $ilopswd) = @_;
    
412
    my $xmlgoo = sprintf($setupgoo, $ilopswd, $elabmanpswd);
413
414
415
416
417
418
419
420
421
422
    if (SendXML($ip, $xmlgoo)) {
	Fatal("Failed to send setup xml to $ip");
    }
    $xmlgoo = sprintf($chpswdgoo, $ilopswd, $elabmanpswd);
    if (SendXML($ip, $xmlgoo)) {
	Fatal("Failed to send chpswd xml to $ip");
    }
    return 0;
}

Leigh B Stoller's avatar
Leigh B Stoller committed
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
sub DoOutlet($)
{
    my ($node_id) = @_;
    
    my $node = Node->Lookup($node_id);
    if (!defined($node)) {
	Fatal("No such node");
    }
    #
    # Add the outlet and authinfo.
    #
    $node->AddOutlet("ilo3", 0,
		     {"key_type"  => "ilo3",
		      "key_role"  => "ssh-key",
		      "key_uid"   => "elabman",
		      "key"       => $DSAKEY}) == 0
	  or Fatal("Could not add outlet record");

    return 0;
}