Index: fedd/fedd_allocate_project.py
===================================================================
--- fedd/fedd_allocate_project.py	(revision 7da9da6d3e2f324bb49a9b9403f751554663dd86)
+++ fedd/fedd_allocate_project.py	(revision 7da9da6d3e2f324bb49a9b9403f751554663dd86)
@@ -0,0 +1,178 @@
+#!/usr/local/bin/python
+
+import os,sys
+
+from BaseHTTPServer import BaseHTTPRequestHandler
+from ZSI import *
+from M2Crypto import SSL
+from M2Crypto.m2xmlrpclib import SSL_Transport
+from M2Crypto.SSL.SSLServer import SSLServer
+import M2Crypto.httpslib
+import xmlrpclib
+
+import re
+import random
+import string
+import subprocess
+import tempfile
+import copy
+
+from fedd_services import *
+from fedd_util import *
+import parse_detail
+
+class fedd_allocate_project_local:
+    def __init__(self, dp=False, url=None):
+	"""
+	Initializer.  Parses a configuration if one is given.
+	"""
+
+	self.dynamic_projects = dp
+	self.wap = '/usr/testbed/sbin/wap'
+	self.newproj = '/usr/testbed/sbin/newproj'
+	self.mkproj = '/usr/testbed/sbin/mkproj'
+	self.grantnodetype = '/usr/testbed/sbin/grantnodetype'
+
+    def random_string(self, s, n=3):
+	"""Append n random ASCII characters to s and return the string"""
+	rv = s
+	for i in range(0,n):
+	    rv += random.choice(string.ascii_letters)
+	return rv
+
+    def write_attr_xml(self, file, root, lines):
+	"""
+	Write an emulab config file for a dynamic project.
+
+	Format is <root><attribute name=lines[0]>lines[1]</attribute></root>
+	"""
+	# Convert a pair to an attribute line
+	out_attr = lambda a,v : \
+		'<attribute name="%s"><value>%s</value></attribute>' % (a, v)
+
+	f = os.fdopen(file, "w")
+	f.write("<%s>\n" % root)
+	f.write("\n".join([out_attr(*l) for l in lines]))
+	f.write("</%s>\n" % root)
+	f.close()
+
+
+    def dynamic_project(self, req):
+	"""
+	Create a dynamic project with ssh access
+
+	Req includes the project and resources as a dictionary
+	"""
+	# tempfiles for the parameter files
+	uf, userfile = tempfile.mkstemp(prefix="usr", suffix=".xml",
+		dir="/tmp")
+	pf, projfile = tempfile.mkstemp(prefix="proj", suffix=".xml",
+		dir="/tmp")
+
+	print userfile
+	proj = req['project']
+	# Take the first user and ssh key
+	name = proj.get('name', None) or self.random_string("proj",4)
+	user = proj.get('user', None)
+	if user != None:
+	    user = user[0]	# User is a list, take the first entry
+	    if not user.has_key("userID"):
+		uname = self.random_string("user", 3)
+	    else:
+		uid = proj['userID']
+		# XXX: fedid
+		uname = uid.get('username', None) or \
+			uid.get('kerberosUsername', None) or \
+			uid.get('uri', None)
+		if uname == None:
+		    raise fedd_proj.service_error(fedd_proj.service_error.req, 
+			    "No ID for user");
+
+	    access = user.get('access', None)
+	    if access != None:
+		ssh = access.get('sshPubkey', None)
+		if ssh == None:
+		    raise fedd_proj.service_error(fedd_proj.service_error.req, 
+			    "No ssh key for user");
+	else:
+	    raise fedd_proj.service_error(fedd_proj.service_error.req, 
+		    "No access information for project");
+
+	# uname, name and ssh are set
+	user_fields = [
+		("name", "Federation User %s" % uname),
+		("email", "%s-fed@isi.deterlab.net" % uname),
+		("password", self.random_string("", 8)),
+		("login", uname),
+		("address", "4676 Admiralty"),
+		("city", "Marina del Rey"),
+		("state", "CA"),
+		("zip", "90292"),
+		("country", "USA"),
+		("phone", "310-448-9190"),
+		("title", "None"),
+		("affiliation", "USC/ISI"),
+		("pubkey", ssh)
+	]
+
+	proj_fields = [
+		("name", name),
+		("short description", "dynamic federated project"),
+		("URL", "http://www.isi.edu/~faber"),
+		("funders", "USC/USU"),
+		("long description", "Federation access control"),
+		("public", "1"),
+		("num_pcs", "100"),
+		("linkedtous", "1"),
+		("newuser_xml", userfile)
+	]
+	
+
+	# Write out the files
+	self.write_attr_xml(uf, "user", user_fields)
+	self.write_attr_xml(pf, "project", proj_fields)
+
+	# Generate the commands (only grantnodetype's are dynamic)
+	cmds = [
+		(self.wap, self.newproj, projfile),
+		(self.wap, self.mkproj, name)
+		]
+
+	# Add commands to grant access to any resources in the request.
+	for nt in [ h for r in req.get('resources', []) \
+		if r.has_key('node') and r['node'].has_key('hardware')\
+		    for h in r['node']['hardware'] ] :
+	    cmds.append((self.wap, self.grantnodetype, '-p', name, nt))
+
+	# Create the projects
+	rc = 0
+	for cmd in cmds:
+	    if self.dynamic_projects:
+		try:
+		    rc = subprocess.call(cmd)
+		except OSerror, e:
+		    raise fedd_proj.service_error(\
+			    fedd_proj.service_error.internal,
+			    "Dynamic project subprocess creation error "+ \
+				    "[%s] (%s)" %  (cmd[1], e.strerror))
+	    else:
+		print >>sys.stdout, str(" ").join(cmd)
+
+	    if rc != 0: 
+		raise fedd_proj.service_error(\
+			fedd_proj.service_error.internal,
+			"Dynamic project subprocess error " +\
+				"[%s] (%d)" % (cmd[1], rc))
+	# Clean up tempfiles
+	os.unlink(userfile)
+	os.unlink(projfile)
+	rv = {\
+	    'project': {\
+		'name': { 'username': name }, 
+		'user' : [ {\
+		    'userID': { 'username' : uname },
+		    'access': [ { 'sshPubkey' : ssh } ],
+		} ]\
+	    }\
+	}
+	return rv
Index: fedd/fedd_messages.wsdl
===================================================================
--- fedd/fedd_messages.wsdl	(revision 21a1c301a0334b83382f0991877bed3f1a6f7c8b)
+++ fedd/fedd_messages.wsdl	(revision 7da9da6d3e2f324bb49a9b9403f751554663dd86)
@@ -20,13 +20,9 @@
 
   <message name="AllocateProjectRequestMessage">
