Index: fedd/federation/experiment_control.py
===================================================================
--- fedd/federation/experiment_control.py	(revision ec3aa4df8176079c445c8d290f5a3b42b202da6f)
+++ fedd/federation/experiment_control.py	(revision ab3d6c59f9482e5a70026b955581d9f81d3f9973)
@@ -36,5 +36,5 @@
 from authorizer import abac_authorizer
 from thread_pool import thread_pool, pooled_thread
-from experiment_info import experiment_info, allocation_info
+from experiment_info import experiment_info, allocation_info, federated_service
 
 import topdl
@@ -49,30 +49,4 @@
 fl = logging.getLogger("fedd.experiment_control")
 fl.addHandler(nullHandler())
-
-
-# Right now, no support for composition.
-class federated_service:
-    def __init__(self, name, exporter=None, importers=None, params=None, 
-	    reqs=None, portal=None):
-	self.name=name
-	self.exporter=exporter
-	if importers is None: self.importers = []
-	else: self.importers=importers
-	if params is None: self.params = { }
-	else: self.params = params
-	if reqs is None: self.reqs = []
-	else: self.reqs = reqs
-
-	if portal is not None:
-	    self.portal = portal
-	else:
-	    self.portal = (name in federated_service.needs_portal)
-
-    def __str__(self):
-	return "name %s export %s import %s params %s reqs %s" % \
-		(self.name, self.exporter, self.importers, self.params,
-			[ (r['name'], r['visibility']) for r in self.reqs] )
-
-    needs_portal = ('SMB', 'seer', 'tmcd', 'project_export', 'seer_master')
 
 class experiment_control_local(experiment_control_legacy):
@@ -1424,7 +1398,8 @@
 	    raise service_error(service_error.protocol,
                         "Bad access response (no access proof)")
-        
+
 	tbparam[tb] = allocation_info(allocID=r['allocID'].get('fedid', None),
-		tb=tb, uri=uri, proof=[r['proof']])
+		tb=tb, uri=uri, proof=[r['proof']], 
+		services=masters.get(tb, None))
 
 	# Collect the responses corresponding to the services this testbed
@@ -1434,4 +1409,7 @@
 	for s in r.get('service', []):
 	    id = s.get('id', None)
+	    # Note that this attaches the response to the object in the masters
+	    # data structure.  (The e_keys index disappears when this fcn
+	    # returns)
 	    if id and id in e_keys:
 		e_keys[id].reqs.append(s)
@@ -1767,11 +1745,9 @@
     def get_testbed_services(self, req, testbeds):
 	"""
-	Parse the services section of the request into into two dicts mapping
-	testbed to lists of federated_service objects.  The first lists all
-	exporters of services, and the second all exporters of services that
-	need control portals in the experiment.
+	Parse the services section of the request into a dict mapping
+	testbed to lists of federated_service objects.  The dict maps all
+	exporters of services to those service objects.
 	"""
 	masters = { }
-	pmasters = { }
 	for s in req.get('service', []):
 	    # If this is a service request with the importall field
@@ -1800,12 +1776,8 @@
 		    if tb in masters: masters[tb].append(fser)
 		    else: masters[tb] = [fser]
-
-		    if fser.portal:
-			if tb not in pmasters: pmasters[tb] = [ fser ]
-			else: pmasters[tb].append(fser)
 		else:
 		    self.log.error('Testbed service does not have name " + \
 			    "and importers')
-	return masters, pmasters
+	return masters
 
     def generate_keys_and_hosts(self, tmpdir, expid, hosts, tbparams):
@@ -2015,5 +1987,6 @@
 	    exp.add_allocation(tbparams[k])
 	    top.elements.append(topdl.Testbed(uri=tbparams[k].uri,
-		type="testbed", localname=[k]))
+		type="testbed", localname=[k], 
+		service=[ s.to_topdl() for s in tbparams[k].services]))
 
 	# Access proofs for the response message
@@ -2106,5 +2079,8 @@
 			testbeds.append(tb)
 
-	    masters, pmasters = self.get_testbed_services(req, testbeds)
+	    masters = self.get_testbed_services(req, testbeds)
+	    # The exporters who need portals
+	    pmasters = dict([(k, [s for s in l if s.portal] ) \
+		    for k, l in masters.items()])
             allocated = { }         # Testbeds we can access
 	    topo ={ }		    # Sub topologies
Index: fedd/federation/experiment_info.py
===================================================================
--- fedd/federation/experiment_info.py	(revision ec3aa4df8176079c445c8d290f5a3b42b202da6f)
+++ fedd/federation/experiment_info.py	(revision ab3d6c59f9482e5a70026b955581d9f81d3f9973)
@@ -2,6 +2,34 @@
 
 import copy
+import topdl
 from datetime import datetime, timedelta
 from numbers import Number
+
+class federated_service:
+    def __init__(self, name, exporter=None, importers=None, params=None, 
+	    reqs=None, portal=None):
+	self.name=name
+	self.exporter=exporter
+	if importers is None: self.importers = []
+	else: self.importers=importers
+	if params is None: self.params = { }
+	else: self.params = params
+	if reqs is None: self.reqs = []
+	else: self.reqs = reqs
+
+	if portal is not None:
+	    self.portal = portal
+	else:
+	    self.portal = (name in federated_service.needs_portal)
+
+    def to_topdl(self):
+	return topdl.Service(name=self.name, importer=self.importers)
+
+    def __str__(self):
+	return "name %s export %s import %s params %s reqs %s" % \
+		(self.name, self.exporter, self.importers, self.params,
+			[ (r['name'], r['visibility']) for r in self.reqs] )
+
+    needs_portal = ('SMB', 'seer', 'tmcd', 'project_export', 'seer_master')
 
 class allocation_info:
@@ -11,9 +39,11 @@
     contact it.  In addition, the proofs of access are included.
     """
-    def __init__(self, allocID, tb, uri, proof=None):
+    def __init__(self, allocID, tb, uri, proof=None, services=None):
 	self.allocID = allocID
 	self.tb = tb
 	self.uri = uri
 	self.proof = proof
+	if services is not None: self.services = services
+	else: self.services = [ ]
 	self.attrs = { }
 
