source: fedkit/splitter.pl @ ad5639c

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since ad5639c was ad5639c, checked in by Ted Faber <faber@…>, 16 years ago

retry with different credentials on failure

  • Property mode set to 100644
File size: 42.3 KB
Line 
1#!/usr/bin/perl
2
3use strict;
4
5use Getopt::Std;
6use IO::File;
7use IO::Dir;
8use IO::Pipe;
9use File::Copy;
10
11my @scripts = ("fed_bootstrap", "federate.sh", "smbmount.FreeBSD.pl", 
12    "smbmount.Linux.pl", "make_hosts", "fed-tun.pl", "fed_evrepeater",
13    "rc.accounts.patch");
14my $local_script_dir = ".";
15my($pid, $gid);                 # Process and group IDs for calling parse.tcl
16my $splitter_config;            # Configuration file
17my $debug;                      # True if thecalled in debug mode
18my $verbose;                    # True for extra progress reports
19my $startem;                    # If true, start the sub-experiments
20my $eid;                        # Experiment ID
21my $tcl;                        # The experiment description (topology file)
22my $master;                     # Master testbed
23my $tmpdir;                     # tmp files
24my $tb_config;                  # testbed configurations
25my $smb_share;                  # Share to mount from the master
26my $project_user;               # User to mount project dirs as
27my $auth_proj;                  # Local project for resource access
28my($gw_pubkey, $gw_pubkey_base);# Connector pubkey (full path & basename)
29my($gw_secretkey, $gw_secretkey_base);# Connector secret key (full path &
30                                # basename)
31my($keytype);                   # Type (DSA or RSA) of generated gateway keys
32my $tcl_splitter;               # tcl program to split experiments
33                                # (changed during devel)
34my $tclsh;                      # tclsh to call directly (changed during devel)
35my $fedd_client;                # Program to call for testbed access params
36my $muxmax;                     # Maximum number of links/lans over 1 gw pair
37my @tarfiles;                   # Tarfiles in use by this experiment
38my @rpms;                       # Rpms in use by this experiment
39my $timeout;                    # The timeout to use for experiment swap ins
40my %opts;                       # Parsed options
41
42my $tbparams = {};              # Map of the per-testbed parameters from the
43                                # testbeds file.  It is a reference to a hash
44                                # of hashes (because it's passed around a bunch
45                                # and it's nicer to have one access pattern
46                                # throughout the script, in the main loop and
47                                # the subroutines).  That access is exemplified
48                                # by  $tbparams->{'deter'}->{'domain'} which is
49                                # the domain parameter of the DETER testbed. 
50my $fail_soft;                  # Do not swap failed sub-experiments out
51my $max_children=1;             # Maximum number of simultaneous swap-ins
52
53# Default commands for starting experiment and gateway nodes.  Testbeds can
54# override these.  (The 'm' prefixed commands are for operating as the master
55# testbed.)
56my $def_expstart = "sudo -H /bin/sh FEDDIR/fed_bootstrap >& /tmp/federate";
57my $def_mexpstart = "sudo -H FEDDIR/make_hosts FEDDIR/hosts";
58my $def_gwstart = "sudo -H FEDDIR/fed-tun.pl -f GWCONF>& /tmp/bridge.log";
59my $def_mgwstart = "sudo -H FEDDIR/fed-tun.pl -f GWCONF >& /tmp/bridge.log";
60my $def_gwimage = "FBSD61-TUNNEL2";
61my $def_gwtype = "pc";
62
63# Parse the config file.  The format is a colon-separated parameter name
64# followed by the value of that parameter to the end of the line.  This parses
65# that format and puts the parameters into the referenced hash.  Parameter
66# names are mapped to lower case, parameter values are unchanged.  Returns 0 on
67# failure (e.g. file open) and 1 on success.
68sub parse_config {
69    my($file, $href) = @_;
70    my $fh = new IO::File($file);
71       
72    unless ($fh) {
73        warn "Can't open $file: $!\n";
74        return 0;
75    }
76
77    while (<$fh>) {
78        next if /^\s*#/ || /^\s*$/;     # Skip comments & blanks
79        chomp;
80        /^([^:]+):\s*(.*)/ && do {
81            my $key = $1; 
82
83            $key =~ tr/A-Z/a-z/;
84            $href->{$key} = $2;
85            next;
86        };
87        warn "Unparasble line in $file: $_\n";
88    }
89    $fh->close();   # It will close when it goes out of scope, but...
90    return 1;
91}
92
93# Parse an easier-to-read testbeds file (the original was comma-separated
94# unadorned strings).  The format is a testbed scope as [testbed] followed by
95# the colon-separated attribute-value pairs for the testbed.  Right now these
96# go into a set of global hashes indexed by testbed, but that should probably
97# change.  The file parameter is an open IO::Handle.  &parse_testbeds_filename
98# opens the file and calls this.  Parse_testbeds can be used on pipes as well,
99# e.g. fedd_client output.
100sub parse_testbeds {
101    my($fh, $tbparams) = @_;        # Testbeds file and parameter hash
102    my $tb;                         # Current testbed
103    # Convert attribute in the file to tbparams hash key
104    my %attr_to_hash = (
105        "opsnode" => "host",
106        "user" => "user",
107        "domain" => "domain",
108        "project" => "project",
109        "connectortype" => "gwtype",
110        "slavenodestartcmd" => "expstart",
111        "slaveconnectorstartcmd" => "gwstart",
112        "masternodestartcmd" => "mexpstart",
113        "masterconnectorstartcmd" => "mgwstart",
114        "connectorimage" => "gwimage",
115        "fileserver" => "fs",
116        "boss" => "boss",
117        "eventserver" => "eventserver",
118        "tunnelcfg" => "tun",
119        "uri" => "uri",
120        "access" => "access"
121    );
122
123    while (<$fh>) {
124        next if /^\s*#/ || /^\s*$/;     # Skip comments & blanks
125        print STDERR "testbeds: $_";
126        chomp;
127        /^\s*\[(.*)\]/ && do {
128            $tb = $1;
129            $tbparams->{$tb} = {} unless $tbparams->{$tb};
130            next;
131        };
132
133        /^([^:]+):\s*(.*)/ && do {
134            unless ($tb) {
135                warn "Ignored attribute definition before testbed: $_\n ";
136                next;
137            }
138            my $key = $1; 
139            $key =~ tr/A-Z/a-z/;
140            my $var = $attr_to_hash{$key};
141
142            if ($var) { $tbparams->{$tb}->{$var} = $2; }
143            else { warn "Unknown keyword $key\n"; }
144
145            next;
146        };
147        warn "Unparasble line: $_\n";
148    }
149    return 1;
150}
151
152
153# Open the given file name and parse the testbeds file it contains by calling
154# &parse_testbeds.
155sub parse_testbeds_filename {
156    my($file, $tbparams) = @_;      # Testbeds file and parameter hash
157    my $fh = new IO::File($file);   # Testbeds filehandle
158
159    if ($fh) {
160        my $rv = &parse_testbeds($fh, $tbparams);
161        $fh->close();   # It will close when it goes out of scope, but...
162        $rv;
163    }
164    else {
165        warn "Can't open $file: $!\n";
166        return 0;
167    }
168}
169
170# Generate SSH keys for use by the gateways.  The parameters are the type and
171# the filename for the private key.  The pubkey will be stored in a filename
172# with the same name as the private key but with .pub appended.  Type can be
173# dsa or rsa.
174
175sub generate_ssh_keys {
176    my($type, $dest) = @_;
177
178    $type =~ tr/A-Z/a-z/;
179    return 0 if $type !~ /(rsa|dsa)/;
180    system("/usr/bin/ssh-keygen -t $type -N \"\" -f $dest");
181    return $@ ? 0 : 1;
182}
183
184# use scp to transfer a file, reporting true if successful and false otherwise.
185# Parameters are the local file name, the ssh host destination (either hostname
186# oe user@host), and an optional destination file name or directory.  If no
187# destination is given, the file is transferred to the given user's home
188# directory.  If only a machine is given in the ssh host destination, the
189# current user is used.
190sub scp_file {
191    my($file, $user, $host, $dest) = @_;
192
193    # XXX system with a relative pathname is sort of gross
194    system("scp $file $user\@$host:$dest");
195    if ($?) {
196        warn "scp failed $?\n";
197        return 0;
198    }
199    else { return 1; }
200}
201
202# use ssh to execute the given command on the machine (and as the user) in
203# $where.  Parameters are the ssh destination directive ($where) and the
204# command to execute, and a prefix to be placed on a message generated if the
205# command fails.   On failure print a warning if a warning prefix was given and
206# return false.  If timeout is given fork a process and set an alarm of that
207# many seconds.  Timeouts also return 0;
208sub ssh_cmd {
209    my($user, $host, $cmd, $wname, $timeout) = @_;
210    my $pid;                # Child pid
211
212    $timeout = 0 unless $timeout;   # Force default timeout
213
214    if ( $pid = fork () ) {
215        # Parent process
216        # The eval acts as a signal catcher.  If the alarm goes off inside
217        # the eval, the die will put "alarm\n" into $@, otherwise the
218        # return value of the execution in the child process will be used.
219        my $rv = eval {
220            local $SIG{'ALRM'} = sub{ die "alarm\n"; };
221            my $rv;
222
223            alarm $timeout;
224            $rv = waitpid($pid, 0);
225            alarm 0;
226            $rv;
227        };
228
229        # If the eval succeeded, $@ will be null and we can use $rv, which
230        # is the return code from the subprocess.  If the eval timed out,
231        # print a warning and assume the best.
232        if ($@ eq "alarm\n" ) {
233            warn "$wname timed out - pid $pid still live\n";
234            return 1;
235        }
236        else {
237            return $rv;
238        }
239    }
240    else {
241        # Child process
242        exec("ssh $user\@$host $cmd");
243        exit 0;
244    }
245}
246
247# Ship local copies of the federation scripts out to the given host.  If any of
248# the script transfers fails, return 0.  The scripts to transfer are from the
249# global @scripts and are found locally in $local_script_dir (another global).
250sub ship_scripts {
251    my($host, $user, $dest_dir) = @_;       # Where, who, where remotely
252    my $s;
253
254    &ssh_cmd($user, $host, "mkdir -p $dest_dir");
255    for $s (@scripts) {
256        &scp_file("$local_script_dir/$s", $user, $host, $dest_dir) || 
257            return 0;
258    }
259    return 1;
260}
261
262# Ship per-testbed configuration generated by this script to the remote /proj
263# directories on the remote testbeds
264sub ship_configs {
265    my($host, $user, $src_dir, $dest_dir) = @_;     # Where, who, where remotely
266    my($d, $f);
267
268    $d = IO::Dir->new($src_dir) || return 0;
269
270    # All directories under $tmpdir are 770 so we can delete them later.
271    &ssh_cmd($user, $host, "mkdir -p $dest_dir") || return 0;
272    &ssh_cmd($user, $host, "chmod 770 $dest_dir") || return 0;
273    while ( $f = $d->read()) {
274        next if $f =~ /^\./;
275        if ( -d "$src_dir/$f" ) {
276            &ship_configs($host, $user, "$src_dir/$f", "$dest_dir/$f") || 
277                return 0;
278        }
279        else {
280            &scp_file("$src_dir/$f", $user, $host, $dest_dir) || return 0;
281        }
282    }
283    return 1;
284}
285
286# Start a sub section of the experiment on a given testbed.  The testbed and
287# the user to start the experiment as are pulled from the global per-testbed
288# hash, passed in as $tbparams, as is the project name on the remote testbed.
289# Parameters are the testbed and the experiment id.  Configuration files are
290# scp-ed over to the target testbed from the global $tmpdir/$tb directory.
291# Then the current state of the experiment determined using expinfo.  From that
292# state, the experiment is either created, modified or spapped in.  If
293# everything succeeds, true is returned.  If the global verbose is set progress
294# messages are printed.
295sub start_segment {
296    my($tb, $eid, $tbparams, $timeout) = @_;# testbed, experiment ID,
297                                            # per-testbed parameters and remote
298                                            # swap-in timeout
299    my $host =                              # Host name of remote ops (FQDN)
300        $tbparams->{$tb}->{'host'} . $tbparams->{$tb}->{'domain'};
301    my $user = $tbparams->{$tb}->{'user'};  # user to pass to ssh
302    my $pid = $tbparams->{$tb}->{'project'};# remote project to start the
303                                            # experiment under
304    my $tclfile = "./$eid.$tb.tcl";         # Local tcl file with the
305                                            # sub-experiment
306    my $proj_dir = "/proj/$pid/exp/$eid/tmp";  # Where to stash federation stuff
307    my $tarfiles_dir = "/proj/$pid/tarfiles/$eid";  # Where to stash tarfiles
308    my $rpms_dir = "/proj/$pid/rpms/$eid";  # Where to stash rpms
309    my $to_hostname = "$proj_dir/hosts";    # remote hostnames file
310    my $state;                              # State of remote experiment
311    my $status = new IO::Pipe;              # The pipe to get status
312
313    # Determine the status of the remote experiment
314    $status->reader("ssh $user\@$host /usr/testbed/bin/expinfo $pid $eid") || 
315        die "Can't ssh to $user\@$host:$!\n";
316    # XXX: this is simple now.  Parsing may become more complex
317    while (<$status>) {
318        /State: (\w+)/ && ($state = $1);
319        /No\s+such\s+experiment/ && ($state = "none");
320    }
321    $status->close();
322    print "$tb: $state\n";
323
324    # Copy the experiment definition data over
325    print "transferring subexperiment to $tb\n" if $verbose;
326    &scp_file("$tmpdir/$tb/$tclfile", $user, $host) || return 0;
327    # Clear out any old experiment data; if not deleted, copies over it by
328    # different users will fail.
329    # (O /bin/csh, how evil thou art.  The -c and the escaped single quotes
330    # force the /bin/sh interpretation of the trailing * (which we need to keep
331    # tmp around))  Again, this needs to be done more properly once we have a
332    # non-ssh interface here.)
333    print "clearing experiment subdirs on $tb\n" if $verbose;
334    &ssh_cmd($user, $host, "/bin/sh -c \\'/bin/rm -rf $proj_dir/*\\'") || 
335        return 0;
336    print "clearing experiment tarfiles subdirs on $tb\n" if $verbose;
337    &ssh_cmd($user, $host, "/bin/rm -rf $tarfiles_dir/") || 
338        return 0;
339    print "creating tarfiles subdir $tarfiles_dir on $tb\n" if $verbose;
340    &ssh_cmd($user, $host, "mkdir -p $tarfiles_dir", "create tarfiles") || 
341        return 0;
342    print "clearing experiment rpms subdirs on $tb\n" if $verbose;
343    &ssh_cmd($user, $host, "/bin/rm -rf $rpms_dir/") || 
344        return 0;
345    print "creating rpms subdir $rpms_dir on $tb\n" if $verbose;
346    &ssh_cmd($user, $host, "mkdir -p $rpms_dir", "create rpms") || 
347        return 0;
348    # Remote experiment is active.  Modify it.
349    if ($state eq "active") {
350        print "Transferring federation support files to $tb\n" if $verbose;
351        # First copy new scripts and hostinfo into the remote /proj
352        &scp_file("$tmpdir/hostnames", $user, $host, $to_hostname) ||
353            return 0;
354        &ship_scripts($host, $user, $proj_dir) || return 0;
355        &ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
356
357        if ( -d "$tmpdir/tarfiles") {
358            &ship_configs($host, $user, "$tmpdir/tarfiles", $tarfiles_dir) || 
359                return 0;
360        }
361
362        if ( -d "$tmpdir/rpms") {
363            &ship_configs($host, $user, "$tmpdir/rpms", $rpms_dir) || 
364                return 0;
365        }
366
367        print "Modifying $eid in place on $tb\n" if $verbose;
368        &ssh_cmd($user, $host, "/usr/testbed/bin/modexp -r -s -w $pid " . 
369            "$eid $tclfile", "modexp", $timeout) || return 0;
370        return 1;
371    }
372
373    # Remote experiment is swapped out, modify it and swap it in.
374    if ($state eq "swapped") {
375        print "Transferring federation support files to $tb\n" if $verbose;
376        # First copy new scripts and hostinfo into the remote /proj (because
377        # the experiment exists, the directory tree should be there.
378        &scp_file("$tmpdir/hostnames", $user, $host, $to_hostname) ||
379            return 0;
380        &ship_scripts($host, $user, $proj_dir) || return 0;
381        &ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
382        if ( -d "$tmpdir/tarfiles") {
383            &ship_configs($host, $user, "$tmpdir/tarfiles", $tarfiles_dir) || 
384                return 0;
385        }
386
387        if ( -d "$tmpdir/rpms") {
388            &ship_configs($host, $user, "$tmpdir/rpms", $rpms_dir) || 
389                return 0;
390        }
391
392        print "Modifying $eid on $tb\n" if $verbose;
393        &ssh_cmd($user, $host, "/usr/testbed/bin/modexp -w $pid $eid $tclfile", 
394            "modexp") || return 0;
395        print "Swapping $eid in on $tb\n" if $verbose;
396        # Now start up
397        &ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid in", 
398            "swapexp", $timeout) || return 0;
399        return 1;
400    }
401
402    # No remote experiment.  Create one.  We do this in 2 steps so we can put
403    # the configuration files and scripts into the new experiment directories.
404    if ($state eq "none") {
405
406        if ( -d "$tmpdir/tarfiles") {
407            # Tarfiles have to exist for the creation to work
408            print "copying tarfiles to $tb\n";
409            &ship_configs($host, $user, "$tmpdir/tarfiles", $tarfiles_dir) || 
410                return 0;
411        }
412
413        if ( -d "$tmpdir/rpms") {
414            &ship_configs($host, $user, "$tmpdir/rpms", $rpms_dir) || 
415                return 0;
416        }
417
418        print "Creating $eid on $tb\n" if $verbose;
419        &ssh_cmd($user, $host, "/usr/testbed/bin/startexp -i -f -w -p " . 
420            "$pid -e $eid $tclfile", "startexp") || return 0;
421        # After startexp succeeds, the per-experiment directories exist on the
422        # remote testbed.
423        print "Transferring federation support files to $tb\n" if $verbose;
424        # First copy new scripts and hostinfo into the remote /proj
425        &scp_file("$tmpdir/hostnames", $user, $host, $to_hostname) ||
426            return 0;
427        &ship_scripts($host, $user, $proj_dir) || return 0;
428        &ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
429        # Now start up
430        print "Swapping $eid in on $tb\n" if $verbose;
431        &ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid in", 
432            "swapexp", $timeout) || return 0;
433        return 1;
434    }
435
436    # Every branch for a known state returns.  If execution gets here, the
437    # state is unknown.
438    warn "unknown state: $state\n";
439    return 0;
440}
441
442# Swap out a sub-experiment - probably because another has failed.  Arguments
443# are testbed and experiment.  Most of the control flow is similar to
444# start_segment, though much simpler.
445sub stop_segment {
446    my($tb, $eid, $tbparams) = @_;          # testbed, experiment ID and
447                                            # per-testbed parameters
448    my $user = $tbparams->{$tb}->{'user'};  # testbed user
449    my $host =                              # Ops node
450        $tbparams->{$tb}->{'host'} . $tbparams->{$tb}->{'domain'};
451    my $pid = $tbparams->{$tb}->{'project'};# testbed project
452
453    print "Stopping $eid on $tb\n" if $verbose;
454    &ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid out", 
455        "swapexp (out)") || return 0;
456    return 1;
457}
458
459# Fill tbparams with results from the fedd call.  The command is passed in and
460# a string with any relevant error conditions is returned.  undef is success.
461sub fedd_access_request{
462    my($cmd) = @_;
463    my($rv)=undef;
464
465    system("$cmd 2> /tmp/splitter.err.$$ > /tmp/splitter.$$" );
466
467    if ( ! $? ) {
468        print "Parsing /tmp/splitter.$$\n";
469        &parse_testbeds_filename("/tmp/splitter.$$", $tbparams) ||
470            ($rv =  "Error reading fedd output: $!\n");
471    }
472    else {
473        my $f = new IO::File("/tmp/splitter.err.$$");
474        $rv =  "Fedd_client error:\n";
475        while (<$f>) { $rv .= $_; }
476        $f->close();
477    }
478    unlink("/tmp/splitter.$$", "/tmp/splitter.err.$$");
479    return $rv;
480}
481
482$pid = $gid = "dummy";              # Default project and group to pass to
483                                    # $tcl_splitter above.  These are total
484                                    # dummy arguments;  the splitter doesn't
485                                    # use them at all, but we supply them to
486                                    # keep our changes to the parser minimal.
487# Argument processing.
488getopts('Ft:c:p:f:ndvNP:', \%opts);
489$splitter_config = $opts{'c'} || "./splitter.conf";
490$debug = $opts{'d'};
491$verbose = $opts{'v'} || $opts{'d'};
492
493&parse_config("$splitter_config", \%opts) || 
494    die "Cannot read config file $splitter_config: $!\n";
495
496warn "-N does nothing now.  Only one testbeds format supported.\n"
497    if $opts{'N'};
498$fail_soft = $opts{'F'} || $opts{'failsoft'};
499$startem = $opts{'n'} ? 0 : 1;          # If true, start the sub-experiments
500$timeout = $opts{'t'} || $opts{'timeout'};
501$eid = $opts{'experiment'};             # Experiment ID
502$tcl = $opts{'f'} || shift;             # The experiment description
503$master = $opts{'master'};              # Master testbed
504$tmpdir = $opts{'tmpdir'} || $opts{'tempdir'}|| "/tmp"; # tmp files
505$tb_config = $opts{'testbeds'} || "./testbeds"; # testbed configurations
506$local_script_dir = $opts{'scriptdir'}; # Local scripts
507$muxmax  = $opts{'muxlimit'} || 3;      # Number of connections muxed on one
508                                        # gateway
509
510$max_children = $opts{'p'} || $opts{'maxchildren'} 
511    if $opts{'p'} || $opts{'maxchildren'};
512
513$smb_share = $opts{'smbshare'} ||       # Share to mount from the master
514    die "Must give an SMB share\n";
515$project_user = $opts{'smbuser'} ||     # User to mount project dirs as
516    die "Must give an SMB user\n";
517$auth_proj = $opts{'P'};
518
519# tcl program to split experiments (changed during devel)
520$tcl_splitter = $opts{'tclparse'} || "/usr/testbed/lib/ns2ir/parse.tcl";
521# tclsh to call directly (changed during devel)
522$tclsh = $opts{'tclsh'} || "/usr/local/bin/otclsh";
523# fedd_client to get testbed access parameters
524$fedd_client = $opts{'feddclient'} || "fedd_client";
525
526# Prefix to avoid collisions
527$tmpdir .= "/split$$";
528
529print "Temp files are in $tmpdir\n" if $verbose;
530# Create a workspace
531unless (-d "$tmpdir") {
532    mkdir("$tmpdir") || die "Can't create $tmpdir: $!";
533}
534
535# If the keys are given, use them.  Otherwise create a set under $tmpdir
536
537if ( $opts{'gatewatpubkey'} && $opts{'gatewaysecretkey'}) {
538    $gw_pubkey = $opts{'gatewaypubkey'};
539    $gw_secretkey = $opts{'gatewaysecretkey'};
540}
541else {
542    $keytype = $opts{'gatewaykeytype'} || "rsa";
543    mkdir("$tmpdir/keys") || die "Can't create temoprary key dir: $!\n";
544    $gw_pubkey = "$tmpdir/keys/fed.$keytype.pub";
545    $gw_secretkey = "$tmpdir/keys/fed.$keytype";
546    print "Generating $keytype keys\n" if $verbose;
547    generate_ssh_keys($keytype, $gw_secretkey) || 
548        die "Cannot generate kets:$@\n";
549}
550# Generate the basenames
551($gw_pubkey_base = $gw_pubkey) =~ s#.*/##;
552($gw_secretkey_base = $gw_secretkey) =~ s#.*/##;
553
554
555
556# Validate scripts directory
557for my $s (@scripts) {
558    die "$local_script_dir/$s not in local script directory. Try -d\n"
559        unless -r "$local_script_dir/$s";
560}
561
562die "Must supply file, master and experiment" unless $master && $tcl && $eid;
563
564&parse_testbeds_filename($tb_config, $tbparams) ||
565    die "Cannot testbed congfigurations from $tb_config: $!\n";
566
567# Open a pipe to the splitter program and start it parsing the experiments
568my $pipe = new IO::Pipe;
569# NB no more -p call on parse call.
570$pipe->reader("$tclsh $tcl_splitter -s -x $muxmax -m $master  $pid $gid $eid $tcl") || 
571    die "Cannot execute $tclsh $tcl_splitter -s -x $muxmax -m $master $pid $gid $eid $tcl:$!\n";
572
573# Parsing variables
574my $ctb;                        # Current testbed
575my %allocated;                  # If allocated{$tb} > 0, $tb is in use
576my $destfile;                   # File that the sub-experiment tcl file is
577                                # being written to, or "" if none.  Also used
578                                # for hostnames file.
579my $desthandle;                 # File handle for distfile
580my $gateways;                   # when gateway lists are being processed this
581                                # is the testbed whose gateways are being
582                                # gathered.
583my $control_gateway;            # Control net gateway for the current testbed
584my %active_end;                 # If active_end{"a-b"} > 0 then a is the active
585                                # end of the a <-> b connector pair.
586
587# Parse the splitter output.  This loop creates the sub experiments, gateway
588# configurations and hostnames file
589while (<$pipe>) {
590    # Allbeds lists all the testbeds that this experiment accesses.  This code
591    # acquires access to them and pulls in their access parameters from fedd.
592    (/^#\s+Begin\s+Allbeds/../^#\s+End\s+Allbeds/) && do {
593        next if /^#/;
594        chomp;
595
596        my $tb;         # Current testbed
597        my @nodes;      # Current testbed node requests
598
599        # The Allbeds line has the testbed name first separated by the node
600        # requirements of the testbeds.  A node requirement is separated form
601        # teh testbed name and other node requirements by a vertical bar (|).
602        # This pulls the testbed off the front (which must be present) and
603        # splits the node descriptors out by the vertical bar.  The first
604        # vertical bar (the one after the testbed) is removed by the intial
605        # regular expression to avoid a null entry in @nodes.  The node
606        # requests are of the form image:type:count and can be passed directly
607        # to fedd_client as parameters.
608        /([^|]+)\|?(.*)/ && do {
609            my $n;      # Scratch
610
611            ($tb , $n) = ($1, $2);
612            @nodes = split(/\|/, $n);
613        };
614
615        # If this testbed has not had its access parameters read from fedd, try
616        # to read them, if we have a way to talk to fedd
617        unless ($tbparams->{$tb}->{'access'} || !$fedd_client) { 
618            my $access_pipe = new IO::Pipe || 
619                die "Can't open pipe to fedd:$!\n";
620            my $proj = $auth_proj ? " -p $auth_proj " : "";
621            my @cmds;
622            my $rv;
623
624            print("Checking access to $tb using " . $tbparams->{$tb}->{'uri'}
625                . "\n") if $verbose;
626
627            # First access command, implicitly uses localhost fedd
628            push(@cmds,"$fedd_client -t " . 
629                $tbparams->{$tb}->{'uri'} .  " -T $ENV{HOME}/cacert.pem ".
630                "-l $tb $proj" . (@nodes ? " -n " : " ") .
631                join(" -n ", @nodes));
632            # Second try access command, implicitly directly contact testbed
633            push(@cmds,"$fedd_client -t " . 
634                $tbparams->{$tb}->{'uri'} .  " -u " .
635                $tbparams->{$tb}->{'uri'} .  " -T $ENV{HOME}/cacert.pem ".
636                "-l $tb $proj" . (@nodes ? " -n " : " ") .
637                join(" -n ", @nodes));
638
639            foreach my $c (@cmds) {
640                print "$c\n" if $verbose;
641                $rv = &fedd_access_request($c);
642                warn($rv) if $rv;
643
644                last if $rv eq undef;
645            }
646            die "Cannot get access to $tb\n"  if $rv;
647        }
648        next;
649    };
650
651    # Start of a sub-experiment
652    /^#\s+Begin\s+Testbed\s+\((\w+)\)/ && do {
653        $ctb = $1;
654
655        # If we know the testbed, start collecting its sub experiment tcl
656        # description.  If not, warn the user.
657        if ($tbparams->{$ctb}->{'access'}) {
658            $allocated{$ctb}++; # Keep track of the testbeds allocated
659
660            unless (-d "$tmpdir/$ctb") {
661                mkdir("$tmpdir/$ctb") || die "Can't create $tmpdir/$ctb: $!";
662            }
663            $destfile = "$tmpdir/$ctb/$eid.$ctb.tcl";
664
665            $desthandle = new IO::File(">$destfile") || 
666                die "Cannot open $destfile:$!\n";
667        }
668        else{
669            warn "No such testbed $ctb\n";
670            $destfile = "";
671        }
672        next;
673    };
674
675    # End of that experiment
676    /^#\s+End\s+Testbed\s+\((\w+)\)/ && do {
677        # Simple syntax check and close out this experiment's tcl description
678        die "Mismatched testbed markers ($1, $ctb)\n" unless ($1 eq $ctb);
679        $desthandle->close() if $desthandle;
680        $destfile = $ctb = "";
681        next;
682    };
683
684    # Beginning of a gateway set
685    /^#\s+Begin\s+gateways\s+\((\w+)\)/ && do {
686        $gateways = $1;
687        # If we've heard of this tb, create the config lines for it one at a
688        # time.
689        if ($allocated{$gateways}) {
690            # Just in case.  This directory should already have been created
691            # above.
692            unless (-d "$tmpdir/$gateways") {
693                mkdir("$tmpdir/$gateways") || 
694                    die "Can't create $tmpdir/$gateways: $!";
695            }
696        }
697        else {
698            warn "Gateways given (and ignored) for testbed not in use: " .
699                "$gateways\n";
700            $gateways = 0;
701        }
702        next;
703    };
704    # End of the gateways section.  Output the client config for this testbed
705    /^#\s+End\s+gateways\s+\((\w+)\)/ && do {
706        die "Mismatched gateway markers ($1, $gateways)\n" 
707            unless !$gateways || $gateways == $1;
708
709        if ($control_gateway ) {
710            # Client config
711            my $cc = new IO::File(">$tmpdir/$gateways/client.conf");
712            my $master_project = $tbparams->{$master}->{'project'};
713            die "Can't open $tmpdir/$gateways/client.conf: $!\n" unless $cc;
714            print $cc "ControlGateway: $control_gateway\n";
715            print $cc "SMBShare: $smb_share\n";
716            print $cc "ProjectUser: $project_user\n";
717            print $cc "ProjectName: $master_project\n";
718            $cc->close();
719        }
720        else { warn "No control gateway for $gateways?\n"; }
721           
722        $gateways = 0;
723        next;
724    };
725    # Beginning of the hostnames list.  Collection is always in the hostnames
726    # file.
727    /^#\s+Begin\s+hostnames/ && do {
728        $destfile = "$tmpdir/hostnames";
729        $desthandle = new IO::File(">$destfile") || 
730            die "Can't open $destfile:$!\n";
731        next;
732    };
733    # end of the hostnames list.
734    /^#\s+End\s+hostnames/ && do {
735        $desthandle->close();
736        $destfile = "";
737        next;
738    };
739
740    # Generate gateway configuration info, one file per line
741    $gateways && do {
742        chomp;
743        my($dtb, $myname, $desthost, $type) = split(" ", $_);
744
745        # Many of these are to simplify print statements
746        my $sdomain =                           # domain for the source
747            $tbparams->{$gateways}->{'domain'};
748        my $ddomain =                           # domain for the destination
749            $tbparams->{$dtb}->{'domain'};
750        my $sproject =                          # Project of the source
751            $tbparams->{$gateways}->{'project'};
752        my $dproject =                          # Project of the destination
753            $tbparams->{$dtb}->{'project'};
754        my $fs =                                # Master fs node (FQDN)
755            $tbparams->{$master}->{'fs'} .  $tbparams->{$master}->{'domain'};
756        my $boss =                              # Master boss node (FQDN)
757            $tbparams->{$master}->{'boss'} .  $tbparams->{$master}->{'domain'};
758        my $event_server =                      # Master event-server (FQDN)
759            $tbparams->{$master}->{'eventserver'} . 
760            $tbparams->{$master}->{'domain'};
761        my $remote_event_server =               # Slave event-server (FQDN)
762            $tbparams->{$dtb}->{'eventserver'} . 
763            $tbparams->{$dtb}->{'domain'};
764        my $remote_script_dir =                 # Remote fed script location
765            "/proj/" . $dproject . "/exp/$eid/tmp";
766        my $local_script_dir =                  # Local fed script location
767            "/proj/" . $sproject . "/exp/$eid/tmp";
768        my $active;                             # Is this the active side of
769                                                # the connector?
770        my $tunnel_cfg =                        # Use DETER's config stuff
771            $tbparams->{$gateways}->{'tun'} || "false";
772                                   
773
774        $sdomain = ".$eid." . $tbparams->{$gateways}->{'project'} . "$sdomain";
775        $ddomain = ".$eid." . $tbparams->{$dtb}->{'project'} . "$ddomain";
776
777        my $conf_file = "$myname$sdomain.gw.conf";
778        my $remote_conf_file = "$desthost$ddomain.gw.conf";
779        # translate to lower case so the `hostname` hack for specifying
780        # configuration files works.
781        $conf_file =~ tr/A-Z/a-z/;
782        $remote_conf_file =~ tr/A-Z/a-z/;
783
784        # If either end of this link is in the master side of the testbed, that
785        # side is the active end. Otherwise the first testbed encountered in
786        # the file will be the active end.  The $active_end variable keeps
787        # track of those decisions
788        if ( $dtb eq $master ) { $active = "false"; }
789        elsif ($gateways eq $master ) { $active = "true"; }
790        elsif ( $active_end{"$dtb-$gateways"} ) { $active="false"; }
791        else { $active_end{"$gateways-$dtb"}++; $active = "true"; }
792
793        # This is used to create the client configuration.
794        $control_gateway = "$myname$sdomain"
795            if $type =~ /(control|both)/;
796
797        # Write out the file
798        my $gwconfig = new IO::File(">$tmpdir/$gateways/$conf_file")|| 
799            die "can't open $tmpdir/$gateways/$conf_file: $!\n";
800
801        print $gwconfig "Active: $active\n";
802        print $gwconfig "TunnelCfg: $tunnel_cfg\n";
803        print $gwconfig "BossName: $boss\n";
804        print $gwconfig "FsName: $fs\n";
805        print $gwconfig "EventServerName: $event_server\n";
806        print $gwconfig "RemoteEventServerName: $remote_event_server\n";
807        print $gwconfig "Type: $type\n";
808        print $gwconfig "RemoteScriptDir: $remote_script_dir\n";
809        print $gwconfig "EventRepeater: $local_script_dir/fed_evrepeater\n";
810        print $gwconfig "RemoteExperiment: $dproject/$eid\n";
811        print $gwconfig "LocalExperiment: $sproject/$eid\n";
812        print $gwconfig "RemoteConfigFile: " . 
813            "$remote_script_dir/$remote_conf_file\n";
814        print $gwconfig "Peer: $desthost$ddomain\n";
815        print $gwconfig "Pubkeys: " . 
816            "/proj/$sproject/exp/$eid/tmp/$gw_pubkey_base\n";
817        print $gwconfig "Privkeys: " .
818            "/proj/$sproject/exp/$eid/tmp/$gw_secretkey_base\n";
819        $gwconfig->close();
820
821        # This testbed has a gateway (most will) so make a copy of the keys it
822        # needs in this testbed's subdirectory.  start_segment will transfer
823        # them.
824        unless (-r "$tmpdir/$gateways/$gw_pubkey_base" ) {
825            copy($gw_pubkey, "$tmpdir/$gateways/$gw_pubkey_base") ||
826                die "Can't copy pubkeys ($gw_pubkey to " . 
827                    "$tmpdir/$gateways/$gw_pubkey_base): $!\n";
828        }
829        if ($active eq "true" ) {
830            unless (-r "$tmpdir/$gateways/$gw_secretkey_base" ) {
831                copy($gw_secretkey, "$tmpdir/$gateways/$gw_secretkey_base") ||
832                    die "Can't copy secret keys ($gw_secretkey to " . 
833                        "$tmpdir/$gateways/$gw_secretkey_base): $!\n";
834            }
835        }
836
837        #done processing gateway entry, ready for next line
838        next; 
839    };
840    (/^#\s+Begin\s+tarfiles/../^#\s+End\s+tarfiles/) && do {
841        next if /^#/;
842        chomp;
843        push(@tarfiles, $_);
844        next;
845    };
846    (/^#\s+Begin\s+rpms/../^#\s+End\s+rpms/) && do {
847        next if /^#/;
848        chomp;
849        push(@rpms, $_);
850        next;
851    };
852
853    next unless $destfile;  # Unidentified testbed, ignore config
854    # local copies that can be used in the substitutions below
855    my $gwtype = $tbparams->{$ctb}->{'gwtype'} || $def_gwtype;
856    my $gwimage = $tbparams->{$ctb}->{'gwimage'} || $def_gwimage;
857    my $mgwstart = $tbparams->{$ctb}->{'mgwstart'} || $def_mgwstart;
858    my $mexpstart = $tbparams->{$ctb}->{'mexpstart'} || $def_mexpstart;
859    my $gwstart = $tbparams->{$ctb}->{'gwstart'} || $def_gwstart;
860    my $expstart = $tbparams->{$ctb}->{'expstart'} || $def_expstart;
861    my $project = $tbparams->{$ctb}->{'project'};
862
863    # Substitute variables
864    s/GWTYPE/$gwtype/g;
865    s/GWIMAGE/$gwimage/g;
866    if ($ctb eq $master ) {
867        s/GWSTART/$mgwstart/g;
868        s/EXPSTART/$mexpstart/g;
869    }
870    else {
871        s/GWSTART/$gwstart/g;
872        s/EXPSTART/$expstart/g;
873    }
874    # XXX: oh is this bad
875    s#GWCONF#FEDDIR\`hostname\`.gw.conf#g;
876    s#PROJDIR#/proj/$project/#g;
877    s#EID#$eid#g;
878    s#FEDDIR#/proj/$project/exp/$eid/tmp/#g;
879    print $desthandle $_;
880}
881$pipe->close();
882die "No nodes in master testbed ($master)\n" unless $allocated{$master};
883
884# Copy tarfiles and rpms needed at remote sites to the staging directories.
885# Start_segment will distribute them
886for my $t  (@tarfiles) {
887    die "tarfile '$t' unreadable: $!\n" unless -r $t;
888    unless (-d "$tmpdir/tarfiles") {
889        mkdir("$tmpdir/tarfiles") || 
890            die "Can't create $tmpdir/tarfiles:$!\n";
891    }
892    copy($t, "$tmpdir/tarfiles") || 
893        die "Can't copy $t to  $tmpdir/tarfiles:$!\n";
894}
895
896for my $r  (@rpms) {
897    die "rpm '$r' unreadable: $!\n" unless -r $r;
898    unless (-d "$tmpdir/rpms") {
899        mkdir("$tmpdir/rpms") || 
900            die "Can't create $tmpdir/rpms:$!\n";
901    }
902    copy($r, "$tmpdir/rpms") || 
903        die "Can't copy $r to  $tmpdir/rpms:$!\n";
904}
905
906exit(0) unless $startem;
907
908my %started;                # If $started{$tb} then $tb successfully started
909my %child;                  # If $child{$pid} then a process with that pid is
910                            # working on a starting a segment
911my $nworking = 0;           # Number of children working on swapin
912my $pid;                    # Scratch variable for pids
913
914# Start up the slave sub-experiments first
915TESTBED:
916for my $tb  (keys %allocated) {
917    if ( $tb ne $master ) {
918        while ( $nworking == $max_children ) {
919            print "Waiting for a child process to complete\n" if $verbose;
920            if (($pid = wait()) != -1 ) {
921                # The $? >> 8 is the exit code of the subprocess, which is
922                # non-zero if the &start_segment routine failed.
923                my $exit_code = ($? >> 8);
924
925                print "Child $pid completed exit code ($exit_code)\n"
926                    if $verbose;
927                $nworking--;
928                $started{$child{$pid}}++ unless $exit_code;
929                if ($child{$pid} ) { delete $child{$pid}; }
930                else { warn "Reaped a pid we did not start?? ($pid)\n"; }
931                last TESTBED if $exit_code;
932            }
933            else { warn "wait returned without reaping: $!\n"; }
934        }
935        if ( $pid = fork() ) {
936            # Parent process
937            $nworking ++;
938            $child{$pid} = $tb;
939            print "Started process $pid to start testbed $tb\n"
940                if $verbose;
941        }
942        else {
943            # Child.  Note that we reverse the sense of the return code when it
944            # becomes an exit value.  Zero exit values indicate success.
945            exit(!&start_segment($tb, $eid, $tbparams, $timeout));
946        }
947    }
948}
949
950# Now wait for any still running processes.
951while ( $nworking ) {
952    print "Waiting for a child process to complete ($nworking running)\n" 
953        if $verbose;
954    if (($pid = wait()) != -1 ) {
955        # The $? >> 8 is the exit code of the subprocess, which is
956        # non-zero if the &start_segment routine failed.
957        my $exit_code = ($? >> 8);
958
959        print "Child $pid completed exit code ($exit_code)\n"
960            if $verbose;
961        $nworking--;
962        $started{$child{$pid}}++ unless $exit_code;
963        if ($child{$pid} ) { delete $child{$pid}; }
964        else { warn "Reaped a pid we did not start?? ($pid)\n"; }
965    }
966    else { warn "wait returned without reaping: $!\n"; }
967}
968
969# Now the master
970if (&start_segment($master, $eid, $tbparams, $timeout)) { 
971    $started{$master}++;
972}
973
974# If any testbed failed, swap the rest out.
975if ( !$fail_soft && scalar(keys %started) != scalar(keys %allocated)) {
976    for my $tb (keys %started) { &stop_segment($tb, $eid, $tbparams); }
977    print "Error starting experiment\n";
978    exit(1);
979}
980print "Experiment started\n";
981print "Deleting $tmpdir (-d to leave them in place)\n" if $verbose && !$debug;
982system("rm -rf $tmpdir") unless $debug;
983exit(0);    # set the exit value
984
985=pod
986
987=head1 NAME
988
989B<splitter.pl>
990
991=head1 SYNOPSIS
992
993B<splitter.pl> [B<-ndF>] [B<-t> I<secs>] [B<-c> F<config_file>]
994    [B<-f> F<experiment_tcl>] [B<-p> I<max_procs>] [F<experiment_tcl>]
995
996=head1 DESCRIPTION
997
998B<splitter.pl> invokes the DETER experiment parser to split an annotated
999experiment into multiple sub-experments and instantiates the sub-experiments on
1000their intended testbeds.  Annotation is accomplished using the
1001tb-set-node-testbed command, added to the parser.
1002
1003Much of the script's behavior depends on the configuration file, specified with
1004the B<-c> flag and defaulting to F<./splitter.conf>.
1005
1006The testbed labels supplied in the B<tb-set-node-testbed> command are
1007meaningful based on their presence in the testbeds file.  that file can be
1008specified in the configuration file using the B<Testbeds> directive, and
1009defaults to F<./testbeds>.  The syntax is described below.
1010
1011Most of the intermediate files are staged in a sub-directory of a temporary
1012files directory and deleted at the end of the script.  Specifying the B<-d>
1013flag on the command line avoids the deletion for debbugging.  By default the
1014temporary files directory is directory is F</tmp> and can be reset in the
1015configuration file using the B<Tmpdir> directive.  Intermediate files are
1016stored under a subdirectory formed by adding the process ID of the splitter
1017process.  For example, if the temporary files directory is F</tmp> and the
1018B<splitter.pl> process ID is 2323, the temporary files will be stored in
1019F</tmp/split2323/>.
1020
1021The expreriment is split out into one experiment description per testbed in the
1022temporary directory named as F<experiment.testbed.tcl> where the experiment is
1023the experiment ID given in the configuration file, and the testbed is the
1024tb-set-node-testbed parameter for the nodes in the file.
1025
1026If the B<-n> option is absent the sub-experiments are then instantiated on
1027their testbeds.  (Here B<-n> is analogous to its use in L<make(1)>).
1028Per-testbed parameters are set in the testbeds file.  Sub-experiments on
1029slave testbeds are instantiated in a random order, but the master testbed is
1030currently instantiated last.
1031
1032Slave testbeds can be swapped in in parallel by specifying the B<-p> parameter
1033and the maximum number of simultaneous processes to start.
1034
1035Scripts to start federation (the federation kit) are copied into the local
1036experiment's tmp file - e.g., F</proj/DETER/exp/simple-split/tmp>.  These are
1037taken from the directory given by the B<ScriptDir> directive in the
1038configuration file.
1039
1040If B<-t> is given the parameter is treated as a parameter to B<Timeout> in
1041F<splitter.conf>.
1042
1043If any sub-experiment fails to instantiate, the other sub-exeriments are
1044swapped out.  B<-F> avoids this swap out, which can also be specified as
1045B<SoftFail: true> in F<splitter.conf>
1046
1047=head2 Configuration File
1048
1049The configuration file is a simple set of colon-separated parameters and
1050values.  A configuration file must be present, either specified in the B<-c>
1051flag or the default F<./splitter.conf>.  All the parameter names are case
1052insensitive, but should not include any whitespace.  Parameter values may
1053include whitespace, but no newlines.
1054
1055Possible parameters are:
1056
1057=over 5
1058
1059=item Experiment
1060
1061The name of the experiment on the various testbeds
1062
1063=item Master
1064
1065The master testbed label from the testbeds file, described below.
1066
1067=item Testbeds
1068
1069The testbeds file described below, giving per-testbed parameters.  If this
1070directive is absent the testbeds file defaults to F<./testbeds>
1071
1072=item ScriptDir
1073
1074Location of the default federation scripts, i.e. the federation kit.
1075
1076=item GatewayPubkey
1077
1078=item GatewaySecretKey
1079
1080The names of the files containing secret and public keys to use in setting up
1081tunnels between testbeds.  If given they are used, otherwise keys are generated.
1082
1083=item GatewayKeyType
1084
1085This controls the kind of SSH keys generated to configure the geatways.  If
1086given this must be B<dsa> or B<rsa>, and it defaults to B<rsa>.  The parameter
1087is csase insensitive.
1088
1089=item TmpDir
1090
1091=item TempDir
1092
1093The directory where temporary files are created.  These are synonyms, but
1094should both be specified, B<TmpDir> has priority.  If neither is specified,
1095F</tmp> is used.
1096
1097=item SMBShare
1098
1099The SMB share on the master testbed that will be exported to remote clients.
1100
1101=item SMBUser
1102
1103The experiment user to mount project directories as.  This user needs to be a
1104member of the exported experiment - that is one of the users in the project
1105containing this experiment on the master testbed.
1106
1107=item Timeout
1108
1109Value in seconds after which a swap-in operatioin will be considered a success.
1110Often long swap-ins will hang when there are partial failures.  This works
1111around this issue.  (This behavior can be requested on the command line by
1112specifying B<-t> I<secs>.)
1113
1114=item FailSoft
1115
1116If not set, failure of any sub experiment swaps the rest out.  Setting this to
1117any value avoids this swap out.  (This behavior can be requested on the command
1118line by specifying B<-F>.)
1119
1120=item MuxLimit
1121
1122The maximum bumber of links/lans carried by one gateway pair
1123
1124=item Tclparse
1125
1126The pathname to the experiment parsing program.  Only developers should set
1127this.
1128
1129=item Tclsh
1130
1131The pathname to the local oTcl shell.  Only developers should set
1132this.
1133
1134=back
1135
1136=head2 Testbeds file
1137
1138The configuration file (F<./testbeds> unless overridden by B<-c>) is a file of
1139scoped attribute-value pairs where each attribute is specified on a separate
1140line of the configuration file.  Each testbed's parameters are preceeded by the
1141testbed label in brackets ([]) on a line by itself.  After that the parameters
1142are specified as parameter: value.  This is essentially the same format as the
1143configuration file.  Parameters are:
1144
1145=over 4
1146
1147=item User
1148
1149The user under which to make requests to this testbed.  The user running
1150B<splitter.pl> must be able to authenicate as this user under L<ssh(1)> to this
1151testbed.
1152
1153=item OpsNode
1154
1155The host name of the testbed's ops node.  The user calling B<splitter.pl> must
1156be able to execute commands on this host via L<ssh(1)>.
1157
1158=item Domain
1159
1160The domain of nodes in this testbed (including the ops host).  This parameter
1161should always start with a period.
1162
1163=item Project
1164
1165The project under which to instantiate sub-experiments on this testbed.
1166
1167=item ConnectorType
1168
1169The node type for inter-testbed connector nodes on this testbed.
1170
1171=item SlaveNodeStartCmd
1172
1173The start command to run on experimental nodes when this testbed is used as a
1174slave.  In all the start commands the following string substitutions are made:
1175
1176=over 10
1177
1178=item FEDDIR
1179
1180The local experiment's federation scripts directory.  Each local experiment
1181will have this replaced by the scripts directory on its local boss.
1182
1183=item GWCONF
1184
1185The full pathname of the gateway configuration file.  As with FEDDIR, this is
1186on the local boss.
1187
1188=item PROJDIR
1189
1190The project directory on the local boss.
1191
1192=item EID
1193
1194The local experiment name.
1195
1196=back
1197
1198All startcmds specified in F<testbeds> undergo these expansions.
1199
1200=item SlaveConnectorStartCmd
1201
1202The start command to run on gateway nodes when this testbed is used as a slave.
1203The same string substitutions are made in this command as in SlaveNodeStartCmd.
1204
1205=item MasterNodeStartCmd
1206
1207The start command to run on experimental nodes when this testbed is used as a
1208master.  The same string substitutions are made in this command as in
1209SlaveNodeStartCmd.
1210
1211=item MasterConnectorStartCmd
1212
1213The start command to run on gateway nodes when this testbed is used as a
1214master.  The same string substitutions are made in this command as in
1215SlaveNodeStartCmd.
1216
1217=item ConnectorImage
1218
1219The disk image to be loaded on a gateway node on this testbed.
1220
1221=item FileServer
1222
1223The node in the master testbed from which filesystems are mounted.
1224
1225=item Boss
1226
1227The node in the master testbed that controls the testbed.
1228
1229=item TunnelCfg
1230
1231True if the connector needs to do DETER federation.  This parameter will
1232probably be removed.
1233
1234
1235=back
1236
1237=head1 ENVIRONMENT
1238
1239B<splitter.pl> does not directly make use of environment variables, but calls
1240out to L<ssh(1)> and (indirectly) to L<sh(1)>, which may be influenced by the
1241environment.
1242
1243=head1 BUGS
1244
1245A deprecated B<-N> flag was used to select testbeds file format.  Only one
1246format is supported now, and B<-N> generates a warning, but otherwise does not
1247affect B<splitter.pl>.
1248
1249=head1 SEE ALSO
1250
1251L<sh(1)>, L<ssh(1)>
1252
1253=cut
Note: See TracBrowser for help on using the repository browser.