node_reboot.in 7.52 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh Stoller's avatar
Leigh Stoller committed
2
#
3
# Copyright (c) 2000-2010 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
# 
# {{{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/>.
# 
# }}}
Leigh Stoller's avatar
Leigh Stoller committed
23
#
Leigh Stoller's avatar
Leigh Stoller committed
24
use strict;
25 26 27 28
use English;
use Getopt::Std;

#
29 30
# Reboot a node (or nodes). Will power cycle the node as a last resort.
# Use -e option to reboot all nodes in an experiment.
31
#
32 33
# Exit value is 0 if all nodes reboot okay, or the number of nodes
# could not be rebooted.
34 35 36
#
sub usage()
{
37 38 39 40
    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" .
41
	  "Use the -s option to turn on silent mode\n" .
42 43 44
	  "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" .
45
	  "Use the -b option to reboot nodes in PXEWAIT mode\n" .
46 47
	  "Use the -a option to reboot all free nodes\n".
	  "Use the -c option to reconfig nodes instead of rebooting\n".
48 49
	  "Use the -f option to power cycle (and not wait for nodes to die)\n".
	  "Use the -F option to reboot even if the type is marked nonrebootable\n");
50 51
    exit(-1);
}
52
# The hidden -r option runs this in "realmode", ie don't send an event, but
53
# really do the work instead.  Hidden -W option specifies the waittime. 
54
my $optlist     = "dfe:wrkacbpsW:F";
55
my $debug       = 0;
56
my $silent      = 0;
57 58 59 60 61
my $powercycle  = 0;
my $waitmode    = 0;
my $realmode    = 1; # XXX Temporary, until we make event sending the default.
my $killmode    = 0;
my $reconfig    = 0;
62
my $rebootmode  = 0;
63
my $prepare     = 0;
64
my $force       = 0;
65
my $waittime;
66 67 68 69 70

#
# Configure variables
#
my $TB		= "@prefix@";
71
my $CLIENT_BIN  = "@CLIENT_BINDIR@";
72 73

# Locals
Leigh Stoller's avatar
Leigh Stoller committed
74
my @nodes	= ();
75 76

#
77
# Testbed Support libraries
78
#
79 80
use lib "@prefix@/lib";
use libdb;
81
use libreboot;
Leigh Stoller's avatar
Leigh Stoller committed
82 83 84
use Experiment;
use Node;
use User;
85 86 87 88 89 90

# 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
91
$| = 1;
92 93 94 95 96

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

Leigh Stoller's avatar
Leigh Stoller committed
101 102 103
#
# Verify user and get his DB uid and other info for later.
#
104 105 106 107 108 109 110
my $this_user;
if ($UID) {
    $this_user = User->ThisUser();
    if (! defined($this_user)) {
	die("*** $0:\n".
	    "    You ($UID) do not exist!\n");
    }
Leigh Stoller's avatar
Leigh Stoller committed
111 112
}

113 114 115 116
#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
Leigh Stoller's avatar
Leigh Stoller committed
117
my %options = ();
118 119 120 121
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
122 123
    $debug = 1;
}
124 125 126
if (defined($options{"s"})) {
    $silent = 1;
}
127 128 129
if (defined($options{"b"})) {
    $rebootmode = 1;
}
130
if (defined($options{"f"})) {
131
    $powercycle = 1;
132
}
133 134 135
if (defined($options{"k"})) {
    $killmode = 1;
}
136 137 138
if (defined($options{"w"})) {
    $waitmode = 1;
}
139 140 141
if (defined($options{"p"})) {
    $prepare = 1;
}
142 143
if (defined($options{"r"})) {
    $realmode = 1;
144
}
145
if (defined($options{"c"})) {
146 147
    $reconfig = 1;
}
148 149 150
if (defined($options{"W"})) {
    $waittime = $options{"W"};
}
151 152 153
if (defined($options{"F"})) {
    $force = 1;
}
154

