2 # PLCAPI XML-RPC and SOAP interfaces
4 # Aaron Klingaman <alk@absarokasoft.com>
5 # Mark Huang <mlhuang@cs.princeton.edu>
7 # Copyright (C) 2004-2006 The Trustees of Princeton University
17 # See "2.2 Characters" in the XML specification:
19 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
21 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
23 invalid_xml_ascii = map(chr, range(0x0, 0x8) + [0xB, 0xC] + range(0xE, 0x1F))
24 xml_escape_table = string.maketrans("".join(invalid_xml_ascii), "?" * len(invalid_xml_ascii))
26 def xmlrpclib_escape(s, replace = string.replace):
28 xmlrpclib does not handle invalid 7-bit control characters. This
29 function augments xmlrpclib.escape, which by default only replaces
30 '&', '<', and '>' with entities.
33 # This is the standard xmlrpclib.escape function
34 s = replace(s, "&", "&")
35 s = replace(s, "<", "<")
36 s = replace(s, ">", ">",)
38 # Replace invalid 7-bit control characters with '?'
39 return s.translate(xml_escape_table)
41 def xmlrpclib_dump(self, value, write):
43 xmlrpclib cannot marshal instances of subclasses of built-in
44 types. This function overrides xmlrpclib.Marshaller.__dump so that
45 any value that is an instance of one of its acceptable types is
46 marshalled as that type.
48 xmlrpclib also cannot handle invalid 7-bit control characters. See
52 # Use our escape function
53 args = [self, value, write]
54 if isinstance(value, (str, unicode)):
55 args.append(xmlrpclib_escape)
58 # Try for an exact match first
59 f = self.dispatch[type(value)]
61 # Try for an isinstance() match
62 for Type, f in self.dispatch.iteritems():
63 if isinstance(value, Type):
66 raise TypeError, "cannot marshal %s objects" % type(value)
70 # You can't hide from me!
71 xmlrpclib.Marshaller._Marshaller__dump = xmlrpclib_dump
73 # SOAP support is optional
76 from SOAPpy.Parser import parseSOAPRPC
77 from SOAPpy.Types import faultType
78 from SOAPpy.NS import NS
79 from SOAPpy.SOAPBuilder import buildSOAP
83 from PLC.Config import Config
84 from PLC.Faults import *
88 methods = PLC.Methods.methods
90 def __init__(self, config = "/etc/planetlab/plc_config", encoding = "utf-8"):
91 self.encoding = encoding
93 # Better just be documenting the API
98 self.config = Config(config)
100 # Initialize database connection
101 if self.config.PLC_DB_TYPE == "postgresql":
102 from PLC.PostgreSQL import PostgreSQL
103 self.db = PostgreSQL(self)
106 raise PLCAPIError, "Unsupported database type " + self.config.PLC_DB_TYPE
108 def callable(self, method):
110 Return a new instance of the specified method.
114 if method not in self.methods:
115 raise PLCInvalidAPIMethod, method
117 # Get new instance of method
119 classname = method.split(".")[-1]
120 module = __import__("PLC.Methods." + method, globals(), locals(), [classname])
121 return getattr(module, classname)(self)
122 except ImportError, AttributeError:
123 raise PLCInvalidAPIMethod, method
125 def call(self, source, method, *args):
127 Call the named method from the specified source with the
131 function = self.callable(method)
132 function.source = source
133 return function(*args)
135 def handle(self, source, data):
137 Handle an XML-RPC or SOAP request from the specified source.
140 # Parse request into method name and arguments
142 interface = xmlrpclib
143 (args, method) = xmlrpclib.loads(data)
144 methodresponse = True
146 if SOAPpy is not None:
148 (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
151 # XXX Support named arguments
156 result = self.call(source, method, *args)
157 except PLCFault, fault:
158 # Handle expected faults
159 if interface == xmlrpclib:
161 methodresponse = None
162 elif interface == SOAPpy:
163 result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
164 result._setDetail("Fault %d: %s" % (fault.faultCode, fault.faultString))
167 if interface == xmlrpclib:
168 if not isinstance(result, PLCFault):
170 data = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
171 elif interface == SOAPpy:
172 data = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)