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
8 # $Id: API.py,v 1.6 2006/10/27 18:57:32 mlhuang Exp $
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)
105 raise PLCAPIError, "Unsupported database type " + config.PLC_DB_TYPE
107 def callable(self, method):
109 Return a new instance of the specified method.
113 if method not in self.methods:
114 raise PLCInvalidAPIMethod, method
116 # Get new instance of method
118 classname = method.split(".")[-1]
119 module = __import__("PLC.Methods." + method, globals(), locals(), [classname])
120 return getattr(module, classname)(self)
121 except ImportError, AttributeError:
122 raise PLCInvalidAPIMethod, method
124 def call(self, source, method, *args):
126 Call the named method from the specified source with the
130 function = self.callable(method)
131 function.source = source
132 return function(*args)
134 def handle(self, source, data):
136 Handle an XML-RPC or SOAP request from the specified source.
139 # Parse request into method name and arguments
141 interface = xmlrpclib
142 (args, method) = xmlrpclib.loads(data)
143 methodresponse = True
145 if SOAPpy is not None:
147 (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
150 # XXX Support named arguments
155 result = self.call(source, method, *args)
156 except PLCFault, fault:
157 # Handle expected faults
158 if interface == xmlrpclib:
160 methodresponse = None
161 elif interface == SOAPpy:
162 result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
163 result._setDetail("Fault %d: %s" % (fault.faultCode, fault.faultString))
166 if interface == xmlrpclib:
167 if not isinstance(result, PLCFault):
169 data = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
170 elif interface == SOAPpy:
171 data = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)