tbprerun.in 12.5 KB
Newer Older
1
#!/usr/bin/perl -w
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2
3
#
# EMULAB-COPYRIGHT
4
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
5
6
# All rights reserved.
#
7
use English;
8
use Getopt::Std;
9
10
11

# tbprerun

Leigh B. Stoller's avatar
Leigh B. Stoller committed
12
13
14
15
16
# This is the first program in the
# tbprerun/tbswapin/tbswapout/.../tbend sequences.  It's main purpose
# is to interpret the NS file and create the appropriate entries in
# virt_nodes and virt_lans.  After this script ends successfully the
# NS file is no longer necessary.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
17
18
19
#
sub usage()
{
20
    print STDERR "Usage: $0 [-f] pid eid nsfile\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
21
22
    exit(-1);
}
23
24
25
26
my $optlist = "fz";
my $force   = 0;
my $zeemode = 0;
my $zeeopt  = "";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
27
my $state;
28

29
30
31
32
#
# Configure variables
#
my $TBROOT   = "@prefix@";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
33
34
35
36
37
38
my $TESTMODE = @TESTMODE@;
my $wrapper  = "$TBROOT/libexec/assign_wrapper";
my $batchexp = "$TBROOT/bin/batchexp";
my $template = "$TBROOT/lib/ns2ir/elabinelab.ns";
my $endexp   = "$TBROOT/bin/endexp";
my $exptidx;
39

40
# Untaint the path
41
$ENV{'PATH'} = "/usr/bin:/bin:$TBROOT/libexec:$TBROOT/libexec/ns2ir" . 
Chad Barb's avatar
   
Chad Barb committed
42
    ":$TBROOT/libexec/vis:$TBROOT/sbin:$TBROOT/bin";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
43

44
45
46
#
# Testbed Support libraries
#
47
use lib "@prefix@/lib";
48
49
use libdb;
use libtestbed;
50

51
52
53
54
#
# Turn off line buffering on output
#
$| = 1;
55

Leigh B. Stoller's avatar
Leigh B. Stoller committed
56
#
57
58
# 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
59
#
60
61
%options = ();
if (! getopts($optlist, \%options)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
62
63
    usage();
}
64
if (defined($options{"f"})) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
65
66
    $force = 1;
}
67
68
69
70
if (defined($options{"z"})) {
    $zeemode = 1;
    $zeeopt  = "-p";
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
71
72
if (@ARGV != 3) {
    usage();
73
}
74
my ($pid,$eid,$nsfile) = @ARGV;
75
76

if (! -r $nsfile) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
77
78
    die("*** $0:\n".
	"    NS File '$nsfile' does not exist!\n");
79
80
}

81
print "Beginning pre run for $pid/$eid. " . TBTimeStamp() . "\n";
Robert Ricci's avatar
Robert Ricci committed
82
TBDebugTimeStamp("tbprerun started");
83
84

if (! ($state = ExpState($pid, $eid))) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
85
86
    die("*** $0:\n".
	"    No such experiment $pid/$eid\n");
87
}
Chad Barb's avatar
   
Chad Barb committed
88

89
90
91
92
93
if (! ($gid = ExpGroup($pid,$eid))) {
    die("*** $0:\n".
	"    Unable to get group for $pid/$eid\n");
}

94
95
96
97
98
99
100
101
#
# These are the valid states for running this script.
#
if (!$force &&
    !($state eq EXPTSTATE_PRERUN || $state eq EXPTSTATE_MODIFY_PARSE ||
      $state eq EXPTSTATE_MODIFY_REPARSE)) {
    die("*** $0:\n".
	"    Experiment is not in the proper state: $state\n");
102
103
}

