tbprerun.in 11.6 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
#
# 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!");

#
160
161
162
163
164
165
166
167
168
169
# 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) {
170
171
    # Inner experiment better exist.
    fatal("Inner experiment $elabinelab_eid does not exist!")
172
	if (defined($elabinelab_eid) && !ExpState($pid, $elabinelab_eid));
173
174
175
176
177
178
179
180
181
182
183
184
185
    
    # 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";
}

186
187
188
189
190
191
192
193
194
#
# 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
195
	fatal("NS File is way too big!");
196
197
198
199
    }
    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
200
	fatal("Error storing the NS file into the database!");
201
202
203
    }
}

204
205
206
207
#
# 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. 
# 
208
if ($state eq EXPTSTATE_PRERUN && !$zeemode) {
209
210
211
212
    TBDebugTimeStamp("prerender started in background");
    print "Precomputing visualization ...\n";
    system("prerender -t $pid $eid");
}
Chad Barb's avatar
   
Chad Barb committed
213

214
#
215
# See if using the new ipassign.
216
#
217
218
219
220
221
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
222
    fatal("Error getting ipassign arguments!");
223
}
224
my ($use_ipassign, $ipassign_args) = $query_result->fetchrow();
225
226
227
228
if ($use_ipassign) {
    if (!defined $ipassign_args) {
	$ipassign_args = "";
    }
229
230
231
    TBDebugTimeStamp("ipassign_wrapper started");
    print "Doing IP assignment ...\n";
    
232
    if (system("ipassign_wrapper $ipassign_args $pid $eid")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
233
	fatal("ipassign_wrapper failed!");
234
    }
235
}
236

237
#
238
# Fire up the route calculator. 
239
240
241
242
#
if (!$zeemode) {
    TBDebugTimeStamp("static route calculator started");
    print "Setting up static routes (if requested) ... \n";
243

244
245
246
    if (system("staticroutes $pid $eid")) {
	fatal("Static route calculation failed!");
    }
247
248
}

249
250
251
252
#
# Generate a topo map that is used by the remote nodes to create the
# routes (ddijk) and the /etc/hosts file.
#
253
254
255
TBDebugTimeStamp("gentopofile started");
print "Generating topomap ...\n";

256
if (system("gentopofile $pid $eid")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
257
258
259
260
261
262
263
    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.
#
264
265
266
267
268
269
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
270
271
272
273
274
275
276
277
278
279
280
281
282
}

#
# 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!");
}

283
if (0 && $security_level >= TBDB_SECLEVEL_ORANGE()) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
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
    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'");
    }
}

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#
# 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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
#
# 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'");
    }
407
408
}

Robert Ricci's avatar
Robert Ricci committed
409
TBDebugTimeStamp("tbprerun finished");
410
411
print "Pre run finished. " . TBTimeStamp() . "\n";
exit(0);
412