-    <part name="AllocateProjectRequestBody" type="xsd1:projectType"/>
+    <part name="AllocateProjectRequestBody" type="xsd1:projectAllocType"/>
   </message>
 
   <message name="AllocateProjectResponseMessage">
     <part name="AllocateProjectResponseBody" type="xsd1:projectType"/>
-  </message>
-
-  <message name="AllocateProjectFaultMessage">
-    <part name="AllocateProjectFaultBody" type="xsd1:faultType"/>
   </message>
 
@@ -39,5 +35,5 @@
       <output message="tns:RequestAccessResponseMessage"/>
       <fault name="RequestAccessFault" 
-	message="tns:RequestAccessFaultMessage"/>
+	message="tns:FaultMessage"/>
     </operation>
 
@@ -46,5 +42,5 @@
       <output message="tns:AllocateProjectResponseMessage"/>
       <fault name="AllocateProjectFault" 
-	message="tns:AllocateProjectFaultMessage"/>
+	message="tns:FaultMessage"/>
     </operation>
   </portType>
Index: fedd/fedd_proj.py
===================================================================
--- fedd/fedd_proj.py	(revision 21a1c301a0334b83382f0991877bed3f1a6f7c8b)
+++ fedd/fedd_proj.py	(revision 7da9da6d3e2f324bb49a9b9403f751554663dd86)
@@ -12,5 +12,4 @@
 
 import re
-import random
 import string
 import subprocess
@@ -20,5 +19,7 @@
 from fedd_services import *
 from fedd_util import *
+from fedd_allocate_project import *
 import parse_detail
+from service_error import *
 
 class fedd_proj:
@@ -58,46 +59,4 @@
 		return "access_proj('%s', [])" % self.name
 
