import pgdb at the last minute to avoid requiring pgdb at doc build time
[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.5 2006/10/24 13:47:35 mlhuang Exp $
9 #
10
11 import sys
12 import traceback
13
14 import xmlrpclib
15
16 def dump(self, value, write):
17     """
18     xmlrpclib cannot marshal instances of subclasses of built-in
19     types. This function overrides xmlrpclib.Marshaller.__dump so that
20     any value that is an instance of one of its acceptable types is
21     marshalled as that type.
22     """
23
24     try:
25         # Try for an exact match first
26         f = self.dispatch[type(value)]
27     except KeyError:
28         # Try for an isinstance() match
29         for Type, f in self.dispatch.iteritems():
30             if isinstance(value, Type):
31                 f(self, value, write)
32                 return
33         raise TypeError, "cannot marshal %s objects" % type(value)
34     else:
35         f(self, value, write)        
36
37 # You can't hide from me!
38 xmlrpclib.Marshaller._Marshaller__dump = dump
39
40 # SOAP support is optional
41 try:
42     import SOAPpy
43     from SOAPpy.Parser import parseSOAPRPC
44     from SOAPpy.Types import faultType
45     from SOAPpy.NS import NS
46     from SOAPpy.SOAPBuilder import buildSOAP
47 except ImportError:
48     SOAPpy = None
49
50 from PLC.Config import Config
51 from PLC.Faults import *
52 import PLC.Methods
53
54 class PLCAPI:
55     methods = PLC.Methods.methods
56
57     def __init__(self, config = "/etc/planetlab/plc_config", encoding = "utf-8"):
58         self.encoding = encoding
59
60         # Better just be documenting the API
61         if config is None:
62             return
63
64         # Load configuration
65         self.config = Config(config)
66
67         # Initialize database connection
68         if self.config.PLC_DB_TYPE == "postgresql":
69             from PLC.PostgreSQL import PostgreSQL
70             self.db = PostgreSQL(self)
71         else:
72             raise PLCAPIError, "Unsupported database type " + config.PLC_DB_TYPE
73
74     def callable(self, method):
75         """
76         Return a new instance of the specified method.
77         """
78
79         # Look up method
80         if method not in self.methods:
81             raise PLCInvalidAPIMethod, method
82
83         # Get new instance of method
84         try:
85             classname = method.split(".")[-1]
86             module = __import__("PLC.Methods." + method, globals(), locals(), [classname])
87             return getattr(module, classname)(self)
88         except ImportError, AttributeError:
89             raise PLCInvalidAPIMethod, method
90
91     def call(self, source, method, *args):
92         """
93         Call the named method from the specified source with the
94         specified arguments.
95         """
96
97         function = self.callable(method)
98         function.source = source
99         return function(*args)
100
101     def handle(self, source, data):
102         """
103         Handle an XML-RPC or SOAP request from the specified source.
104         """
105
106         # Parse request into method name and arguments
107         try:
108             interface = xmlrpclib
109             (args, method) = xmlrpclib.loads(data)
110             methodresponse = True
111         except Exception, e:
112             if SOAPpy is not None:
113                 interface = SOAPpy
114                 (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
115                 method = r._name
116                 args = r._aslist()
117                 # XXX Support named arguments
118             else:
119                 raise e
120
121         try:
122             result = self.call(source, method, *args)
123         except PLCFault, fault:
124             # Handle expected faults
125             if interface == xmlrpclib:
126                 result = fault
127                 methodresponse = None
128             elif interface == SOAPpy:
129                 result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
130                 result._setDetail("Fault %d: %s" % (fault.faultCode, fault.faultString))
131
132         # Return result
133         if interface == xmlrpclib:
134             if not isinstance(result, PLCFault):
135                 result = (result,)
136             data = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
137         elif interface == SOAPpy:
138             data = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)
139
140         return data