Index: fedd/fedd_create_experiment.py
===================================================================
--- fedd/fedd_create_experiment.py	(revision 7add1a3e8896353beb42f1c67af10f915aa102ec)
+++ fedd/fedd_create_experiment.py	(revision d0ae12d132a15761aec26000e42160013fd808de)
@@ -7,4 +7,6 @@
 from M2Crypto.SSL.SSLServer import SSLServer
 import M2Crypto.httpslib
+
+import xml.parsers.expat
 
 import re
@@ -31,4 +33,5 @@
 	    cert_file=None,
 	    cert_pwd=None,
+	    debug=False,
 	    muxmax=2,
 	    project_user = "faber",
@@ -53,4 +56,5 @@
 	self.cert_file = cert_file
 	self.cert_pwd = cert_pwd
+	self.debug = debug
 	self.muxmax = muxmax
 	self.project_user = project_user
@@ -113,18 +117,24 @@
 	scp a file to the remote host.
 	"""
-	
-	rv = call([self.scp_exec, file, "%s@%s:%s" % (user, host, dest)])
-	if rv == 0:
+
+	scp_cmd = [self.scp_exec, file, "%s@%s:%s" % (user, host, dest)]
+	if not self.debug:
+	    rv = call(scp_cmd)
+	else:
+	    print "debug: %s" % " ".join(scp_cmd)
+	    rv = 0
+
+	return rv == 0
+
+    def ssh_cmd(self, user, host, cmd, wname=None, timeout=0):
+	sh_str = "%s %s@%s %s" % (self.ssh_exec, user, host, cmd)
+
+	if not self.debug:
+	    # This should be done more carefully
+	    sub = Popen(sh_str, shell=True)
+	    return sub.wait() == 0
+	else:
+	    print "debug: %s" % sh_str 
 	    return True
-	else:
-	    # XXX
-	    print >>sys.stdout, "Failed to scp %s to %s@%s" % (file, user, host)
-	    return False
-
-    def ssh_cmd(self, user, host, cmd, wname=None, timeout=0):
-	# This should be done more carefully
-	sub = Popen("%s %s@%s %s" % (self.ssh_exec, user, host, cmd), 
-		shell=True)
-	return sub.wait() == 0
 
     def ship_scripts(self, host, user, dest_dir):
@@ -160,6 +170,5 @@
 	pid = tbparams[tb]['project']
 	# XXX
-	# base_confs = ( "hosts", "vtopo.xml", "viz.xml")
-	base_confs = ( "hosts", "vtopo.xml")
+	base_confs = ( "hosts", "vtopo.xml", "viz.xml")
 	tclfile = "%s.%s.tcl" % (eid, tb)
 	expinfo_exec = "/usr/testbed/bin/expinfo"
@@ -317,5 +326,139 @@
 			    % (self.ssh_keygen, rv))
 
-    def genviz(self, topo_file, viz_file): pass
+    def genviz(self, topo_file, viz_file):
+	"""
+	Generate the visualization file from the topology file
+	"""
+
+	class topo_parse:
+	    """
+	    Parse the vtopo file into a set of lans, links and nodes.
+	    """
+	    def __init__(self):
+		self.links = {}		# Each link is a list of at most 2 nodes
+		self.lans = {}		# Lans have more than 2 nodes
+		self.nodes = {}		# The nodes in the experiment
+		self.lan = {}		# The current link/len being collected
+		self.in_lan = False	# Is the conatining element lan?
+		self.in_node = False	# Is the conatining element node?
+		self.chars = None	# Last set of marked-up chars
+
+	    def start_element(self, name, attrs):
+		"""
+		New element started.  Set flags and clear lan
+		"""
+		if name == "node":
+		    self.in_node = True
+		elif name == "lan":
+		    self.in_lan = True
+		    self.lan.clear()
+	    
+	    def end_element(self, name):
+		"""
+		End of element.  Collect data if appropriate
+
+		If a node or lan is ending, create an entry in nodes or
+		lans/links.  If a vname/vnode in a node or lan is ending,
+		capture its name.  If a lan is ending, add the node to the
+		evolving link or lan.
+		"""
+		if name == "node":
+		    self.in_node = False
+		if self.in_node and name == "vname":
+		    self.nodes[self.chars] = "node"
+		if self.in_lan:
+		    if name != "lan":
+			if name == 'vname' or name == 'vnode':
+			    self.lan[name] = self.chars
+		    else:
+			self.in_lan = False
+			vname = self.lan['vname']
+			links = self.links.get(vname, [])
+			if len(links) == 2:
+			    # This link needs to be a lan instead
+			    self.nodes[vname] = "lan"
+			    self.lans[vname] = \
+				    [ l for l in links, self.lan['vnode'] ]
+			    del self.links[vname]
+			    self.lan = {}
+			    return
+			lans = self.lans.get(vname, [])
+			if len(lans) > 0:
+			    lans.append(self.lan['vnode'])
+			    self.lan = {}
+			    return
+			if not vname in self.links:
+			    self.links[vname] = []
+			self.links[vname].append(self.lan['vnode'])
+			self.lan = {}
+			return
+
+	    def found_chars(self, data):
+		"""
+		Capture marked up chars for later
+		"""
+		self.chars = data
+
+	neato = "/usr/local/bin/neato"
+	# These are used to parse neato output and to create the visualization
+	# file.
+	vis_re = re.compile('^\s*"?([\w\-]+)"?\s+\[.*pos="(\d+),(\d+)"')
+	vis_fmt = "<node><name>%s</name><x>%s</x><y>%s</y><type>" + \
+		"%s</type></node>" 
+	try:
+	    df, dotname = tempfile.mkstemp()
+	    dotfile = os.fdopen(df, 'w')
+	    infile = open(topo_file, 'r')
+	    out = open(viz_file, "w")
+	except IOError:
+	    raise service_error(service_error.internal,
+		    "Failed to open file in genviz")
+
+	# Parse the topology file using XML tools
+	tp = topo_parse();
+	parser = xml.parsers.expat.ParserCreate()
+	parser.StartElementHandler = tp.start_element
+	parser.EndElementHandler = tp.end_element
+	parser.CharacterDataHandler = tp.found_chars
+
+	parser.ParseFile(infile)
+
+	# Generate a dot/neato input file from the links, nodes and lans
+	try:
+	    print >>dotfile, "graph G {"
+	    for n in tp.nodes.keys():
+		print >>dotfile, '\t"%s"' % n
+	    for l in tp.links.keys():
+		print >>dotfile, " -- ".join(tp.links[l])
+	    for l in tp.lans.keys():
+		for n in tp.lans[l]:
+		    print >>dotfile, '\t "%s" -- "%s"' % (n,l)
+	    print >>dotfile, "}"
+	    dotfile.close()
+	except IOError:
+	    raise service_error(service_error.internal, "Cannot write dot file")
+
+	# Use dot to create a visualization
+	dot = Popen([neato, '-Gstart=rand', '-Gepsilon=0.005', '-Gmaxiter=2000',
+		'-Gpack=true', dotname], stdout=PIPE)
+
+	# Translate dot to emulab format
+	try:
+	    print >>out, "<vis>"
+	    for line in dot.stdout:
+		m = vis_re.match(line)
+		if m:
+		    n, x, y = (m.group(1), m.group(2), m.group(3))
+		    if tp.nodes.has_key(n):
+			print >>out, vis_fmt % (n, x, y, tp.nodes[n])
+	    print >>out, "</vis>"
+	    out.close()
+	except IOError:
+	    raise service_error(service_error.internal,
+		    "Failed to write visualization file")
+	rv = dot.wait()
+	os.remove(dotname)
+	return rv == 0
+
 
     def get_access(self, tb, nodes, user, tbparam):
@@ -778,5 +921,5 @@
 	fail_soft = False
 	# XXX
-	startem = True
+	startem = False
 
 	try:
@@ -851,4 +994,5 @@
 			"Bad tcl parse? %s" % line)
 
+	self.genviz(self.tmpdir + "/vtopo.xml", self.tmpdir + "/viz.xml")
 	if not startem: return True
 
@@ -893,4 +1037,6 @@
     
     parser = OptionParser()
+    parser.add_option('-d', '--debug', dest='debug', default=False,
+	    action='store_true', help='print actions rather than take them')
     parser.add_option('-f', '--file', dest='tcl', help='tcl file to parse')
     opts, args  = parser.parse_args()
@@ -907,4 +1053,5 @@
 
     obj = fedd_create_experiment_local(
+	    debug=opts.debug,
 	    scripts_dir="/users/faber/testbed/federation",
 	    cert_file="./fedd_client.pem", cert_pwd="faber", 