-    # This is used to make the service error reporting independent of the
-    # transport.  The XMLRPC and SOAP dispatchers will convert it into
-    # transport-specific errors
-    class service_error(RuntimeError):
-	access = 1
-	proxy= 2
-	req = 3
-	server_config = 4
-	internal = 5
-	code_str = { 
-	    access : "Access Denied",
-	    proxy : "Proxy Error",
-	    req : "Badly Formed Request",
-	    server_config: "Server Configuration Error",
-	    internal : "Internal Error"
-	}
-	str_code = dict([ (v, k) for k, v in code_str.iteritems() ])
-	client_errors = ( req )
-	server_errors = ( access, proxy, server_config, internal)
-
-	def __init__(self, code=None, desc=None, from_string=None):
-	    self.code = code
-	    self.desc = desc
-	    if code == None:
-		self.set_code_from_string(from_string)
-	    RuntimeError.__init__(self, desc)
-
-	def code_string(self, code=None):
-	    code = code or self.code
-	    return fedd_proj.service_error.code_str.get(code)
-	
-	def set_code_from_string(self, errstr):
-	    self.code = fedd_proj.service_error.str_code.get(errstr,
-		   fedd_proj. service_error.internal)
-	    return self.code
-	
-	def is_client_error(self):
-	    return self.code in fedd_proj.service_error.client_errors
-
-	def is_server_error(self):
-	    return self.code in fedd_proj.service_error.server_errors
-
     # Used to report errors parsing the configuration files, not in providing
     # service
@@ -123,4 +82,6 @@
 	self.fedid_default = "user"
 	self.restricted = []
+
+	# Delete these
 	self.wap = '/usr/testbed/sbin/wap'
 	self.newproj = '/usr/testbed/sbin/newproj'
@@ -131,4 +92,6 @@
 	if config != None: 
 	    self.read_config(config)
+	self.allocate_project = \
+	    fedd_allocate_project_local(self.dynamic_projects)
 
     def dump_state(self):
@@ -150,21 +113,4 @@
 	print "Restricted: %s" % str(',').join(sorted(self.restricted))
 
