source: fedkit/splitter.pl @ 321c0cb

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

add FsName? BossName? and TunnelCfg?

  • Property mode set to 100644
File size: 21.5 KB
Line 
1#!/usr/bin/perl
2
3use Getopt::Std;
4use IO::File;
5use IO::Dir;
6use IO::Pipe;
7use File::Copy;
8
9@scripts = ("federate.sh", "smbmount.pl");
10$local_script_dir = ".";
11
12# Parse the config file.  The format is a colon-separated parameter name
13# followed by the value of that parameter to the end of the line.  This parses
14# that format and puts the parameters into the referenced hash.  Parameter
15# names are mapped to lower case, parameter values are unchanged.  Returns 0 on
16# failure (e.g. file open) and 1 on success.
17sub parse_config {
18    my($file, $href) = @_;
19    my($fh) = new IO::File($file);
20       
21    unless ($fh) {
22        warn "Can't open $file: $!\n";
23        return 0;
24    }
25
26    while (<$fh>) {
27        next if /^\s*#/ || /^\s*$/;     # Skip comments & blanks
28        chomp;
29        /^([^:]+):\s*(.*)/ && do {
30            my($key) = $1; 
31
32            $key =~ tr/A-Z/a-z/;
33            $href->{$key} = $2;
34            next;
35        };
36        warn "Unparasble line in $file: $_\n";
37    }
38    $fh->close();   # It will close when it goes out of scope, but...
39    return 1;
40}
41
42
43# use scp to transfer a file, reporting true if successful and false otherwise.
44# Parameters are the local file name, the ssh host destination (either hostname
45# oe user@host), and an optional destination file name or directory.  If no
46# destination is given, the file is transferred to the given user's home
47# directory.  If only a machine is given in the ssh host destination, the
48# current user is used.
49sub scp_file {
50    my($file, $where, $dest) = @_;
51
52    # XXX system with a relative pathname is sort of gross
53    system("scp $file $where:$dest");
54    if ($?) {
55        warn "scp failed $?\n";
56        return 0;
57    }
58    else { return 1; }
59}
60
61# use ssh to execute the given command on the machine (and as the user) in
62# $where.  Parameters are the ssh destination directive ($where) and the
63# command to execute, and a prefix to be placed on a message generated if the
64# command fails.   On failure print a warning if a warning prefix was given and
65# return false.
66sub ssh_cmd {
67    my($user, $host, $cmd, $wname) = @_;
68
69    # XXX system with a relative pathname is sort of gross
70    system ("ssh $user\@$host $cmd");
71    if ($?) {
72        warn "$wname failed $?\n" if $wname;
73        return 0;
74    }
75    else { return 1; }
76}
77
78# Ship local copies of the federation scripts out to the given host.  If any of
79# the script transfers fails, return 0.  The scripts to transfer are from the
80# global @scripts and are found locally in $local_script_dir (another global).
81sub ship_scripts {
82    my($host, $user, $dest_dir) = @_;       # Where, who, where remotely
83    my($s);
84
85    &ssh_cmd($user, $host, "mkdir -p $dest_dir");
86    for $s (@scripts) {
87        &scp_file("$local_script_dir/$s", "$user\@$host", $dest_dir) || 
88            return 0;
89    }
90    return 1;
91}
92
93# Ship per-testbed configuration generated by this script to the remote /proj
94# directories on the remote testbeds
95sub ship_configs {
96    my($host, $user, $src_dir, $dest_dir) = @_;     # Where, who, where remotely
97    my($d, $f);
98
99
100    $d = IO::Dir->new($src_dir) || return 0;
101
102    &ssh_cmd($user, $host, "mkdir -p $dest_dir");
103    while ( $f = $d->read()) {
104        next if $f =~ /^\./;
105        if ( -d "$src_dir/$f" ) {
106            &ship_configs($host, $user, "$src_dir/$f", "$dest_dir/$f") || 0;
107        }
108        else {
109            &scp_file("$src_dir/$f", "$user\@$host", $dest_dir) || return 0;
110        }
111    }
112    return 1;
113}
114
115
116
117
118# Start a sub section of the experiment on a given testbed.  The testbed and
119# the user to start the experiment as are pulled from the global per-testbed
120# hash, as is the project name on the remote testbed.  Parameters are the
121# testbed and the experiment id.  Configuration files are scp-ed over to the
122# target testbed from the global $tmpdir/$tb directory.  Then the current state
123# of the experiment determined using expinfo.  From that state, the experiment
124# is either created, modified or spapped in.  If everything succeeds, true is
125# returned.
126sub start_segment {
127    my($tb, $eid) = @_;                     # testbed and experiment ID
128    my($host) = "$host{$tb}$domain{$tb}";   # Host name of remote ops (FQDN)
129    my($user) = $user{$tb};                 # user to pass to ssh
130    my($pid) = $project{$tb};               # remote project to start the
131                                            # experiment under
132    my($tclfile) = "./$eid.$tb.tcl";        # Local tcl file with the
133                                            # sub-experiment
134    my($proj_dir) = "/proj/$pid/exp/$eid/tmp";  # Where to stash federation stuff
135    my($to_hostname) = "$proj_dir/hosts";   # remote hostnames file
136    my($status) = new IO::Pipe;             # The pipe to get status
137
138    # Determine the status of the remote experiment
139    $status->reader("ssh $user\@$host /usr/testbed/bin/expinfo $pid $eid") || 
140        die "Can't ssh to $user\@$host:$!\n";
141
142    # XXX: this is simple now.  Parsing may become more complex
143    while (<$status>) {
144        /State: (\w+)/ && ($state = $1);
145        /No\s+such\s+experiment/ && ($state = "none");
146    }
147    $status->close();
148    print "$tb: $state\n";
149
150    # Copy the experiment definition data over (unless the host is local)
151    &scp_file("$tmpdir/$tb/$tclfile", "$user\@$host") || return 0;
152    # Remote experiment is active.  Modify it.
153    if ($state eq "active") {
154        # First copy new scripts and hostinfo into the remote /proj
155        &scp_file("$tmpdir/$tb/hostnames", "$user\@$host", $to_hostname) ||
156            return 0;
157        &ship_scripts($host, $user, $proj_dir) || return 0;
158        &ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
159        &ssh_cmd($user, $host, "/usr/testbed/bin/modexp -r -s -w $pid " . 
160            "$eid $tclfile", "modexp") || return 0;
161        return 1;
162    }
163
164    # Remote experiment is swapped out, modify it and swap it in.
165    if ($state eq "swapped") {
166        # First copy new scripts and hostinfo into the remote /proj (because
167        # the experiment exists, the directory tree should be there.
168        &scp_file("./hostnames", "$user\@$host", $to_hostname) || return 0;
169        &ship_scripts($host, $user, $proj_dir) || return 0;
170        &ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
171        &ssh_cmd($user, $host, "/usr/testbed/bin/modexp -w $pid $eid $tclfile", 
172            "modexp") || return 0;
173        # Now start up
174        &ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid in", 
175            "swapexp") || return 0;
176        return 1;
177    }
178
179    # No remote experiment.  Create one.  We do this in 2 steps so we can put
180    # the configuration files and scripts into the new experiment directories.
181    if ($state eq "none") {
182        &ssh_cmd($user, $host, "/usr/testbed/bin/startexp -f -w -p " . 
183            "$pid -e $eid $tclfile", "startexp") || return 0;
184        # First copy new scripts and hostinfo into the remote /proj
185        &scp_file("./hostnames", "$user\@$host", $to_hostname) || return 0;
186        &ship_scripts($host, $user, $proj_dir) || return 0;
187        &ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
188        # Now start up
189        &ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid in", 
190            "swapexp") || return 0;
191        return 1;
192    }
193
194    # Every branch for a known state returns.  If execution gets here, the
195    # state is unknown.
196    warn "unknown state: $state\n";
197    return 0;
198}
199
200# Swap out a sub-experiment - probably because another has failed.  Arguments
201# are testbed and experiment.  Most of the control flow is similar to
202# start_segment, though much simpler.
203sub stop_segment {
204    my($tb, $eid) = @_;
205    my($user) = "$user{$tb}";
206    my($host) = "$host{$tb}$domain{$tb}";
207    my($pid) = $project{$tb};
208
209    &ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid out", 
210        "swapexp (out)") || return 0;
211    return 1;
212}
213
214
215$pid = $gid = "dummy";              # Default project and group to pass to
216                                    # $tcl_splitter above.  These are total
217                                    # dummy arguments;  the splitter doesn't
218                                    # use them at all, but we supply them to
219                                    # keep our changes to the parser minimal.
220# Argument processing.
221getopts('c:f:nd', \%opts);
222$splitter_config = $opts{'c'} || "./splitter.conf";
223$debug = $opts{'d'};
224&parse_config("$splitter_config", \%opts) || 
225    die "Cannot read config file $splitter_conf: $!\n";
226
227
228$startem = $opts{'n'} ? 0 : 1;          # If true, start the sub-experiments
229$eid = $opts{'experiment'};             # Experiment ID
230$tcl = $opts{'f'} || shift;             # The experiment description
231$master = $opts{'master'};              # Master testbed
232$tmpdir = $opts{'tmpdir'} || $opts{'tempdir'}|| "/tmp"; # tmp files
233$tb_config = $opts{'testbeds'} || "./testbeds"; # testbed configurations
234$local_script_dir = $opts{'scriptdir'}; # Local scripts
235# For now specify these.  We may want to generate them later.
236$gw_pubkey = $opts{'gatewaypubkey'};
237($gw_pubkey_base = $gw_pubkey) =~ s#.*/##;
238$gw_secretkey = $opts{'gatewaysecretkey'};
239($gw_secretkey_base = $gw_secretkey) =~ s#.*/##;
240
241# tcl program to split experiments (changed during devel)
242$tcl_splitter = $opts{'tclparse'} || "/usr/testbed/lib/ns2ir/parse.tcl";
243# tclsh to call directly (changed during devel)
244$tclsh = $opts{'tclsh'} || "/usr/local/bin/otclsh";
245
246# Prefix to avoid collisions
247$tmpdir .= "/split$$";
248
249# Create a workspace
250unless (-d "$tmpdir") {
251    mkdir("$tmpdir") || die "Can't create $tmpdir: $!";
252}
253
254# Validate scripts directory
255for $s (@scripts) {
256    die "$local_script_dir/$s not in local script directory. Try -d\n"
257        unless -r "$local_script_dir/$s";
258}
259
260die "Must supply file, master and experiment" unless $master && $tcl && $eid;
261
262# Read a hash of per-testbed parameters from the local configurations.
263$conf = new IO::File($tb_config) || 
264    die "can't read testbed configutions from $tb_config: $!\n";
265while (<$conf>) {
266    next if /^#/;
267    chomp;
268    ($tb, $h, $d, $u, $p, $es, $gs, $mes, $mgs, $t, $i, $fs, $boss, $tun) = 
269        split(":", $_);
270    $host{$tb} = $h;
271    $user{$tb} = $u;
272    $domain{$tb} = $d;
273    $project{$tb} = $p;
274    $gwtype{$tb} = $t;
275    $expstart{$tb} = $es;
276    $gwstart{$tb} = $gs;
277    $mexpstart{$tb} = $mes;
278    $mgwstart{$tb} = $mgs;
279    $gwimage{$tb} = $i;
280    $fs{$tb} = $fs;
281    $boss{$tb} = $boss;
282    $tun{$tb} = $tun;
283
284    # Make sure the domain starts with a period
285    $domain{$tb} = ".$domain{$tb}" unless $domain{$tb} =~ /^\./;
286}
287$conf->close();
288
289# Open a pipe to the splitter program and start it parsing the experiments
290$pipe = new IO::Pipe;
291# NB no more -p call on parse call.
292$pipe->reader("$tclsh $tcl_splitter -s -m $master  $pid $gid $eid $tcl") || 
293    die "Cannot execute $tclsh $tcl_splitter -s -m $master $pid $gid $eid $tcl:$!\n";
294
295# Parse the splitter output.  This loop creates the sub experiments, gateway
296# configurations and hostnames file
297while (<$pipe>) {
298    # Start of a sub-experiment
299    /^#\s+Begin\s+Testbed\s+\((\w+)\)/ && do {
300        $ctb = $1;
301
302        # If we know the testbed, start collecting its sub experiment tcl
303        # description.  If not, warn the caller and ignore the configuration of
304        # this testbed.
305        if ($host{$ctb}) {
306            $allocated{$ctb}++; # Keep track of the testbeds allocated
307
308            unless (-d "$tmpdir/$ctb") {
309                mkdir("$tmpdir/$ctb") || die "Can't create $tmpdir/$ctb: $!";
310            }
311            $destfile = "$tmpdir/$ctb/$eid.$ctb.tcl";
312
313            open(FILE, ">$destfile") || die "Cannot open $destfile:$!\n";
314        }
315        else { 
316            warn "No such testbed $ctb\n";
317            $destfile = "";
318        }
319        next;
320    };
321
322    # End of that experiment
323    /^#\s+End\s+Testbed\s+\((\w+)\)/ && do {
324        # Simple syntax check and close out this experiment's tcl description
325        die "Mismatched testbed markers ($1, $ctb)\n" unless ($1 eq $ctb);
326        close(FILE);
327        $destfile = $ctb = "";
328        next;
329    };
330
331    # Beginning of a gateway set
332    /^#\s+Begin\s+gateways\s+\((\w+)\)/ && do {
333        $gateways = $1;
334        # If we've heard of this tb, create the config lines for it one at a
335        # time.
336        if ($allocated{$gateways}) {
337            # Just in case.  This directory should already have been created
338            # above.
339            unless (-d "$tmpdir/$gateways") {
340                mkdir("$tmpdir/$gateways") || 
341                    die "Can't create $tmpdir/$gateways: $!";
342            }
343        }
344        else {
345            warn "Gateways given (and ignored) for testbed not in use: " .
346                "$gateways\n";
347            $gateways = 0;
348        }
349        next;
350    };
351    /^#\s+End\s+gateways\s+\((\w+)\)/ && do {
352        die "Mismatched gateway markers ($1, $gateways)\n" 
353            unless !$gateways || $gateways == $1;
354        $gateways = 0;
355        next;
356    };
357    # Beginning of the hostnames list.  Collection is always in the hostnames
358    # file.
359    /^#\s+Begin\s+hostnames/ && do {
360        $destfile = "$tmpdir/hostnames";
361        open(FILE, ">$destfile") || die "Can't open $destfile:$!\n";
362        next;
363    };
364    # end of the hostnames list.
365    /^#\s+End\s+hostnames/ && do {
366        close(FILE);
367        $destfile = "";
368        next;
369    };
370
371    # Generate gateway configuration info, one file per line
372    $gateways && do {
373        chomp;
374        my($dtb, $myname, $desthost, $type) = split(" ", $_);
375        my($sdomain) = $domain{$gateways};      # domain for the source
376        my($ddomain) = $domain{$dtb};           # domain for the destination
377        my($sproject) = $project{$gateways};    # Project of the destination
378
379        $sdomain = ".$eid.$project{$gateways}$sdomain";
380        $ddomain = ".$eid.$project{$dtb}$ddomain";
381
382        # If either end of this link is in the master side of the testbed, that
383        # side is the active end. Otherwise the first testbed encountered in
384        # the file will be the active end.  The $active_end variable keeps
385        # track of those decisions
386        if ( $dtb eq $master ) { $active = "false"; }
387        elsif ($gateways eq $master ) { $active = "true"; }
388        elsif ( $active_end{"$dtb-$gateways"} ) { $active="false"; }
389        else { $active_end{"$gateways-$dtb"}++; $active = "true"; }
390
391        # Write out the file
392        open(GWCONFIG, ">$tmpdir/$gateways/$myname$sdomain.gw.conf") || 
393            die "can't open $tmpdir/$gateways/$myname$sdomain.gw.conf: $!\n";
394
395        print GWCONFIG "Active: $active\n";
396        print GWCONFIG "TunnelCfg: $tun{$gateways}\n";
397        print GWCONFIG "BossName: $boss{$master}$domain{$master}\n";
398        print GWCONFIG "FsName: $fs{$master}$domain{$master}\n";
399        print GWCONFIG "Type: $type\n";
400        print GWCONFIG "Peer: $desthost$ddomain\n";
401        print GWCONFIG "Pubkeys: " . 
402            "/proj/$sproject/exp/$eid/tmp/$gw_pubkey_base\n";
403        print GWCONFIG "Privkeys: " .
404            "/proj/$sproject/exp/$eid/tmp/$gw_secretkey_base\n";
405        close(GWCONFIG);
406
407        # This testbed has a gateway (most will) so make a copy of the keys it
408        # needs in this testbed's subdirectory.  start_segment will transfer
409        # them.
410        unless (-r "$tmpdir/$gateways/$gw_pubkey_base" ) {
411            copy($gw_pubkey, "$tmpdir/$gateways/$gw_pubkey_base") ||
412                die "Can't copy pubkeys ($gw_pubkey to " . 
413                    "$tmpdir/$gateways/$gw_pubkey_base): $!\n";
414        }
415        if ($active eq "true" ) {
416            unless (-r "$tmpdir/$gateways/$gw_secretkey_base" ) {
417                copy($gw_secretkey, "$tmpdir/$gateways/$gw_secretkey_base") ||
418                    die "Can't copy secret keys ($gw_secretkey to " . 
419                        "$tmpdir/$gateways/$gw_secretkey_base): $!\n";
420            }
421        }
422
423        #done processing gateway entry, ready for next line
424        next; 
425    };
426    (/^#\s+Begin\s+tarfiles/../^#\s+End\s+tarfiles/) && do {
427        next if /^#/;
428        chomp;
429        push(@tarfiles, $_);
430        next;
431    };
432
433    next unless $destfile;  # Unidentified testbed, ignore config
434
435    # Substitute variables
436    s/GWTYPE/$gwtype{$ctb}/g;
437    s/GWIMAGE/$gwimage{$ctb}/g;
438    if ($ctb eq $master ) {
439        s/GWSTART/$mgwstart{$ctb}/g;
440        s/EXPSTART/$mexpstart{$ctb}/g;
441    }
442    else {
443        s/GWSTART/$gwstart{$ctb}/g;
444        s/EXPSTART/$expstart{$ctb}/g;
445    }
446    # XXX: oh is this bad
447    s#GWCONF#FEDDIR\`hostname\`.gw.conf#g;
448    s#FEDDIR#/proj/$project{$ctb}/exp/$eid/tmp/#g;
449    print FILE;
450}
451$pipe->close();
452die "No nodes in master testbed ($master)\n" unless $allocated{$master};
453
454for $t (@tarfiles) {
455    die "tarfile '$t' unreadable: $!\n" unless -r $t;
456    for $tb (keys %allocated) {
457        unless (-d "$tmpdir/$tb/tarfiles") {
458            mkdir("$tmpdir/$tb/tarfiles") || 
459                die "Can't create $tmpdir/$tb/tarfiles:$!\n";
460        }
461        copy($t, "$tmpdir/$tb/tarfiles") || 
462                die "Can't copy $t to  $tmpdir/$tb/tarfiles:$!\n";
463    }
464}
465
466exit(0) unless $startem;
467
468# Start up the slave sub-experiments first
469TESTBED:
470for $tb (keys %allocated) {
471    if ($tb ne $master) {
472        if (&start_segment($tb, $eid)) { $started{$tb}++; }
473        else { last TESTBED; }
474    }
475}
476
477# Now the master
478if (&start_segment($master, $eid)) { 
479    $started{$master}++;
480}
481
482# If any testbed failed, swap the rest out.
483if ( scalar(keys %started) != scalar(keys %allocated)) {
484    for $tb (keys %started) { &stop_segment($tb, $eid); }
485    print "Error starting experiment\n";
486    exit(1);
487}
488print "Experiment started\n";
489system("rm -rf $tmpdir") unless $debug;
490exit(0);    # set the exit value
491
492=pod
493
494=head1 NAME
495
496B<splitter.pl>
497
498=head1 SYNOPSIS
499
500B<splitter.pl> [B<-nd>] [B<-c> F<config_file>] [B<-f> F<experiment_tcl>]
501    [F<experiment_tcl>]
502
503=head1 DESCRIPTION
504
505B<splitter.pl> invokes the DETER experiment parser to split an annotated
506experiment into multiple sub-experments and instantiates the sub-experiments on
507their intended testbeds.  Annotation is accomplished using the
508tb-set-node-testbed command, added to the parser.
509
510The testbed labels are meaningful based on their presence in the testbeds file.
511that file can be specified in the configuration file using the B<Testbeds>
512directive, and defaults to F<./testbeds>.  The syntax is described below.
513
514Most of the intermediate files are staged in a sub-directory of a temporary
515files directory and deleted at the end of the script.  Specifying the B<-d>
516flag on the command line avoids the deletion for debbugging.  By default the
517temporary files directory is directory is F</tmp> and can be reset in the
518configuration file using the B<Tmpdir> directive.  Intermediate files are
519stored under a subdirectory formed by adding the process ID of the splitter
520process.  For example, if the temporary files directory is F</tmp> and the
521B<splitter.pl> process ID is 2323, the temporary files will be stored in
522F</tmp/split2323/>.
523
524The expreriment is split out into one experiment description per testbed in the
525temporary directory named as F<experiment.testbed.tcl> where the experiment is
526the experiment ID given in the configuration file, and the testbed is the
527tb-set-node-testbed parameter for the nodes in the file.
528
529If the B<-n> option is absent the sub-experiments are then instantiated on
530their testbeds.  (Here B<-n> is analogous to its use in L<make(1)>).
531Per-testbed parameters are set in the testbeds file.  Sub-experiments on
532slave testbeds are instantiated in a random order, but the master testbed is
533currently instantiated last.
534
535Scripts to start federation (the federation kit) are copied into the local
536experiment's tmp file - e.g., F</proj/DETER/exp/simple-split/tmp>.  These are
537taken from the directory given by the B<ScriptDir> directive in the
538configuration file.
539
540If any sub-experiment fails to instantiate, the other sub-exeriments are
541swapped out.
542
543=head2 Configuration File
544
545The configuration file is a simple attribute-value pair set of colon-separated
546parameters and values.  A configuration file must be present, either specified
547in the B<-c> flag or the default F<./splitter.conf>.  All the parameter names
548are case insensitive, but should not include any whitespace.  Parameter values
549may include whitespace, but no newlines.
550
551Possible parameters are:
552
553=over 5
554
555=item Experiment
556
557The name of the experiment on the various testbeds
558
559=item Master
560
561The master testbed label from the testbeds file, described below.
562
563=item Testbeds
564
565The testbeds file described below, giving per-testbed parameters.  If this
566directive is absent the testbeds file defaults to F<./testbeds>
567
568=item ScriptDir
569
570Location of the default federation scripts, i.e. the federation kit.
571
572=item GatewayPubkey
573
574=item GatewaySecretKey
575
576The names of the files containing secret and public keys to use in setting up
577tunnels between testbeds.  These will eventually be automatically generated.
578
579=item TmpDir
580
581=item TempDir
582
583The directory where temporary files are created.  These are synonyms, but
584should both be specified, B<TmpDir> has priority.  If neither is specified,
585F</tmp> is used.
586
587=item Tclparse
588
589The pathname to the experiment parsing program.  Only developers should set
590this.
591
592=item Tclsh
593
594The pathname to the local oTcl shell.  Only developers should set
595this.
596
597=back
598
599=head2 Testbeds file
600
601The configuration file (F<./testbeds> unless overridden by B<-c>) is a
602colon-separated set of parameters keyed by testbed name.  The fields, in order,
603are:
604
605=over 5
606
607=item name
608
609The testbed to which this line of parameters applies.
610
611=item user
612
613The user under which to make requests to this testbed.  The user running
614B<splitter.pl> must be able to authenicate as this user under L<ssh(1)> to this
615testbed.
616
617=item host
618
619The host name of the testbed's ops node.  The user calling B<splitter.pl> must
620be able to execute commands on this host via L<ssh(1)>.
621
622=item domain
623
624The domain of nodes in this testbed (including the ops host).
625
626=item project
627
628The project under which to instantiate sub-experiments on this testbed.
629
630=item gateway type
631
632The node type for inter-testbed gateway nodes on this testbed.
633
634=item experiment start (slave)
635
636The start command to run on experimental nodes when this testbed is used as a
637slave.  In all the start commands the string FEDDIR will be replaced by the
638local experiment's federation scripts directory and the string GWCONF replaced
639by the gatway configuration file.
640
641=item gateway start (slave)
642
643The start command to run on gateway nodes when this testbed is used as a slave.
644The same string substitutions are made in this command as in experiment start.
645
646=item experiment start (master)
647
648The start command to run on experimental nodes when this testbed is used as a
649master.  The same string substitutions are made in this command as in
650experiment start.
651
652=item gateway start (master)
653
654The start command to run on gateway nodes when this testbed is used as a
655master.  The same string substitutions are made in this command as in
656experiment start.
657
658=item gateway image
659
660The disk image to be loaded on a gateway node on this testbed.
661
662=back
663
664The parsing of the testbeds is extremely simple.  Colons separate each
665field and there is n provision for escaping them at this time.
666
667=head1 ENVIRONMENT
668
669B<splitter.pl> does not directly make use of environment variables, but calls
670out to L<ssh(1)> and (indirectly) to L<sh(1)>, which may be influenced by the
671environment.
672
673=head1 SEE ALSO
674
675L<sh(1)>, L<ssh(1)>
676
677=cut
Note: See TracBrowser for help on using the repository browser.