Index: fedkit/fed_bootstrap
===================================================================
--- fedkit/fed_bootstrap	(revision f778038089aad3e7619a36a2a591bf0549f7d2d1)
+++ fedkit/fed_bootstrap	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
@@ -29,3 +29,3 @@
 # federation script, and run it.
 cd /tmp
-exec /tmp/federate.sh
+exec /tmp/federate.sh "$1" "$2"
Index: fedkit/federate.sh
===================================================================
--- fedkit/federate.sh	(revision f778038089aad3e7619a36a2a591bf0549f7d2d1)
+++ fedkit/federate.sh	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
@@ -166,7 +166,37 @@
 /usr/local/etc/emulab/rc/rc.route boot
 
+# And restore the ifconfig information because SEER wants it. (SEER pulls this
+# from tmcc now
+# "$EMUDIR/tmcc" ifconfig > /var/emulab/boot/tmcc/ifconfig
+
+# SEER feels pain and confusion if times are wrong.  This lays a big lick on
+# ntp sets the time directly and then restarts it.  XXX FreeBSD incantation is
+# untested.
+
+case `uname` in 
+	"FreeBSD")
+		/etc/rc.d/ntpd stop
+		/usr/sbin/ntpdate boss
+		/etc/rc.d/ntpd start
+		;;
+	"Linux")
+		/etc/rc.d/init.d/ntpd stop
+		/usr/sbin/ntpdate boss
+		/etc/rc.d/init.d/ntpd start
+		;;
+esac
+
 # Execute any command passed in as a startcmd.  This basically daisy chains the
-# startcmd.
+# startcmd.  This needs to become a perl script to have uniform behavior here.
+echo "*$1* *$2*"
 if [ ! -z "$1" ]; then 
