Index: fedd/fedd.py
===================================================================
--- fedd/fedd.py	(revision 2dafa0c68ac0b0267acac2379c8ad3b7dd2a67d4)
+++ fedd/fedd.py	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -110,4 +110,5 @@
     def send_fault(self, f, code=500):
 	"""Send a SOAP encoded fault as reply"""
+	print f
 	self.send_xml(f.AsSOAP(processContents="lax"), code)
 
Index: fedd/fedd_access.py
===================================================================
--- fedd/fedd_access.py	(revision 2dafa0c68ac0b0267acac2379c8ad3b7dd2a67d4)
+++ fedd/fedd_access.py	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -38,4 +38,6 @@
     dynamically.  This implements both direct requests and proxies.
     """
+
+    class parse_error(RuntimeError): pass
 
     bool_attrs = ("dynamic_projects", "project_priority")
@@ -82,5 +84,5 @@
 		    self.fedid_category[fedid(hexstr=m.string)] = cat
 		else:
-		    raise parse_error(\
+		    raise self.parse_error(\
 			    "Bad fedid in trust file (%s) line: %d" % \
 			    (trust, lineno))
@@ -93,5 +95,5 @@
 	    # Nothing matched - bad line, raise exception
 	    f.close()
-	    raise parse_error(\
+	    raise self.parse_error(\
 		    "Unparsable line in trustfile %s line %d" % (trust, lineno))
 	f.close()
@@ -121,5 +123,5 @@
 	with a # are ignored.
 
-	Parsing errors result in a parse_error exception being raised.
+	Parsing errors result in a self.parse_error exception being raised.
 	"""
 	lineno=0
@@ -138,5 +140,5 @@
 	access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
 		key_name+'\s*\)\s*->\s*\('+access_proj + '\s*,\s*' + 
-		access_name + '\s*\)', re.IGNORECASE)
+		access_name + '\s*,\s*' + access_name + '\s*\)', re.IGNORECASE)
 
 	def parse_name(n):
@@ -165,5 +167,5 @@
 		continue
 
-	    # Access line (t, p, u) -> (ap, au) line
+	    # Access line (t, p, u) -> (ap, cu, su) line
 	    m = access_re.match(line)
 	    if m != None:
@@ -174,9 +176,9 @@
 		    aps[0] = fedid(hexstr=aps[0])
 
-		au = m.group(5)
-		if au.startswith("fedid:"):
-		    au = fedid(hexstr=aus[len("fedid:"):])
-
-		access_val = (access_project(aps[0], aps[1:]), au)
+		cu = parse_name(m.group(5))
+		su = parse_name(m.group(6))
+
+		access_val = (access_project(aps[0], aps[1:]),
+			parse_name(m.group(5)), parse_name(m.group(6)))
 
 		self.access[access_key] = access_val
@@ -185,5 +187,5 @@
 	    # Nothing matched to here: unknown line - raise exception
 	    f.close()
