node_reboot.in 6.48 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2 3
#
# EMULAB-COPYRIGHT
4
# Copyright (c) 2000-2008 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
5 6
# All rights reserved.
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
7
use strict;
8 9 10 11
use English;
use Getopt::Std;

#
12 13
# Reboot a node (or nodes). Will power cycle the node as a last resort.
# Use -e option to reboot all nodes in an experiment.
14
#
15 16
# Exit value is 0 if all nodes reboot okay, or the number of nodes
# could not be rebooted.
17 18 19
#
sub usage()
{
20 21 22 23
    print(STDERR
	  "Usage: node_reboot [-d] [-f] [-w] [-k] node [node ...]\n" .
	  "       node_reboot [-d] [-f] [-w] [-k] -e pid,eid\n".
	  "Use the -d option to turn on debugging\n" .
24
	  "Use the -s option to turn on silent mode\n" .
25 26 27
	  "Use the -e option to reboot all the nodes in an experiment\n" .
	  "Use the -w option to to wait for nodes is come back up\n" .
	  "Use the -k option to power cycle nodes in PXEWAIT mode\n" .
28
	  "Use the -b option to reboot nodes in PXEWAIT mode\n" .
29 30 31
	  "Use the -a option to reboot all free nodes\n".
	  "Use the -c option to reconfig nodes instead of rebooting\n".
	  "Use the -f option to power cycle (and not wait for nodes to die)\n");
32 33
    exit(-1);
}
34 35
# The hidden -r option runs this in "realmode", ie don't send an event, but
# really do the work instead.
36
my $optlist     = "dfe:wrkacbps";
37
my $debug       = 0;
38
my $silent      = 0;
39 40 41 42 43
my $powercycle  = 0;
my $waitmode    = 0;
my $realmode    = 1; # XXX Temporary, until we make event sending the default.
my $killmode    = 0;
my $reconfig    = 0;
44
my $rebootmode  = 0;
45
my $prepare     = 0;
46 47 48 49 50

#
# Configure variables
#
my $TB		= "@prefix@";
51
my $CLIENT_BIN  = "@CLIENT_BINDIR@";
52 53

# Locals
Leigh B. Stoller's avatar
Leigh B. Stoller committed
54
my @nodes	= ();
55 56

#
57
# Testbed Support libraries
58
#
59 60
use lib "@prefix@/lib";
use libdb;
61
use libreboot;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
62 63 64
use Experiment;
use Node;
use User;
65 66 67 68 69 70

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

# Turn off line buffering on output
Mac Newbold's avatar
Mac Newbold committed
71
$| = 1;
72 73 74 75 76

#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
77 78
    die("*** $0:\n".
        "    Must be root! Maybe its a development version?\n");
79 80
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
81 82 83
#
# Verify user and get his DB uid and other info for later.
#
84 85 86 87 88 89 90
my $this_user;
if ($UID) {
    $this_user = User->ThisUser();
    if (! defined($this_user)) {
	die("*** $0:\n".
	    "    You ($UID) do not exist!\n");
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
91 92
}

93 94 95 96
#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
97
my %options = ();
98 99 100 101
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
102 103
    $debug = 1;
}
104 105 106
if (defined($options{"s"})) {
    $silent = 1;
}
107 108 109
if (defined($options{"b"})) {
    $rebootmode = 1;
}
110
if (defined($options{"f"})) {
111
    $powercycle = 1;
112
}
113 114 115
if (defined($options{"k"})) {
    $killmode = 1;
}
116 117 118
if (defined($options{"w"})) {
    $waitmode = 1;
}
119 120 121
if (defined($options{"p"})) {
    $prepare = 1;
}
122 123
if (defined($options{"r"})) {
    $realmode = 1;
124
}
125
if (defined($options{"c"})) {
126 127
    $reconfig = 1;
}
128

129
if (defined($options{"a"})) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
130 131 132
    usage()
	if (@ARGV);
    
133 134 135
    #
    # Reboot all free nodes
    #
Leigh B. Stoller's avatar
Leigh B. Stoller committed
136
    if ($UID && !$this_user->IsAdmin()) {
137 138
	die("*** $0:\n".
	    "    You not have permission to reboot all free nodes!\n");
139 140 141 142 143 144 145 146 147 148
    }

    my $query_result =
	DBQueryFatal("select n.node_id from nodes as n ".
		     "left join reserved as r on r.node_id=n.node_id ".
		     "left join node_types as nt on nt.type=n.type ".
		     "where nt.class='pc' and n.role='testnode' and ".
		     "      r.pid is NULL");

    if ($query_result->numrows == 0) {
149 150
	die("*** $0:\n".
	    "    There are no free nodes to reboot\n");
151
    }
