Index: fedd/fedd_create_experiment.py
===================================================================
--- fedd/fedd_create_experiment.py	(revision 1af38d6347a52bd6beaadc3f0d421a1e49f04001)
+++ fedd/fedd_create_experiment.py	(revision 3441fe3afc83fc24184c96e923b55ebfda937c2d)
@@ -16,4 +16,6 @@
 import tempfile
 import copy
+
+import traceback
 
 from threading import *
@@ -35,8 +37,9 @@
 	    cert_file=None,
 	    cert_pwd=None,
+	    exp_stem="faber-splitter",
 	    debug=False,
 	    muxmax=2,
 	    nthreads=2,
-	    project_user = "faber",
+	    randomize_experiments=False,
 	    scp_exec="/usr/bin/scp",
 	    scripts_dir="./", 
@@ -50,4 +53,5 @@
 	    tclsh="/usr/local/bin/otclsh",
 	    tcl_splitter="/usr/testbed/lib/ns2ir/parse.tcl",
+	    trace_file=None,
 	    trusted_certs=None,
 	    ):
@@ -58,8 +62,9 @@
 	self.cert_file = cert_file
 	self.cert_pwd = cert_pwd
+	self.exp_stem = exp_stem
 	self.debug = debug
 	self.muxmax = muxmax
 	self.nthreads = nthreads
-	self.project_user = project_user
+	self.randomize_experiments = randomize_experiments
 	self.scp_exec = scp_exec
 	self.scripts_dir = scripts_dir
@@ -72,4 +77,5 @@
 	self.tcl_splitter = tcl_splitter
 	self.tbmap = tbmap
+	self.trace_file = trace_file
 	self.trusted_certs=trusted_certs
 
@@ -137,5 +143,5 @@
     class pooled_thread(Thread):
 	def __init__(self, group=None, target=None, name=None, args=(), 
-		kwargs={}, pdata=None):
+		kwargs={}, pdata=None, trace_file=None):
 	    Thread.__init__(self, group, target, name, args, kwargs)
 	    self.rv = None
@@ -145,4 +151,5 @@
 	    self.kwargs = kwargs
 	    self.pdata = pdata
+	    self.trace_file = trace_file
 	
 	def run(self):
@@ -153,8 +160,18 @@
 		try:
 		    self.rv = self.target(*self.args, **self.kwargs)
-		except e:
-		    self.exception = e
-
-	    print "%s done: %s" % (self.getName(), self.rv)
+		except service_error, s:
+		    self.exception = s
+		    if self.trace_file:
+			print >>self.trace_file, "Thread exception: %s %s" % \
+				(s.code_string(), s.desc)
+		    
+		except:
+		    self.exception = sys.exc_info()[1]
+		    if self.trace_file:
+			print >>self.trace_file, \
+				"Unexpected thread exception: %s" % \
+				self.exception
+			print >>self.trace_file, "Trace: %s" % \
+				traceback.format_exc()
 	    if self.pdata:
 		self.pdata.terminate()
@@ -180,21 +197,40 @@
 
 	scp_cmd = [self.scp_exec, file, "%s@%s:%s" % (user, host, dest)]
+
+	trace = self.trace_file
+	if not trace:
+	    try:
+		trace = open("/dev/null", "w")
+	    except IOError:
+		raise service_error(service_error.internal,
+			"Cannot open /dev/null??");
+
 	if not self.debug:
-	    rv = call(scp_cmd)
+	    rv = call(scp_cmd, stdout=trace, stderr=trace)
 	else:
-	    print "debug: %s" % " ".join(scp_cmd)
+	    if self.trace_file: 
+		print >>self.trace_file, "debug [scp_file]: %s" % \
+			" ".join(scp_cmd)
 	    rv = 0
 
 	return rv == 0
 
-    def ssh_cmd(self, user, host, cmd, wname=None, timeout=0):
+    def ssh_cmd(self, user, host, cmd, wname=None):
 	sh_str = "%s %s@%s %s" % (self.ssh_exec, user, host, cmd)
 
+	trace = self.trace_file
+	if not trace:
+	    try:
+		trace = open("/dev/null", "w")
+	    except IOError:
+		raise service_error(service_error.internal,
+			"Cannot open /dev/null??");
+
 	if not self.debug:
-	    # This should be done more carefully
-	    sub = Popen(sh_str, shell=True)
+	    sub = Popen(sh_str, shell=True, stdout=trace, stderr=trace)
 	    return sub.wait() == 0
 	else:
-	    print "debug: %s" % sh_str 
+	    if self.trace_file:
+		print >>self.trace_file,"debug [ssh_cmd]: %s" % sh_str 
 	    return True
 
