- set allow_none to allow XML-RPC marshalling of None values
[plcapi.git] / PLC / API.py
1 #
2 # PLCAPI XML-RPC and SOAP interfaces
3 #
4 # Aaron Klingaman <alk@absarokasoft.com>
5 # Mark Huang <mlhuang@cs.princeton.edu>
6 #
7 # Copyright (C) 2004-2006 The Trustees of Princeton University
8 # $Id: API.py,v 1.2 2006/09/08 19:43:31 mlhuang Exp $
9 #
10
11 import sys
12 import traceback
13
14 import xmlrpclib
15
16 import SOAPpy
17 from SOAPpy.Parser import parseSOAPRPC
18 from SOAPpy.Types import faultType
19 from SOAPpy.NS import NS
20 from SOAPpy.SOAPBuilder import buildSOAP
21
22 from PLC.Config import Config
23 from PLC.PostgreSQL import PostgreSQL
24 from PLC.Faults import *
25 import PLC.Methods
26
27 class PLCAPI:
28     methods = PLC.Methods.methods
29
30     def __init__(self, config = "/etc/planetlab/plc_config", encoding = "utf-8"):
31         self.encoding = encoding
32
33         # Better just be documenting the API
34         if config is None:
35             return
36
37         # Load configuration
38         self.config = Config(config)
39
40         # Initialize database connection
41         if self.config.PLC_DB_TYPE == "postgresql":
42             self.db = PostgreSQL(self)
43         else:
44             raise PLCAPIError, "Unsupported database type " + config.PLC_DB_TYPE
45
46     def callable(self, method):
47         """
48         Return a new instance of the specified method.
49         """
50
51         # Look up method
52         if method not in self.methods:
53             raise PLCInvalidAPIMethod, method
54
55         # Get new instance of method
56         try:
57             classname = method.split(".")[-1]
58             module = __import__("PLC.Methods." + method, globals(), locals(), [classname])
59             return getattr(module, classname)(self)
60         except ImportError, AttributeError:
61             raise PLCInvalidAPIMethod, method
62
63     def call(self, source, method, *args):
64         """
65         Call the named method from the specified source with the
66         specified arguments.
67         """
68
69         function = self.callable(method)
70         function.source = source
71         return function(*args)
72
73     def handle(self, source, data):
74         """
75         Handle an XML-RPC or SOAP request from the specified source.
76         """
77
78         # Parse request into method name and arguments
79         try:
80             interface = xmlrpclib
81             (args, method) = xmlrpclib.loads(data)
82             methodresponse = True
83         except:
84             interface = SOAPpy
85             (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
86             method = r._name
87             args = r._aslist()
88             # XXX Support named arguments
89
90         try:
91             result = self.call(source, method, *args)
92         except PLCFault, fault:
93             # Handle expected faults
94             if interface == xmlrpclib:
95                 result = fault
96                 methodresponse = None
97             elif interface == SOAPpy:
98                 result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
99                 result._setDetail("Fault %d: %s" % (fault.faultCode, fault.faultString))
100
101         # Return result
102         if interface == xmlrpclib:
103             if not isinstance(result, PLCFault):
104                 result = (result,)
105             data = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
106         elif interface == SOAPpy:
107             data = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)
108
109         return data