104
#
Chad Barb's avatar
Chad Barb committed
105
106
# Cleanup if something goes wrong.
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
107
sub cleanup {
108
109
    print STDERR "Cleaning up after errors.\n";
    if ($state eq EXPTSTATE_PRERUN) {
110
111
112
	# Must kill the prerender process before we remove virt state.
	print "Killing the renderer.\n";
	system("prerender -r $pid $eid");
Chad Barb's avatar
   
Chad Barb committed
113
    }
114
115
    print "Removing experiment state.\n";
    TBExptRemoveVirtualState($pid, $eid );
Chad Barb's avatar
   
Chad Barb committed
116
117
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#
# Fatal error.
#
sub fatal($)
{
    my ($mesg) = @_;
    
    cleanup();
    die("*** $0:\n".
	"    $mesg\n");
}

# Need this below.
TBExptIDX($pid, $eid, \$exptidx)
    or fatal("Failed to get experiment index for $pid/$eid!");

134
135
136
137
138
139
# Must kill any prerender process first!
system("prerender -r $pid $eid");

# Kill old virtual state.
TBExptRemoveVirtualState($pid, $eid);

Leigh B. Stoller's avatar
Leigh B. Stoller committed
140
141
# This setups virt_nodes, virt_names including all IP address calculation
# and tb-* handling.
142
print "Running parser ... " . TBTimeStamp() . "\n";
Robert Ricci's avatar
Robert Ricci committed
143
TBDebugTimeStamp("parser started");
144
if (system("parse-ns $zeeopt $pid $gid $eid $nsfile")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
145
    fatal("Parsing failed!");
146
}
Robert Ricci's avatar
Robert Ricci committed
147
TBDebugTimeStamp("parser finished");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
148
print "Parser done! " . TBTimeStamp() . "\n";
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#
# XXX Until link agent runs on linux.
#
my $events_result =
    DBQueryFatal("select ev.pid,ev.eid,vl.vnode,vl.vname,vn.osname,o.OS ".
		 "  from eventlist as ev ".
		 "left join event_objecttypes as ev_ob on ".
		 "  ev.objecttype=ev_ob.idx ".
		 "left join virt_lans as vl on vl.vname=ev.vname and ".
		 "  vl.pid=ev.pid and vl.eid=ev.eid ".
		 "left join virt_nodes as vn on vn.pid=ev.pid and ".
		 "  vn.eid=ev.eid and vn.vname=vl.vnode ".
		 "left join os_info as o on o.osname=vn.osname and".
		 "  (o.pid=ev.pid or o.pid='emulab-ops') ".
		 "left join experiments as e on e.pid=ev.pid and ".
		 "  e.eid=ev.eid ".
		 "where ev.pid='$pid' and ev.eid='$eid' and ".
		 "  (vl.uselinkdelay!=0 or e.uselinkdelays!=0 or ".
		 "   e.forcelinkdelays!=0) and ev_ob.type='LINK' and ".
		 "  (o.os is NULL or o.os='Linux')");
if ($events_result->num_rows) {
    fatal("Oops, cannot send static events to linkdelay agents on Linux!");
}

174
175
176
177
178
179
180
181
182
183
#
# Need to know if wrapping an experiment.
#
my $elabinelab = 0;
my $elabinelab_eid;

TBExptIsElabInElab($pid, $eid, \$elabinelab, \$elabinelab_eid)
    or fatal("Failed to get ElabInElab attributes!");

#
184
185
186
187
188
189
190
191
192
193
# If we are, rerun the parser so that we can pass in the proper info
# this time around. This is a side effect of allowing the elabinelab
# bit, and the elabinelab eid, to be specified in the NS file. If we
# did this on the command line, then we could skip this step, but I
# think ease of use is better if it can be in the NS file. Remember,
# the user can clear the elabinelab_eid field, so must rerun to clear
# out the nodes. What if they clear the elabinelab bit too? Ick, not
# going to worry about that.
#
if ($elabinelab) {
194
195
    # Inner experiment better exist.
    fatal("Inner experiment $elabinelab_eid does not exist!")
196
	if (defined($elabinelab_eid) && !ExpState($pid, $elabinelab_eid));
197
198
199
200
201
202
203
204
205
206
207
208
209
    
    # Again, kill virtual state.
    TBExptRemoveVirtualState($pid, $eid);

    print "Re-Running parser ... " . TBTimeStamp() . "\n";
    TBDebugTimeStamp("parser started");
    if (system("parse-ns $pid $gid $eid $nsfile")) {
	fatal("Parsing failed!");
    }
    TBDebugTimeStamp("parser finished");
    print "Parser done! " . TBTimeStamp() . "\n";
}

210
211
212
213
214
215
216
217
218
#
# Put the nsfile into the DB, now that we know it parses okay.
#
$nsfile_string = `cat $nsfile`;

if ($nsfile_string) {
    $nsfile_string = DBQuoteSpecial($nsfile_string);

    if (length($nsfile_string) >= DBLIMIT_NSFILESIZE()) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
219
	fatal("NS File is way too big!");
220
221
222
223
    }
    if (!DBQueryWarn("delete from nsfiles where eid='$eid' and pid='$pid'") ||
	!DBQueryWarn("insert into nsfiles (pid, eid, nsfile) ".
		     "VALUES('$pid', '$eid', $nsfile_string)")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
224
	fatal("Error storing the NS file into the database!");
225
226
227
    }
}

