2 # SFA XML-RPC and SOAP interfaces
14 from sfa.trust.auth import Auth
15 from sfa.util.config import *
16 from sfa.util.faults import *
17 from sfa.util.debug import *
18 from sfa.trust.credential import *
19 from sfa.trust.certificate import *
20 from sfa.util.namespace import *
21 from sfa.util.sfalogging import *
23 # See "2.2 Characters" in the XML specification:
25 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
27 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
29 invalid_xml_ascii = map(chr, range(0x0, 0x8) + [0xB, 0xC] + range(0xE, 0x1F))
30 xml_escape_table = string.maketrans("".join(invalid_xml_ascii), "?" * len(invalid_xml_ascii))
32 def xmlrpclib_escape(s, replace = string.replace):
34 xmlrpclib does not handle invalid 7-bit control characters. This
35 function augments xmlrpclib.escape, which by default only replaces
36 '&', '<', and '>' with entities.
39 # This is the standard xmlrpclib.escape function
40 s = replace(s, "&", "&")
41 s = replace(s, "<", "<")
42 s = replace(s, ">", ">",)
44 # Replace invalid 7-bit control characters with '?'
45 return s.translate(xml_escape_table)
47 def xmlrpclib_dump(self, value, write):
49 xmlrpclib cannot marshal instances of subclasses of built-in
50 types. This function overrides xmlrpclib.Marshaller.__dump so that
51 any value that is an instance of one of its acceptable types is
52 marshalled as that type.
54 xmlrpclib also cannot handle invalid 7-bit control characters. See
58 # Use our escape function
59 args = [self, value, write]
60 if isinstance(value, (str, unicode)):
61 args.append(xmlrpclib_escape)
64 # Try for an exact match first
65 f = self.dispatch[type(value)]
68 # Try for an isinstance() match
69 for Type, f in self.dispatch.iteritems():
70 if isinstance(value, Type):
73 raise TypeError, "cannot marshal %s objects" % type(value)
77 # You can't hide from me!
78 xmlrpclib.Marshaller._Marshaller__dump = xmlrpclib_dump
80 # SOAP support is optional
83 from SOAPpy.Parser import parseSOAPRPC
84 from SOAPpy.Types import faultType
85 from SOAPpy.NS import NS
86 from SOAPpy.SOAPBuilder import buildSOAP
91 def import_deep(name):
92 mod = __import__(name)
93 components = name.split('.')
94 for comp in components[1:]:
95 mod = getattr(mod, comp)
103 def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8",
104 methods='sfa.methods', peer_cert = None, interface = None,
105 key_file = None, cert_file = None, cache = cache):
107 self.encoding = encoding
109 # flat list of method names
110 self.methods_module = methods_module = __import__(methods, fromlist=[methods])
111 self.methods = methods_module.all
113 # Better just be documenting the API
118 self.config = Config(config)
119 self.auth = Auth(peer_cert)
120 self.hrn = self.config.SFA_INTERFACE_HRN
121 self.interface = interface
122 self.key_file = key_file
123 self.key = Keypair(filename=self.key_file)
124 self.cert_file = cert_file
125 self.cert = Certificate(filename=self.cert_file)
127 self.credential = None
129 self.time_format = "%Y-%m-%d %H:%M:%S"
130 self.logger=get_sfa_logger()
133 from sfa.server.registry import Registries
134 self.registries = Registries(self)
137 from sfa.server.aggregate import Aggregates
138 self.aggregates = Aggregates(self)
141 def callable(self, method):
143 Return a new instance of the specified method.
146 if method not in self.methods:
147 raise SfaInvalidAPIMethod, method
149 # Get new instance of method
151 classname = method.split(".")[-1]
152 module = __import__(self.methods_module.__name__ + "." + method, globals(), locals(), [classname])
153 callablemethod = getattr(module, classname)(self)
154 return getattr(module, classname)(self)
155 except ImportError, AttributeError:
156 raise SfaInvalidAPIMethod, method
158 def call(self, source, method, *args):
160 Call the named method from the specified source with the
163 function = self.callable(method)
164 function.source = source
166 return function(*args)
169 def handle(self, source, data, method_map):
171 Handle an XML-RPC or SOAP request from the specified source.
173 # Parse request into method name and arguments
175 interface = xmlrpclib
176 self.protocol = 'xmlrpclib'
177 (args, method) = xmlrpclib.loads(data)
178 if method_map.has_key(method):
179 method = method_map[method]
180 methodresponse = True
183 if SOAPpy is not None:
184 self.protocol = 'soap'
186 (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
189 # XXX Support named arguments
194 result = self.call(source, method, *args)
195 except SfaFault, fault:
197 except Exception, fault:
198 #traceback.print_exc(file = log)
199 result = SfaAPIError(fault)
203 response = self.prepare_response(result, method)
206 def prepare_response(self, result, method=""):
208 convert result to a valid xmlrpc or soap response
211 if self.protocol == 'xmlrpclib':
212 if not isinstance(result, SfaFault):
214 response = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
215 elif self.protocol == 'soap':
216 if isinstance(result, Exception):
217 result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
218 result._setDetail("Fault %d: %s" % (result.faultCode, result.faultString))
220 response = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)
222 if isinstance(result, Exception):