-	    raise parse_error("Unknown statement at line %d of %s" % \
+	    raise self.parse_error("Unknown statement at line %d of %s" % \
 		    (lineno, config))
 	f.close()
@@ -287,11 +289,8 @@
 	if not config.has_option("access", "dynamic_projects_url"):
 	    self.allocate_project = \
-		fedd_allocate_project_local(self.dynamic_projects, 
-			None, proj_certs)
+		fedd_allocate_project_local(config)
 	else:
 	    self.allocate_project = \
-		fedd_allocate_project_remote(self.dynamic_projects, 
-			config.get("access", "dynamic_projects_url"),
-			proj_certs)
+		fedd_allocate_project_remote(config)
 
 	# If the project allocator exports services, put them in this object's
@@ -328,17 +327,4 @@
 	    return None
 
-    def strip_unicode(self, obj):
-	"""Loosly de-unicode an object"""
-	if isinstance(obj, dict):
-	    for k in obj.keys():
-		obj[k] = self.strip_unicode(obj[k])
-	    return obj
-	elif isinstance(obj, basestring):
-	    return str(obj)
-	elif getattr(obj, "__iter__", None):
-	    return [ self.strip_unicode(x) for x in obj]
-	else:
-	    return obj
-
     def proxy_xmlrpc_request(self, dt, req):
 	"""Send an XMLRPC proxy request.  Called if the SOAP RPC fails"""
@@ -360,6 +346,5 @@
 	else: url = str(dt)
 
-	r = copy.deepcopy(req)
-	self.strip_unicode(r)
+	r = strip_unicode(copy.deepcopy(req))
 	
 	transport = SSL_Transport(ctx)
@@ -517,5 +502,6 @@
 	# resolve <dynamic> and <same> in found
 	dyn_proj = False
-	dyn_user = False
+	dyn_create_user = False
+	dyn_service_user = False
 
 	if found[0].name == "<same>":
@@ -535,15 +521,27 @@
 	if found[1] == "<same>":
 	    if user_match == "<any>":
-		if user != None: ru = user[0]
+		if user != None: rcu = user[0]
 		else: raise service_error(\
 			service_error.server_config,
 			"Matched <same> on anonymous request")
 	    else:
-		ru = user_match
+		rcu = user_match
 	elif found[1] == "<dynamic>":
-	    ru = None
-	    dyn_user = True
+	    rcu = None
+	    dyn_create_user = True
 	
-	return (rp, ru), (dyn_user, dyn_proj)
+	if found[2] == "<same>":
+	    if user_match == "<any>":
+		if user != None: rsu = user[0]
+		else: raise service_error(\
+			service_error.server_config,
+			"Matched <same> on anonymous request")
+	    else:
+		rsu = user_match
+	elif found[2] == "<dynamic>":
+	    rsu = None
+	    dyn_service_user = True
+
+	return (rp, rcu, rsu), (dyn_create_user, dyn_service_user, dyn_proj)
 
     def build_response(self, alloc_id, ap):
@@ -612,14 +610,16 @@
 			    "Access denied (nodetypes %s)" % \
 			    str(', ').join(inaccessible))
-	    # XXX: This allocates a single user for both service and creation.
-	    # This needs to be made more nuanced.
-	    tmp_ssh = [ x['sshPubkey'] \
-		    for x in req['serviceAccess'] + req['createAccess'] \
-			if x.has_key('sshPubkey')]
-
-	    # Converting to a set collapses duplicates
-	    ssh = set(tmp_ssh)
-
-	    if len(ssh) > 0: 
+	    # These collect the keys for teh two roles into single sets, one
+	    # for creation and one for service.  The sets are a simple way to
+	    # eliminate duplicates
+	    create_ssh = set([ x['sshPubkey'] \
+		    for x in req['createAccess'] \
+			if x.has_key('sshPubkey')])
+
+	    service_ssh = set([ x['sshPubkey'] \
+		    for x in req['serviceAccess'] \
+			if x.has_key('sshPubkey')])
+
+	    if len(create_ssh) > 0 and len(service_ssh) >0: 
 		if dyn[1]: 
 		    # Compose the dynamic project request 
@@ -628,6 +628,15 @@
 				{ 'project' : {\
 				    'user': [ \
-				    { 'access': [ { 'sshPubkey': s } ] } \
-					for s in ssh ] \
+				    { \
+					'access': [ { 'sshPubkey': s } \
+					    for s in service_ssh ], 
+					 'role': "serviceAccess",\
+				    }, \
+				    { \
+					'access': [ { 'sshPubkey': s } \
+					    for s in create_ssh ], 
+					 'role': "experimentCreation",\
+				    }, \
+				    ], \
 				    }\
 				}\
@@ -641,12 +650,28 @@
 		else:
 		    # XXX ssh key additions