-	su -l "$1" -c "$2"
-fi
+	echo "calling *$1* *$2*"
+	case `uname` in
+		"FreeBSD")
+			su -l "$1" -c "$2"
+			;;
+		"Linux")
+			su "$1" --command="$2"
+			;;
+	esac
+fi
Index: fedkit/genviz.pl
===================================================================
--- fedkit/genviz.pl	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
+++ fedkit/genviz.pl	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+
+use strict;
+
+use XML::Parser;
+use IO::File;
+use IO::Pipe;
+
+sub genviz {
+    my($file, $outfile)= @_;
+    my %nodes;
+    my $chars;
+    my $in_node;
+    my $in_lan;
+    my $lan;
+    my %links;
+    my %lans;
+    my $rv;
+    my $dotfile = "/tmp/split$$.dot";
+    my $neato = "/usr/local/bin/neato";
+    my $g = new IO::File(">$dotfile") || return;
+    my $p = new IO::Pipe() || return;
+    my $out = new IO::File(">$outfile");
+
+    sub start_element { 
+	my($expat, $element) = @_;
+	$in_node++ if $element eq "node";
+	if ( $element eq "lan" ) {
+	    $in_lan++;
+	    $lan = {};
+	}
+    }
+
+    sub end_element {
+	my($expat, $element) = @_;
+
+	$in_node = 0 if $element eq "node";
+	$nodes{$chars} = "node" if $in_node && $element eq "vname";
+	if ($in_lan) {
+	    if ( $element ne "lan") {
+		$lan->{$element} = $chars if $element =~/(vname|vnode)/;
+	    }
+	    else {
+		$in_lan = 0;
+		my $vname = $lan->{'vname'};
+		if ( $links{$vname} && @{$links{$vname}} ==2 ) {
+		    # this link needs to be a lan
+		    $nodes{$vname} = "lan";
+		    $lans{$lan->{'vname'}} = ();
+		    foreach my $l (@{$links{$vname}}) {
+			push(@{$lans{$vname}}, $l);
+		    }
+		    push(@{$lans{$vname}}, $lan->{'vnode'});
+		    $links{$vname} = ();
+		    $lan={};
+		    return;
+		}
+		if ( $lans{$vname} && @{$lans{$vname}}) {
+		    push(@{$lans{$vname}}, $lan->{'vnode'});
+		    $lan = {};
+		    return;
+		}
+		$links{$vname} = () unless $links{$vname};
+		push(@{$links{$vname}}, $lan->{'vnode'});
+		$lan = {};
+		return;
+	    }
+	}
+    }
+    sub found_chars { 
+	my($expat, $data) = @_;
+	$chars = $data;
+    }
+
+    my $parser = new XML::Parser(Handlers => { 
+	    Start => \&start_element, 
+	    End => \&end_element,
+	    Char => \&found_chars
+	});
+
+    print "$file\n";
+    $parser->parsefile($file);
+
+    print $g "graph G {\n";
+    foreach my $n (keys %nodes) {
+	print $g "\t$n\n";
+    }
+    foreach my $l (keys %links) {
+	print $g "\t", join(" -- ", @{$links{$l}}), "\n";
+    }
+    foreach my $l (keys %lans) {
+	foreach my $n (@{$lans{$l}}) {
+	    print $g "\t $n -- $l\n";
+	}
+    }
+    print $g "}\n";
+    $g->close();
+    $p->reader("$neato  -Gstart=rand -Gepsilon=0.005 -Gmaxiter=20000 " .
+	"-Gpack=true $dotfile");
+    print $out "<vis>\n";
+    while (<$p>) {
+	/^\s*(\w+)\s+\[.*pos=\"(\d+),(\d+)\"/ && do {
+	    my ($n, $x, $y) = ($1, $2, $3);
+
+	    print "<node><name>$n</name><x>$x</x><y>$y</y><type>$nodes{$n}". 
+		"</type></node>\n" if $nodes{$n};
+	};
+    }
+    print $out "</vis>\n";
+    $p->close();
+    unlink("$dotfile");
+}
+
+genviz($ARGV[0], $ARGV[1]);
Index: fedkit/make_hosts
===================================================================
--- fedkit/make_hosts	(revision f778038089aad3e7619a36a2a591bf0549f7d2d1)
+++ fedkit/make_hosts	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
@@ -18,5 +18,33 @@
 /usr/local/etc/emulab/rc/rc.route 
 
+# SEER feels pain and confusion if times are wrong.  This lays a big lick on
+# ntp sets the time directly and then restarts it.  XXX FreeBSD incantation is
+# untested.
+
+case `uname` in 
+	"FreeBSD")
+		/etc/rc.d/ntpd stop
+		/usr/sbin/ntpdate boss
+		/etc/rc.d/ntpd start
+		;;
+	"Linux")
+		/etc/rc.d/init.d/ntpd stop
+		/usr/sbin/ntpdate boss
+		/etc/rc.d/init.d/ntpd start
+		;;
+esac
+
+# Execute any command passed in as a startcmd.  This basically daisy chains the
+# startcmd.  This needs to become a perl script to have uniform behavior here.
+echo "*$2* *$3*"
 if [ ! -z "$2" ]; then 
-	su -l "$2" -c "$3"
+	echo "calling *$2* *$3*"
+	case `uname` in
+		"FreeBSD")
+			su -l "$2" -c "$3"
+			;;
+		"Linux")
+			su "$2" --command="$3"
+			;;
+	esac
 fi
