startexp.in 9.86 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh Stoller's avatar
Leigh Stoller committed
2 3 4 5 6 7 8

#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#

9
use English;
10
use Getopt::Std;
11

12 13 14 15
#
# This gets invoked from the Web interface. CD into the proper directory
# and do the tb stuff.
#
16 17 18 19
# The -b (batch) argument is so that this script can be part of a batchmode
# that starts/ends experiments offline. In that case, we don't want to put
# it into the background and send email, but just want an exit status 
# returned to the batch system.
20
#
21 22
sub usage()
{
Leigh Stoller's avatar
Leigh Stoller committed
23
    print STDOUT
24
	"Usage: startexp [-b | -f] [-g gid] <pid> <eid> <nsfile>\n";
25 26
    exit(-1);
}
27
my  $optlist = "bg:f";
28

29 30 31
#
# Configure variables
#
32 33 34
my $TB       = "@prefix@";
my $DBNAME   = "@TBDBNAME@";
my $TBOPS    = "@TBOPSEMAIL@";
35
my $TBLOGS   = "@TBLOGSEMAIL@";
36
my $TBINFO   = "$TB/expinfo";
37
my $PROJROOT = "/proj";
38

39 40 41 42 43 44 45
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;

46 47
my $tbdir    = "$TB/bin/";
my $tbdata   = "tbdata";
48
my $batch    = 0;
49
my $frontend = 0;
50
my $errorstat= -1;
51
my $logname;
52

53 54 55 56 57
#
# For debugging all this goo. Leaves the experiment directory intact,
# and placed in a subdir of the project directory.
# 
my $debug    = 1;
58

59 60 61 62 63
#
# Turn off line buffering on output
#
$| = 1;

64 65 66 67 68 69 70
#
# Set umask for start/swap. We want other members in the project to be
# able to swap/end experiments, so the log and intermediate files need
# to be 664 since some are opened for append.
#
umask(0002);

71
#
72 73 74 75
# Untaint the path
# 
$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
76

77
#
78 79 80 81 82 83 84
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
85
if (@ARGV != 3) {
86
    usage();
87
}
88 89
my $pid   = $ARGV[0];
my $eid   = $ARGV[1];
90
my $nsfile= $ARGV[2];
91
if (defined($options{"b"})) {
92
    $batch   = 1;
93
}
94 95 96 97 98 99
if (defined($options{"f"})) {
    $frontend = 1;
}
if ($batch && $frontend) {
    usage();
}
100

Leigh Stoller's avatar
Leigh Stoller committed
101 102 103 104 105 106
if (defined($options{"g"})) {
    $gid = $options{"g"};
}
else {
    $gid = $pid;
}
107 108

#
109
# Untaint the arguments.
110
#
Leigh Stoller's avatar
Leigh Stoller committed
111
if ($pid =~ /^([-\@\w]+)$/) {
112
    $pid = $1;
113
}
114 115 116
else {
    die("Tainted argument $pid!\n");
}
Leigh Stoller's avatar
Leigh Stoller committed
117
if ($eid =~ /^([-\@\w]+)$/) {
118 119
    $eid = $1;
}
120 121 122
else {
    die("Tainted argument $eid!\n");
}
Leigh Stoller's avatar
Leigh Stoller committed
123 124 125
if ($gid =~ /^([-\@\w]+)$/) {
    $gid = $1;
}
126 127 128
else {
    die("Tainted argument $gid!\n");
}
129
# Note different taint check (allow /).
130 131 132 133 134 135 136
if ($nsfile =~ /^([-\w.\/]+)$/) {
    $nsfile = $1;
}
else {
    die("Tainted nsfile name: $nsfile");
}

137 138
my $workdir = TBExptWorkDir($pid, $eid);
my $userdir = TBExptUserDir($pid, $eid);
139
my $repfile = "$eid.report";
140 141
my $user_name;
my $user_email;
142
my $dbuid;
143 144

#
145 146
# Verify user and get his DB uid.
#
147 148 149
if (! UNIX2DBUID($UID, \$dbuid)) {
    die("*** $0:\n".
	"    Go Away! You do not exist in the Emulab Database.\n");
150
}
151

Leigh Stoller's avatar
Leigh Stoller committed
152 153 154 155
#
# Verify that this person is allowed to start the experiment. 
#
if (! TBProjAccessCheck($dbuid, $pid, $gid, TB_PROJECT_CREATEEXPT)) {
156 157
    die("*** $0:\n".
	"    You do not have permission to start experiments in $pid/$gid\n");
158 159
}