228
229
230
231
#
# In update mode, do not start the renderer until later. If update fails we
# want to try to restore old render info rather then rerunning. 
# 
232
if ($state eq EXPTSTATE_PRERUN && !$zeemode) {
233
234
235
236
    TBDebugTimeStamp("prerender started in background");
    print "Precomputing visualization ...\n";
    system("prerender -t $pid $eid");
}
Chad Barb's avatar
   
Chad Barb committed
237

238
#
239
# See if using the new ipassign.
240
#
241
242
243
244
245
my $query_result =
    DBQueryWarn("select use_ipassign, ipassign_args " .
		"from experiments where pid='$pid' and eid='$eid'");

if (!$query_result || ($query_result->num_rows() != 1)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
246
    fatal("Error getting ipassign arguments!");
247
}
248
my ($use_ipassign, $ipassign_args) = $query_result->fetchrow();
249
250
251
252
if ($use_ipassign) {
    if (!defined $ipassign_args) {
	$ipassign_args = "";
    }
253
254
255
    TBDebugTimeStamp("ipassign_wrapper started");
    print "Doing IP assignment ...\n";
    
256
    if (system("ipassign_wrapper $ipassign_args $pid $eid")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
257
	fatal("ipassign_wrapper failed!");
258
    }
259
}
260

261
#
262
# Fire up the route calculator. 
263
264
265
266
#
if (!$zeemode) {
    TBDebugTimeStamp("static route calculator started");
    print "Setting up static routes (if requested) ... \n";
267

268
269
270
    if (system("staticroutes $pid $eid")) {
	fatal("Static route calculation failed!");
    }
271
272
}

273
274
275
276
#
# Generate a topo map that is used by the remote nodes to create the
# routes (ddijk) and the /etc/hosts file.
#
277
278
279
TBDebugTimeStamp("gentopofile started");
print "Generating topomap ...\n";

280
if (system("gentopofile $pid $eid")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
281
282
283
284
285
286
287
    fatal("gentopofile failed!");
}