155
if (defined($options{"a"})) {
Leigh Stoller's avatar
Leigh Stoller committed
156 157 158
    usage()
	if (@ARGV);
    
159 160 161
    #
    # Reboot all free nodes
    #
Leigh Stoller's avatar
Leigh Stoller committed
162
    if ($UID && !$this_user->IsAdmin()) {
163 164
	die("*** $0:\n".
	    "    You not have permission to reboot all free nodes!\n");
165 166 167 168 169 170 171 172 173 174
    }

    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) {
175 176
	die("*** $0:\n".
	    "    There are no free nodes to reboot\n");
177
    }
178

179 180
    while (my ($nodeid) = $query_result->fetchrow_array()) {
	push(@nodes, $nodeid);
181 182
    }
}
183
elsif (defined($options{"e"})) {
184
    #
185
    # Reboot all nodes in an experiment
186
    #
Leigh Stoller's avatar
Leigh Stoller committed
187 188
    usage()
	if (@ARGV);
189

Leigh Stoller's avatar
Leigh Stoller committed
190 191
    my $experiment = Experiment->Lookup($options{"e"});
    if (!defined($experiment)) {
192
	die("*** $0:\n".
Leigh Stoller's avatar
Leigh Stoller committed
193
	    "    Unknown experiment!\n");
194
    }
Leigh Stoller's avatar
Leigh Stoller committed
195 196

    #
197 198 199
    # 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 Stoller's avatar
Leigh Stoller committed
200
    #
Leigh Stoller's avatar
Leigh Stoller committed
201 202
    if ($UID && !$this_user->IsAdmin() &&
	! $experiment->AccessCheck($this_user, TB_EXPT_MODIFY)) {
203
	die("*** $0:\n".
Leigh Stoller's avatar
Leigh Stoller committed
204
	    "    You not have permission to reboot nodes in $experiment!\n");
Leigh Stoller's avatar
Leigh Stoller committed
205
    }
206
    my @nodelist = $experiment->NodeList(0, 1);
Leigh Stoller's avatar
Leigh Stoller committed
207
    if (! @nodelist) {
208
	die("*** $0:\n".
Leigh Stoller's avatar
Leigh Stoller committed
209
            "    There are no nodes reserved in $experiment\n");
210
    }
211
    
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    #
    # 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 Stoller's avatar
Leigh Stoller committed
229 230 231 232
    if ($experiment->IsFirewalled()) {
	$experiment->FirewallAndPort(\$firewall, undef) == 0 or
	    die("*** $0:\n".
		"    Could not get the firewall node for $experiment\n");
233
    }
Leigh Stoller's avatar
Leigh Stoller committed
234 235 236
    foreach my $node (@nodelist) {
	push(@nodes, $node->node_id())
	    if ($node->node_id() ne $firewall);
237 238 239
    }
}
else {
240 241
    #
    # Reboot nodes listed on command line.
Leigh Stoller's avatar
Leigh Stoller committed
242 243 244
    #
    usage()
	if (!@ARGV);
245

Leigh Stoller's avatar
Leigh Stoller committed
246 247 248 249 250
    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
251
	}
Leigh Stoller's avatar
Leigh Stoller committed
252 253 254

	if ($UID && !$this_user->IsAdmin() &&
	    ! $node->AccessCheck($this_user, TB_NODEACCESS_REBOOT)) {
255
	    die("*** $0:\n".
Leigh Stoller's avatar
Leigh Stoller committed
256
		"    You are not allowed to reboot $node!\n");
257
	}
Leigh Stoller's avatar
Leigh Stoller committed
258
	push(@nodes, $node->node_id());
259 260 261
    }
}

262
#
263
# Okay, call into the library using a hash of arguments.
264
#
265 266
my %args   = ();
my %status = ();
267

268
$args{'debug'}       = $debug;
269
$args{'silent'}      = $silent;
270
$args{'powercycle'}  = $powercycle;
271
$args{'rebootmode'}  = $rebootmode;
272 273 274 275
$args{'waitmode'}    = $waitmode;
$args{'realmode'}    = $realmode;
$args{'killmode'}    = $killmode;
$args{'reconfig'}    = $reconfig;
276
$args{'prepare'}     = $prepare;
277
$args{'waittime'}    = $waittime     if defined $waittime;
278
$args{'force'}       = $force;
279
$args{'nodelist'}    = [ @nodes ];
280

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