Index: fedd/fedd_create_experiment.py
===================================================================
--- fedd/fedd_create_experiment.py	(revision 3441fe3afc83fc24184c96e923b55ebfda937c2d)
+++ fedd/fedd_create_experiment.py	(revision 0d830de89964db8478df25aee7ba6b1bfc782163)
@@ -267,5 +267,5 @@
 	pid = tbparams[tb]['project']
 	# XXX
-	base_confs = ( "hosts", "vtopo.xml", "viz.xml")
+	base_confs = ( "hosts",)
 	tclfile = "%s.%s.tcl" % (eid, tb)
 	expinfo_exec = "/usr/testbed/bin/expinfo"
@@ -299,6 +299,7 @@
 		if m: state = "none"
 	rv = status.wait()
-	# No experiment returns a non-zero rv.  If we successfully parsed a
-	# "none" outcome, ignore teh return code.
+	# If the experiment is not present the subcommand returns a non-zero
+	# return value.  If we successfully parsed a "none" outcome, ignore the
+	# return code.
 	if rv != 0 and state != "none":
 	    raise service_error(service_error.internal,
@@ -456,77 +457,54 @@
 			    % (self.ssh_keygen, rv))
 
-    def genviz(self, topo_file, viz_file):
+    def gentopo(self, str):
+	class topo_parse:
+	    def __init__(self):
+		self.str_subelements = ('vname', 'vnode', 'ips', 'ip', 'member')
+		self.int_subelements = ( 'bandwidth',)
+		self.float_subelements = ( 'delay',)
+		self.nodes = [ ]
+		self.lans =  [ ]
+		self.element = { }
+		self.topo = { \
+			'nodes': { 'node': self.nodes  },\
+			'lans' : { 'lan' : self.lans },\
+		    }
+		self.chars = ""
+
+	    def end_element(self, name):
+		if name == 'node':
+		    self.nodes.append(self.element)
+		    self.element = { }
+		elif name == 'lan':
+		    self.lans.append(self.element)
+		    self.element = { }
+		elif name in self.str_subelements:
+		    self.element[name] = self.chars
+		    self.chars = ""
+		elif name in self.int_subelements:
+		    self.element[name] = int(self.chars)
+		    self.chars = ""
+		elif name in self.float_subelements:
+		    self.element[name] = float(self.chars)
+		    self.chars = ""
+
+	    def found_chars(self, data):
+		self.chars += data.rstrip()
+
+
+	tp = topo_parse();
+	parser = xml.parsers.expat.ParserCreate()
+	parser.EndElementHandler = tp.end_element
+	parser.CharacterDataHandler = tp.found_chars
+
+	parser.Parse(str)
+
+	return tp.topo
+	
+
+    def genviz(self, topo):
 	"""
-	Generate the visualization file from the topology file
+	Generate the visualization the virtual topology
 	"""
-
-	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"
@@ -535,35 +513,58 @@
 	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>" 
+		"%s</type></node>"
+
+	try:
+	    # Node names
+	    nodes = [ n['vname'] for n in topo['nodes']['node'] ]
+	    topo_lans = topo['lans']['lan']
+	except KeyError:
+	    raise service_error(service_error.internal, "Bad topology")
+
+	lans = { }
+	links = { }
+
+	# Walk through the virtual topology, organizing the connections into
+	# 2-node connections (links) and more-than-2-node connections (lans).
+	# When a lan is created, it's added to the list of nodes (there's a
+	# node in the visualization for the lan).
+	for l in topo_lans:
+	    if links.has_key(l['vname']):
+		if len(links[l['vname']]) < 2:
+		    links[l['vname']].append(l['vnode'])
+		else:
+		    nodes.append(l['vname'])
+		    lans[l['vname']] = links[l['vname']]
+		    del links[l['vname']]
+		    lans[l['vname']].append(l['vnode'])
+	    elif lans.has_key(l['vname']):
+		lans[l['vname']].append(l['vnode'])
+	    else:
+		links[l['vname']] = [ l['vnode'] ]
+
+
+	# Open up a temporary file for dot to turn into a visualization
 	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():