Index: fedkit/parsevis.py
===================================================================
--- fedkit/parsevis.py	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
+++ fedkit/parsevis.py	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
@@ -0,0 +1,65 @@
+#!/usr/bin/python -u
+import libxml2
+
+class federated_visualization:
+    class callback:
+	def __init__(self):
+	    self.result = { };
+	    self.row = { };
+	    self.node_fields = set(["name", "x", "y", "type"]);
+	    self.float_fields = set(["x", "y"]);
+	    self.last_chars = "";
+	pass
+
+	def startDocument(self):
+	    pass
+
+	def endDocument(self):
+	    pass
+
+	def startElement(self, tag, attrs):
+	    if tag == "node": 
+		self.row = {}
+	    pass
+
+	def endElement(self, tag):
+	    if tag == "node":
+		self.result[self.row["name"]] = self.row;
+	    elif tag in self.node_fields:
+		if tag in self.float_fields:
+		    self.row[tag] = float(self.last_chars);
+		else:
+		    self.row[tag] = self.last_chars;
+		pass
+	    pass
+
+	def characters(self, data):
+	    self.last_chars = data;
+	    pass
+
+	def warning(self, msg):
+	    print "Warning: %s!" %  msg
+	    pass
+
+	def error(self, msg):
+	    print "Error: %s!" %  msg
+	    pass
+
+	def fatalError(self, msg):
+	    global log
+	    print "fatalError: %s:" % (msg)
+
+    def __init__(self, file):
+	self.handler = self.callback()
+	self.result = { };
+	libxml2.SAXParseFile(self.handler, file, 0);
+	self.result = self.handler.result;
+	libxml2.cleanupParser()
+	pass
+pass
+
+if __name__ == "__main__":
+    import sys;
+    parse = federated_visualization(sys.argv[1]);
+    print parse.result
+pass
Index: fedkit/parsevt.py
===================================================================
--- fedkit/parsevt.py	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
+++ fedkit/parsevt.py	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
@@ -0,0 +1,107 @@
+#!/usr/bin/python -u
+import libxml2
+
+class federated_virtual_topology:
+    class callback:
+	def __init__(self):
+	    self.experiment = { };
+	    self.in_node=0;
+	    self.in_lan=0;
+	    self.rows = []
+	    self.row = { }
+	    self.node_fields = set(["vname", "ips"]);
+	    self.lan_fields = set(["vname", "vnode", "bandwidth", "delay", "member", "ip"]);
+	    self.long_fields = set(["bandwidth"]);
+	    self.float_fields = set(["delay"]);
+	    self.last_chars = "";
+	    pass
+
+	def startDocument(self):
+	    pass
+
+	def endDocument(self):
+	    pass
+
+	def startElement(self, tag, attrs):
+	    if ( tag == "node"):
+		self.in_node = 1
+	    elif tag == "lan":
+		self.in_lan = 1
+	    pass
+
+	def endElement(self, tag):
+	    if self.in_node >0 :
+		if tag in self.node_fields :
+		    if tag in self.long_fields:
+			self.row[tag] = long(self.last_chars);
+		    elif tag in self.float_fields:
+			self.row[tag] = float(self.last_chars);
+		    else:
+		        self.row[tag] = self.last_chars;
+		    pass
+		    self.last_chars = "";
+		pass
+		if tag == "node":
+		    # end of a row
+		    self.rows.append(self.row);
+		    self.row = { };
+		    self.in_node=0
+		pass
+	    pass
+	    if tag == "nodes": 
+		self.experiment["nodes"] = self.rows;
+		self.rows = [];
+	    pass
+	    if self.in_lan >0 :
+		if tag in self.lan_fields :
+		    if tag in self.long_fields:
+			self.row[tag] = long(self.last_chars);
+		    elif tag in self.float_fields:
+			self.row[tag] = float(self.last_chars);
+		    else:
+		        self.row[tag] = self.last_chars;
+		    pass
+		    self.last_chars = "";
+		pass
+		if tag == "lan":
+		    # end of a row
+		    self.rows.append(self.row);
+		    self.row = { };
+		    self.in_lan=0
+		pass
+	    pass
+	    if tag == "lans": 
+		self.experiment["lans"] = self.rows;
+		self.rows = [];
+	    pass
+
+	def characters(self, data):
+	    self.last_chars = data;
+	    pass
+
+	def warning(self, msg):
+	    print "Warning: %s!" %  msg
+	    pass
+
+	def error(self, msg):
+	    print "Error: %s!" %  msg
+	    pass
+
+	def fatalError(self, msg):
+	    global log
+	    print "fatalError: %s:" % (msg)
+
+    def __init__(self, file):
+	self.handler = self.callback()
+	self.rv = { };
+	libxml2.SAXParseFile(self.handler, file, 0);
+	self.rv["experiment"] = self.handler.experiment;
+	libxml2.cleanupParser()
+	pass
+pass
+
+if __name__ == "__main__":
+    import sys;
+    parse = federated_virtual_topology(sys.argv[1]);
+    print parse.rv
+pass
Index: fedkit/splitter.pl
===================================================================
--- fedkit/splitter.pl	(revision f778038089aad3e7619a36a2a591bf0549f7d2d1)
+++ fedkit/splitter.pl	(revision 7c3008e112b5820b5d22d88e47ce4ef7af03084d)
@@ -8,4 +8,5 @@
 use IO::Pipe;
 use File::Copy;
