2 # SFA XML-RPC and SOAP interfaces
14 import sfa.util.sfalogging
15 from sfa.trust.auth import Auth
16 from sfa.util.config import *
17 from sfa.util.faults import *
18 from sfa.util.debug import *
19 from sfa.trust.credential import *
20 from sfa.trust.certificate import *
21 from sfa.util.namespace 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)
100 This class acts as a wrapper around an SFA interface manager module, but
101 can be used with any python module. The purpose of this class is raise a
102 SfaNotImplemented exception if the a someone attepmts to use an attribute
103 (could be a callable) thats not available in the library by checking the
104 library using hasattr. This helps to communicate better errors messages
105 to the users and developers in the event that a specifiec operation
106 is not implemented by a libarary and will generally be more helpful than
107 the standard AttributeError
109 def __init__(self, manager, interface):
110 self.manager = manager
111 self.interface = interface
113 def __getattr__(self, method):
115 if not hasattr(self.manager, method):
116 raise SfaNotImplemented(method, self.interface)
117 return getattr(self.manager, method)
124 def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8",
125 methods='sfa.methods', peer_cert = None, interface = None,
126 key_file = None, cert_file = None, cache = cache):
128 self.encoding = encoding
130 # flat list of method names
131 self.methods_module = methods_module = __import__(methods, fromlist=[methods])
132 self.methods = methods_module.all
134 # Better just be documenting the API
139 self.config = Config(config)
140 self.auth = Auth(peer_cert)
141 self.hrn = self.config.SFA_INTERFACE_HRN
142 self.interface = interface
143 self.key_file = key_file
144 self.key = Keypair(filename=self.key_file)
145 self.cert_file = cert_file
146 self.cert = Certificate(filename=self.cert_file)
148 self.credential = None
150 self.time_format = "%Y-%m-%d %H:%M:%S"
151 self.logger=sfa.util.sfalogging.logger
154 from sfa.server.registry import Registries
155 self.registries = Registries(self)
158 from sfa.server.aggregate import Aggregates
159 self.aggregates = Aggregates(self)
162 def get_interface_manager(self, manager_base = 'sfa.managers'):
164 Returns the appropriate manager module for this interface.
165 Modules are usually found in sfa/managers/
168 if self.interface in ['registry']:
169 mgr_type = self.config.SFA_REGISTRY_TYPE
170 manager_module = manager_base + ".registry_manager_%s" % mgr_type
171 elif self.interface in ['aggregate']:
172 mgr_type = self.config.SFA_AGGREGATE_TYPE
173 manager_module = manager_base + ".aggregate_manager_%s" % mgr_type
174 elif self.interface in ['slicemgr', 'sm']:
175 mgr_type = self.config.SFA_SM_TYPE
176 manager_module = manager_base + ".slice_manager_%s" % mgr_type
177 elif self.interface in ['component', 'cm']:
178 mgr_type = self.config.SFA_CM_TYPE
179 manager_module = manager_base + ".component_manager_%s" % mgr_type
181 raise SfaAPIError("No manager for interface: %s" % self.interface)
182 manager = __import__(manager_module, fromlist=[manager_base])
183 # this isnt necessary but will hlep to produce better error messages
184 # if someone tries to access an operation this manager doesn't implement
185 manager = ManagerWrapper(manager, self.interface)
189 def callable(self, method):
191 Return a new instance of the specified method.
194 if method not in self.methods:
195 raise SfaInvalidAPIMethod, method
197 # Get new instance of method
199 classname = method.split(".")[-1]
200 module = __import__(self.methods_module.__name__ + "." + method, globals(), locals(), [classname])
201 callablemethod = getattr(module, classname)(self)
202 return getattr(module, classname)(self)
203 except ImportError, AttributeError:
204 raise SfaInvalidAPIMethod, method
206 def call(self, source, method, *args):
208 Call the named method from the specified source with the
211 function = self.callable(method)
212 function.source = source
214 return function(*args)
217 def handle(self, source, data, method_map):
219 Handle an XML-RPC or SOAP request from the specified source.
221 # Parse request into method name and arguments
223 interface = xmlrpclib
224 self.protocol = 'xmlrpclib'
225 (args, method) = xmlrpclib.loads(data)
226 if method_map.has_key(method):
227 method = method_map[method]
228 methodresponse = True
231 if SOAPpy is not None:
232 self.protocol = 'soap'
234 (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
237 # XXX Support named arguments
242 result = self.call(source, method, *args)
243 except SfaFault, fault:
245 except Exception, fault:
246 traceback.print_exc(file = log)
247 result = SfaAPIError(fault)
251 response = self.prepare_response(result, method)
254 def prepare_response(self, result, method=""):
256 convert result to a valid xmlrpc or soap response
259 if self.protocol == 'xmlrpclib':
260 if not isinstance(result, SfaFault):
262 response = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
263 elif self.protocol == 'soap':
264 if isinstance(result, Exception):
265 result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
266 result._setDetail("Fault %d: %s" % (result.faultCode, result.faultString))
268 response = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)
270 if isinstance(result, Exception):