Index: fedd/federation/local_emulab_segment.py
===================================================================
--- fedd/federation/local_emulab_segment.py	(revision d3c8759502763e1a406b71e373ebea94021c6a05)
+++ fedd/federation/local_emulab_segment.py	(revision 2f6820c51d846a9fc3a9493c9a12008a72228793)
@@ -12,73 +12,9 @@
 import util
 
-class local_emulab_segment:
-    class cmd_timeout(RuntimeError): pass
-
+from local_segment import local_segment
+
+class start_segment(local_segment):
     def __init__(self, log=None, keyfile=None, debug=False):
-	self.log = log or logging.getLogger(\
-		'fedd.access.proxy_emulab_segment')
-	self.certfile = keyfile
-	self.debug = debug
-	self.cmd_timeout = local_emulab_segment.cmd_timeout
-
-    def copy_file(self, src, dest, size=1024):
-        """
-        Exceedingly simple file copy.
-        """
-
-	if not self.debug:
-	    util.copy_file(src, dest, size)
-	else:
-	    self.log.debug("Copy %s to %s" % (src, dest))
-
-    def cmd_with_timeout(self, cmd, wname=None, timeout=None):
-	"""
-	Run a command.  If debug is set, the action
-	is only logged.  Commands are run without stdin, to avoid stray
-	SIGTTINs. If timeout is given and the command runs longer, a
-	cmd_timeout exception is thrown.
-	"""
-
-	try:
-	    dnull = open("/dev/null", "w")
-	except EnvironmentError:
-	    self.log.debug("[cmd_with_timeout]: failed to open /dev/null " + \
-		    "for redirect")
-	    dnull = Null
-
-	self.log.debug("[cmd_with_timeout]: %s" % cmd)
-	if not self.debug:
-	    if dnull:
-		sub = subprocess.Popen(cmd, shell=True, stdout=dnull,
-			stderr=dnull, close_fds=True)
-	    else:
-		sub = subprocess.Popen(cmd, shell=True, close_fds=True)
-	    if timeout:
-		i = 0
-		rv = sub.poll()
-		while i < timeout:
-		    if rv is not None: break
-		    else:
-			time.sleep(1)
-			rv = sub.poll()
-			i += 1
-		else:
-		    self.log.debug("Process exceeded runtime: %s" % cmd)
-		    os.kill(sub.pid, signal.SIGKILL)
-		    raise self.cmd_timeout();
-		return rv == 0
-	    else:
-		return sub.wait() == 0
-	else:
-	    if timeout == 0:
-		self.log.debug("debug timeout raised on %s " % cmd)
-		raise self.cmd_timeout()
-	    else:
-		return True
-
-class start_segment(local_emulab_segment):
-    def __init__(self, log=None, keyfile=None, debug=False):
-	local_emulab_segment.__init__(self, log=log, 
-		keyfile=keyfile, debug=debug)
+	local_segment.__init__(self, log=log, keyfile=keyfile, debug=debug)
 	self.null = """
 set ns [new Simulator]
@@ -93,4 +29,7 @@
 
     def get_state(self, pid, eid):
+	"""
+	Return the state of the experiment as reported by emulab
+	"""
 	# command to test experiment state
 	expinfo_exec = "/usr/testbed/bin/expinfo"  
@@ -141,4 +80,8 @@
 
     def get_mapping(self, pid, eid):
+	"""
+	Get the physical to virtual mapping from the expinfo command and save
+	it in the self.map member.
+	"""
 	# command to test experiment state
 	expinfo_exec = "/usr/testbed/bin/expinfo"  
@@ -199,14 +142,35 @@
 	    return True
 
-
-
-    def __call__(self, parent, eid, pid, user, tclfile, tmpdir, timeout=0):
-	"""
-	Start a sub-experiment on a federant.
-
-	Get the current state, modify or create as appropriate, ship data
-	and configs and start the experiment.  There are small ordering
-	differences based on the initial state of the sub-experiment.
-	"""
+    def make_null_experiment(self, pid, eid, tmpdir):
+	"""
+	Create a null copy of the experiment so that we capture any logs there
+	if the modify fails.  Emulab software discards the logs from a failed
+	startexp.
+	"""
+	try:
+	    f = open("%s/null.tcl" % tmpdir, "w")
+	    print >>f, self.null
+	    f.close()
+	except EnvironmentError, e:
+	    raise service_error(service_error.internal,
+		    "Cannot stage null.tcl: %s" % e.strerror)
+
+	timedout = False
+	try:
+	    if not self.cmd_with_timeout(
+		    ("/usr/testbed/bin/startexp -i -f -w -p %s " + 
+		    "-e %s %s/null.tcl") % (pid, eid, tmpdir), "startexp",
+		    timeout=60 * 10):
+		return False
+	except self.cmd_timeout:
+	    timedout = True
+
+	if timedout:
+	    state = self.get_state(pid, eid)
+	    return state == "swapped"
+	else:
+	    return True
+
+    def set_up_experiment_filespace(self, pid, eid, tmpdir):
 	# Configuration directories on this machine
 	proj_dir = "/proj/%s/exp/%s/tmp" % (pid, eid)
@@ -215,33 +179,4 @@
 	lsoftdir = "%s/software" % tmpdir
 
-	state = self.get_state(pid, eid)
-
-	if state == 'none':
-	    # Create a null copy of the experiment so that we capture any
-	    # logs there if the modify fails.  Emulab software discards the
-	    # logs from a failed startexp
-	    try:
-		f = open("%s/null.tcl" % tmpdir, "w")
-		print >>f, self.null
-		f.close()
-	    except EnvironmentError, e:
-		raise service_error(service_error.internal,
-			"Cannot stage null.tcl: %s" % e.strerror)
-
-	    timedout = False
-	    try:
-		if not self.cmd_with_timeout(
-			("/usr/testbed/bin/startexp -i -f -w -p %s " + 
-			"-e %s %s/null.tcl") % (pid, eid, tmpdir), "startexp",
-			timeout=60 * 10):
-		    return False
-	    except self.cmd_timeout:
-		timedout = True
-
-	    if timedout:
-		state = self.get_state(pid, eid)
-		if state != "swapped":
-		    return False
-	
 	# Set up the experiment's file space
 	if not self.cmd_with_timeout("/bin/rm -rf %s" % proj_dir):
@@ -270,4 +205,51 @@
 	    return False
 
+	return True
+
+    def swap_in(self, pid, eid):
+	"""
+	Swap experiment in.  This includes code to cope with the experiment
+	swaping command timing out, but the experiment being swapped in
+	successfully.
+	"""
+	self.log.info("[start_segment]: Swapping %s" % eid)
+	timedout = False
+	try:
+	    if not self.cmd_with_timeout(
+		    "/usr/testbed/bin/swapexp -w %s %s in" % (pid, eid),
+		    "swapexp", timeout=25*60):
+		return False
+	except self.cmd_timeout:
+	    timedout = True
+       
+	# If the command was terminated, but completed successfully,
+	# report success.
+	if timedout:
+	    self.log.debug("[start_segment]: swapin timed out " +\
+		    "checking state")
+	    state = self.get_state(pid, eid)
+	    self.log.debug("[start_segment]: state is %s" % state)
+	    return state == 'active'
+	else:
+	    return True
+
+    def __call__(self, parent, eid, pid, user, tclfile, tmpdir, timeout=0):
+	"""
+	Start a sub-experiment on a federant.
+
+	Get the current state, modify or create as appropriate, ship data
+	and configs and start the experiment.  There are small ordering
+	differences based on the initial state of the sub-experiment.
+	"""
+
+	state = self.get_state(pid, eid)
+
+	if state == 'none':
+	    if not self.make_null_experiment(pid, eid, tmpdir):
+		return False
+
+	if not self.set_up_experiment_filespace(pid, eid, tmpdir):
+	    return False
+	
 	# Stage the new configuration (active experiments will stay swapped
 	# in now)
@@ -286,31 +268,13 @@
 	# Active experiments are still swapped, this swaps the others in.
 	if state != 'active':
-	    self.log.info("[start_segment]: Swapping %s" % eid)
-	    timedout = False
-	    try:
-		if not self.cmd_with_timeout(
-			"/usr/testbed/bin/swapexp -w %s %s in" % (pid, eid),
-			"swapexp", timeout=25*60):
-		    return False
-	    except self.cmd_timeout:
-		timedout = True
-	   
-	    # If the command was terminated, but completed successfully,
-	    # report success.
-	    if timedout:
-		self.log.debug("[start_segment]: swapin timed out " +\
-			"checking state")
-		state = self.get_state(pid, eid)
-		self.log.debug("[start_segment]: state is %s" % state)
-		if state != 'active':
-		    return False
+	    if not self.swap_in(pid, eid):
+		return False
 	# Everything has gone OK.
 	self.get_mapping(pid,eid)
 	return True
 
-class stop_segment(local_emulab_segment):
+class stop_segment(local_segment):
     def __init__(self, log=None, keyfile=None, debug=False):
-	local_emulab_segment.__init__(self,
-		log=log, keyfile=keyfile, debug=debug)
+	local_segment.__init__(self, log=log, keyfile=keyfile, debug=debug)
 
     def __call__(self, parent, user, pid, eid):