@@ -238,18 +274,37 @@
 	rpms_dir = "/proj/%s/rpms/%s" % (pid, eid)
 	state_re = re.compile("State:\s+(\w+)")
-	state = "none"
-
-	status = Popen([self.ssh_exec, "%s@%s" % (user, host), 
-		expinfo_exec, pid, eid], stdout=PIPE)
+	no_exp_re = re.compile("^No\s+such\s+experiment")
+	state = None
+	cmd = [self.ssh_exec, "%s@%s" % (user, host), expinfo_exec, pid, eid]
+
+
+	if self.trace_file:
+	    print >>self.trace_file, "status request: %s" % " ".join(cmd)
+	
+	if not self.trace_file:
+	    try:
+		st_file = open("/dev/null", "w")
+	    except IOError:
+		raise service_error(service_error.internal, 
+			"Cannot open /dev/null!?")
+	else:
+	    st_file = self.trace_file
+
+	status = Popen(cmd, stdout=PIPE, stderr=st_file)
 	for line in status.stdout:
 	    m = state_re.match(line)
 	    if m: state = m.group(1)
+	    else:
+		m = no_exp_re.match(line)
+		if m: state = "none"
 	rv = status.wait()
-	if rv != 0:
+	# No experiment returns a non-zero rv.  If we successfully parsed a
+	# "none" outcome, ignore teh return code.
+	if rv != 0 and state != "none":
 	    raise service_error(service_error.internal,
 		    "Cannot get status of segment %s:%s/%s" % (tb, pid, eid))
-	# XXX
-	print "%s: %s" % (tb, state)
-	print "transferring experiment to %s" % tb
+	if self.trace_file:
+	    print >>self.trace_file, "%s: %s" % (tb, state)
+	    print >>self.trace_file, "transferring experiment to %s" % tb
 
 	if not self.scp_file("%s/%s/%s" % (tmpdir, tb, tclfile), user, host):
@@ -283,5 +338,6 @@
 			"%s/rpms" % tmpdir, tarfiles_dir):
 		    return False
-	    print "Modifying %s on %s" % (eid, tb)
+	    if self.trace_file:
+		print >>self.trace_file, "Modifying %s on %s" % (eid, tb)
 	    if not self.ssh_cmd(user, host,
 		    "/usr/testbed/bin/modexp -r -s -w %s %s %s" % \
@@ -308,13 +364,15 @@
 			"%s/rpms" % tmpdir, tarfiles_dir):
 		    return False
-	    print "Modifying %s on %s" % (eid, tb)
+	    if self.trace_file:
+		print >>self.trace_file, "Modifying %s on %s" % (eid, tb)
 	    if not self.ssh_cmd(user, host,
 		    "/usr/testbed/bin/modexp -w %s %s %s" % (pid, eid, tclfile),
 		    "modexp"):
 		return False
-	    print "Swapping %s in on %s" % (eid, tb)
+	    if self.trace_file:
+		print >>self.trace_file, "Swapping %s in on %s" % (eid, tb)
 	    if not self.ssh_cmd(user, host,
 		    "/usr/testbed/bin/swapexp -w %s %s in" % (pid, eid),
-		    "swapexp", timeout):
+		    "swapexp"):
 		return False
 	    return True
@@ -333,8 +391,9 @@
 			"%s/rpms" % tmpdir, tarfiles_dir):
 		    return False
-	    print "Creating %s on %s" % (eid, tb)
+	    if self.trace_file:
+		print >>self.trace_file, "Creating %s on %s" % (eid, tb)
 	    if not self.ssh_cmd(user, host,
 		    "/usr/testbed/bin/startexp -i -f -w -p %s -e %s %s" % \
-			    (pid, eid, tclfile), "startexp", timeout):
+			    (pid, eid, tclfile), "startexp"):
 		return False
 	    # After startexp the per-experiment directories exist
@@ -348,13 +407,14 @@
 		    proj_dir):
 		return False
-	    print "Swapping %s in on %s" % (eid, tb)
+	    if self.trace_file:
+		print >>self.trace_file, "Swapping %s in on %s" % (eid, tb)
 	    if not self.ssh_cmd(user, host,
 		    "/usr/testbed/bin/swapexp -w %s %s in" % (pid, eid),
-		    "swapexp", timeout):
+		    "swapexp"):
 		return False
 	    return True
 	else:
-	    # XXX
-	    print "unknown state %s" % state
+	    if self.trace_file:
+		print >>self.trace_file, "unknown state %s" % state
 	    return False
 
@@ -364,8 +424,8 @@
 	pid = tbparams[tb]['project']
 