-    def get_id(self, id):
-	"""
-	Utility to get an object from the polymorphic IDType.
-	
-	Only fedids and usernames are currently understood.  If neither is
-	present None is returned.  If both are present (which is a bug) the
-	fedid is returned.
-	"""
-	if id == None: 
-	    return None
-	elif getattr(id, "get_element_fedid", None) != None:
-	    return fedid(id.get_element_fedid())
-	elif getattr(id, "get_element_username", None) != None:
-	    return id.get_element_username()
-	else:
-	    return None
-
     def get_users(self, obj):
 	"""
@@ -177,103 +123,4 @@
 	    return None
 
-    def random_string(self, s, n=3):
-	"""Append n random ASCII characters to s and return the string"""
-	rv = s
-	for i in range(0,n):
-	    rv += random.choice(string.ascii_letters)
-	return rv
-
-    def write_attr_xml(self, file, root, lines):
-	"""
-	Write an emulab config file for a dynamic project.
-
-	Format is <root><attribute name=lines[0]>lines[1]</attribute></root>
-	"""
-	# Convert a pair to an attribute line
-	out_attr = lambda a,v : \
-		'<attribute name="%s"><value>%s</value></attribute>' % (a, v)
-
-	f = os.fdopen(file, "w")
-	f.write("<%s>\n" % root)
-	f.write("\n".join([out_attr(*l) for l in lines]))
-	f.write("</%s>\n" % root)
-	f.close()
-
-
-    def dynamic_project(self, found, ssh):
-	"""Create a dynamic project with ssh access"""
-	user_fields = [
-		("name", "Federation User %s" % found[1]),
-		("email", "%s-fed@isi.deterlab.net" % found[1]),
-		("password", self.random_string("", 8)),
-		("login", found[1]),
-		("address", "4676 Admiralty"),
-		("city", "Marina del Rey"),
-		("state", "CA"),
-		("zip", "90292"),
-		("country", "USA"),
-		("phone", "310-448-9190"),
-		("title", "None"),
-		("affiliation", "USC/ISI")
-	]
-
-	proj_fields = [
-		("name", found[0].name),
-		("short description", "dynamic federated project"),
-		("URL", "http://www.isi.edu/~faber"),
-		("funders", "USC/USU"),
-		("long description", "Federation access control"),
-		("public", "1"),
-		("num_pcs", "100"),
-		("linkedtous", "1")
-	]
-
-	# tempfiles for the parameter files
-	uf, userfile = tempfile.mkstemp(prefix="usr", suffix=".xml",
-		dir="/tmp")
-	pf, projfile = tempfile.mkstemp(prefix="proj", suffix=".xml",
-		dir="/tmp")
-
-	# A few more dynamic fields
-	for s in ssh:
-	    user_fields.append(("pubkey", s))
-	proj_fields.append(("newuser_xml", userfile))
-
-	# Write out the files
-	self.write_attr_xml(uf, "user", user_fields)
-	self.write_attr_xml(pf, "project", proj_fields)
-
-	# Generate the commands (only grantnodetype's are dynamic)
-	cmds = [
-		(self.wap, self.newproj, projfile),
-		(self.wap, self.mkproj, found[0].name)
-		]
-	for nt in found[0].node_types:
-	    cmds.append((self.wap, self.grantnodetype, '-p', found[0].name, nt))
-
-	# Create the projects
-	rc = 0
-	for cmd in cmds:
-	    if self.dynamic_projects:
-		try:
-		    rc = subprocess.call(cmd)
-		except OSerror, e:
-		    raise fedd_proj.service_error(\
-			    fedd_proj.service_error.internal,
-			    "Dynamic project subprocess creation error "+ \
-				    "[%s] (%s)" %  (cmd[1], e.strerror))
-	    else:
-		print >>sys.stdout, str(" ").join(cmd)
-
-	    if rc != 0: 
-		raise fedd_proj.service_error(\
-			fedd_proj.service_error.internal,
-			"Dynamic project subprocess error " +\
-				"[%s] (%d)" % (cmd[1], rc))
-	# Clean up tempfiles
-	os.unlink(userfile)
-	os.unlink(projfile)
-
-    
     def strip_unicode(self, obj):
 	"""Loosly de-unicode an object"""
@@ -298,5 +145,5 @@
 	    ctx = fedd_ssl_context(self.cert_file, tc, password=self.cert_pwd)
 	except SSL.SSLError:
-	    raise fedd_proj.service_error(fedd_proj.service_error.server_config,
+	    raise service_error(service_error.server_config,
 		    "Server certificates misconfigured")
 
@@ -320,8 +167,8 @@
 	    resp, method = xmlrpclib.loads(resp)
 	except xmlrpclib.Fault, f:
-	    se = fedd_proj.service_error(None, f.faultString, f.faultCode)
+	    se = service_error(None, f.faultString, f.faultCode)
 	    raise se
 	except xmlrpclib.Error, e:
-	    raise fedd_proj.service_error(fedd_proj.service_error.proxy, 
+	    raise service_error(service_error.proxy, 
 		    "Remote XMLRPC Fault: %s" % e)
 	
@@ -329,5 +176,5 @@
 	    return resp[0]['RequestAccessResponseBody']
 	else:
-	    raise fedd_proj.service_error(fedd_proj.service_error.proxy, 
+	    raise service_error(service_error.proxy, 
 		    "Bad proxy response")
 
@@ -346,5 +193,5 @@
 	    ctx = fedd_ssl_context(self.cert_file, tc, password=self.cert_pwd)
 	except SSL.SSLError:
-	    raise fedd_proj.service_error(fedd_proj.service_error.server_config, 
+	    raise service_error(service_error.server_config, 
 		    "Server certificates misconfigured")
 
@@ -361,5 +208,5 @@
 	    resp = port.RequestAccess(msg)
 	except ZSI.ParseException, e:
-	    raise fedd_proj.service_error(fedd_proj.service_error.proxy,
+	    raise service_error(service_error.proxy,
 		    "Bad format message (XMLRPC??): %s" %
 		    str(e))
@@ -369,5 +216,5 @@
 	    return r['RequestAccessResponseBody']
 	else:
-	    raise fedd_proj.service_error(fedd_proj.service_error.proxy,
+	    raise service_error(service_error.proxy,
 		    "Bad proxy response")
 
@@ -439,8 +286,8 @@
 		fedids = [ u for u in user if isinstance(u, type(fid))]
 		if len(fedids) > 1:
-		    raise fedd_proj.service_error(service_error.req,
+		    raise service_error(service_error.req,
 			    "User asserting multiple fedids")
 		elif len(fedids) == 1 and fedids[0] != fid:
-		    raise fedd_proj.service_error(service_error.req,
+		    raise service_error(service_error.req,
 			    "User asserting different fedid")
 	    project = None
@@ -448,15 +295,13 @@
 	elif principal_type == "project":
 	    if isinstance(project, type(fid)) and fid != project:
-		raise fedd_proj.service_error(service_error.req,
+		raise service_error(service_error.req,
 			"Project asserting different fedid")
 	    tb = None
 
 	# Ready to look up access
-	print "Lookup %s %s %s: " % (tb, project, user)
 	found, user_match = self.find_access((tb, project, user))
-	print "Found: ", found
 	
 	if found == None:
-	    raise fedd_proj.service_error(fedd_proj.service_error.access,
+	    raise service_error(service_error.access,
 		    "Access denied")
 
@@ -469,9 +314,9 @@
 		found[0].name = project
 	    else : 
-		raise fedd_proj.service_error(\
-			fedd_proj.service_error.server_config,
+		raise service_error(\
+			service_error.server_config,
 			"Project matched <same> when no project given")
 	elif found[0].name == "<dynamic>":
-	    found[0].name = self.random_string("project", 3)
+	    found[0].name = None
 	    dyn_proj = True
 
@@ -479,16 +324,16 @@
 	    if user_match == "<any>":
 		if user != None: found = (found[0], user[0])
-		else: raise fedd_proj.service_error(\
-			fedd_proj.service_error.server_config,
+		else: raise service_error(\
+			service_error.server_config,
 			"Matched <same> on anonymous request")
 	    else:
 		found = (found[0], user_match)
 	elif found[1] == "<dynamic>":
-	    found = (found[0], self.random_string("user", 4))
+	    found = (found[0], None)
 	    dyn_user = True
 	
 	return found, (dyn_user, dyn_proj)
 
-    def build_response(self, alloc_id, ap, ssh):
+    def build_response(self, alloc_id, ap):
 	"""
 	Create the SOAP response.
