Index: fedd/fedd_client.py
===================================================================
--- fedd/fedd_client.py	(revision bb3769ac25051bcf7f09fdfe67757a05f649195e)
+++ fedd/fedd_client.py	(revision 0c0b13ca734c711f3cea448e8300b3fb32131b65)
@@ -147,4 +147,23 @@
 	print >>out, "%s: %s" % (a['attribute'], a['value'])
 
+def exit_with_fault(dict, out=sys.stderr):
+    """ Print an error message and exit.
+
+    The dictionary contains the RequestAccessFaultBody elements."""
+    codestr = ""
+
+    if dict.has_key('errstr'):
+	codestr = "Error: %s" % dict['errstr']
+
+    if dict.has_key('code'):
+	if len(codestr) > 0 : 
+	    codestr += " (%d)" % dict['code']
+	else:
+	    codestr = "Error Code: %d" % dict['code']
+
+    print>>out, codestr
+    print>>out, "Description: %s" % dict['desc']
+    sys.exit(dict.get('code', 20))
+
 def add_ssh_key(option, opt_str, value, parser, access_keys):
     try:
@@ -274,6 +293,4 @@
 if opts.debug > 1: print >>sys.stderr, msg
 
-print FaultType.typecode;
-
 if opts.transport == "soap":
     loc = feddServiceLocator();
@@ -299,23 +316,21 @@
 	sys.exit("Malformed response (XMLPRC?): %s" % e)
     except ZSI.FaultException, e:
-	print dir(e.fault.detail[0])
-	print e.fault.detail[0].RequestAccessFaultBody.get_element_code()
-	d = unpack_soap(e.fault.detail[0].get_element_RequestAccessFaultBody())
-	f = unpack_soap(e.fault.detail[0])
-	print "unpack ", d
-	print "unpack ", f
-	sys.exit("Fault: %s %s" % (str(e.fault.code), str(e.fault.string)))
-
+	resp = e.fault.detail[0]
 
     if (resp != None):
-	resp_body = resp.get_element_RequestAccessResponseBody()
-	if ( resp_body != None): 
-	    try:
-		resp_dict = unpack_soap(resp_body)
-		if opts.debug > 1: print >>sys.stderr, resp_dict
-		print_response_as_testbed(resp_dict, opts.label)
-	    except RuntimeError, e:
-		sys.exit("Bad response. %s" % e.messgae)
-	else: sys.exit("No body in resonpse!?")
+	if 'get_element_RequestAccessResponseBody' in dir(resp): 
+	    resp_body = resp.get_element_RequestAccessResponseBody()
+	    if ( resp_body != None): 
+		try:
+		    resp_dict = unpack_soap(resp_body)
+		    if opts.debug > 1: print >>sys.stderr, resp_dict
+		    print_response_as_testbed(resp_dict, opts.label)
+		except RuntimeError, e:
+		    sys.exit("Bad response. %s" % e.message)
+	elif 'get_element_RequestAccessFaultBody' in dir(resp): 
+	    resp_body = resp.get_element_RequestAccessFaultBody()
+	    if resp_body != None: 
+		exit_with_fault(unpack_soap(resp_body))
+	else: sys.exit("No body in response!?")
     else: sys.exit("No response?!?")
 elif opts.transport == "xmlrpc":
@@ -331,14 +346,19 @@
 	resp, method = loads(resp)
     except Error, e:
-	sys.exit("Fault: %s" % e)
+	resp = { 'RequestAccessFaultBody': \
+		{ 'errstr' : e.faultCode, 'desc' : e.faultString } }
 
 
     if (resp != None):