152

153 154
    while (my ($nodeid) = $query_result->fetchrow_array()) {
	push(@nodes, $nodeid);
155 156
    }
}
157
elsif (defined($options{"e"})) {
158
    #
159
    # Reboot all nodes in an experiment
160
    #
Leigh B. Stoller's avatar
Leigh B. Stoller committed
161 162
    usage()
	if (@ARGV);
163

Leigh B. Stoller's avatar
Leigh B. Stoller committed
164 165
    my $experiment = Experiment->Lookup($options{"e"});
    if (!defined($experiment)) {
166
	die("*** $0:\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
167
	    "    Unknown experiment!\n");
168
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
169 170

    #
171 172 173
    # Verify permission to muck with this experiment. This is to head off
    # permission problems early; the nodes are indvidually checked later
    # in the library.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
174
    #
Leigh B. Stoller's avatar
Leigh B. Stoller committed
175 176
    if ($UID && !$this_user->IsAdmin() &&
	! $experiment->AccessCheck($this_user, TB_EXPT_MODIFY)) {
177
	die("*** $0:\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
178
	    "    You not have permission to reboot nodes in $experiment!\n");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
179
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
180 181
    my @nodelist = $experiment->NodeList();
    if (! @nodelist) {
182
	die("*** $0:\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
183
            "    There are no nodes reserved in $experiment\n");
184
    }
185
    
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    #
    # If this is a firewalled experiment, don't reboot the firewall.
    # If you want to reboot the firewall, you have to do it individually.
    #
    # Two reasons.  One is that the firewall is not conceptually part of
    # the experiment and the user should not "be aware" of it.  This
    # is a pretty lame reason because pretty much everywhere else, the
    # firewall IS part of the experiment.  That leads to reason number
    # two: rebooting the firewall causes all other nodes in the experiment
    # to become disconnected until the firewall reboots.  For some machines,
    # that are also rebooting as you recall, not getting PXE info for a
    # significant amount of time causes them to fail to the next boot.
    # For some machines this might mean halting ("Strike any key to continue"),
    # as there is no other boot possibility.  This means ya gotta come back
    # later and reboot all those nodes again.
    #
    my $firewall = "";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
203 204 205 206
    if ($experiment->IsFirewalled()) {
	$experiment->FirewallAndPort(\$firewall, undef) == 0 or
	    die("*** $0:\n".
		"    Could not get the firewall node for $experiment\n");
207
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
208 209 210
    foreach my $node (@nodelist) {
	push(@nodes, $node->node_id())
	    if ($node->node_id() ne $firewall);
211 212 213
    }
}
else {
214 215
    #
    # Reboot nodes listed on command line.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
216 217 218
    #
    usage()
	if (!@ARGV);
219

Leigh B. Stoller's avatar
Leigh B. Stoller committed
220 221 222 223 224
    foreach my $n (@ARGV) {
	my $node = Node->Lookup($n);
	if (!defined($node)) {
	    die("*** $0:\n".
		"    Node $n does not exist!\n");
Mac Newbold's avatar
Mac Newbold committed
225
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
226 227 228

	if ($UID && !$this_user->IsAdmin() &&
	    ! $node->AccessCheck($this_user, TB_NODEACCESS_REBOOT)) {
229
	    die("*** $0:\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
230
		"    You are not allowed to reboot $node!\n");
231
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
232
	push(@nodes, $node->node_id());
233 234 235
    }
}

236
#
237
# Okay, call into the library using a hash of arguments.
238
#
239 240
my %args   = ();
my %status = ();
241

242
$args{'debug'}       = $debug;
243
$args{'silent'}      = $silent;
244
$args{'powercycle'}  = $powercycle;
245
$args{'rebootmode'}  = $rebootmode;
246 247 248 249
$args{'waitmode'}    = $waitmode;
$args{'realmode'}    = $realmode;
$args{'killmode'}    = $killmode;
$args{'reconfig'}    = $reconfig;
250
$args{'prepare'}     = $prepare;
251
$args{'nodelist'}    = [ @nodes ];
252

253
exit(nodereboot(\%args, \%status));