3 # Interactive shell for testing PLCAPI
5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2005 The Trustees of Princeton University
13 from PLC.API import PLCAPI
14 from PLC.Parameter import Mixed
15 from PLC.Auth import Auth
16 from PLC.Config import Config
17 from PLC.Method import Method
18 from PLC.PyCurl import PyCurlTransport
23 Wrapper to call a method either directly or remotely and
24 automagically add the authentication structure if necessary.
27 def __init__(self, shell, name, func, auth = None):
33 def __call__(self, *args, **kwds):
35 Automagically add the authentication structure if the function
36 requires it and it has not been specified.
40 (not args or not isinstance(args[0], dict) or \
41 (not args[0].has_key('AuthMethod') and \
42 not args[0].has_key('session'))):
43 args = (self.auth,) + args
46 self.shell.calls.append({'methodName': self.name, 'params': list(args)})
49 return self.func(*args, **kwds)
53 # Add API functions to global scope
58 url = None, xmlrpc = False, cacert = None,
59 # API authentication method
61 # Password authentication
62 role = None, user = None, password = None, tenant = None,
63 # Session authentication
66 Initialize a new shell instance. Re-initializes globals.
70 # If any XML-RPC options have been specified, do not try
71 # connecting directly to the DB.
72 if (url, method, user, password, role, tenant, cacert, xmlrpc) != \
73 (None, None, None, None, None, None, None, False):
76 # Otherwise, first try connecting directly to the DB. This
77 # absolutely requires a configuration file; the API
78 # instance looks for one in a default location if one is
79 # not specified. If this fails, try connecting to the API
84 self.api = PLCAPI(config)
85 self.config = self.api.config
88 except Exception, err:
89 # Try connecting to the API server via XML-RPC
90 self.api = PLCAPI(None)
94 self.config = Config()
96 self.config = Config(config)
97 except Exception, err:
98 # Try to continue if no configuration file is available
102 if self.config is None:
103 raise Exception, "Must specify API URL"
105 url = "https://" + self.config.api_host + \
106 ":" + str(self.config.api_port) #+ \
107 #"/" + self.config.PI_PATH + "/"
110 cacert = self.config.api_ca_ssl_cert
113 if cacert is not None:
114 self.server = xmlrpclib.ServerProxy(url, PyCurlTransport(url, cacert), allow_none = 1)
116 self.server = xmlrpclib.ServerProxy(url, allow_none = 1)
118 # Set up authentication structure
119 # Default is to use session or capability authentication
120 if not method or method == "capability":
121 # Load defaults from configuration file if using capability
123 if user is None and self.config is not None:
124 user = self.config.nova_admin_user
125 if password is None and self.config is not None:
126 password = self.config.nova_admin_password
127 if tenant is None and self.config is not None:
128 tenant = self.config.nova_admin_tenant
130 if role == "anonymous" or method == "anonymous":
131 self.auth = {'AuthMethod': "anonymous"}
134 raise Exception, "Must specify username"
137 raise Exception, "Must specify password"
139 self.auth = {'AuthMethod': method,
141 'AuthString': password,
145 self.auth['Role'] = role
149 def load_methods(self):
151 for method in PLC.API.PLCAPI.all_methods:
152 api_function = self.api.callable(method)
154 if self.server is None:
155 # Can just call it directly
158 func = getattr(self.server, method)
160 # If the function requires an authentication structure as
161 # its first argument, automagically add an auth struct to
163 if api_function.accepts and \
164 (isinstance(api_function.accepts[0], Auth) or \
165 (isinstance(api_function.accepts[0], Mixed) and \
166 filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))):
171 callable = Callable(self, method, func, auth)
173 # Add to ourself and the global environment. Add dummy
174 # subattributes to support tab completion of methods with
175 # dots in their names (e.g., system.listMethods).
177 paths = method.split(".")
181 if not hasattr(self, first):
183 setattr(self, first, obj)
184 # Also add to global environment if specified
185 if globals is not None:
188 obj = getattr(self, first)
191 if not hasattr(obj, path):
192 if path == paths[-1]:
193 setattr(obj, path, callable)
195 setattr(obj, path, Dummy())
196 obj = getattr(obj, path)
198 setattr(self, method, callable)
199 # Also add to global environment if specified
200 if globals is not None:
201 globals[method] = callable
203 # Override help(), begin(), and commit()
204 if globals is not None:
205 globals['help'] = self.help
206 globals['begin'] = self.begin
207 globals['commit'] = self.commit
213 def help(self, topic = None):
214 if isinstance(topic, Callable):
215 pydoc.pager(self.system.methodHelp(topic.name))
221 raise Exception, "multicall already in progress"
229 results = self.system.multicall(self.calls)
230 for result in results:
231 if type(result) == type({}):
232 raise xmlrpclib.Fault(result['faultCode'], result['faultString'])
233 elif type(result) == type([]):
234 ret.append(result[0])
236 raise ValueError, "unexpected type in multicall result"