sched_reload.in 9.58 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh Stoller's avatar
Leigh Stoller committed
2
#
3
# Copyright (c) 2000-2016 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
use English;
26
use Getopt::Std;
27 28 29 30 31 32 33
   
#
# 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.
# 
34 35
sub usage()
{
36
    print STDOUT "Usage: sched_reload [-f | -n] [-m <imageid>] ".
37
	         "[[-p <pid>] -i <imagename>] <node> [node ...]\n".
38
		 "       sched_reload <options> -e pid,eid\n".
39
		 "       sched_reload <options> -t type [type ...]\n".
40
		 "       sched_reload <options> -c class\n".
41 42 43
	"Use -i to specify a comma seperated list of image IDs.\n".
	"       Use the node default otherwise.\n".
	"Use -m to specify the internal name(s) if an image ID.\n".
Leigh Stoller's avatar
Leigh Stoller committed
44
	"Use -f to force reload. Fail if node cannot be reserved.\n".
45
	"Use -n to pend reload for the reload daemon.\n".
46
        "Use -e to schedule a reload for all nodes in an experiment.\n".
47 48
        "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";
49 50
    exit(-1);
}
51
my  $optlist = "fnp:i:e:m:tc:";
52 53 54 55

#
# Configure variables
#
56
my $TB     = "@prefix@";
57

58 59 60
#
# Load the Testbed support stuff. 
#
61 62
use lib "@prefix@/lib";
use libdb;
Leigh Stoller's avatar
Leigh Stoller committed
63 64 65
use Project;
use User;
use Node;
66
use OSImage;
Leigh Stoller's avatar
Leigh Stoller committed
67
use Experiment;
68

69
#
70
# These come from the library.
71
# 
72 73 74
my $RELOADPID	= NODERELOADING_PID;
my $RELOADEID	= NODERELOADING_EID;
my $PENDINGEID	= NODERELOADPENDING_EID;
75

Leigh Stoller's avatar
Leigh Stoller committed
76
my $osload      = "$TB/bin/os_load -s";
77
my $nalloc      = "$TB/bin/nalloc";
78
my $name        = "";
79 80 81
my $error       = 0;
my $debug       = 0;
my $force	= 0;
82
my $pend	= 0;
83
my @nodes       = ();
84
my $type	= TB_DEFAULT_RELOADTYPE;
Leigh Stoller's avatar
Leigh Stoller committed
85 86
my $project;
my $experiment;
87
my @images      = ();
Leigh Stoller's avatar
Leigh Stoller committed
88
my %imagenodes  = ();
89 90 91 92 93 94 95

# 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

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

#
# Mere users cannot schedule reloads.
#
if ($UID && !$this_user->IsAdmin()) {
    die("*** $0:\n".
	"     Only root or TB administrators can schedule disk reloads.\n");
}

113
#
114 115 116
# Parse command arguments. Once we return from getopts, all that should be
# 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{"f"})) {
122
    $force = 1;
123 124
}
if (defined($options{"n"})) {
125
    $pend = 1;
126
}
127 128 129
if ($pend and $force) {
    usage();
}
130 131 132
if (defined($options{"i"}) && defined($options{"m"})) {
    usage();
}
Leigh Stoller's avatar
Leigh Stoller committed
133

Leigh Stoller's avatar
Leigh Stoller committed
134 135 136 137
#
# Find the image (if not the default).
#
if (defined($options{"i"})) {
138
    my $imagepid    = TB_OPSPID;
139 140 141
    if (defined($options{"p"})) {
	$imagepid = $options{"p"};
    }
Leigh Stoller's avatar
Leigh Stoller committed
142 143
    $project = Project->Lookup($imagepid);
    if (!defined($project)) {
144
	die("*** $0:\n".
Leigh Stoller's avatar
Leigh Stoller committed
145 146 147 148
	    "    No such project $imagepid!\n");
    }
    # This is untainted.
    $imagepid = $project->pid();
149

150 151
    for my $id (split /,/, $options{"i"}) {
	# Look up image in project.
152
	my $image = OSImage->Lookup($imagepid, $id);
153 154 155 156 157
	if (!defined($image)) {
	    die("*** $0:\n".
		"    No such image: $imagepid/$id\n");
	}
	# This is untainted.
158
	push @images, $image;
Leigh Stoller's avatar
Leigh Stoller committed
159
    }
Leigh Stoller's avatar
Leigh Stoller committed
160 161
}
elsif (defined($options{"m"})) {
162
    for my $id (split /,/, $options{"m"}) {
163
	my $image = OSImage->Lookup($id);
164 165 166 167 168
	if (!defined($image)) {
	    die("*** $0:\n".
		"    No such image: $id\n");
	}
	# This is untainted.
169
	push @images, $image;
Leigh Stoller's avatar
Leigh Stoller committed
170 171
    }
}
Leigh Stoller's avatar
Leigh Stoller committed
172 173 174 175

