Index: fedd/federation/emulab_access.py
===================================================================
--- fedd/federation/emulab_access.py	(revision d2c231599830a576b9ad430ebef3e106154b1ec9)
+++ fedd/federation/emulab_access.py	(revision 49051fbc223d9249e8f37e44c216618e2b93e6e9)
@@ -90,4 +90,5 @@
 	self.local_seer_image = config.get("access", "local_seer_image")
 	self.local_seer_start = config.get("access", "local_seer_start")
+	self.seer_master_start = config.get("access", "seer_master_start")
 	self.ssh_privkey_file = config.get("access","ssh_privkey_file")
 	self.ssh_pubkey_file = config.get("access","ssh_pubkey_file")
@@ -130,10 +131,16 @@
 	# XXX: Configurable
 	self.exports = set(('SMB', 'seer', 'tmcd', 'userconfig', 
-	    'project_export', 'local_seer_control'))
-	self.imports = set(('SMB', 'seer', 'userconfig'))
-
-	if not self.local_seer_image or not self.local_seer_software or \
-		not self.local_seer_start:
+	    'project_export', 'local_seer_control', 'seer_master'))
+	self.imports = set(('SMB', 'seer', 'userconfig', 'seer_master'))
+
+	if not self.local_seer_image or not self.local_seer_software:
 	    self.exports.discard('local_seer_control')
+	    self.exports.discard('seer_master')
+
+	if not self.local_seer_start:
+	    self.exports.discard('local_seer_control')
+
+	if not self.seer_master_start:
+	    self.exports.discard('seer_master')
 
         if auth: self.auth = auth
@@ -605,4 +612,12 @@
 		}
 
+    def export_seer_master(self, id, state, project, user):
+	return { 
+		'id': id,
+		'name': 'seer_master',
+		'visibility': 'export',
+		'server': 'http://seer_master:16606',
+		}
+
     def export_tmcd(self, id, state, project, user):
 	return { 
@@ -657,4 +672,7 @@
 		    elif sname == 'local_seer_control':
 			exp.append(self.export_local_seer(id, state, project,
+			    user))
+		    elif sname == 'seer_master':
+			exp.append(self.export_seer_master(id, state, project,
 			    user))
 	return (exp, state)
@@ -1055,4 +1073,7 @@
 	    print >>f, "Service: %s" % s['name']
 
+	def client_seer_master(f, s):
+	    print >>f, 'PortalAlias: seer_master';
+
 	def client_smb(f, s):
 	    print >>f, "Service: %s" % s['name']
@@ -1079,4 +1100,16 @@
 		'userconfig': client_null,
 		'project_export': client_null,
+		'seer_master': client_seer_master,
+	    }
+
+	def client_seer_master_export(f, s):
+	    print >>f, "AddedNode: seer_master"
+
+	def client_seer_local_export(f, s):
+	    print >>f, "AddedNode: control"
+
+	client_export_service_out = {
+		'seer_master': client_seer_master_export,
+		'seer_local': client_seer_local_export,
 	    }
 
@@ -1096,4 +1129,5 @@
 		'project_export': server_null,
 		'seer': server_seer,
+		'seer_master': server_port,
 	    }
 	# XXX: end un hardcode this
@@ -1109,5 +1143,5 @@
 	for e in [ e for e in topo.elements \
 		if isinstance(e, topdl.Computer) and e.get_attribute('portal')]:
-	    myname = e.name[0]
+	    myname = e.name
 	    type = e.get_attribute('portal_type')
 
@@ -1167,4 +1201,8 @@
 			s.get('visibility','') == 'import':
 		    client_service_out[s['name']](f, s)
+		if s.get('name', '') in self.exports and \
+			s.get('visibility', '') == 'export' and \
+			s['name'] in client_export_service_out:
+		    client_export_service_out[s['name']](f, s)
 	    # Seer uses this.
 	    if mproj and meid:
