source: fedkit/splitter.pl @ fe459d0

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

better startup beginnings of new testbeds format (off for now)

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