sched_reload.in 8.3 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh Stoller's avatar
Leigh Stoller committed
2 3 4

#
# EMULAB-COPYRIGHT
5
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
Leigh Stoller's avatar
Leigh Stoller committed
6 7 8
# All rights reserved.
#

9
use English;
10
use Getopt::Std;
11 12 13 14 15 16 17
   
#
# Schedule the reloading of a disk partition on a node. If the node is
# currently not reserved, start the loading now after reserving it to 
# testbed:reloading. Otherwise, put the right info into the database, and 
# nfree will do it when the node gets freed.
# 
18 19
sub usage()
{
20
    print STDOUT "Usage: sched_reload [-f | -n] [-m <imageid>] ".
21
	         "[[-p <pid>] -i <imagename>] <node> [node ...]\n".
22
		 "       sched_reload <options> -e pid,eid\n".
23
		 "       sched_reload <options> -t type [type ...]\n".
24
		 "       sched_reload <options> -c class\n".
25 26
	"Use -i to specify an image name. Use node default otherwise.\n".
	"Use -m to specify an image ID (internal name, TB admins only!).\n".
Leigh Stoller's avatar
Leigh Stoller committed
27
	"Use -f to force reload. Fail if node cannot be reserved.\n".
28
	"Use -n to pend reload for the reload daemon.\n".
29
        "Use -e to schedule a reload for all nodes in an experiment.\n".
30 31
        "Use -t to schedule a reload for all nodes of a particular type.\n".
        "Use -c to schedule a reload for all nodes of a particular class.\n";
32 33
    exit(-1);
}
34
my  $optlist = "fnp:i:e:m:tc:";
35 36 37 38

#
# Configure variables
#
39
my $TB     = "@prefix@";
40

41 42 43
#
# Load the Testbed support stuff. 
#
44 45
use lib "@prefix@/lib";
use libdb;
46

47
#
48
# These come from the library.
49
# 
50 51 52
my $RELOADPID	= NODERELOADING_PID;
my $RELOADEID	= NODERELOADING_EID;
my $PENDINGEID	= NODERELOADPENDING_EID;
53

Leigh Stoller's avatar
Leigh Stoller committed
54
my $osload      = "$TB/bin/os_load -s";
55
my $nalloc      = "$TB/bin/nalloc";
56
my $name        = "";
57 58 59
my $error       = 0;
my $debug       = 0;
my $force	= 0;
60
my $pend	= 0;
61
my @nodes       = ();
62
my $type	= TB_DEFAULT_RELOADTYPE;
Leigh Stoller's avatar
Leigh Stoller committed
63
my $usedefault  = 1;
64
my $imagename;
Leigh Stoller's avatar
Leigh Stoller committed
65
my $imageid;
66
my $imagepid    = TB_OPSPID;
Leigh Stoller's avatar
Leigh Stoller committed
67
my %imagenodes  = ();
68
my @row;
69
my $eidmode     = 0;
70
my $typemode    = 0;
71
my $classmode   = 0;
72
my @types	= ();
73
my $class;
74 75
my $pid;
my $eid;
76 77 78 79 80 81 82 83

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

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