#
# Figure out what nodes to reload.
#
176
if (defined($options{"e"})) {
Leigh Stoller's avatar
Leigh Stoller committed
177 178 179 180 181
    usage()
	if (@ARGV);

    $experiment = Experiment->Lookup($options{"e"});
    if (!defined($experiment)) {
182
	die("*** $0:\n".
Leigh Stoller's avatar
Leigh Stoller committed
183
	    "    No such experiment!\n");
184
    }
Leigh Stoller's avatar
Leigh Stoller committed
185 186 187 188
    @nodes = $experiment->NodeList();
    if (!@nodes) {
	die("*** $0:\n".
	    "    There are no nodes allocated to $experiment!\n");
189 190
    }
}
191
elsif (defined($options{"t"})) {
Leigh Stoller's avatar
Leigh Stoller committed
192 193
    usage()
	if (!@ARGV);
194 195 196 197
    
    #
    # Untaint types,
    #
Leigh Stoller's avatar
Leigh Stoller committed
198
    foreach my $type (@ARGV) {
199 200 201 202 203 204
	if ($type =~ /^([-\w]+)$/) {
	    $type = $1;
	}
	else {
	    die("Bad type name: $type.");
	}
Leigh Stoller's avatar
Leigh Stoller committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
	my $query_result =
	    DBQueryFatal("select node_id from nodes ".
			 "where type='$type' and role='testnode'");

	while (my ($nodeid) = $query_result->fetchrow_array()) {
	    my $node = Node->Lookup($nodeid);
	    if (!defined($node)) {
		die("*** $0:\n".
		    "    Could not map node $nodeid to its object!\n");
	    }
	    push(@nodes, $node);
	}
    }
    if (!@nodes) {
	die("*** $0:\n".
	    "    There are no nodes of any type!\n");
221 222
    }
}
223
elsif (defined($options{"c"})) {
Leigh Stoller's avatar
Leigh Stoller committed
224
    my $class     = $options{"c"};
225 226 227 228 229 230 231 232 233 234 235

    if ($class =~ /^([-\w]+)$/) {
	$class = $1;
    }
    else {
	die("*** Bad data in $class\n");
    }
    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'");
Leigh Stoller's avatar
Leigh Stoller committed
236 237 238 239 240 241 242
    
    while (my ($nodeid) = $query_result->fetchrow_array()) {
	my $node = Node->Lookup($nodeid);
	if (!defined($node)) {
	    die("*** $0:\n".
		"    Could not map node $nodeid to its object!\n");
	}
243 244
	push(@nodes, $node);
    }
Leigh Stoller's avatar
Leigh Stoller committed
245 246 247 248
    if (!@nodes) {
	die("*** $0:\n".
	    "    There are no nodes of class $class!\n");
    }
249
}
250
else {
Leigh Stoller's avatar
Leigh Stoller committed
251 252 253 254 255 256 257 258 259
    # Nodes on command line
    usage()
	if (@ARGV < 1);

    foreach my $n (@ARGV) {
	my $node = Node->Lookup($n);
	if (!defined($node)) {
	    die("*** $0:\n".
		"    Node $n does not exist!\n");
260 261 262
	}
	push(@nodes, $node);
    }
263
}
264