-		    ap = { 'project': \
-			    { 'name' : { 'localname' : found[0].name },\
-			      'user' : [ {\
-				'userID': { 'localname' : found[1] }, \
-				'access': [ { 'sshPubkey': s } for s in ssh]}\
-				]\
+		    preq = {'StaticProjectRequestBody' : \
+			    { 'project': \
+				{ 'name' : { 'localname' : found[0].name },\
+				  'user' : [ \
+				    {\
+					'userID': { 'localname' : found[1] }, \
+					'access': [ { 'sshPubkey': s } 
+					    for s in create_ssh ],
+					'role': 'experimentCreation'\
+				    },\
+				    {\
+					'userID': { 'localname' : found[2] }, \
+					'access': [ { 'sshPubkey': s } 
+					    for s in service_ssh ],
+					'role': 'serviceAccess'\
+				    },\
+				]}\
 			    }\
 		    }
+		    if restricted != None and len(restricted) > 0:
+			preq['StaticProjectRequestBody']['resources'] = \
+			    [ {'node': { 'hardware' :  [ h ] } } \
+				    for h in restricted ]
+		    ap = self.allocate_project.static_project(preq)
 	    else:
 		raise service_error(service_error.req, 
Index: fedd/fedd_allocate_project.py
===================================================================
--- fedd/fedd_allocate_project.py	(revision 2dafa0c68ac0b0267acac2379c8ad3b7dd2a67d4)
+++ fedd/fedd_allocate_project.py	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -35,19 +35,25 @@
 fl.addHandler(nullHandler())
 
+
 class fedd_allocate_project_local:
     """
     Allocate projects on this machine in response to an access request.
     """
-    def __init__(self, dp=False, url=None, certs=None):
+    def __init__(self, config):
 	"""
 	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'
+	self.debug = config.get("access", "debug_project", False)
+	self.wap = config.get('access', 'wap', '/usr/testbed/sbin/wap')
+	self.newproj = config.get('access', 'newproj',
+		'/usr/testbed/sbin/newproj')
+	self.mkproj = config.get('access', 'mkproj', '/usr/testbed/sbin/mkproj')
+	self.addpubkey = config.get('access', 'addpubkey', 
+		'/usr/testbed/sbin/taddpubkey')
+	self.grantnodetype = config.get('access', 'grantnodetype', 
+		'/usr/testbed/sbin/grantnodetype')
 	self.log = logging.getLogger("fedd.allocate.local")
+	set_log_level(config, "access", self.log)
 
 	# Internal services are SOAP only
@@ -56,5 +62,9 @@
 		AllocateProjectRequestMessage.typecode,\
 		self.dynamic_project, AllocateProjectResponseMessage,\
-		"AllocateProjectResponseBody")\
+		"AllocateProjectResponseBody"), 
+		"StaticProject": make_soap_handler(\
+		StaticProjectRequestMessage.typecode,\
+		self.static_project, StaticProjectResponseMessage,\
+		"StaticProjectResponseBody")\
 		}
 	self.xmlrpc_services = { }
@@ -179,16 +189,14 @@
 	for cmd in cmds:
 	    self.log.debug("[dynamic_project]: %s" % ' '.join(cmd))
-	    if self.dynamic_projects:
+	    if not self.debug:
 		try:
 		    rc = subprocess.call(cmd)
 		except OSerror, e:
-		    raise fedd_proj.service_error(\
-			    fedd_proj.service_error.internal,
+		    raise service_error(service_error.internal,
 			    "Dynamic project subprocess creation error "+ \
 				    "[%s] (%s)" %  (cmd[1], e.strerror))
 
 	    if rc != 0: 
-		raise fedd_proj.service_error(\
-			fedd_proj.service_error.internal,
+		raise service_error(service_error.internal,
 			"Dynamic project subprocess error " +\
 				"[%s] (%d)" % (cmd[1], rc))
@@ -207,32 +215,78 @@
 	return rv
 
-
-class fedd_allocate_project_remote:
-    """
-    Allocate projects on a remote machine using the internal SOAP interface
-    """
-    def __init__(self, dp=False, url=None, certs=None):
-	"""
-	Initializer.  Parses a configuration if one is given.
-	"""
-
-	self.dynamic_projects = dp
-	self.url = url
-
-	if certs != None and isinstance(certs, type(tuple())):
-	    self.cert_file, self.trusted_certs, self.cert_pwd = certs
-	else:
-	    self.cert_file, self.trusted_certs, self.cert_pwd = \
-		    (None, None, None)
-	self.soap_services = { }
-	self.xmlrpc_services = { }
+    def static_project(self, req, fedid=None):
+	"""
+	Be certain that the local project in the request has access to the
+	proper resources and users have correct keys.  Add them if necessary.
+	"""
+
+	cmds =  []
+
+	# While we should be more careful about this, for the short term, add
+	# the keys to the specified users.
+
+	try:
+	    users = req['StaticProjectRequestBody']['project']['user']
+	    pname = req['StaticProjectRequestBody']['project']\
+		    ['name']['localname']
+	    resources = req['StaticProjectRequestBody'].get('resources', [])
+	except KeyError:
+	    raise service_error(service_error.req, "Badly formed request")
+
+
+	for u in users:
+	    try:
+		name = u['userID']['localname']
+	    except KeyError:
+		raise service_error(service_error.req, "Badly formed user")
+	    for sk in [ k['sshPubkey'] for k in u.get('access', []) \
+		    if k.has_key('sshPubkey')]:
+		cmds.append((self.addpubkey, '-w', '-u', name, '-k', sk))
 	
-    def dynamic_project(self, req, fedid=None):
+
+	# Add commands to grant access to any resources in the request.  The
+	# list comprehension pulls out the hardware types in the node entries
+	# in the resources list.
+	for nt in [ h for r in 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', pname, nt))
+
+	# Run the commands 
+	rc = 0
+	for cmd in cmds:
+	    self.log.debug("[static_project]: %s" % ' '.join(cmd))
+	    if not self.debug:
+		try:
+		    rc = subprocess.call(cmd)
+		except OSError, e:
+		    print "Static project subprocess creation error "+ \
+				    "[%s] (%s)" %  (cmd[0], e.strerror)
+		    raise service_error(service_error.internal,
+			    "Static project subprocess creation error "+ \
+				    "[%s] (%s)" %  (cmd[0], e.strerror))
+
+	    if rc != 0: 
+		raise service_error(service_error.internal,
+			"Static project subprocess error " +\
+				"[%s] (%d)" % (cmd[0], rc))
+
+	return { 'project': req['StaticProjectRequestBody']['project']}
+
+def make_proxy(method, req_name, req_alloc, resp_name):
+    """
+    Construct the proxy calling function from the given parameters.
+    """
+
+    # Define the proxy, NB, the parameters to make_proxy are visible to the
+    # definition of proxy.
+    def proxy(self, req, fedid=None):
 	"""
 	Send req on to a remote project instantiator.
 
-	Req is just the projectAllocType object.  This function re-wraps it.
+	Req is just the message to be sent.  This function re-wraps it.
 	It also rethrows any faults.
 	"""
+
 	# No retry loop here.  Proxy servers must correctly authenticate
 	# themselves without help
@@ -249,23 +303,84 @@
 		transdict={ 'ssl_context' : ctx })
 
-	if req.has_key('AllocateProjectRequestBody'):
-	    req = req['AllocateProjectRequestBody']
+	if req.has_key(req_name):
+	    req = req[req_name]
 	else:
 	    raise service_error(service_error.req, "Bad formated request");
 
 	# Reconstruct the full request message
-	msg = AllocateProjectRequestMessage()
-	msg.set_element_AllocateProjectRequestBody(
-		pack_soap(msg, "AllocateProjectRequestBody", req))
+	msg = req_alloc()
+	set_elem = getattr(msg, "set_element_%s" % req_name)
+	set_elem(pack_soap(msg, req_name, req))
 	try:
-	    resp = port.AllocateProject(msg)
+	    mattr = getattr(port, method)
+	    resp = mattr(msg)
 	except ZSI.ParseException, e:
 	    raise service_error(service_error.proxy,
 		    "Bad format message (XMLRPC??): %s" % str(e))
+	except ZSI.FaultException, e:
+	    resp = e.fault.detail[0]
+
 	r = unpack_soap(resp)
 
-	if r.has_key('AllocateProjectResponseBody'):
-	    return r['AllocateProjectResponseBody']
+	if r.has_key(resp_name):
+	    return r[resp_name]
 	else:
 	    raise service_error(service_error.proxy, "Bad proxy response")
-
+    # NB: end of proxy function definition	
+    return proxy
+
+class fedd_allocate_project_remote:
+    """
+    Allocate projects on a remote machine using the internal SOAP interface
+    """
+    dynamic_project = make_proxy("AllocateProject",
+	    "AllocateProjectRequestBody", AllocateProjectRequestMessage,
+	    "AllocateProjectResponseBody")
+    static_project = make_proxy("StaticProject",
+	    "StaticProjectRequestBody", StaticProjectRequestMessage,
+	    "StaticProjectResponseBody")
+
+    def __init__(self, config):
+	"""
+	Initializer.  Parses a configuration if one is given.
+	"""
+
+	self.debug = config.get("access", "debug_project", False)
+	self.url = config.get("access", "dynamic_projects_url", "")
+
+	self.cert_file = config.get("access", "cert_file", None)
+	self.cert_pwd = config.get("access", "cert_pwd", None)
+	self.trusted_certs = config.get("access", "trusted_certs", None)
+
+	# Certs are promoted from the generic to the specific, so without a if
+	# no dynamic project certificates, then proxy certs are used, and if
+	# none of those the main certs.
+
+	if config.has_option("globals", "proxy_cert_file"):
+	    if not self.cert_file:
+		self.cert_file = config.get("globals", "proxy_cert_file")
+		if config.has_option("globals", "porxy_cert_pwd"):
+		    self.cert_pwd = config.get("globals", "proxy_cert_pwd")
+
+	if config.has_option("globals", "proxy_trusted_certs") and \
+		not self.trusted_certs:
+		self.trusted_certs = \
+			config.get("globals", "proxy_trusted_certs")
+
+	if config.has_option("globals", "cert_file"):
+	    has_pwd = config.has_option("globals", "cert_pwd")
+	    if not self.cert_file:
+		self.cert_file = config.get("globals", "cert_file")
+		if has_pwd: 
+		    self.cert_pwd = config.get("globals", "cert_pwd")
+
+	if config.get("globals", "trusted_certs") and not self.trusted_certs:
+		self.trusted_certs = \
+			config.get("globals", "trusted_certs")
+
+	self.soap_services = { }
+	self.xmlrpc_services = { }
+	self.log = logging.getLogger("fedd.allocate.remote")
+	set_log_level(config, "access", self.log)
+	#self.dynamic_project = fedd_allocate_project_remote.dynamic_project
+	#self.static_project = fedd_allocate_project_remote.static_project
Index: fedd/fedd_experiment_control.py
===================================================================
--- fedd/fedd_experiment_control.py	(revision 2dafa0c68ac0b0267acac2379c8ad3b7dd2a67d4)
+++ fedd/fedd_experiment_control.py	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -320,4 +320,6 @@
 	except pickle.PicklingError, e:
 	    self.log.error("Pickling problem: %s" % e)
+	except TypeError, e:
+	    self.log.error("Pickling problem (TypeError): %s" % e)
 
     # Call while holding self.state_lock
@@ -441,12 +443,16 @@
 	    self.log.error("[start_segment]: can't open /dev/null: %s" %e)
 
-	status = Popen(cmd, stdout=PIPE, stderr=dev_null)
-	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 self.debug:
+	    state = 'swapped'
+	    rv = 0
+	else:
+	    status = Popen(cmd, stdout=PIPE, stderr=dev_null)
+	    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 the experiment is not present the subcommand returns a non-zero
@@ -766,5 +772,4 @@
 	else: return None
 
-
     def get_access(self, tb, nodes, user, tbparam):
 	"""
@@ -835,7 +840,17 @@
 	except ZSI.ParseException, e:
 	    raise service_error(service_error.req,
-		    "Bad format message (XMLRPC??): %s" %
-		    str(e))
-	r = unpack_soap(resp)
+		    "Bad format message (XMLRPC??): %s" % str(e))
+	except ZSI.FaultException, e:
+	    resp = e.fault.detail[0]
+
+	# Again, weird incompatibilities rear their head.  userRoles, which are
+	# restricted strings, seem to be encoded by ZSI as non-unicode strings
+	# in a way that confuses the pickling and XMLRPC sending systems.
+	# Explicitly unicoding them seems to fix this, though it concerns me
+	# some.  It may be that these things are actually a ZSI string
+	# subclass, and the character encoding is not the major issue.  In any
+	# case, making all the subclasses of basestring into unicode strings
+	# unifies the response format and solves the problem.
+	r = make_unicode(unpack_soap(resp))
 
 	if r.has_key('RequestAccessResponseBody'):
@@ -844,5 +859,4 @@
 	    raise service_error(service_error.proxy,
 		    "Bad proxy response")
-
 
 	e = r['emulab']
@@ -868,5 +882,5 @@
 		if key:
 		    tbparam[tb][key]= a['value']
-
+	
     def remote_splitter(self, uri, desc, master):
 
Index: fedd/fedd_internal_bindings.wsdl
===================================================================
--- fedd/fedd_internal_bindings.wsdl	(revision 2dafa0c68ac0b0267acac2379c8ad3b7dd2a67d4)
+++ fedd/fedd_internal_bindings.wsdl	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -37,4 +37,25 @@
 	</fault>
       </operation>
+      <operation name="StaticProject">
+	<documentation>
+	  The bindings of this operation are straightforward SOAP RPC 1.1.
+	</documentation>
+	<soap:operation soapAction="StaticProject"/> 
+	<input>
+	  <soap:body use="encoded" parts="tns:StaticProjectRequestBody"
+	    namespace="http://www.isi.edu/faber/fedd_internal.wsdl"
+	    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+	</input>
+	<output>
+	  <soap:body use="encoded" parts="tns:StaticProjectResponseBody"
+	    namespace="http://www.isi.edu/faber/fedd_internal.wsdl"
+	    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+	</output>
+	<fault>
+	  <soap:fault use="encoded"  name="tns:StaticProjectFault"
+	    namespace="http://www.isi.edu/faber/fedd_internal.wsdl"
+	    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+	</fault>
+      </operation>
       <operation name="Ns2Split">
 	<documentation>
Index: fedd/fedd_internal_messages.wsdl
===================================================================
--- fedd/fedd_internal_messages.wsdl	(revision 2dafa0c68ac0b0267acac2379c8ad3b7dd2a67d4)
+++ fedd/fedd_internal_messages.wsdl	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -26,4 +26,13 @@
   </message>
 
+  <message name="StaticProjectRequestMessage">
+    <part name="StaticProjectRequestBody" type="xsd1:projectAllocType"/>
+  </message>
+
+  <message name="StaticProjectResponseMessage">
+    <part name="StaticProjectResponseBody" 
+      type="xsd1:projectAllocResponseType"/>
+  </message>
+
   <message name="Ns2SplitRequestMessage">
     <part name="Ns2SplitRequestBody" type="xsd1:ns2SplitRequestType"/>
@@ -43,5 +52,16 @@
       <input message="tns:AllocateProjectRequestMessage"/>
       <output message="tns:AllocateProjectResponseMessage"/>
-      <fault name="AllocateProjectFault" message="tns:FaultMessage"/>
+      <fault name="InternalFault" message="tns:FaultMessage"/>
+    </operation>
+    <operation name="StaticProject">
+      <documentation>
+	This internal interface allows the part of a fedd running on a machine
+	other than the boss node of the emulab in question to request
+	modification of a static project.  For example keys can be added or
+	node access granted.
+      </documentation>
+      <input message="tns:StaticProjectRequestMessage"/>
+      <output message="tns:StaticProjectResponseMessage"/>
+      <fault name="InternalFault" message="tns:FaultMessage"/>
     </operation>
     <operation name="Ns2Split">
@@ -53,5 +73,5 @@
       <input message="tns:Ns2SplitRequestMessage"/>
       <output message="tns:Ns2SplitResponseMessage"/>
-      <fault name="Ns2SplitFault" message="tns:FaultMessage"/>
+      <fault name="InternalFault" message="tns:FaultMessage"/>
     </operation>
   </portType>
Index: fedd/fedd_util.py
===================================================================
--- fedd/fedd_util.py	(revision 2dafa0c68ac0b0267acac2379c8ad3b7dd2a67d4)
+++ fedd/fedd_util.py	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -287,4 +287,31 @@
 
 
+def strip_unicode(obj):
+    """Walk through a message and convert all strings to non-unicode strings"""
+    if isinstance(obj, dict):
+	for k in obj.keys():
+	    obj[k] = strip_unicode(obj[k])
+	return obj
+    elif isinstance(obj, basestring):
+	return str(obj)
+    elif getattr(obj, "__iter__", None):
+	return [ strip_unicode(x) for x in obj]
+    else:
+	return obj
+
+def make_unicode(obj):
+    """Walk through a message and convert all strings to unicode"""
+    if isinstance(obj, dict):
+	for k in obj.keys():
+	    obj[k] = make_unicode(obj[k])
+	return obj
+    elif isinstance(obj, basestring):
+	return unicode(obj)
+    elif getattr(obj, "__iter__", None):
+	return [ make_unicode(x) for x in obj]
+    else:
+	return obj
+
+
 def generate_fedid(subj, bits=2048, log=None, dir=None, trace=sys.stderr):
     """
@@ -396,4 +423,5 @@
     return handler
 
+
 def set_log_level(config, sect, log):
     """ Set the logging level to the value passed in sect of config."""