+	    for n in nodes:
 		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]:
+	    for l in links.keys():
+		print >>dotfile, '\t"%s" -- "%s"' %  tuple(links[l])
+	    for l in lans.keys():
+		for n in lans[l]:
 		    print >>dotfile, '\t "%s" -- "%s"' % (n,l)
 	    print >>dotfile, "}"
 	    dotfile.close()
+	except TypeError:
+	    raise service_error(service_error.internal,
+		    "Single endpoint link in vtopo")
 	except IOError:
 	    raise service_error(service_error.internal, "Cannot write dot file")
@@ -573,21 +574,25 @@
 		'-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")
+	# Translate dot to vis format
+	vis_nodes = [ ]
+	vis = { 'node': vis_nodes }
+	for line in dot.stdout:
+	    m = vis_re.match(line)
+	    if m:
+		vn = m.group(1)
+		vis_node = {'name': vn, \
+			'x': int(m.group(2)),\
+			'y' : int(m.group(3)),\
+		    }
+		if vn in links.keys() or vn in lans.keys():
+		    vis_node['type'] = 'lan'
+		else:
+		    vis_node['type'] = 'node'
+		vis_nodes.append(vis_node)
 	rv = dot.wait()
+
 	os.remove(dotname)
-	return rv == 0
+	if rv == 0 : return vis
+	else: return None
 
 
@@ -1035,4 +1040,25 @@
 		return True
 
+    class shunt_to_string:
+	def __init__(self, begin, end):
+	    self.begin = re.compile(begin)
+	    self.end = re.compile(end)
+	    self.in_shunt = False
+	    self.str = ""
+	
+	def __call__(self, line):
+	    if not self.in_shunt:
+		if self.begin.match(line):
+		    self.in_shunt = True
+		    return True
+		else:
+		    return False
+	    else:
+		if self.end.match(line):
+		    self.in_shunt = False
+		else:
+		    self.str += line
+		return True
+
     def create_experiment(self, req, fid):
 	try:
@@ -1102,6 +1128,6 @@
 	parse_gateways = self.gateways(eid, master, tmpdir,
 		gw_pubkey_base, gw_secretkey_base, self.copy_file)
-	parse_vtopo = self.shunt_to_file("^#\s+Begin\s+Vtopo",
-		    "^#\s+End\s+Vtopo", tmpdir + "/vtopo.xml")
+	parse_vtopo = self.shunt_to_string("^#\s+Begin\s+Vtopo",
+		    "^#\s+End\s+Vtopo")
 	parse_hostnames = self.shunt_to_file("^#\s+Begin\s+hostnames",
 		    "^#\s+End\s+hostnames", tmpdir + "/hosts")
@@ -1131,5 +1157,13 @@
 			"Bad tcl parse? %s" % line)
 
-	self.genviz(tmpdir + "/vtopo.xml", tmpdir + "/viz.xml")
+	vtopo = self.gentopo(parse_vtopo.str)
+	if not vtopo:
+	    raise service_error(service_error.internal, 
+		    "Failed to generate virtual topology")
+
+	vis = self.genviz(vtopo)
+	if not vis:
+	    raise service_error(service_error.internal, 
+		    "Failed to generate visualization")
 
 	# Copy tarfiles and rpms needed at remote sites into a staging area
@@ -1200,5 +1234,7 @@
 	return { 'emulab' : [ tbparams[tb]['emulab'] \
 		for tb in tbparams.keys() \
-		    if tbparams[tb].has_key('emulab') ] }
+		    if tbparams[tb].has_key('emulab') ],\
+		    'experiment': vtopo,\
+		    'vis' : vis }
 
 if __name__ == '__main__':