265 266 267 268
#
# VIRTNODE HACK: Virtual nodes are special. Do not reload!
#
my @temp = ();
Leigh Stoller's avatar
Leigh Stoller committed
269 270
foreach my $node (@nodes) {
    if ($node->isvirtnode()) {
271 272 273 274 275 276 277 278 279 280 281
	print "*** Skipping virtual node $node ...\n";
	next;
    }
    push(@temp, $node);
}
@nodes = @temp;
if (! @nodes) {
    print "No nodes to load. Exiting ...\n";
    exit(0);
}

282
#
283
# Loop through each node.
284 285 286
# 
my @load_list=();
foreach my $node (@nodes) {
Leigh Stoller's avatar
Leigh Stoller committed
287 288
    my $pc = $node->node_id();
    my $allocated  = 0;
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
    my $failed = 0;
    my @this_imageids = ();

    #
    # Watch for image aliases.
    #
    foreach my $image (@images) {
	if ($image->isImageAlias()) {
	    my $tmp = $image->MapToImage($node->type());
	    if (!defined($tmp)) {
		print STDERR "Could not map $image to an image for $pc!";
		$failed = 1;
		next;
	    }
	    $image = $tmp;
	}
	push(@this_imageids, $image->imageid());
    }
    next
	if ($failed);
    
    my $this_imageid = @this_imageids ? join ',', @this_imageids : undef;
Leigh Stoller's avatar
Leigh Stoller committed
311 312 313 314

    #
    # Get default imageid for this node if none specified on comand line.
    #
315 316 317
    if (!defined($this_imageid)) {
	$this_imageid = $node->default_imageid();
	if (!defined($this_imageid)) {
318 319 320
	    print STDERR
		"*** Node $pc does not have a default imageid. Skipping!\n";
	    next;
Leigh Stoller's avatar
Leigh Stoller committed
321 322
	}
    }
Leigh Stoller's avatar
Leigh Stoller committed
323 324 325

    if (!$node->IsReserved()) {
        print STDERR "Reserving $node and adding to list.\n";
326 327 328 329 330 331 332
	my $eid;
	if ($pend) {
	    $eid = $PENDINGEID;
	}
	else {
	    $eid = $RELOADEID;
	}
333
	my $cmd = "$nalloc -f $RELOADPID $eid $pc";
334

335 336 337
        if ( system($cmd) != 0 ) {
	    print STDERR "WARNING: Could not reserve $pc!\n";
	} else {
338 339 340 341
	    #
	    # Kill the last_reservation so that whoever gets the node next
	    # won't be fooled into thinking a reload is required.
	    #
342
	    DBQueryFatal("delete from last_reservation where node_id='$pc'");
343
	    $allocated = 1;
344
	}
Leigh Stoller's avatar
Leigh Stoller committed
345 346 347
    }
    else {
        print STDERR "$node is already reserved.\n";      
348
    }
349 350 351 352 353 354 355 356 357 358

    #
    # 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.
359
    if ($node->SetSchedReload($this_imageid, $type) != 0) {
360 361 362 363
	die("*** $0:\n".
	    "    Could not set scheduled reload for $pc!"); 
    }
    
Leigh Stoller's avatar
Leigh Stoller committed
364 365 366 367 368 369 370 371 372 373
    #
    # 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) {
374 375
	if (! defined($imagenodes{$this_imageid})) {
	    $imagenodes{$this_imageid} = [ $pc ];
Leigh Stoller's avatar
Leigh Stoller committed
376 377
	}
	else {
378
	    push(@{ $imagenodes{$this_imageid} }, $pc);
Leigh Stoller's avatar
Leigh Stoller committed
379 380
	}
    }
381 382
}

383
if ($pend) {
Leigh Stoller's avatar
Leigh Stoller committed
384
    print STDOUT "Reload Scheduling Done! There were $error failures!\n";
385 386 387
    exit $error;
}

Leigh Stoller's avatar
Leigh Stoller committed
388 389 390 391 392
#
# 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) ) {
Leigh Stoller's avatar
Leigh Stoller committed
393
    my @nodelist = @{ $imagenodes{$id} };
Leigh Stoller's avatar
Leigh Stoller committed
394

395
    my $cmd = "$osload -m $id @nodelist";
Leigh Stoller's avatar
Leigh Stoller committed
396 397 398 399 400 401

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

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