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.
69 # If any XML-RPC options have been specified, do not try
70 # connecting directly to the DB.
71 if (url, method, user, password, role, tenant, cacert, xmlrpc) != \
72 (None, None, None, None, None, None, None, False):
75 # Otherwise, first try connecting directly to the DB. This
76 # absolutely requires a configuration file; the API
77 # instance looks for one in a default location if one is
78 # not specified. If this fails, try connecting to the API
83 self.api = PLCAPI(config)
84 self.config = self.api.config
88 # Try connecting to the API server via XML-RPC
89 self.api = PLCAPI(None)
93 self.config = Config()
95 self.config = Config(config)
96 except Exception, err:
97 # Try to continue if no configuration file is available
101 if self.config is None:
102 raise Exception, "Must specify API URL"
104 url = "https://" + self.config.api_host + \
105 ":" + str(self.config.api_port) #+ \
106 #"/" + self.config.PI_PATH + "/"
109 cacert = self.config.api_ca_ssl_cert
112 if cacert is not None:
113 self.server = xmlrpclib.ServerProxy(url, PyCurlTransport(url, cacert), allow_none = 1)
115 self.server = xmlrpclib.ServerProxy(url, allow_none = 1)
117 # Set up authentication structure
118 # Default is to use session or capability authentication
119 if not method or method == "capability":
120 # Load defaults from configuration file if using capability
122 method = "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
147 for method in PLC.API.PLCAPI.all_methods:
148 api_function = self.api.callable(method)
150 if self.server is None:
151 # Can just call it directly
154 func = getattr(self.server, method)
156 # If the function requires an authentication structure as
157 # its first argument, automagically add an auth struct to
159 if api_function.accepts and \
160 (isinstance(api_function.accepts[0], Auth) or \
161 (isinstance(api_function.accepts[0], Mixed) and \
162 filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))):
167 callable = Callable(self, method, func, auth)
169 # Add to ourself and the global environment. Add dummy
170 # subattributes to support tab completion of methods with
171 # dots in their names (e.g., system.listMethods).
173 paths = method.split(".")
177 if not hasattr(self, first):
179 setattr(self, first, obj)
180 # Also add to global environment if specified
181 if globals is not None:
184 obj = getattr(self, first)
187 if not hasattr(obj, path):
188 if path == paths[-1]:
189 setattr(obj, path, callable)
191 setattr(obj, path, Dummy())
192 obj = getattr(obj, path)
194 setattr(self, method, callable)
195 # Also add to global environment if specified
196 if globals is not None:
197 globals[method] = callable
199 # Override help(), begin(), and commit()
200 if globals is not None:
201 globals['help'] = self.help
202 globals['begin'] = self.begin
203 globals['commit'] = self.commit
209 def help(self, topic = None):
210 if isinstance(topic, Callable):
211 pydoc.pager(self.system.methodHelp(topic.name))
217 raise Exception, "multicall already in progress"
225 results = self.system.multicall(self.calls)
226 for result in results:
227 if type(result) == type({}):
228 raise xmlrpclib.Fault(result['faultCode'], result['faultString'])
229 elif type(result) == type([]):
230 ret.append(result[0])
232 raise ValueError, "unexpected type in multicall result"