#
84 85 86 87 88 89 90 91
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"f"})) {
92
    $force = 1;
93 94
}
if (defined($options{"n"})) {
95
    $pend = 1;
96
}
97 98 99
if ($pend and $force) {
    usage();
}
100 101 102
if (defined($options{"i"}) && defined($options{"m"})) {
    usage();
}
Leigh Stoller's avatar
Leigh Stoller committed
103
if (defined($options{"i"})) {
104
    $imagename  = $options{"i"};
Leigh Stoller's avatar
Leigh Stoller committed
105 106
    $usedefault = 0;

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    if ($imagename =~ /^([-\w\.\+]+)$/) {
	$imagename = $1;
    }
    else {
	die("Bad data in $imagename.");
    }
    
    if (defined($options{"p"})) {
	$imagepid = $options{"p"};
	
	if ($imagepid =~ /^([-\w\.\+]+)$/) {
	    $imagepid = $1;
	}
	else {
	    die("*** Bad data in $imagepid.\n");
	}
    }
}
if (defined($options{"m"})) {
    $imageid = $options{"m"};
    $usedefault = 0;

    if ($imageid =~ /^([-\w\.\+]+)$/) {
Leigh Stoller's avatar
Leigh Stoller committed
130 131 132
	$imageid = $1;
    }
    else {
133
	die("*** Bad data in $imageid\n");
Leigh Stoller's avatar
Leigh Stoller committed
134 135
    }
}
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
if (defined($options{"e"})) {
    if (@ARGV) {
	usage();
    }
    
    $eidmode = $options{"e"};
    if ($eidmode =~ /([-\w]*),([-\w]*)/) {
	$pid = $1;
	$eid = $2;
    }
    else {
	print STDOUT "Invalid argument to -e option: $eidmode\n";
	usage();
    }
}
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
elsif (defined($options{"t"})) {
    if (!@ARGV) {
	usage();
    }
    
    $typemode = 1;

    #
    # Untaint types,
    #
    foreach my $type ( @ARGV ) {
	if ($type =~ /^([-\w]+)$/) {
	    $type = $1;
	}
	else {
	    die("Bad type name: $type.");
	}
    
	push(@types, $type);
    }
}
172 173 174 175 176 177 178 179 180 181 182
elsif (defined($options{"c"})) {
    $classmode = 1;
    $class     = $options{"c"};

    if ($class =~ /^([-\w]+)$/) {
	$class = $1;
    }
    else {
	die("*** Bad data in $class\n");
    }
}
183 184 185 186 187
else {
    if (@ARGV < 1) {
	usage();
    }
}
188

189 190 191 192
if ($eidmode) {
    if (! (@nodes = ExpNodes($pid, $eid))) {
	die("*** $0:\n".
	    "    There are no nodes allocated to experiment $pid/$eid!");
193
    }
194
}
195 196 197 198 199 200 201 202 203 204 205
elsif ($typemode) {
    foreach my $type ( @types ) {
	my $query_result =
	    DBQueryFatal("select node_id from nodes ".
			 "where type='$type' and role='testnode'");

	while (my ($node) = $query_result->fetchrow_array()) {
	    push(@nodes, $node);
	}
    }
}
206 207 208 209 210 211 212 213 214 215
elsif ($classmode) {
    my $query_result =
	    DBQueryFatal("select n.node_id from nodes as n ".
			 "left join node_types as nt on n.type=nt.type ".
			 "where nt.class='$class' and n.role='testnode'");

    while (my ($node) = $query_result->fetchrow_array()) {
	push(@nodes, $node);
    }
}
216 217 218 219 220 221 222 223 224 225 226
else {
    #
    # Untaint nodes.
    #
    foreach my $node ( @ARGV ) {
	if ($node =~ /^([-\@\w]+)$/) {
	    $node = $1;
	}
	else {
	    die("Bad node name: $node.");
	}
227
    
228 229
	push(@nodes, $node);
    }
230
}
231

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
#
# VIRTNODE HACK: Virtual nodes are special. Do not reload!
#
my @temp = ();
foreach my $node ( @nodes ) {
    if (TBIsNodeVirtual($node)) {
	print "*** Skipping virtual node $node ...\n";
	next;
    }
    push(@temp, $node);
}
@nodes = @temp;
if (! @nodes) {
    print "No nodes to load. Exiting ...\n";
    exit(0);
}

249
#
250 251
# Root and admin types can do whatever they want.
# Mere users cannot schedule reloads.
252
#
253
if ($UID && !TBAdmin($UID)) {
254 255
    die("*** $0:\n".
	"     Only root or TB administrators can schedule disk reloads.\n");
256 257 258
}

#
259
# Confirm a valid imagename if one was specified. Otherwise we are going
Leigh Stoller's avatar
Leigh Stoller committed
260
# to pull it out of the DB for each node.
261
#
262 263
if (defined($imagename)) {
    if (! ($imageid = TBImageID($imagepid, $imagename))) {
264
	die("*** $0:\n".
265
	    "    No such image $imagename in project $imagepid!\n");
Leigh Stoller's avatar
Leigh Stoller committed
266
    }
267 268 269
}

