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