@@ -1192,6 +1230,6 @@
 		s = ""
 		if isinstance(e, topdl.Computer):
-		    if self.node_info.has_key(e.name[0]):
-			i = self.node_info[e.name[0]]
+		    if self.node_info.has_key(e.name):
+			i = self.node_info[e.name]
 			for ifname, vlan, type in i:
 			    for i in e.interface:
@@ -1203,14 +1241,14 @@
 				raise service_error(service_error.internal,
 					"No interface %s on element %s" % \
-						(ifname, e.name[0]))
+						(ifname, e.name))
 			    # XXX: do netmask right
 			    if type =='link':
 				s = ("tb-allow-external $%s dragonportal " + \
 					"ip %s vlan %s netmask 255.255.255.0\n") % \
-					(e.name[0], addr, vlan)
+					(e.name, addr, vlan)
 			    elif type =='lan':
 				s = ("tb-allow-external $%s dragonportal " + \
 					"ip %s vlan %s usurp %s\n") % \
-					(e.name[0], addr, vlan, subs)
+					(e.name, addr, vlan, subs)
 			    else:
 				raise service_error(service_error_internal,
@@ -1223,5 +1261,5 @@
 
 	    def __call__(self, e):
-		return e.name[0] not in self.nodes
+		return e.name not in self.nodes
 
 
@@ -1284,5 +1322,5 @@
 			e.set_attribute('startup', node_cmd)
 
-		dinf = [i[0] for i in dragon_map.get(e.name[0], []) ]
+		dinf = [i[0] for i in dragon_map.get(e.name, []) ]
 		# Remove portal interfaces that do not connect to DRAGON
 		e.interface = [i for i in e.interface \
@@ -1415,4 +1453,106 @@
 			'Bad Services missing info for import %s' % c)
 
+    def configure_userconf(self, services):
+	"""
+	If the userconf service was imported, collect the configuration data.
+	"""
+	for s in services:
+	    s_name = s.get('name', '')
+	    s_vis = s.get('visibility','')
+	    if s_name  == 'userconfig' and s_vis == 'import':
+		# Collect ther server and certificate info.
+		u = s.get('server', None)
+		for a in s.get('fedAttr', []):
+		    if a.get('attribute',"") == 'cert':
+			cert = a.get('value', None)
+			break
+		else:
+		    cert = None
+
+		if cert:
+		    # Make a temporary certificate file for get_url.  The
+		    # finally clause removes it whether something goes
+		    # wrong (including an exception from get_url) or not.
+		    try:
+			tfos, tn = tempfile.mkstemp(suffix=".pem")
+			tf = os.fdopen(tfos, 'w')
+			print >>tf, cert
+			tf.close()
+			self.log.debug("Getting userconf info: %s" % u)
+			get_url(u, tn, tmpdir, "userconf")
+			self.log.debug("Got userconf info: %s" % u)
+		    except IOError, e:
+			raise service_error(service.error.internal, 
+				"Cannot create temp file for " + 
+				"userconfig certificates: %s e")
+		    except:
+			t, v, st = sys.exc_info()
+			raise service_error(service_error.internal,
+				"Error retrieving %s: %s" % (s, v))
+		    finally:
+			if tn: os.remove(tn)
+		else:
+		    raise service_error(service_error.req,
+			    "No certificate for retreiving userconfig")
+		break
+
+    def add_seer_node(self, topo, name, startup):
+	"""
+	Add a seer node to the given topology, with the startup command passed
+	in.
+	"""
+	c_node = topdl.Computer(
+		name=name, 
+		os= topdl.OperatingSystem(
+		    attribute=[
+		    { 'attribute': 'osid', 
+			'value': self.local_seer_image },
+		    ]),
+		attribute=[
+		    { 'attribute': 'startup', 'value': startup },
+		    ]
+		)
+	self.add_kit(c_node, self.local_seer_software)
+	topo.elements.append(c_node)
+
+    def configure_seer_services(self, services, topo, softdir):
+	local_seer = False
+	collect_seer = False
+	seer_master= False
+	for s in services:
+	    s_name = s.get('name', '')
+	    s_vis = s.get('visibility','')
+
+	    if s_name  == 'local_seer_control'  and s_vis == 'export':
+		local_seer = True
+	    elif s_name == 'seer_master':
+		if s_vis == 'import':
+		    collect_seer = True
+		elif s_vis == 'export':
+		    seer_master = True
+	
+	# We've got the whole picture now, so add nodes if needed and configure
+	# them to interconnect properly.
+	if local_seer or seer_master:
+	    # Copy local seer control node software to the tempdir
+	    for l, f in self.local_seer_software:
+		base = os.path.basename(f)
+		copy_file(f, "%s/%s" % (softdir, base))
+	# If we're collecting seers somewhere the controllers need to talk to
+	# the master.  In testbeds that export the service, that will be a
+	# local node that we'll add below.  Elsewhere it will be the control
+	# portal that will port forward to the exporting master.
+	if local_seer:
+	    if collect_seer:
+		startup = "%s %s" % (self.local_seer_start, "seer_master")
+	    else:
+		startup = self.local_seer_start
+	    self.add_seer_node(topo, 'control', startup)
+	# If this is the seer master, add that node, too.
+	if seer_master:
+	    self.add_seer_node(topo, 'seer_master', self.seer_master_start)
+
+
+
     def StartSegment(self, req, fid):
 
@@ -1524,64 +1664,6 @@
 	    # If the userconf service was imported, collect the configuration
 	    # data.
-	    for s in services:
-		s_name = s.get('name', '')
-		s_vis = s.get('visibility','')
-		if s_name  == 'userconfig' and s_vis == 'import':
-		    # Collect ther server and certificate info.
-		    u = s.get('server', None)
-		    for a in s.get('fedAttr', []):
-			if a.get('attribute',"") == 'cert':
-			    cert = a.get('value', None)
-			    break
-		    else:
-			cert = None
-
-		    if cert:
-			# Make a temporary certificate file for get_url.  The
-			# finally clause removes it whether something goes
-			# wrong (including an exception from get_url) or not.
-			try:
-			    tfos, tn = tempfile.mkstemp(suffix=".pem")
-			    tf = os.fdopen(tfos, 'w')
-			    print >>tf, cert
-			    tf.close()
-			    self.log.debug("Getting userconf info: %s" % u)
-			    get_url(u, tn, tmpdir, "userconf")
-			    self.log.debug("Got userconf info: %s" % u)
-			except IOError, e:
-			    raise service_error(service.error.internal, 
-				    "Cannot create temp file for " + 
-				    "userconfig certificates: %s e")
-			except:
-			    t, v, st = sys.exc_info()
-			    raise service_error(service_error.internal,
-				    "Error retrieving %s: %s" % (s, v))
-			finally:
-			    if tn: os.remove(tn)
-		    else:
-			raise service_error(service_error.req,
-				"No certificate for retreiving userconfig")
-		    break
-		if s_name  == 'local_seer_control'  and s_vis == 'export':
-		    # Copy local seer control node software to the tempdir
-		    for l, f in self.local_seer_software:
-			base = os.path.basename(f)
-			copy_file(f, "%s/%s" % (softdir, base))
-
-		    # Add a control node to the local topology
-		    c_node = topdl.Computer(
-			    name='control', 
-			    os= topdl.OperatingSystem(
-				attribute=[
-				{ 'attribute': 'osid', 
-				    'value': self.local_seer_image },
-				]),
-			    attribute=[
-				{ 'attribute': 'startup',
-				    'value': self.local_seer_start },
-				]
-			    )
-		    self.add_kit(c_node, self.local_seer_software)
-		    topo.elements.append(c_node)
+	    self.configure_userconf(services)
+	    self.configure_seer_services(services, topo, softdir)
 
 	    proj = None