+use XML::Parser;
 
 my @scripts = ("fed_bootstrap", "federate.sh", "smbmount.FreeBSD.pl", 
@@ -352,4 +353,9 @@
 	&scp_file("$tmpdir/hostnames", $user, $host, $to_hostname) ||
 	    return 0;
+	# Copy the virtual topology out as well
+	&scp_file("$tmpdir/vtopo.xml", $user, $host, "$proj_dir/vtopo.xml") ||
+	    return 0;
+	&scp_file("$tmpdir/viz.xml", $user, $host, "$proj_dir/viz.xml") ||
+	    return 0;
 	&ship_scripts($host, $user, $proj_dir) || return 0;
 	&ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
@@ -378,4 +384,10 @@
 	&scp_file("$tmpdir/hostnames", $user, $host, $to_hostname) ||
 	    return 0;
+	# Copy the virtual topology out as well
+	&scp_file("$tmpdir/vtopo.xml", $user, $host, "$proj_dir/vtopo.xml") ||
+	    return 0;
+	&scp_file("$tmpdir/viz.xml", $user, $host, "$proj_dir/viz.xml") ||
+	    return 0;
+	&ship_scripts($host, $user, $proj_dir) || return 0;
 	&ship_scripts($host, $user, $proj_dir) || return 0;
 	&ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
@@ -425,4 +437,10 @@
 	&scp_file("$tmpdir/hostnames", $user, $host, $to_hostname) ||
 	    return 0;
+	# Copy the virtual topology out as well
+	&scp_file("$tmpdir/vtopo.xml", $user, $host, "$proj_dir/vtopo.xml") ||
+	    return 0;
+	&scp_file("$tmpdir/viz.xml", $user, $host, "$proj_dir/viz.xml") ||
+	    return 0;
+	&ship_scripts($host, $user, $proj_dir) || return 0;
 	&ship_scripts($host, $user, $proj_dir) || return 0;
 	&ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
@@ -477,4 +495,110 @@
     unlink("/tmp/splitter.$$", "/tmp/splitter.err.$$");
     return $rv;
+}
+
+# Generate visualization info from the topo file.
+sub genviz {
+    my($file, $outfile)= @_;
+    my %nodes;
+    my $chars;
+    my $in_node;
+    my $in_lan;
+    my $lan;
+    my %links;
+    my %lans;
+    my $rv;
+    my $dotfile = "/tmp/split$$.dot";
+    my $neato = "/usr/local/bin/neato";
+    my $g = new IO::File(">$dotfile") || return;
+    my $p = new IO::Pipe() || return;
+    my $out = new IO::File(">$outfile") || die "open $!\n";
+
+    sub start_element { 
+	my($expat, $element) = @_;
+	$in_node++ if $element eq "node";
+	if ( $element eq "lan" ) {
+	    $in_lan++;
+	    $lan = {};
+	}
+    }
+
+    sub end_element {
+	my($expat, $element) = @_;
+
+	$in_node = 0 if $element eq "node";
+	$nodes{$chars} = "node" if $in_node && $element eq "vname";
+	if ($in_lan) {
+	    if ( $element ne "lan") {
+		$lan->{$element} = $chars if $element =~/(vname|vnode)/;
+	    }
+	    else {
+		$in_lan = 0;
+		my $vname = $lan->{'vname'};
+		if ( $links{$vname} && @{$links{$vname}} ==2 ) {
+		    # this link needs to be a lan
+		    $nodes{$vname} = "lan";
+		    $lans{$lan->{'vname'}} = ();
+		    foreach my $l (@{$links{$vname}}) {
+			push(@{$lans{$vname}}, $l);
+		    }
+		    push(@{$lans{$vname}}, $lan->{'vnode'});
+		    delete $links{$vname};
+		    $lan={};
+		    return;
+		}
+		if ( $lans{$vname} && @{$lans{$vname}}) {
+		    push(@{$lans{$vname}}, $lan->{'vnode'});
+		    $lan = {};
+		    return;
+		}
+		$links{$vname} = () unless $links{$vname};
+		push(@{$links{$vname}}, $lan->{'vnode'});
+		$lan = {};
+		return;
+	    }
+	}
+    }
+    sub found_chars { 
+	my($expat, $data) = @_;
+	$chars = $data;
+    }
+
+    my $parser = new XML::Parser(Handlers => { 
+	    Start => \&start_element, 
+	    End => \&end_element,
+	    Char => \&found_chars
+	});
+
+    print "$file\n";
+    $parser->parsefile($file);
+
+    print $g "graph G {\n";
+    foreach my $n (keys %nodes) {
+	print $g "\t\"$n\"\n";
+    }
+    foreach my $l (keys %links) {
+	print $g "\t", join(" -- ", @{$links{$l}}), "\n";
+    }
+    foreach my $l (keys %lans) {
+	foreach my $n (@{$lans{$l}}) {
+	    print $g "\t \"$n\" -- \"$l\"\n";
+	}
+    }
+    print $g "}\n";
+    $g->close();
+    $p->reader("$neato  -Gstart=rand -Gepsilon=0.005 -Gmaxiter=20000 " .
+	"-Gpack=true $dotfile");
+    print $out "<vis>\n";
+    while (<$p>) {
+	/^\s*"?([\w\-]+)"?\s+\[.*pos=\"(\d+),(\d+)\"/ && do {
+	    my ($n, $x, $y) = ($1, $2, $3);
+
+	    print $out "<node><name>$n</name><x>$x</x><y>$y</y><type>" . 
+		"$nodes{$n}</type></node>\n" if $nodes{$n};
+	};
+    }
+    print $out "</vis>\n";
+    $p->close();
+    unlink("$dotfile");
 }
 
@@ -583,11 +707,24 @@
 my %active_end;			# If active_end{"a-b"} > 0 then a is the active
 				# end of the a <-> b connector pair.
+my $vtopo;			# IO::File for virtual topology info
 
 # Parse the splitter output.  This loop creates the sub experiments, gateway
 # configurations and hostnames file
 while (<$pipe>) {
-    # Vtopo is virtual topology about the entire experiment.  Right now ignore
-    # it.  We'll pass it to SEER soon.
+    # Vtopo is virtual topology about the entire experiment. Copy it to the
+    # $tmpdir for distribution far and wide.
     (/^#\s+Begin\s+Vtopo/../^#\s+End\s+Vtopo/) && do {
+	/^#\s+Begin/ && do {
+	    $vtopo = new IO::File(">$tmpdir/vtopo.xml");
+	    warn "Can't open $tmpdir/vtopo.xml:$!\n" unless $vtopo;
+	    next;
+	};
+	/^#\s+End/ && do {
+	    $vtopo->close() if $vtopo;
+	    undef $vtopo;
+	    genviz("$tmpdir/vtopo.xml", "$tmpdir/viz.xml");
+	    next;
+	};
+	print $vtopo $_  if $vtopo;
 	next;
     };
