Index: fedd/Makefile
===================================================================
--- fedd/Makefile	(revision eea2aa4f6735dc50113b4c80788c35b26d3a4787)
+++ fedd/Makefile	(revision eec716b04d02fc83402be818a98dda0175afb080)
@@ -5,5 +5,5 @@
 		${MODULE_DIR}/fedd_internal_services.py \
 		${MODULE_DIR}/fedd_services_types.py
-WSDL_FILES= ../wsdl/fedd.wsdl ../wsdl/fedd_types.xsd ../wsdl/fedd_internal.wsdl ../wsdl/abac.wsdl ../wsdl/abac_types.xsd
+WSDL_FILES= ../wsdl/fedd.wsdl ../wsdl/fedd_types.xsd ../wsdl/fedd_internal.wsdl ../wsdl/abac.wsdl ../wsdl/abac_types.xsd ../wsdl/topdl.xsd
 
 VERSION=1.30
Index: fedd/federation/topdl.py
===================================================================
--- fedd/federation/topdl.py	(revision eec716b04d02fc83402be818a98dda0175afb080)
+++ fedd/federation/topdl.py	(revision eec716b04d02fc83402be818a98dda0175afb080)
@@ -0,0 +1,343 @@
+#!/usr/local/bin/python
+
+class base:
+    @staticmethod
+    def init_class(c, arg):
+	if isinstance(arg, dict):
+	    return c(**arg)
+	elif isinstance(arg, c):
+	    return arg
+	else:
+	    return None
+
+    @staticmethod
+    def make_list(a):
+	if isinstance(a, basestring) or isinstance(a, dict): return [ a ]
+	elif getattr(a, '__iter__', None): return a
+	else: return [ a ]
+
+
+
+class ConsistencyError(RuntimeError): pass
+
+class Attribute(base):
+    def __init__(self, attribute, value):
+	self.attribute = attribute
+	self.value = value
+
+    def to_dict(self):
+	return { 'attribute': self.attribute, 'value': self.value }
+
+class Capacity(base):
+    def __init__(self, rate, kind):
+	self.rate = rate
+	self.kind = kind
+
+    def to_dict(self):
+	return { 'rate': self.rate, 'kind': self.kind }
+
+class Latency(base):
+    def __init__(self, time, kind):
+	self.time = time
+	self.kind = kind
+
+    def to_dict(self):
+	return { 'time': self.time, 'kind': self.kind }
+
+class Substrate(base):
+    def __init__(self, name, capacity=None, latency=None, attribute=[]):
+	self.name = name
+	self.capacity = self.init_class(Capacity, capacity)
+	self.latency = self.init_class(Latency, latency)
+	self.attribute = [ self.init_class(Attribute, a) \
+		for a in self.make_list(attribute) ]
+	self.interfaces = [ ]
+
+    def to_dict(self):
+	rv = { 'name': self.name }
+	if self.capacity:
+	    rv['capacity'] = self.capacity.to_dict()
+	if self.latency:
+	    rv['latency'] = self.latency.to_dict()
+	if self.attribute:
+	    rv['attribute'] = [ a.to_dict() for a in self.attribute ]
+	return rv
+
+class CPU(base):
+    def __init__(self, type, attribute=[]):
+	self.type = type
+	self.attribute = [ self.init_class(Attribute, a) for a in \
+		self.make_list(attribute) ]
+
+    def to_dict(self):
+	rv = { 'type': self.type}
+	if self.attribute:
+	    rv['attribute'] = [ a.to_dict() for a in self.attribute ]
+	return rv
+
+class Storage(base):
+    def __init__(self, amount, persistence, attribute=[]):
+	if isinstance(amount, basestring):
+	    self.amount = float(amount)
+	else:
+	    self.amount = amount
+	self.presistence = persistence
+	self.attribute = [ self.init_class(Attribute, a) \
+		for a in self.make_list(attribute) ]
+
+    def to_dict(self):
+	rv = { 'amount': float(self.amount), 'persistence': self.persistence }
+	if self.attribute:
+	    rv['attribute'] = [ a.to_dict() for a in self.attribute ]
+	return rv
+
+class OperatingSystem(base):
+    def __init__(self, name, version=None, distribution=None,
+	    distributionversion=None, attribute=[]):
+	self.name = name
+	self.version = version
+	self.distribution = distribution
+	self.distributionversion = distributionversion
+	self.attribute = [ self.init_class(Attribute, a) \
+		for a in self.make_list(attribute) ]
+
+    def to_dict(self):
+	rv = { 'name': self.name }
+	if self.version: rv['version'] = self.version
+	if self.distribution: rv['version'] = self.distribution
+	if self.distributionversion: rv['version'] = self.distributionversion
+	if self.attribute:
+	    rv['attribute'] = [ a.to_dict() for a in self.attribute ]
+	return rv
+
+class Software(base):
+    def __init__(self, location, install=None, attribute=[]):
+	self.location = location
+	self.install = install
+	self.attribute = [ self.init_class(Attribute, a)\
+		for a in self.make_list(attribute) ]
+
+    def to_dict(self):
+	rv = { 'location': self.location }
+	if self.install: rv['install'] = self.install
+	if self.attribute:
+	    rv['attribute'] = [ a.to_dict() for a in self.attribute ]
+	return rv
+
+class Interface(base):
+    def __init__(self, substrate, capacity=None, latency=None, attribute=[],
+	    element=None):
+	if isinstance(substrate, basestring):
+	    self.substrate = [ substrate ]
+	else:
+	    self.substrate = substrate
+	self.capacity = self.init_class(Capacity, capacity)
+	self.latency = self.init_class(Latency, latency)
+	self.attribute = [ self.init_class(Attribute, a) \
+		for a in self.make_list(attribute) ]
+	self.element = element 
+	self.subs = [ ]
+
+    def to_dict(self):
+	rv = { 'substrate': self.substrate }
+	if self.capacity:
+	    rv['capacity'] = self.capacity.to_dict()
+	if self.latency:
+	    rv['latency'] = self.latency.to_dict()
+	if self.attribute:
+	    rv['attribute'] = [ a.to_dict() for a in self.attribute ]
+	return rv
+
+class Computer(base):
+    def __init__(self, cpu=[], os=[], software=[], storage=[], interface=[],
+	    attribute=[]):
+	def assign_element(i):
+	    i.element = self
+
+	self.cpu = [ self.init_class(CPU, c)  for c in self.make_list(cpu) ]
+	self.os = [ self.init_class(OperatingSystem, c) \
+		for c in self.make_list(os) ]
+	self.software = [ self.init_class(Software, c) \
+		for c in self.make_list(software) ]
+	self.storage = [ self.init_class(Storage, c) \
+		for c in self.make_list(storage) ]
+	self.interface = [ self.init_class(Interface, c) \
+		for c in self.make_list(interface) ]
+	self.attribute = [ self.init_class(Attribute, a) \
+		for a in self.make_list(attribute) ]
+	map(assign_element, self.interface)
+
+    def to_dict(self):
+	rv = { }
+	if self.cpu:
+	    rv['cpu'] = [ c.to_dict() for  c in self.cpu ]
+	if self.os:
+	    rv['os'] = [ o.to_dict() for o in self.os ]
+	if self.software:
+	    rv['software'] = [ s.to_dict() for s in self.software ]
+	if self.storage:
+	    rv['storage'] = [ s.to_dict for s in self.storage ]
+	if self.interface:
+	    rv['interface'] = [ i.to_dict() for i in self.interface ]
+	if self.attribute:
+	    rv['attribute'] = [ i.to_dict() for i in self.attribute ]
+	return rv
+
+class Other(base):
+    def __init__(self, interface=[], attribute=[]):
+	self.interface = [ self.init_class(Interface, c) \
+		for c in self.make_list(interface) ]
+	self.attribute = [ self.init_class(Attribute, c) \
+		for c in self.make_list(attribute) ]
+
+    def to_dict(self):
+	if self.interface:
+	    rv['interface'] = [ i.to_dict() for i in self.interface ]
+	if self.attribute:
+	    rv['attribute'] = [ a.to_dict() for a in self.attribute ]
+
+
+class Topology(base):
+    @staticmethod
+    def init_element(e):
+	"""
+	e should be of the form { typename: args } where args is a dict full of
+	the right parameters to initialize the element.  e should have only one
+	key, but we walk e's keys in an arbitrary order and instantiate the
+	first key we know how to.
+	"""
+	classmap = {
+		'computer': Computer,
+		'other': Other,
+	    }
+
+	for k in e.keys():
+	    cl = classmap.get(k, None)
+	    if cl: return cl(**e[k])
+
+    def __init__(self, substrates=[], elements=[]):
+	self.substrates = [ self.init_class(Substrate, s) \
+		for s in self.make_list(substrates) ]
+	self.elements = [ self.init_element(e) \
+		for e in self.make_list(elements) ]
+	# Could to this init in one gulp, but we want to look for duplicate
+	# substrate names
+	substrate_map = { }
+	for s in self.substrates:
+	    if not substrate_map.has_key(s.name):
+		substrate_map[s.name] = s
+	    else:
+		raise ConsistencyError("Duplicate substrate name %s" % s.name)
+
+	substrate_map = dict([ (s.name, s) for s in self.substrates ])
+	for e in self.elements:
+	    for i in e.interface:
+		for sn in i.substrate:
+		    # NB, interfaces have substrate names in their substrate
+		    # attribute.
+		    if substrate_map.has_key(sn):
+			sub = substrate_map[sn]
+			i.subs.append(sub)
+			sub.interfaces.append(i)
+		    else:
+			raise ConsistencyError("No such substrate for %s" % sn)
+
+    def to_dict(self):
+	rv = { }
+	if self.substrates:
+	    rv['substrates'] = [ s.to_dict() for s in self.substrates ]
+	if self.elements:
+	    rv['elements'] = [ s.to_dict() for s in self.elements ]
+	return rv
+
+def topology_from_xml(file=None, top="topology"):
+    import xml.parsers.expat
+
+    class parser:
+	def __init__(self):
+	    self.stack = [ ]
+	    self.chars = ""
+	    self.key = ""
+	    self.have_chars = False
+	    self.current = { }
+	    self.in_cdata = False
+	
+	def start_element(self, name, attrs):
+	    self.chars = ""
+	    self.have_chars = False
+	    self.key = str(name)
+	    self.stack.append((self.current, self.key))
+	    self.current = { }
+
+	def end_element(self, name):
+	    if self.have_chars:
+		self.chars = self.chars.strip()
+		if len(self.chars) >0:
+		    addit = self.chars
+		else:
+		    addit = self.current
+	    else:
+		addit = self.current
+
+	    parent, key = self.stack.pop()
+	    if parent.has_key(key):
+		if isinstance(parent[key], list):
+		    parent[key].append(addit)
+		else:
+		    parent[key] = [parent[key], addit]
+	    else:
+		parent[key] = addit
+	    self.current = parent
+	    self.key = key
+
+	    self.chars = ""
+	    self.have_chars = False
+
+	def char_data(self, data):
+	    self.have_chars = True
+	    self.chars += data
+
+    p = parser()
+    xp = xml.parsers.expat.ParserCreate()
+
+    xp.StartElementHandler = p.start_element
+    xp.EndElementHandler = p.end_element
+    xp.CharacterDataHandler = p.char_data
+
+    f = open(file, "r")
+
+    xp.ParseFile(f)
+
+    return Topology(**p.current[top])
+
+def topology_to_xml(t, top=None):
+    """
+    Print the topology as XML.  This is quick and dirty, but should work for
+    many purposes.  Convert the topology to a dict and print it recursively.
+    """
+    from xml.sax.saxutils import escape
+
+    def dict_to_xml(e, top=None):
+	if top: rv = "<%s>" % top
+	else: rv = ""
+
+	for k in e.keys():
+	    if isinstance(e[k], (basestring, int, float, long)):
+		rv += "<%s>%s</%s>" % (k, escape(e[k]), k)
+	    elif isinstance(e[k], dict):
+		rv += "<%s>%s</%s>" % (k, dict_to_xml(e[k]), k)
+	    elif getattr(e[k], '__iter__', None):
+		for ee in e[k]:
+		    if isinstance(ee, dict):
+			rv += "<%s>%s</%s>" % (k, dict_to_xml(ee), k)
+		    else:
+			rv += "<%s>%s</%s>" % (k, escape(ee), k)
+	    else:
+		raise ConsistencyError("What is this?? %s %s" % (k, e[k]))
+	if top: rv += "</%s>" % top
+	return rv
+
+    return dict_to_xml(t.to_dict(), top)
+
+
+