#
# Do an assign_prerun to set the min/max nodes. Generates a top file too.
# This is the only DB state that is modified during a top only run.
#
288
289
290
291
292
293
if (!$zeemode) {
    TBDebugTimeStamp("assign prerun started");
    print "Doing a pre-assign ...\n";
    if (system("$wrapper -t $pid $eid")) {
	fatal("assign prerun failed!");
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
294
295
296
297
298
299
300
301
302
303
304
305
306
}

#
# If security level indicates that the experiment needs to be run ElabInElab
# then must generate a parallel experiment for the container. When the user
# swaps in the original experiment, it will really be this experiment that
# is swapped in. Its sorta bogus, but hey its a prototype.
#
my $security_level;
if (! TBExptSecurityLevel($pid, $eid, \$security_level)) {
    fatal("Could not get security_level from DB!");
}

307
if (0 && $security_level >= TBDB_SECLEVEL_ORANGE()) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
    my $elabinelab_eid = $eid . "-" . "elabinelab";
    my $tempnsfile     = "/tmp/$pid-$elabinelab_eid-$$.nsfile";
    my $maxnodes;
    my $elabinelab_exptidx;

    if (! TBExptMinMaxNodes($pid, $eid, undef, \$maxnodes)) {
	fatal("Could not get max nodes from DB!");
    }

    #
    # Munge the template.
    #
    system("cat $template | sed -e 's/\@MAXPCS\@/$maxnodes/' ".
	   "                    -e 's/\@SECURITY_LEVEL\@/$security_level/' ".
	   "    >$tempnsfile");

    TBDebugTimeStamp("recursive batchexp started");
    print "Creating elabinelab container experiment.\n";
    if (system("$batchexp -q -i -w -f -n ".
	       "  -L 'ElabInElab Container for $eid' ".
	       "  -E 'ElabInElab Container for $eid - DO NOT DELETE' ".
	       "  -p $pid -e $elabinelab_eid $tempnsfile")) {
	fatal("Could not create elabinelab container experiment!");
    }
    TBDebugTimeStamp("recursive batchexp finished");

    #
    # Link the two experiments together.
    #
    if (! (DBQueryWarn("update experiments set ".
		       " elabinelab_eid='$eid' ".
		       "where pid='$pid' and eid='$elabinelab_eid'") &&
	   DBQueryWarn("update experiments set ".
		       "  elabinelab_eid='$elabinelab_eid' ".
		       "where pid='$pid' and eid='$eid'"))) {
	system("$endexp -w -q $pid $elabinelab_eid");
	fatal("Could not update DB for cross linking experiments!");
    }
    #
    # This stats stuff is not done in testmode.
    # 
    if (! $TESTMODE) {
	# IDX for newly created elabinelab experiment.
	TBExptIDX($pid, $elabinelab_eid, \$elabinelab_exptidx)
	    or fatal("Failed to get experiment index for $pid/$elabinelab_eid!");

	# Cross link the other experiment (code below is for this experiment).
	DBQueryWarn("update experiment_stats set ".
		    "   elabinelab_exptidx=$exptidx ".
		    "where pid='$pid' and eid='$elabinelab_eid' and ".
		    "      exptidx='$elabinelab_exptidx'");
    }
}

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#
# If wrapping an experiment, copy over type/fixnode info to wrapper.
# We already made sure the experiment was created with enough nodes (see
# the parser) but now we have to make sure that the type/fixnode stuff will
# correspond to what the wrapped experiment really wants. It is a lot easier
# to do this here then in the parser.
#
if ($elabinelab && defined($elabinelab_eid)) {
    my $outer_result;
    my $inner_result;
    my $maxnodes;

    if (! ($inner_result =
	   DBQueryWarn("select type,fixed from virt_nodes ".
		       "where pid='$pid' and eid='$elabinelab_eid'"))) {
	fatal("Could not get virt_node info for $pid/$elabinelab_eid!");
    }
    if (! ($outer_result =
	   DBQueryWarn("select vname from virt_nodes ".
		       "where pid='$pid' and eid='$eid' and ".
		       "      inner_elab_role='node'"))) {
	fatal("Could not get virt_node info for $pid/$eid!");
    }

    #
    # Move over info for each real node in the inner experiment. Delay
    # nodes will be extra nodes in the outer experiment, but those do
    # not currently get type/fix info since the the user has no way to
    # do that in the NS file.
    # 
    while (my ($type,$fixed) = $inner_result->fetchrow_array()) {
	my ($vname) = $outer_result->fetchrow_array();

	fatal("Not enough nodes in outer experiment!")
	    if (!defined($vname));

	DBQueryWarn("update virt_nodes set ".
		    "  type='$type',fixed='$fixed' ".
		    "where pid='$pid' and eid='$eid' and vname='$vname'")
	    or fatal("Could not get virt_node info for $pid/$eid!");
    }
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
#
# This stats stuff is not done in testmode.
# 
if (! $TESTMODE) {
    #
    # Deal with ElabInElab stuff, which is updated after the parse and prerun.
    # Want to move this to the stats records. Ditto for the security stuff,
    # which was handled above.
    #
    my $elabinelab_exptidx;

    if ($elabinelab || $security_level) {
	DBQueryWarn("update experiment_stats set ".
		    "   elabinelab=$elabinelab, ".
		    "   security_level=$security_level ".
		    "where pid='$pid' and eid='$eid' and exptidx='$exptidx'");
    }

    if ($elabinelab_eid) {
	TBExptIDX($pid, $elabinelab_eid, \$elabinelab_exptidx)
	    or fatal("Failed to get experiment index for $pid/$elabinelab_eid!");
    
	DBQueryWarn("update experiment_stats set ".
		    "   elabinelab_exptidx=$elabinelab_exptidx ".
		    "where pid='$pid' and eid='$eid' and exptidx='$exptidx'");
    }
431
432
}

Robert Ricci's avatar
Robert Ricci committed
433
TBDebugTimeStamp("tbprerun finished");
434
435
print "Pre run finished. " . TBTimeStamp() . "\n";
exit(0);
436