source: fedkit/splitter.pl @ 2ef2c5b

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

collect tarfiles info and stage them for federated experiments

  • Property mode set to 100644
File size: 21.3 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) = split(":", $_);
269    $host{$tb} = $h;
270    $user{$tb} = $u;
271    $domain{$tb} = $d;
272    $project{$tb} = $p;
273    $gwtype{$tb} = $t;
274    $expstart{$tb} = $es;
275    $gwstart{$tb} = $gs;
276    $mexpstart{$tb} = $mes;
277    $mgwstart{$tb} = $mgs;
278    $gwimage{$tb} = $i;
279
280    # Make sure the domain starts with a period
281    $domain{$tb} = ".$domain{$tb}" unless $domain{$tb} =~ /^\./;
282}
283$conf->close();
284
285# Open a pipe to the splitter program and start it parsing the experiments
286$pipe = new IO::Pipe;
287# NB no more -p call on parse call.
288$pipe->reader("$tclsh $tcl_splitter -s -m $master  $pid $gid $eid $tcl") || 
289    die "Cannot execute $tclsh $tcl_splitter -s -m $master $pid $gid $eid $tcl:$!\n";
290
291# Parse the splitter output.  This loop creates the sub experiments, gateway
292# configurations and hostnames file
293while (<$pipe>) {
294    # Start of a sub-experiment
295    /^#\s+Begin\s+Testbed\s+\((\w+)\)/ && do {
296        $ctb = $1;
297
298        # If we know the testbed, start collecting its sub experiment tcl
299        # description.  If not, warn the caller and ignore the configuration of
300        # this testbed.
301        if ($host{$ctb}) {
302            $allocated{$ctb}++; # Keep track of the testbeds allocated
303
304            unless (-d "$tmpdir/$ctb") {
305                mkdir("$tmpdir/$ctb") || die "Can't create $tmpdir/$ctb: $!";
306            }
307            $destfile = "$tmpdir/$ctb/$eid.$ctb.tcl";
308
309            open(FILE, ">$destfile") || die "Cannot open $destfile:$!\n";
310        }
311        else { 
312            warn "No such testbed $ctb\n";
313            $destfile = "";
314        }
315        next;
316    };
317
318    # End of that experiment
319    /^#\s+End\s+Testbed\s+\((\w+)\)/ && do {
320        # Simple syntax check and close out this experiment's tcl description
321        die "Mismatched testbed markers ($1, $ctb)\n" unless ($1 eq $ctb);
322        close(FILE);
323        $destfile = $ctb = "";
324        next;
325    };
326
327    # Beginning of a gateway set
328    /^#\s+Begin\s+gateways\s+\((\w+)\)/ && do {
329        $gateways = $1;
330        # If we've heard of this tb, create the config lines for it one at a
331        # time.
332        if ($allocated{$gateways}) {
333            # Just in case.  This directory should already have been created
334            # above.
335            unless (-d "$tmpdir/$gateways") {
336                mkdir("$tmpdir/$gateways") || 
337                    die "Can't create $tmpdir/$gateways: $!";
338            }
339        }
340        else {
341            warn "Gateways given (and ignored) for testbed not in use: " .
342                "$gateways\n";
343            $gateways = 0;
344        }
345        next;
346    };
347    /^#\s+End\s+gateways\s+\((\w+)\)/ && do {
348        die "Mismatched gateway markers ($1, $gateways)\n" 
349            unless !$gateways || $gateways == $1;
350        $gateways = 0;
351        next;
352    };
353    # Beginning of the hostnames list.  Collection is always in the hostnames
354    # file.
355    /^#\s+Begin\s+hostnames/ && do {
356        $destfile = "$tmpdir/hostnames";
357        open(FILE, ">$destfile") || die "Can't open $destfile:$!\n";
358        next;
359    };
360    # end of the hostnames list.
361    /^#\s+End\s+hostnames/ && do {
362        close(FILE);
363        $destfile = "";
364        next;
365    };
366
367    # Generate gateway configuration info, one file per line
368    $gateways && do {
369        chomp;
370        my($dtb, $myname, $desthost, $type) = split(" ", $_);
371        my($sdomain) = $domain{$gateways};      # domain for the source
372        my($ddomain) = $domain{$dtb};           # domain for the destination
373        my($sproject) = $project{$gateways};    # Project of the destination
374
375        $sdomain = ".$eid.$project{$gateways}$sdomain";
376        $ddomain = ".$eid.$project{$dtb}$ddomain";
377
378        # If either end of this link is in the master side of the testbed, that
379        # side is the active end. Otherwise the first testbed encountered in
380        # the file will be the active end.  The $active_end variable keeps
381        # track of those decisions
382        if ( $dtb eq $master ) { $active = "false"; }
383        elsif ($gateways eq $master ) { $active = "true"; }
384        elsif ( $active_end{"$dtb-$gateways"} ) { $active="false"; }
385        else { $active_end{"$gateways-$dtb"}++; $active = "true"; }
386
387        # Write out the file
388        open(GWCONFIG, ">$tmpdir/$gateways/$myname$sdomain.gw.conf") || 
389            die "can't open $tmpdir/$gateways/$myname$sdomain.gw.conf: $!\n";
390        print GWCONFIG "Active: $active\n";
391        print GWCONFIG "Type: $type\n";
392        print GWCONFIG "Peer: $desthost$ddomain\n";
393        print GWCONFIG "Pubkeys: " . 
394            "/proj/$sproject/exp/$eid/tmp/$gw_pubkey_base\n";
395        print GWCONFIG "Privkeys: " .
396            "/proj/$sproject/exp/$eid/tmp/$gw_secretkey_base\n";
397        close(GWCONFIG);
398
399        # This testbed has a gateway (most will) so make a copy of the keys it
400        # needs in this testbed's subdirectory.  start_segment will transfer
401        # them.
402        unless (-r "$tmpdir/$gateways/$gw_pubkey_base" ) {
403            copy($gw_pubkey, "$tmpdir/$gateways/$gw_pubkey_base") ||
404                die "Can't copy pubkeys ($gw_pubkey to " . 
405                    "$tmpdir/$gateways/$gw_pubkey_base): $!\n";
406        }
407        if ($active eq "true" ) {
408            unless (-r "$tmpdir/$gateways/$gw_secretkey_base" ) {
409                copy($gw_secretkey, "$tmpdir/$gateways/$gw_secretkey_base") ||
410                    die "Can't copy secret keys ($gw_secretkey to " . 
411                        "$tmpdir/$gateways/$gw_secretkey_base): $!\n";
412            }
413        }
414
415        #done processing gateway entry, ready for next line
416        next; 
417    };
418    (/^#\s+Begin\s+tarfiles/../^#\s+End\s+tarfiles/) && do {
419        next if /^#/;
420        chomp;
421        push(@tarfiles, $_);
422        next;
423    };
424
425    next unless $destfile;  # Unidentified testbed, ignore config
426
427    # Substitute variables
428    s/GWTYPE/$gwtype{$ctb}/g;
429    s/GWIMAGE/$gwimage{$ctb}/g;
430    if ($ctb eq $master ) {
431        s/GWSTART/$mgwstart{$ctb}/g;
432        s/EXPSTART/$mexpstart{$ctb}/g;
433    }
434    else {
435        s/GWSTART/$gwstart{$ctb}/g;
436        s/EXPSTART/$expstart{$ctb}/g;
437    }
438    # XXX: oh is this bad
439    s#GWCONF#FEDDIR\`hostname\`.gw.conf#g;
440    s#FEDDIR#/proj/$project{$ctb}/exp/$eid/tmp/#g;
441    print FILE;
442}
443$pipe->close();
444die "No nodes in master testbed ($master)\n" unless $allocated{$master};
445
446for $t (@tarfiles) {
447    die "tarfile '$t' unreadable: $!\n" unless -r $t;
448    for $tb (keys %allocated) {
449        unless (-d "$tmpdir/$tb/tarfiles") {
450            mkdir("$tmpdir/$tb/tarfiles") || 
451                die "Can't create $tmpdir/$tb/tarfiles:$!\n";
452        }
453        copy($t, "$tmpdir/$tb/tarfiles") || 
454                die "Can't copy $t to  $tmpdir/$tb/tarfiles:$!\n";
455    }
456}
457
458exit(0) unless $startem;
459
460# Start up the slave sub-experiments first
461TESTBED:
462for $tb (keys %allocated) {
463    if ($tb ne $master) {
464        if (&start_segment($tb, $eid)) { $started{$tb}++; }
465        else { last TESTBED; }
466    }
467}
468
469# Now the master
470if (&start_segment($master, $eid)) { 
471    $started{$master}++;
472}
473
474# If any testbed failed, swap the rest out.
475if ( scalar(keys %started) != scalar(keys %allocated)) {
476    for $tb (keys %started) { &stop_segment($tb, $eid); }
477    print "Error starting experiment\n";
478    exit(1);
479}
480print "Experiment started\n";
481system("rm -rf $tmpdir") unless $debug;
482exit(0);    # set the exit value
483
484=pod
485
486=head1 NAME
487
488B<splitter.pl>
489
490=head1 SYNOPSIS
491
492B<splitter.pl> [B<-nd>] [B<-c> F<config_file>] [B<-f> F<experiment_tcl>]
493    [F<experiment_tcl>]
494
495=head1 DESCRIPTION
496
497B<splitter.pl> invokes the DETER experiment parser to split an annotated
498experiment into multiple sub-experments and instantiates the sub-experiments on
499their intended testbeds.  Annotation is accomplished using the
500tb-set-node-testbed command, added to the parser.
501
502The testbed labels are meaningful based on their presence in the testbeds file.
503that file can be specified in the configuration file using the B<Testbeds>
504directive, and defaults to F<./testbeds>.  The syntax is described below.
505
506Most of the intermediate files are staged in a sub-directory of a temporary
507files directory and deleted at the end of the script.  Specifying the B<-d>
508flag on the command line avoids the deletion for debbugging.  By default the
509temporary files directory is directory is F</tmp> and can be reset in the
510configuration file using the B<Tmpdir> directive.  Intermediate files are
511stored under a subdirectory formed by adding the process ID of the splitter
512process.  For example, if the temporary files directory is F</tmp> and the
513B<splitter.pl> process ID is 2323, the temporary files will be stored in
514F</tmp/split2323/>.
515
516The expreriment is split out into one experiment description per testbed in the
517temporary directory named as F<experiment.testbed.tcl> where the experiment is
518the experiment ID given in the configuration file, and the testbed is the
519tb-set-node-testbed parameter for the nodes in the file.
520
521If the B<-n> option is absent the sub-experiments are then instantiated on
522their testbeds.  (Here B<-n> is analogous to its use in L<make(1)>).
523Per-testbed parameters are set in the testbeds file.  Sub-experiments on
524slave testbeds are instantiated in a random order, but the master testbed is
525currently instantiated last.
526
527Scripts to start federation (the federation kit) are copied into the local
528experiment's tmp file - e.g., F</proj/DETER/exp/simple-split/tmp>.  These are
529taken from the directory given by the B<ScriptDir> directive in the
530configuration file.
531
532If any sub-experiment fails to instantiate, the other sub-exeriments are
533swapped out.
534
535=head2 Configuration File
536
537The configuration file is a simple attribute-value pair set of colon-separated
538parameters and values.  A configuration file must be present, either specified
539in the B<-c> flag or the default F<./splitter.conf>.  All the parameter names
540are case insensitive, but should not include any whitespace.  Parameter values
541may include whitespace, but no newlines.
542
543Possible parameters are:
544
545=over 5
546
547=item Experiment
548
549The name of the experiment on the various testbeds
550
551=item Master
552
553The master testbed label from the testbeds file, described below.
554
555=item Testbeds
556
557The testbeds file described below, giving per-testbed parameters.  If this
558directive is absent the testbeds file defaults to F<./testbeds>
559
560=item ScriptDir
561
562Location of the default federation scripts, i.e. the federation kit.
563
564=item GatewayPubkey
565
566=item GatewaySecretKey
567
568The names of the files containing secret and public keys to use in setting up
569tunnels between testbeds.  These will eventually be automatically generated.
570
571=item TmpDir
572
573=item TempDir
574
575The directory where temporary files are created.  These are synonyms, but
576should both be specified, B<TmpDir> has priority.  If neither is specified,
577F</tmp> is used.
578
579=item Tclparse
580
581The pathname to the experiment parsing program.  Only developers should set
582this.
583
584=item Tclsh
585
586The pathname to the local oTcl shell.  Only developers should set
587this.
588
589=back
590
591=head2 Testbeds file
592
593The configuration file (F<./testbeds> unless overridden by B<-c>) is a
594colon-separated set of parameters keyed by testbed name.  The fields, in order,
595are:
596
597=over 5
598
599=item name
600
601The testbed to which this line of parameters applies.
602
603=item user
604
605The user under which to make requests to this testbed.  The user running
606B<splitter.pl> must be able to authenicate as this user under L<ssh(1)> to this
607testbed.
608
609=item host
610
611The host name of the testbed's ops node.  The user calling B<splitter.pl> must
612be able to execute commands on this host via L<ssh(1)>.
613
614=item domain
615
616The domain of nodes in this testbed (including the ops host).
617
618=item project
619
620The project under which to instantiate sub-experiments on this testbed.
621
622=item gateway type
623
624The node type for inter-testbed gateway nodes on this testbed.
625
626=item experiment start (slave)
627
628The start command to run on experimental nodes when this testbed is used as a
629slave.  In all the start commands the string FEDDIR will be replaced by the
630local experiment's federation scripts directory and the string GWCONF replaced
631by the gatway configuration file.
632
633=item gateway start (slave)
634
635The start command to run on gateway nodes when this testbed is used as a slave.
636The same string substitutions are made in this command as in experiment start.
637
638=item experiment start (master)
639
640The start command to run on experimental nodes when this testbed is used as a
641master.  The same string substitutions are made in this command as in
642experiment start.
643
644=item gateway start (master)
645
646The start command to run on gateway nodes when this testbed is used as a
647master.  The same string substitutions are made in this command as in
648experiment start.
649
650=item gateway image
651
652The disk image to be loaded on a gateway node on this testbed.
653
654=back
655
656The parsing of the testbeds is extremely simple.  Colons separate each
657field and there is n provision for escaping them at this time.
658
659=head1 ENVIRONMENT
660
661B<splitter.pl> does not directly make use of environment variables, but calls
662out to L<ssh(1)> and (indirectly) to L<sh(1)>, which may be influenced by the
663environment.
664
665=head1 SEE ALSO
666
667L<sh(1)>, L<ssh(1)>
668
669=cut
Note: See TracBrowser for help on using the repository browser.