#
270
# Loop through each node.
271 272 273 274
# 
my @load_list=();
foreach my $node (@nodes) {
    my $pc = $node;
275
    my $allocated = 0;
276

277
    $sth = DBQueryFatal("select node_id from nodes where node_id='$pc'");
278
    if ($sth->num_rows() != 1) {
279
      print STDERR "Node $pc does not exist. Skipping $pc.\n";
280 281
      next;
    }
Leigh Stoller's avatar
Leigh Stoller committed
282 283 284 285 286

    #
    # Get default imageid for this node if none specified on comand line.
    #
    if ($usedefault) {
287 288 289 290
	if (! DefaultImageID($node, \$imageid) || !defined($imageid)) {
	    print STDERR
		"*** Node $pc does not have a default imageid. Skipping!\n";
	    next;
Leigh Stoller's avatar
Leigh Stoller committed
291 292
	}
    }
293
    
294
    print STDERR "Checking if $pc is reserved...";
295
    $sth = DBQueryFatal("select node_id from reserved where node_id='$pc'");
296
   
297 298
    if ( ($sth->num_rows()) < 1) {
        print STDERR "Available.\nReserving and adding to list.\n";
299 300 301 302 303 304 305 306 307
	my $eid;
	if ($pend) {
	    $eid = $PENDINGEID;
	}
	else {
	    $eid = $RELOADEID;
	}
	my $cmd = "$nalloc $RELOADPID $eid $pc";

308 309 310
        if ( system($cmd) != 0 ) {
	    print STDERR "WARNING: Could not reserve $pc!\n";
	} else {
311 312 313 314
	    #
	    # Kill the last_reservation so that whoever gets the node next
	    # won't be fooled into thinking a reload is required.
	    #
315
	    DBQueryFatal("delete from last_reservation where node_id='$pc'");
316
	    $allocated = 1;
317 318 319 320
	}
    } else {
        print STDERR "Reserved.\n";      
    }
321 322 323 324 325 326 327 328 329 330

    #
    # If force and not able to reserve, do not pend a reload.
    # 
    if ($force && !$allocated) {
	$error++;
	next;
    }

    # Put it in the reloads table so TMCD knows to free it.
331 332 333 334 335
    if (! TBSetSchedReload($pc, $imageid, $type)) {
	die("*** $0:\n".
	    "    Could not set scheduled reload for $pc!"); 
    }
    
Leigh Stoller's avatar
Leigh Stoller committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    #
    # The point of this hash table is so that we can gather up all the
    # nodes per imageid and issue single requests to os_load, so that it
    # can optimize things (say, for Frisbee). It is possible to get multiple
    # imageids when using the node defaults instead of a command line imageid.
    #
    # Note that building up a hashed array of lists is a mighty odd operation
    # in PERL, hence this funny looking code!
    #
    if ($allocated) {
	if (! defined($imagenodes{$imageid})) {
	    $imagenodes{$imageid} = [ $node ];
	}
	else {
	    push(@{ $imagenodes{$imageid} }, $node);
	}
    }
353 354
}

355
if ($pend) {
Leigh Stoller's avatar
Leigh Stoller committed
356
    print STDOUT "Reload Scheduling Done! There were $error failures!\n";
357 358 359
    exit $error;
}

Leigh Stoller's avatar
Leigh Stoller committed
360 361 362 363 364 365 366
#
# Now issue the reloads, one per imageid. The hash table we created above
# stores a list of nodes for each imageid.
#
foreach my $id ( keys(%imagenodes) ) {
    my @nodelist = @{ $imagenodes{$imageid} };

367
    my $cmd = "$osload -m $id @nodelist";
Leigh Stoller's avatar
Leigh Stoller committed
368 369 370 371 372 373

    print "Issuing $cmd\n";
    if (system($cmd)) {
	print STDERR "*** WARNING: Failed $cmd\n";
	$error++;
    }
374 375
}

Leigh Stoller's avatar
Leigh Stoller committed
376
print STDOUT "Reload Scheduling Done! There were $error failures!\n";
377
exit $error;