160
#
161
# Get email info for user.
162
#
163
if (! UserDBInfo($dbuid, \$user_name, \$user_email)) {
164 165
    die("*** $0:\n".
        "    Cannot determine your name and email address.\n");
166 167
}

168
#
169 170
# Check to make sure the experiment record exists. The experiment must
# be in the "new" state when using this interface. 
171
#
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
$query_result =
    DBQueryFatal("SELECT * FROM experiments WHERE eid='$eid' and pid='$pid'");

if (! $query_result->numrows) {
    die("*** $0:\n".
        "    No experiment record for $pid/$eid exists!\n");
}

my %row		= $query_result->fetchhash();
my $estate	= $row{'state'};
my $expt_path   = $row{'path'};
my $expt_name   = $row{'expt_name'};
my $expt_created= $row{'expt_created'};
my $expt_expires= $row{'expt_expires'};
my $expt_started;

if ($estate ne EXPTSTATE_NEW) {
    die("*** $0:\n".
	"    Experiment $pid/$eid is already configured (or configuring)!\n");
}

193
if (! chdir($workdir)) {
194
    die("*** $0:\n".
195
	"    Could not chdir to $workdir: $!\n");
196 197
}

198 199 200 201 202
#
# Lock the experiment record with the timestamp so that it cannot
# terminated or swapped. This is basically a wrapper state for the
# variety of actual states.
#
203
TBLockExp($pid, $eid);
204

205 206 207 208 209
#
# The rest of this goes into the background so that the user sees
# immediate response. We will send email later when the experiment
# is actually torn down.
#
210
if (! $batch) {
211
    $logname = TBExptCreateLogFile($pid, $eid, "startexp");
212
    TBExptSetLogFile($pid, $eid, $logname);
213
    TBExptOpenLogFile($pid, $eid);
214
    
215
    if (TBBackGround($logname)) {
216 217 218 219 220 221 222 223 224
	#
	# Parent exits normally
	#
	print STDOUT
	    "Experiment $pid/$eid is now configuring\n".
	    "You will be notified via email when the experiment is ".
	    "ready to use\n";
	exit(0);
    }
225 226
}

227
#
228 229
# Run the various scripts. We want to propogate the error from tbprerun
# and tbrun back out, hence the bogus looking errorstat variable.
230
#
231
if (system("$tbdir/tbprerun $pid $eid $nsfile") != 0) {
232
    $errorstat = $? >> 8;
233
    fatal("tbprerun failed!\n");
234
}
235
# So fatal errors run tbend.
236
$estate = EXPTSTATE_PRERUN;
237

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
if (! $frontend) {
    if (system("$tbdir/tbswapin  $pid $eid") != 0) {
	$errorstat = $? >> 8;
	fatal("tbswapin failed!\n");
    }
    # So fatal errors run tbswapout,
    $estate = EXPTSTATE_ACTIVE;

    #
    # Look for the unsual case of more than 2 nodes and no vlans. Send a
    # warning message.
    #
    if (ExpNodes($pid, $eid) > 2) {
	my $vlans_result =
	    DBQueryFatal("select * from vlans where ".
			 "pid='$pid' and eid='$eid'");
    
	if (!$vlans_result->numrows) {
	    SENDMAIL("$user_name <$user_email>",
		     "WARNING: Experiment Configuration: $pid/$eid",
		     "This experiment has zero network links defined.\n".
		     "Please check your NS file to verify this is what you ".
		     "want!\n",
		     "$user_name <$user_email>",
		     "Cc: $TBOPS", ($nsfile));
	}
    }
265 266
}

267
if (system("$tbdir/tbreport -b $pid $eid 2>&1 > $repfile") != 0) {
268
    fatal("tbreport failed!\n");
269 270
}

271
#
272
# Increment the project experiment count/lastdate. This is informational. 
273
#
274
DBQueryWarn("update projects ".
275
	    "set expt_count=expt_count+1, expt_last=now() ".
276
	    "where pid='$pid'");
277

278 279
#
# Gen up a date for the started field of the record, and insert it.
280
# Unlock the experiment at the same time.
281
# 
282
$expt_started  = DBDateTime();
283 284
DBQueryWarn("update experiments set ".
	    "expt_start='$expt_started', expt_locked=NULL ".
285
	    "WHERE eid='$eid' and pid='$pid'");
286

287 288 289 290
#
# In batchmode, send the report to stdout for the batch daemon.
#
if ($batch) {
291
    system("$tbdir/tbreport -b $pid $eid");
292 293
    print STDOUT "\n\n";
}
294

295 296 297
# Yippie!
print STDOUT "Setup Success\n";

