Index: fedd/federation/experiment_control.py
===================================================================
--- fedd/federation/experiment_control.py	(revision 65d3ac110d392ffaa1018634dcecc86d87831c6f)
+++ fedd/federation/experiment_control.py	(revision de059e1d03c893cb486a77249f0fad5acd89abf2)
@@ -1314,8 +1314,9 @@
     def allocate_ips_to_topo(self, top):
 	"""
-	Add an ip4_address attribute to all the hosts in the topology, based on
-	the shared substrates on which they sit.  An /etc/hosts file is also
-	created and returned as a list of hostfiles entries.  We also return
-	the allocator, because we may need to allocate IPs to portals
+	Add an ip4_address attribute to all the hosts in the topology that have
+	not already been assigned one by the user, based on the shared
+	substrates on which they sit.  An /etc/hosts file is also created and
+	returned as a list of hostfiles entries.  We also return the allocator,
+	because we may need to allocate IPs to portals
 	(specifically DRAGON portals).
 	"""
@@ -1327,41 +1328,100 @@
 	hosts = [ ]
 
+	assigned = { }
+	assigned_subs = set()
+	# Look for IP networks that were already assigned to the experiment by
+	# the user.  We assume that users are smart in making that assignment,
+	# but we check them later.
+	for e in top.elements:
+	    if not isinstance(e, topdl.Computer): continue
+	    for inf in e.interface:
+		a = inf.get_attribute('ip4_address')
+		if not a: continue
+		for s in inf.substrate:
+		    assigned_subs.add(s)
+		a = ip_addr(a)
+		n = ip_addr(inf.get_attribute('ip4_netmask'))
+
+		sz = 0x100000000 - long(n)
+		net = (long(a) & long(n)) & 0xffffffff
+		if net in assigned:
+		    if sz > assigned[net]: assigned[net] = sz
+		assigned[net] = sz
+
+	# Walk all substrates, smallest to largest, assigning IP addresses to
+	# the unassigned and checking the assigned.  Build an /etc/hosts file
+	# as well.
 	for idx, s in enumerate(subs):
-	    net_size = len(s.interfaces)+2
-
-	    a = ips.allocate(net_size)
-	    if a :
-		base, num = a
-		if num < net_size: 
-		    raise service_error(service_error.internal,
-			    "Allocator returned wrong number of IPs??")
+	    if s.name not in assigned_subs:
+		net_size = len(s.interfaces)+2
+
+		# Get an ip allocation for this substrate.  Make sure the
+		# allocation does not collide with any user allocations.
+		ok = False
+		while not ok:
+		    a = ips.allocate(net_size)
+		    if not a:
+			raise service_error(service_error.req, 
+				"Cannot allocate IP addresses")
+		    base, num = a
+		    if num < net_size: 
+			raise service_error(service_error.internal,
+				"Allocator returned wrong number of IPs??")
+		    # Check for overlap.  An assigned network could contain an
+		    # endpoint of the new allocation or the new allocation
+		    # could contain an endpoint of an assigned network.
+		    # NB the else is attached to the for - if the loop is not
+		    # exited by a break, ok = True.
+		    for n, sz in assigned.items():
+			if base >= n and base < n + sz:
+			    ok = False
+			    break
+			if base + num > n and base + num  < n + sz:
+			    ok = False
+			    break
+			if n >= base and n < base + num:
+			    ok = False
+			    break
+			if n+sz > base and n+sz < base + num:
+			    ok = False
+			    break
+		    else:
+			ok = True
+			    
+		mask = ips.min_alloc
+		while mask < net_size:
+		    mask *= 2
+
+		netmask = ((2**32-1) ^ (mask-1))
 	    else:
-		raise service_error(service_error.req, 
-			"Cannot allocate IP addresses")
-	    mask = ips.min_alloc
-	    while mask < net_size:
-		mask *= 2
-
-	    netmask = ((2**32-1) ^ (mask-1))
-
+		base = 0
+
+	    # Add the attributes and build hosts
 	    base += 1
 	    for i in s.interfaces:
-		i.attribute.append(
-			topdl.Attribute('ip4_address', 
-			    "%s" % ip_addr(base)))
-		i.attribute.append(
-			topdl.Attribute('ip4_netmask', 
-			    "%s" % ip_addr(int(netmask))))
-
+		# Add address and masks to interfaces on unassigned substrates
+		if s.name not in assigned_subs:
+		    i.attribute.append(
+			    topdl.Attribute('ip4_address', 
+				"%s" % ip_addr(base)))
+		    i.attribute.append(
+			    topdl.Attribute('ip4_netmask', 
+				"%s" % ip_addr(int(netmask))))
+
+		# Make /etc/hosts entries
+		addr = i.get_attribute('ip4_address')
+		if addr is None:
+		    raise service_error(service_error.req, 
+			    "Partially assigned substrate %s" %s.name)
 		hname = i.element.name
-		if ifs.has_key(hname):
+		if hname in ifs:
 		    hosts.append("%s\t%s-%s %s-%d" % \
-			    (ip_addr(base), hname, s.name, hname,
-				ifs[hname]))
+			    (addr, hname, s.name, hname, ifs[hname]))
 		else:
+		    # First IP allocated it the default ip according to hosts,
+		    # so the extra alias is here.
 		    ifs[hname] = 0
 		    hosts.append("%s\t%s-%s %s-%d %s" % \
-			    (ip_addr(base), hname, s.name, hname,
-				ifs[hname], hname))
+			    (addr, hname, s.name, hname, ifs[hname], hname))
 
 		ifs[hname] += 1