-	try:
-	    print resp
-	    resp_dict = resp[0]['RequestAccessResponseBody']
-	    if opts.debug > 1: print >>sys.stderr, resp_dict
-	    print_response_as_testbed(resp_dict, opts.label)
-	except RuntimeError, e:
-	    sys.exit("Bad response. %s" % e.messgae)
+	if resp.has_key('RequestAccessReponseBody'): 
+	    try:
+		resp_dict = resp[0]['RequestAccessResponseBody']
+		if opts.debug > 1: print >>sys.stderr, resp_dict
+		print_response_as_testbed(resp_dict, opts.label)
+	    except RuntimeError, e:
+		sys.exit("Bad response. %s" % e.messgae)
+	elif resp.has_key('RequestAccessFaultBody'):
+	    exit_with_fault(resp['RequestAccessFaultBody'])
+	else: sys.exit("No body in response!?")
+
     else: sys.exit("No response?!?")
Index: fedd/fedd_proj.py
===================================================================
--- fedd/fedd_proj.py	(revision bb3769ac25051bcf7f09fdfe67757a05f649195e)
+++ fedd/fedd_proj.py	(revision 0c0b13ca734c711f3cea448e8300b3fb32131b65)
@@ -20,4 +20,5 @@
 from fedd_services import *
 from fedd_util import *
+import parse_detail
 
 if False:
@@ -66,10 +67,48 @@
 		return "access_proj('%s', [])" % self.name
 
-    # These are used to make the error reporting independent of the transport.
-    # The XMLRPC and SOAP dispatchers will convert them to transport-specific
-    # errors
-    class server_error(RuntimeError): pass
-    class client_error(RuntimeError): pass
-    # Error parsing config
+    # 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
     class parse_error(RuntimeError): pass
 
@@ -229,5 +268,6 @@
 		    rc = subprocess.call(cmd)
 		except OSerror, e:
-		    raise fedd_proj.server_error(
+		    raise fedd_proj.service_error(\
+			    fedd_proj.service_error.internal,
 			    "Dynamic project subprocess creation error "+ \
 				    "[%s] (%s)" %  (cmd[1], e.strerror))
@@ -236,5 +276,6 @@
 
 	    if rc != 0: 
-		raise fedd_proj.server_error(
+		raise fedd_proj.service_error(\
+			fedd_proj.service_error.internal,
 			"Dynamic project subprocess error " +\
 				"[%s] (%d)" % (cmd[1], rc))
@@ -266,5 +307,6 @@
 	    ctx = fedd_ssl_context(self.cert_file, tc, password=self.cert_pwd)
 	except SSL.SSLError:
-	    raise fedd_proj.server_error("Server certificates misconfigured")
+	    raise fedd_proj.service_error(fedd_proj.service_error.server_config,
+		    "Server certificates misconfigured")
 
 	# Of all the dumbass things.  The XMLRPC library in use here won't
@@ -286,11 +328,16 @@
 		    { "RequestAccessRequestBody": r})
 	    resp, method = xmlrpclib.loads(resp)
+	except xmlrpclib.Fault, f:
+	    se = fedd_proj.service_error(None, f.faultString, f.faultCode)
+	    raise se
 	except xmlrpclib.Error, e:
-	    raise fedd_proj.server_error("Remote XMLRPC Fault: %s" % e)
+	    raise fedd_proj.service_error(fedd_proj.service_error.proxy, 
+		    "Remote XMLRPC Fault: %s" % e)
 	
 	if resp[0].has_key('RequestAccessResponseBody'):
 	    return resp[0]['RequestAccessResponseBody']
 	else:
-	    raise fedd_proj.server_error("Bad proxy response")
+	    raise fedd_proj.service_error(fedd_proj.service_error.proxy, 
+		    "Bad proxy response")
 
     def proxy_request(self, dt, req):
@@ -308,5 +355,6 @@
 	    ctx = fedd_ssl_context(self.cert_file, tc, password=self.cert_pwd)
 	except SSL.SSLError:
-	    raise fedd_proj.server_error("Server certificates misconfigured")
+	    raise fedd_proj.service_error(fedd_proj.service_error.server_config, 
+		    "Server certificates misconfigured")
 
 	loc = feddServiceLocator();
@@ -322,9 +370,7 @@
 	    resp = port.RequestAccess(msg)
 	except ZSI.ParseException, e:
-	    raise fedd_proj.server_error("Bad format message (XMLRPC??): %s" %
+	    raise fedd_proj.service_error(fedd_proj.service_error.proxy,
+		    "Bad format message (XMLRPC??): %s" %
 		    str(e))
-	except ZSI.FaultException, e:
-	    raise e.fault
-
 	r = unpack_soap(resp)
 
@@ -332,5 +378,6 @@
 	    return r['RequestAccessResponseBody']
 	else:
-	    raise fedd_proj.server_error("Bad proxy response")
+	    raise fedd_proj.service_error(fedd_proj.service_error.proxy,
+		    "Bad proxy response")
 
     def permute_wildcards(self, a, p):
@@ -401,8 +448,8 @@
 		fedids = [ u for u in user if isinstance(u, type(fid))]
 		if len(fedids) > 1:
-		    raise fedd_proj.client_error(
+		    raise fedd_proj.service_error(service_error.req,
 			    "User asserting multiple fedids")
 		elif len(fedids) == 1 and fedids[0] != fid:
-		    raise fedd_proj.client_error(
+		    raise fedd_proj.service_error(service_error.req,
 			    "User asserting different fedid")
 	    project = None
@@ -410,5 +457,5 @@
 	elif principal_type == "project":
 	    if isinstance(project, type(fid)) and fid != project:
-		raise fedd_proj.client_error(
+		raise fedd_proj.service_error(service_error.req,
 			"Project asserting different fedid")
 	    tb = None
@@ -419,5 +466,7 @@
 	print "Found: ", found
 	
-	if found == None: raise fedd_proj.server_error("Access denied")
+	if found == None:
+	    raise fedd_proj.service_error(fedd_proj.service_error.access,
+		    "Access denied")
 
 	# resolve <dynamic> and <same> in found
@@ -429,5 +478,6 @@
 		found[0].name = project
 	    else : 
-		raise fedd_proj.server_error(
+		raise fedd_proj.service_error(\
+			fedd_proj.service_error.server_config,
 			"Project matched <same> when no project given")
 	elif found[0].name == "<dynamic>":
@@ -438,5 +488,6 @@
 	    if user_match == "<any>":
 		if user != None: found = (found[0], user[0])
-		else: raise fedd_proj.server_error(
+		else: raise fedd_proj.service_error(\
+			fedd_proj.service_error.server_config,
 			"Matched <same> on anonymous request")
 	    else:
@@ -487,5 +538,5 @@
 	    req = req['RequestAccessRequestBody']
 	else:
-	    raise fedd_proj.client_error("No request!?")
+	    raise fedd_proj.service_error(service_error.req, "No request!?")
 
 	if req.has_key('destinationTestbed'):
@@ -505,5 +556,5 @@
 						t not in found[0].node_types]
 		if len(inaccessible) > 0:
-		    raise fedd_proj.server_error(
+		    raise fedd_proj.service_error(fedd_proj.service_error.access,
 			    "Access denied (nodetypes %s)" % \
 			    str(', ').join(inaccessible))
@@ -516,5 +567,6 @@
 		else: pass    # SSH key additions
 	    else:
-		raise fedd_proj.client_error("SSH access parameters required")
+		raise fedd_proj.service_error(service_error.req, 
+			"SSH access parameters required")
 
 	    resp = self.build_response(req['allocID'], found, ssh)
@@ -525,17 +577,26 @@
 		# Proxy the request using SOAP
 		return self.proxy_request(dt, req)
-	    except RuntimeError, f:
-		p_fault = f
-	  
-	   
-	    # This runs if SOAP failed
-	    try:
+	    except fedd_proj.service_error, e:
+		if e.code == fedd_proj.service_error.proxy: p_fault = None
+		else: raise
+	    except ZSI.FaultException, f:
+		p_fault = f.fault.detail[0]
+		    
+
+	    # If we could not get a valid SOAP response to the request above,
+	    # try the same address using XMLRPC and let any faults flow back
+	    # out.
+	    if p_fault == None:
 		return self.proxy_xmlrpc_request(dt, req)
-		print "here"
-	    except RuntimeError:
-		# Both failed, return the SOAP error.  If somehow that
-		# exception is gone, return the XMLRPC one
-		if p_fault != None: raise p_fault
-		else: raise
+	    else:
+		# Build the fault
+		body = p_fault.get_element_RequestAccessFaultBody()
+		if body != None:
+		    raise service_error(body.get_element_code(),
+				body.get_element_desc());
+		else:
+		    raise fedd_proj.service_error(\
+			    fedd_proj.service_error.proxy,
+			    "Undefined fault from proxy??");
 
     def soap_RequestAccess(self, ps, fid):
@@ -556,5 +617,6 @@
 	    return xmlrpclib.dumps(({ "RequestAccessResponseBody": msg },))
 	else:
-	    raise server_error("No response generated?!");
+	    raise service_error(fedd_proj.service_error.internal,
+		    "No response generated?!");
 
     def read_trust(self, trust):
@@ -729,12 +791,15 @@
 	    try:
 		return getattr(self, fedd_proj.soap_methods[method])(req, fid)
-	    except fedd_proj.server_error, e:
-		de = ns0.faultType_Def((ns0.faultType_Def.schema,"RequestAccessFaultBody")).pyclass()
-		de._code=100
-		de._desc="temp"
-		f = Fault(Fault.Server, "Server Test", detail=de)
-		raise f
-	    except fedd_proj.client_error, e:
-		raise Fault(Fault.Client, str(e))
+	    except fedd_proj.service_error, e:
+		de = ns0.faultType_Def(
+			(ns0.faultType_Def.schema,
+			    "RequestAccessFaultBody")).pyclass()
+		de._code=e.code
+		de._errstr=e.code_string()
+		de._desc=e.desc
+		if  e.is_server_error():
+		    raise Fault(Fault.Server, e.code_string(), detail=de)
+		else:
+		    raise Fault(Fault.Client, e.code_string(), detail=de)
 	else:
 	    raise Fault(Fault.Client, "Unknown method: %s" % method)
@@ -744,8 +809,6 @@
 	    try:
 		return getattr(self, fedd_proj.xmlrpc_methods[method])(req, fid)
-	    except fedd_proj.server_error, e:
-		raise xmlrpclib.Fault("server", str(e))
-	    except fedd_proj.client_error, e:
-		raise xmlrpclib.Fault("client", str(e))
+	    except fedd_proj.service_error, e:
+		raise xmlrpclib.Fault(e.code_string(), e.desc)
 	else:
 	    raise xmlrpclib.Fault(100, "Unknown method: %s" % method)
Index: fedd/fedd_types.xsd
===================================================================
--- fedd/fedd_types.xsd	(revision bb3769ac25051bcf7f09fdfe67757a05f649195e)
+++ fedd/fedd_types.xsd	(revision 0c0b13ca734c711f3cea448e8300b3fb32131b65)
@@ -185,5 +185,14 @@
   <xsd:complexType name="faultType">
     <xsd:sequence>
-      <xsd:element name="code" type="xsd:int"/>
+      <xsd:element name="code" type="xsd:int">
+	<xsd:restriction>
+	  <xsd:enumeration value="1"/>	<!-- access denied -->
+	  <xsd:enumeration value="2"/>	<!-- proxy error -->
+	  <xsd:enumeration value="3"/>	<!-- badly formed request -->
+	  <xsd:enumeration value="4"/>	<!-- internal error -->
+	</xsd:restriction>
+      </xsd:element>
+      <xsd:element name="errstr" minOccurs="0" maxOccurs="1" type="xsd:string">
+      </xsd:element>
       <xsd:element name="desc" type="xsd:string"/>
     </xsd:sequence>