298
#
299
# Try to copy off the files for testbed information gathering.
300
#
301
TBSaveExpLogFiles($pid, $eid);
302

303 304 305 306 307
#
# Make a copy of the work dir in the user visible space so the user
# can see the log files.
#
system("cp -Rfp $workdir/ $userdir/tbdata");
308

309
#
310
# In batch mode, just exit without sending email. 
311 312 313 314 315
#
if ($batch) {
    exit(0);
}

316 317 318 319 320
#
# Close up the log file so the webpage stops.
#
TBExptCloseLogFile($pid, $eid);

321 322 323
#
# Dump the report file and the log file to the user via email. 
#
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
my $message = 
    "Your experiment `$eid' in project `$pid' is now configured.\n" .
    "Here is the experiment summary detailing the nodes that were\n" .
    "allocated to you. You may use the `Qualified Name' to log on\n" .
    "to your nodes. See /etc/hosts on your nodes (when running\n" .
    "FreeBSD, Linux, or NetBSD) for the IP mapping on each node\n" .
    "\n" .
    "User:        $user_name\n" .
    "EID:         $eid\n" .
    "PID:         $pid\n" .
    "GID:         $gid\n" .
    "Name:        $expt_name\n" .
    "Created:     $expt_created\n" .
    "Expires:     $expt_expires\n" .
    "Started:     $expt_started\n" .
    "Directory:   $expt_path\n".
    "\n".
    "Appended at the end is the output of the experiment setup. If you\n" .
    "have any questions or comments, please include the output below\n" .
343
    "in your message to $TBOPS";
344 345 346
   

SENDMAIL("$user_name <$user_email>",
347
	 "New Experiment Created: $pid/$eid",
348 349 350 351 352
	 $message,
	 "$user_name <$user_email>",
	 "Bcc: $TBLOGS",
	 ($repfile, $logname, $nsfile));

353 354
# Done!
exit(0);
355

356 357
sub fatal()
{
358
    my($mesg)     = $_[0];
359 360

    print STDOUT "$mesg\n";
361
    print STDOUT "Cleaning up and exiting with status $errorstat ...\n";
362 363 364 365

    #
    # If we got far enough to allocate nodes, must run tbend.
    #
366
    if ($estate ne EXPTSTATE_NEW) {
367
	tbendit();
368
    }
369 370

    #
371 372
    # In batch mode, exit. Must unlock the experiment since the record
    # is kept by the batch system until it is finished or canceled.
373
    # The batch daemon might terminate the experiment based on the error.
374 375
    #
    if ($batch) {
376
	TBUnLockExp($pid, $eid);
377
	exit($errorstat);
378 379
    }

380 381 382
    # Clear the logfile so the webpage stops. 
    TBExptClearLogFile($pid, $eid);
    
383
    #
384
    # Send a message to the testbed list. 
385
    #
386
    SENDMAIL("$user_name <$user_email>",
387
	     "Experiment Configure Failure: $pid/$eid",
388 389 390
	     $mesg,
	     "$user_name <$user_email>",
	     "Cc: $TBOPS",
391
	     ($logname, "assign.log", "wanassign.log", $nsfile));
392

393
    #
394 395
    # Copy off the workdir to the user directory, Then back up both of
    # them for post-mortem debugging.
396
    #
397 398 399 400 401
    system("/bin/cp -Rfp $workdir/ $userdir/tbdata");
    system("/bin/rm -rf  ${workdir}-failed");
    system("/bin/mv -f   $workdir ${workdir}-failed");
    system("/bin/rm -rf  ${userdir}-failed");
    system("/bin/mv -f   $userdir ${userdir}-failed");
402 403 404 405 406

    #
    # Clear the record and cleanup.
    # 
    TBExptDestroy($pid, $eid);    
407
    
408
    exit($errorstat);
409
}
410 411 412 413 414 415 416

#
# If tbprerun finishes, but tbrun fails, lets do a tbend to make sure
# the nodes and vlans are released.
# 
sub tbendit()
{
417 418 419
    if ($estate eq EXPTSTATE_ACTIVE) {
	print "Running tbswapout with arguments: $pid $eid\n";
	if (system("$tbdir/tbswapout $pid $eid") != 0) {
Leigh Stoller's avatar
Leigh Stoller committed
420
	    print "tbswapout failed!\n";
421
	}
Leigh Stoller's avatar
Leigh Stoller committed
422
    }
423 424
    print "Running tbend with arguments: -force $pid $eid\n";
    if (system("$tbdir/tbend -force $pid $eid") != 0) {
Leigh Stoller's avatar
Leigh Stoller committed
425 426
	print "tbend failed!\n";
    }
427
}