-	# XXX: 
-	print "Stopping %s on %s" % (eid, tb)
+	if self.trace_file:
+	    print >>self.trace_file, "Stopping %s on %s" % (eid, tb)
 	return self.ssh_cmd(user, host,
-		"/usr/testbed/bin/swapexp -w %d %d out" % (pid, eid))
+		"/usr/testbed/bin/swapexp -w %s %s out" % (pid, eid))
 
 	
@@ -379,6 +439,16 @@
 	t = type.lower();
 	if t not in valid_types: raise ValueError
+
+	trace = self.trace_file
+	if not trace:
+	    try:
+		trace = open("/dev/null", "w")
+	    except IOError:
+		raise service_error(service_error.internal,
+			"Cannot open /dev/null??");
+
 	# May raise CalledProcessError
-	rv = call([self.ssh_keygen, '-t', t, '-N', '', '-f', dest])
+	rv = call([self.ssh_keygen, '-t', t, '-N', '', '-f', dest],
+		stdout=trace, stderr=trace)
 	if rv != 0:
 	    raise service_error(service_error.internal, 
@@ -535,4 +605,5 @@
 	    'connectortype': 'gwtype',
 	    'tunnelcfg': 'tun',
+	    'smbshare': 'smbshare',
 	}
 
@@ -605,5 +676,6 @@
 		"fs": e['fileServer'],
 		"eventserver": e['eventServer'],
-		"project": unpack_id(p['name'])
+		"project": unpack_id(p['name']),
+		"emulab" : e
 		}
 
@@ -730,5 +802,5 @@
 
     class gateways:
-	def __init__(self, eid, smbshare, master, tmpdir, gw_pubkey,
+	def __init__(self, eid, master, tmpdir, gw_pubkey,
 		gw_secretkey, copy_file):
 	    self.begin_gateways = \
@@ -740,5 +812,4 @@
 
 	    self.eid = eid
-	    self.smbshare = smbshare
 	    self.master = master
 	    self.tmpdir = tmpdir
@@ -850,7 +921,9 @@
 			    print >>cc, "ControlGateway: %s" % \
 				    self.control_gateway
-			    print >>cc, "SMBSHare: %s" % self.smbshare
+			    if tbparams[self.master].has_key('smbshare'):
+				print >>cc, "SMBSHare: %s" % \
+					tbparams[self.master]['smbshare']
 			    print >>cc, "ProjectUser: %s" % \
-				    tbparams[self.current_gateways]['user']
+				    tbparams[self.master]['user']
 			    print >>cc, "ProjectName: %s" % \
 				    tbparams[self.master]['project']
@@ -860,7 +933,7 @@
 				    "Error creating client config")
 		    else:
-			# XXX
-			print >>sys.stderr, "No control gateway for %s" %\
-				self.current_gateways
+			if self.trace_file:
+			    print >>sys.stderr, "No control gateway for %s" %\
+				    self.current_gateways
 		    self.current_gateways = None
 		else:
@@ -977,15 +1050,10 @@
 	pid = "dummy"
 	gid = "dummy"
-	# XXX
-	eid = "faber-splitter"
-	# XXX
-	master = "deter"
-	# XXX
-	smbshare="USERS"
+	eid = self.exp_stem
+	if self.randomize_experiments:
+	    for i in range(0,5):
+		eid += random.choice(string.ascii_letters)
 	# XXX
 	fail_soft = False
-	# XXX
-	startem = True
-
 
 	try:
@@ -1017,4 +1085,8 @@
 	if user == None:
 	    raise service_error(service_error.req, "No user")
+
+	master = req.get('master', None)
+	if master == None:
+	    raise service_error(service_error.req, "No master testbed label")
 	
 	
@@ -1028,5 +1100,5 @@
 	parse_current_testbed = self.current_testbed(eid, tmpdir)
 	parse_allbeds = self.allbeds(self.get_access)
-	parse_gateways = self.gateways(eid, smbshare, master, tmpdir,
+	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",
@@ -1060,5 +1132,4 @@
 
 	self.genviz(tmpdir + "/vtopo.xml", tmpdir + "/viz.xml")
-	if not startem: return True
 
 	# Copy tarfiles and rpms needed at remote sites into a staging area
@@ -1093,5 +1164,5 @@
 	    t  = self.pooled_thread(target=self.start_segment, 
 		    args=(tb, eid, tbparams, tmpdir, 0), name=tb,
-		    pdata=thread_pool_info)
+		    pdata=thread_pool_info, trace_file=self.trace_file)
 	    threads.append(t)
 	    t.start()
@@ -1113,11 +1184,21 @@
 
 	# If one failed clean up