@@ -496,5 +341,7 @@
 	Build the dictionary description of the response and use
 	fedd_utils.pack_soap to create the soap message.  NB that alloc_id is
-	a fedd_services_types.IDType_Holder pulled from the incoming message
+	a fedd_services_types.IDType_Holder pulled from the incoming message.
+	ap is the allocate project message returned from a remote project
+	allocation (even if that allocation was done locally).
 	"""
 	# Because alloc_id is already a fedd_services_types.IDType_Holder,
@@ -508,12 +355,5 @@
 		    'fileServer': self.fileserver,
 		    'eventServer': self.eventserver,
-		    'project': {
-			'name': pack_id(ap[0].name),
-			'user': [ {
-			    'userID': pack_id(ap[1]),
-			    'access' : [ { 'sshPubkey': x } for x in ssh ],
-			    }
-			]
-		    }
+		    'project': ap['project']
 		},
 	    }
@@ -529,5 +369,5 @@
 	    req = req['RequestAccessRequestBody']
 	else:
-	    raise fedd_proj.service_error(service_error.req, "No request!?")
+	    raise service_error(service_error.req, "No request!?")
 
 	if req.has_key('destinationTestbed'):
@@ -537,15 +377,18 @@
 	    # Request for this fedd
 	    found, dyn = self.lookup_access(req, fid)
+	    restricted = None
+	    ap = None
 
 	    # Check for access to restricted nodes
 	    if req.has_key('resources') and req['resources'].has_key('node'):
 		resources = req['resources']
-		inaccessible = [ t for n in resources['node'] \
-				if n.has_key('hardware') != None \
+		restricted = [ t for n in resources['node'] \
+				if n.has_key('hardware') \
 				    for t in n['hardware'] \
-					if t in self.restricted and \
-						t not in found[0].node_types]
+					if t in self.restricted ]
+		inaccessible = [ t for t in restricted \
+				    if t not in found[0].node_types]
 		if len(inaccessible) > 0:
-		    raise fedd_proj.service_error(fedd_proj.service_error.access,
+		    raise service_error(service_error.access,
 			    "Access denied (nodetypes %s)" % \
 			    str(', ').join(inaccessible))
@@ -555,11 +398,27 @@
 
 	    if len(ssh) > 0: 
-		if dyn[1]: self.dynamic_project(found, ssh)
+		if dyn[1]: 
+		    # Compose the dynamic project request 
+		    # (only dynamic, dynamic currently allowed)
+		    preq = { 'project' : {\
+				    'user': [ \
+				    { 'access': { 'sshPubkey': s } } \
+					for s in ssh ] \
+				}\
+			    }
+		    if restricted != None and len(restricted) > 0:
+			preq['resources'] =  [ {'node': { 'hardware' :  [ h ]\
+			    } } for h in restricted ]
+				
+
+		    #self.dynamic_project(found, ssh)
+		    ap = self.allocate_project.dynamic_project(preq)
+		    # XXX: fill in response values into the real response
 		else: pass    # SSH key additions
 	    else:
-		raise fedd_proj.service_error(service_error.req, 
+		raise service_error(service_error.req, 
 			"SSH access parameters required")
 
-	    resp = self.build_response(req['allocID'], found, ssh)
+	    resp = self.build_response(req['allocID'], ap)
 	    return resp
 	else:
@@ -568,6 +427,6 @@
 		# Proxy the request using SOAP
 		return self.proxy_request(dt, req)
-	    except fedd_proj.service_error, e:
-		if e.code == fedd_proj.service_error.proxy: p_fault = None
+	    except service_error, e:
+		if e.code == service_error.proxy: p_fault = None
 		else: raise
 	    except ZSI.FaultException, f:
@@ -584,9 +443,9 @@
 		body = p_fault.get_element_RequestAccessFaultBody()
 		if body != None:
-		    raise fedd_proj.service_error(body.get_element_code(),
+		    raise service_error(body.get_element_code(),
 				body.get_element_desc());
 		else:
-		    raise fedd_proj.service_error(\
-			    fedd_proj.service_error.proxy,
+		    raise service_error(\
+			    service_error.proxy,
 			    "Undefined fault from proxy??");
 
@@ -595,5 +454,5 @@
 
 	msg = self.RequestAccess(unpack_soap(req), fedid)
-	
+
 	resp = RequestAccessResponseMessage()
 	resp.set_element_RequestAccessResponseBody(
@@ -608,5 +467,5 @@
 	    return xmlrpclib.dumps(({ "RequestAccessResponseBody": msg },))
 	else:
-	    raise service_error(fedd_proj.service_error.internal,
+	    raise service_error(service_error.internal,
 		    "No response generated?!");
 
@@ -782,5 +641,5 @@
 	    try:
 		return getattr(self, fedd_proj.soap_methods[method])(req, fid)
-	    except fedd_proj.service_error, e:
+	    except service_error, e:
 		de = ns0.faultType_Def(
 			(ns0.faultType_Def.schema,
@@ -800,5 +659,5 @@
 	    try:
 		return getattr(self, fedd_proj.xmlrpc_methods[method])(req, fid)
-	    except fedd_proj.service_error, e:
+	    except service_error, e:
 		raise xmlrpclib.Fault(e.code_string(), e.desc)
 	else:
Index: fedd/fedd_types.xsd
===================================================================
--- fedd/fedd_types.xsd	(revision 21a1c301a0334b83382f0991877bed3f1a6f7c8b)
+++ fedd/fedd_types.xsd	(revision 7da9da6d3e2f324bb49a9b9403f751554663dd86)
@@ -139,4 +139,17 @@
       <xsd:element name="fedAttr" type="tns:fedAttrType" minOccurs="0"
 	maxOccurs="unbounded"/>
+    </xsd:sequence>
+  </xsd:complexType>
+
+  <xsd:complexType name="projectAllocType">
+    <xsd:annotation>
+      <xsd:documentation>
+	The information needed to create a dynamic project
+      </xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="project" type="tns:projectType"/>
+      <xsd:element name="resources" type="tns:resourcesType" 
+	minOccurs="0" maxOccurs="1"/>
     </xsd:sequence>
   </xsd:complexType>
