Index: fedd/fedd_client.py
===================================================================
--- fedd/fedd_client.py	(revision cdb62d9d1953339e8766ddd35824da043b8af2ea)
+++ fedd/fedd_client.py	(revision df783c1ca49a9d217508072d7226d4777a7179ac)
@@ -13,4 +13,5 @@
 from federation.util import fedd_ssl_context, pack_id, unpack_id
 from federation.remote_service import service_caller
+from federation import topdl
 
 from optparse import OptionParser, OptionValueError
@@ -477,5 +478,4 @@
 		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
 	except RuntimeError, e:
-	    print e
 	    sys.exit("Error processing RPC: %s" % e)
 
@@ -565,5 +565,4 @@
 		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
 	except RuntimeError, e:
-	    print e
 	    sys.exit("Error processing RPC: %s" % e)
 
@@ -625,5 +624,4 @@
 		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
 	except RuntimeError, e:
-	    print e
 	    sys.exit("Error processing RPC: %s" % e)
 
@@ -677,65 +675,14 @@
 	"""
 
-	fedd_rpc.__init__(self, 'Vtopo')
-
-    def gen_image(self, d, file, fmt, neato, labels, pix=None):
-
-	# Open up a temporary file for dot to turn into a visualization
-	try:
-	    df, dotname = tempfile.mkstemp(prefix='fedd_client', suffix=".dot")
-	    dotfile = os.fdopen(df, 'w')
-	except IOError:
-	    raise service_error(service_error.internal,
-		    "Failed to open file in genviz")
-
-	if not neato:
-	    for f in ['/usr/bin/neato', '/usr/local/bin/neato', 
-		    '/usr/bin/dot', '/usr/local/bin/dot']:
-		if os.access(f, os.X_OK):
-		    neato = f
-		    break
-	    else:
-		sys.exit("Cannot find graph rendering program")
-
-	cmd = [neato, '-Gsplines=true']
-	if fmt != 'dot': cmd.append('-T%s' % fmt)
-	if file:
-	    cmd.append('-o')
-	    cmd.append(file)
-	cmd.append(dotname)
-
-	nodes = d.get('node',[])
-
-	if len(nodes) < 10: size = 5
-	elif len(nodes) < 50: size = 10
-	else: size = 18
-
-	if pix:
-	    dpi = pix / size
-	else:
-	    dpi = None
-
-
-	print >>dotfile, "graph G {"
-	if dpi:
-	    print >>dotfile, '\tgraph [size="%i,%i", dpi="%i", ratio=fill];' \
-		    % (size, size, dpi)
-	else:
-	    print >>dotfile, '\tgraph [size="%i,%i", ratio=fill];' \
-		    % (size, size)
-
-	if labels:
-	    print >>dotfile, '\tnode [fontname=arial,fontsize=9,label="\N"];'
-	    print >>dotfile, '\tedge [fontname=arial,fontsize=9];\n'
-	else:
-	    print >>dotfile, '\tnode [label=""];'
+	fedd_rpc.__init__(self, op)
+
+    @staticmethod
+    def gen_dot_topo(d, labels, dotfile):
+	lans = { }
+	links = { }
 
 	for n in d.get('node', []):
 	    print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n['vname']
 	    print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
-
-
-	lans = { }
-	links = { }
 
 	# Collect lan members (we have to draw extra stuff for these)
@@ -777,4 +724,59 @@
 		    print >>dotfile, '\t"%s" -- "%s" ' % (l[0][0], l[1][0])
 
+
+    def gen_image(self, d, nodes, file, fmt, neato, labels, pix=None):
+
+	# Open up a temporary file for dot to turn into a visualization
+	try:
+	    df, dotname = tempfile.mkstemp(prefix='fedd_client', suffix=".dot")
+	    dotfile = os.fdopen(df, 'w')
+	except IOError:
+	    raise service_error(service_error.internal,
+		    "Failed to open file in genviz")
+
+	if not neato:
+	    for f in ['/usr/bin/neato', '/usr/local/bin/neato', 
+		    '/usr/bin/dot', '/usr/local/bin/dot']:
+		if os.access(f, os.X_OK):
+		    neato = f
+		    break
+	    else:
+		sys.exit("Cannot find graph rendering program")
+
+	cmd = [neato, '-Gsplines=true']
+	if fmt != 'dot': cmd.append('-T%s' % fmt)
+	if file:
+	    cmd.append('-o')
+	    cmd.append(file)
+	cmd.append(dotname)
+
+	#nodes = d.get('node',[])
+
+	if nodes < 10: size = 5
+	elif nodes < 50: size = 10
+	else: size = 18
+
+	if pix:
+	    dpi = pix / size
+	else:
+	    dpi = None
+
+
+	print >>dotfile, "graph G {"
+	if dpi:
+	    print >>dotfile, '\tgraph [size="%i,%i", dpi="%i", ratio=fill];' \
+		    % (size, size, dpi)
+	else:
+	    print >>dotfile, '\tgraph [size="%i,%i", ratio=fill];' \
+		    % (size, size)
+
+	if labels:
+	    print >>dotfile, '\tnode [fontname=arial,fontsize=9,label="\N"];'
+	    print >>dotfile, '\tedge [fontname=arial,fontsize=9];\n'
+	else:
+	    print >>dotfile, '\tnode [label=""];'
+
+
+	self.gen_dot_topo(d, labels, dotfile)
 	print >>dotfile, "}"
 	dotfile.close()
@@ -854,11 +856,11 @@
 		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
 	except RuntimeError, e:
-	    print e
 	    sys.exit("Error processing RPC: %s" % e)
 
 
 	if resp_dict.has_key('vtopo'):
-	    self.gen_image(resp_dict['vtopo'], file, fmt, opts.neato,
-		    opts.labels, opts.pixels)
+	    self.gen_image(resp_dict['vtopo'], 
+		    len(resp_dict['vtopo'].get('node', [])),
+		    file, fmt, opts.neato, opts.labels, opts.pixels)
 	else:
 	    sys.exit("Bad response. %s" % e.message)
@@ -1000,5 +1002,4 @@
 	    file = None
 
-
 	try:
 	    resp_dict = self.do_rpc(req,
@@ -1010,5 +1011,4 @@
 		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
 	except RuntimeError, e:
-	    print e
 	    sys.exit("Error processing RPC: %s" % e)
 
@@ -1018,8 +1018,117 @@
 		sys.exit("Bad response: could not split")
 	    topo = self.generate_topo_dict(resp_dict['output'])
-	    self.gen_image(topo, file, fmt, opts.neato, opts.labels,
-		    opts.pixels)
+	    self.gen_image(topo, len(topo.get('node', [])), file, fmt,
+		    opts.neato, opts.labels, opts.pixels)
 	else:
 	    sys.exit("Bad response. %s" % e.message)
+
+class topdl_image(image):
+    def __init__(self, op='Ns2Split'): 
+	"""
+	Null constructor
+	"""
+
+	image.__init__(self, 'Ns2Split')
+
+    @staticmethod
+    def gen_dot_topo(t, labels, dotfile):
+	lans = [ s for s in t.substrates if len(s.interfaces) != 2]
+	links = [ s for s in t.substrates if len(s.interfaces) == 2]
+
+	i = 0
+	for n in t.elements:
+	    if n.name:
+		print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n.name[0]
+	    else:
+		print >>dotfile, \
+			'\t"unnamed_node%d" [shape=box,style=filled,\\' % i
+		i += 1
+	    print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
+
+	# Encode the lans and the links
+	for l in lans:
+	    print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l.name
+	    print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]'
+	    for i in l.interfaces:
+		ip = i.get_attribute('ip4_address')
+		if labels and ip:
+		    print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \
+			    (l.name, i.element.name[0], ip)
+		else:
+		    print >>dotfile, '\t"%s" -- "%s"' % \
+			    (l.name, i.element.name[0])
+
+	for l in links:
+	    s, d = l.interfaces[0:2] 
+	    sip = s.get_attribute('ip4_address')
+	    dip = s.get_attribute('ip4_address')
+	    if labels and sip and dip:
+		print >>dotfile, \
+			('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \
+			'headlabel="%s"]') % \
+			(s.element.name[0], d.element.name[0], l.name,
+			    sip, dip)
+	    else:
+		print >>dotfile, '\t"%s" -- "%s" ' % \
+			(s.element.name[0], d.element.name[0])
+    def __call__(self):
+	"""
+	The control flow.  Compose the request and print the response.
+	"""
+	access_keys = []
+	# Process the options using the customized option parser defined above
+	parser = fedd_ns_image_opts(access_keys, self.add_ssh_key,
+		self.add_x509_cert)
+
+	(opts, args) = parser.parse_args()
+
+	if opts.trusted:
+	    if ( not os.access(opts.trusted, os.R_OK) ) :
+		sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+	if opts.debug > 0: opts.tracefile=sys.stderr
+
+	(user, cert) = self.get_user_info([])
+
+	if opts.cert != None: cert = opts.cert
+
+	if cert == None:
+	    sys.exit("No certificate given (--cert) or found")
+
+	if os.access(cert, os.R_OK):
+	    fid = fedid(file=cert)
+	else:
+	    sys.exit("Cannot read certificate (%s)" % cert)
+
+	if opts.file:
+	    exp_desc = ""
+	    try:
+		top = topdl.topology_from_xml(file=opts.file, top="experiment")
+	    except IOError:
+		sys.exit("Cannot read description file (%s)" % opts.file)
+	else:
+	    sys.exit("Must specify an experiment description (--file)")
+
+	if not opts.master:
+	    opts.master="dummy"
+
+
+	if opts.format and opts.outfile:
+	    fmt = opts.format
+	    file = opts.outfile
+	elif not opts.format and opts.outfile:
+	    fmt = opts.outfile[-3:]
+	    if fmt not in ("png", "jpg", "dot", "svg"):
+		sys.exit("Unexpected file type and no format specified")
+	    file = opts.outfile
+	elif opts.format and not opts.outfile:
+	    fmt = opts.format
+	    file = None
+	else:
+	    fmt="dot"
+	    file = None
+
+	self.gen_image(top, len(top.elements), file, fmt, opts.neato, 
+		opts.labels, opts.pixels)
 
 class terminate(fedd_rpc):
@@ -1495,5 +1604,6 @@
 	'status': status(),\
 	'terminate': terminate(),\
-	'spewlog': spew_log(),
+	'spewlog': spew_log(),\
+	'topdl_image': topdl_image(),\
     }
 
Index: fedd/federation/topdl.py
===================================================================
--- fedd/federation/topdl.py	(revision cdb62d9d1953339e8766ddd35824da043b8af2ea)
+++ fedd/federation/topdl.py	(revision df783c1ca49a9d217508072d7226d4777a7179ac)
@@ -5,5 +5,9 @@
     def init_class(c, arg):
 	if isinstance(arg, dict):
-	    return c(**arg)
+	    try:
+		return c(**arg)
+	    except:
+		print "%s" % arg
+		raise
 	elif isinstance(arg, c):
 	    return arg
@@ -16,4 +20,14 @@
 	elif getattr(a, '__iter__', None): return a
 	else: return [ a ]
+
+    def get_attribute(self, key):
+	rv = None
+	attrs = getattr(self, 'attributes', None)
+	if attrs:
+	    for a in attrs:
+		if a.attribute == key:
+		    rv = a.value
+		    break
+	return rv
 
 
@@ -93,5 +107,5 @@
 
 class OperatingSystem(base):
-    def __init__(self, name, version=None, distribution=None,
+    def __init__(self, name=None, version=None, distribution=None,
 	    distributionversion=None, attribute=[]):
 	self.name = name
@@ -103,5 +117,6 @@
 
     def to_dict(self):
-	rv = { 'name': self.name }
+	rv = { }
+	if self.name: rv['name'] = self.name
 	if self.version: rv['version'] = self.version
 	if self.distribution: rv['version'] = self.distribution
@@ -249,5 +264,5 @@
 	return rv
 
-def topology_from_xml(file=None, top="topology"):
+def topology_from_xml(string=None, file=None, top="topology"):
     import xml.parsers.expat
 
@@ -303,7 +318,13 @@
     xp.CharacterDataHandler = p.char_data
 
-    f = open(file, "r")
-
-    xp.ParseFile(f)
+    if file and string:
+	raise RuntimeError("Only one of file and string")
+    elif file:
+	f = open(file, "r")
+	xp.ParseFile(f)
+    elif string:
+	xp.Parse(string, isfinal=True)
+    else:
+	return None
 
     return Topology(**p.current[top])
