sched_reload.in 9.17 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
3
# Copyright (c) 2000-2007 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 B. Stoller's avatar
Leigh B. Stoller committed
23
#
Leigh B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
63 64 65 66 67
use Project;
use User;
use Node;
use Image;
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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
85 86
my $project;
my $experiment;
87
my @imageids;
Leigh B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
133

Leigh B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
142 143
    $project = Project->Lookup($imagepid);
    if (!defined($project)) {
144
	die("*** $0:\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
145 146 147 148
	    "    No such project $imagepid!\n");
    }
    # This is untainted.
    $imagepid = $project->pid();
149

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

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

    $experiment = Experiment->Lookup($options{"e"});
    if (!defined($experiment)) {
182
	die("*** $0:\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
183
	    "    No such experiment!\n");
184
    }
Leigh B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
192 193
    usage()
	if (!@ARGV);
194 195 196 197
    
    #
    # Untaint types,
    #
Leigh B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
245 246 247 248
    if (!@nodes) {
	die("*** $0:\n".
	    "    There are no nodes of class $class!\n");
    }
249
}
250
else {
Leigh B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
287 288
    my $pc = $node->node_id();
    my $allocated  = 0;
Kevin Atkinson's avatar
Kevin Atkinson committed
289
    my $this_imageid = @imageids ? join ',', @imageids : undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
290 291 292 293

    #
    # Get default imageid for this node if none specified on comand line.
    #
294 295 296
    if (!defined($this_imageid)) {
	$this_imageid = $node->default_imageid();
	if (!defined($this_imageid)) {
297 298 299
	    print STDERR
		"*** Node $pc does not have a default imageid. Skipping!\n";
	    next;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
300 301
	}
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
302 303 304

    if (!$node->IsReserved()) {
        print STDERR "Reserving $node and adding to list.\n";
305 306 307 308 309 310 311
	my $eid;
	if ($pend) {
	    $eid = $PENDINGEID;
	}
	else {
	    $eid = $RELOADEID;
	}
312
	my $cmd = "$nalloc -f $RELOADPID $eid $pc";
313

314 315 316
        if ( system($cmd) != 0 ) {
	    print STDERR "WARNING: Could not reserve $pc!\n";
	} else {
317 318 319 320
	    #
	    # Kill the last_reservation so that whoever gets the node next
	    # won't be fooled into thinking a reload is required.
	    #
321
	    DBQueryFatal("delete from last_reservation where node_id='$pc'");
322
	    $allocated = 1;
323
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
324 325 326
    }
    else {
        print STDERR "$node is already reserved.\n";      
327
    }
328 329 330 331 332 333 334 335 336 337

    #
    # 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.
338
    if ($node->SetSchedReload($this_imageid, $type) != 0) {
339 340 341 342
	die("*** $0:\n".
	    "    Could not set scheduled reload for $pc!"); 
    }
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
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) {
353 354
	if (! defined($imagenodes{$this_imageid})) {
	    $imagenodes{$this_imageid} = [ $pc ];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
355 356
	}
	else {
357
	    push(@{ $imagenodes{$this_imageid} }, $pc);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
358 359
	}
    }
360 361
}

362
if ($pend) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
363
    print STDOUT "Reload Scheduling Done! There were $error failures!\n";
364 365 366
    exit $error;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
367 368 369 370 371
#
# 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 B. Stoller's avatar
Leigh B. Stoller committed
372
    my @nodelist = @{ $imagenodes{$id} };
Leigh B. Stoller's avatar
Leigh B. Stoller committed
373

374
    my $cmd = "$osload -m $id @nodelist";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
375 376 377 378 379 380

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
383
print STDOUT "Reload Scheduling Done! There were $error failures!\n";
384
exit $error;