-	if not fail_soft and len(failed) > 0:
-	    for tb in failed:
-		self.stop_segment(tb, eid, tbparams)
+	if len(failed) > 0:
+	    succeeded = [tb for tb in allocated.keys() if tb not in failed]
+	    if fail_soft:
+		raise service_error(service_error.partial, \
+			"Partial swap in on %s" % ",".join(succeeded))
+	    else:
+		for tb in succeeded:
+		    self.stop_segment(tb, eid, tbparams)
+		raise service_error(service_error.federant,
+		    "Swap in failed on %s" % ",".join(failed))
 	else:
-	    print "Experiment started"
-
-	# XXX: return value
+	    if self.trace_file:
+		print >>self.trace_file, "Experiment started"
+
+	return { 'emulab' : [ tbparams[tb]['emulab'] \
+		for tb in tbparams.keys() \
+		    if tbparams[tb].has_key('emulab') ] }
 
 if __name__ == '__main__':
@@ -1128,5 +1209,23 @@
 	    action='store_true', help='print actions rather than take them')
     parser.add_option('-f', '--file', dest='tcl', help='tcl file to parse')
+    parser.add_option('-m', '--master', dest='master', 
+	    help='testbed label for matster testbd')
+    parser.add_option('-t', '--trace', dest='trace', default=None, 
+	    help='file to print intermediate messages to')
+    parser.add_option('-T', '--trace-stderr', dest='trace', 
+	    action='store_const',const=sys.stderr,
+	    help='file to print intermediate messages to')
     opts, args  = parser.parse_args()
+
+    trace_file = None
+    if opts.trace:
+	try:
+	    trace_file = open(opts.trace, 'w')
+	except IOError:
+	    print >>sys.stderr, "Can't open trace file"
+
+    if opts.debug:
+	if not trace_file:
+	    trace_file = sys.stderr
 
     if opts.tcl != None:
@@ -1139,4 +1238,7 @@
     else:
 	sys.exit("Must specify a file name")
+
+    if not opts.master:
+	sys.exit("Must supply master tb label (--master)");
 
     obj = fedd_create_experiment_local(
@@ -1151,8 +1253,12 @@
 		'ucb':'https://users.isi.deterlab.net:23237',
 		},
+	    trace_file=trace_file
 	) 
-    obj.create_experiment( {\
+    rv = obj.create_experiment( {\
 	    'experimentdescription' : content, 
+	    'master' : opts.master, 
 	    'user': [ {'userID' : { 'username' : 'faber' } } ],
 	    },
 	    None)
+
+    print rv
Index: fedd/fedd_types.xsd
===================================================================
--- fedd/fedd_types.xsd	(revision 1af38d6347a52bd6beaadc3f0d421a1e49f04001)
+++ fedd/fedd_types.xsd	(revision 3441fe3afc83fc24184c96e923b55ebfda937c2d)
@@ -235,4 +235,5 @@
 	maxOccurs="unbounded"/>
       <xsd:element name="experimentdescription" type="xsd:base64Binary"/>
+      <xsd:element name="master" type="xsd:string"/>
     </xsd:sequence>
   </xsd:complexType>
@@ -245,5 +246,5 @@
     </xsd:annotation>
     <xsd:sequence>
-      <xsd:element name="project" type="projectType" minOccurs="1" 
+      <xsd:element name="emulab" type="emulabType" minOccurs="1" 
 	maxOccurs="unbounded"/>
     </xsd:sequence>
@@ -257,5 +258,8 @@
 	  <xsd:enumeration value="2"/>	<!-- proxy error -->
 	  <xsd:enumeration value="3"/>	<!-- badly formed request -->
-	  <xsd:enumeration value="4"/>	<!-- internal error -->
+	  <xsd:enumeration value="4"/>	<!-- server configuration error -->
+	  <xsd:enumeration value="5"/>	<!-- internal error -->
+	  <xsd:enumeration value="6"/>	<!-- partial instantiation -->
+	  <xsd:enumeration value="7"/>	<!-- federant error -->
 	</xsd:restriction>
       </xsd:element>
Index: fedd/service_error.py
===================================================================
--- fedd/service_error.py	(revision 1af38d6347a52bd6beaadc3f0d421a1e49f04001)
+++ fedd/service_error.py	(revision 3441fe3afc83fc24184c96e923b55ebfda937c2d)
@@ -10,4 +10,6 @@
     server_config = 4
     internal = 5
+    partial = 6
+    federant = 7
     code_str = { 
 	access : "Access Denied",
@@ -15,5 +17,7 @@
 	req : "Badly Formed Request",
 	server_config: "Server Configuration Error",
-	internal : "Internal Error"
+	internal : "Internal Error",
+	partial: "Partial embedding",
+	federant: "Federant error"
     }
     str_code = dict([ (v, k) for k, v in code_str.iteritems() ])
