3 # Interactive shell for testing PLCAPI
5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2005 The Trustees of Princeton University
8 # $Id: Shell.py,v 1.11 2006/11/02 15:31:02 mlhuang Exp $
19 # Append PLC to the system path
20 sys.path.append(os.path.dirname(os.path.realpath(sys.argv[0])))
22 from PLC.API import PLCAPI
23 from PLC.Parameter import Mixed
24 from PLC.Auth import Auth
25 from PLC.Config import Config
26 from PLC.Method import Method
30 config = "/etc/planetlab/plc_config"
37 if len(sys.argv) < 2 or not os.path.exists(sys.argv[1]):
38 # Parse options if called interactively
40 print "Usage: %s [OPTION]..." % sys.argv[0]
42 print " -f, --config=FILE PLC configuration file"
43 print " -h, --url=URL API URL"
44 print " -m, --method=METHOD API authentication method"
45 print " -u, --user=EMAIL API user name"
46 print " -p, --password=STRING API password"
47 print " -r, --role=ROLE API role"
48 print " -x, --xmlrpc Use XML-RPC interface"
49 print " --help This message"
53 (opts, argv) = getopt.getopt(sys.argv[1:],
55 ["config=", "cfg=", "file=",
59 "password=", "pass=", "authstring=",
63 except getopt.GetoptError, err:
64 print "Error: ", err.msg
67 for (opt, optval) in opts:
68 if opt == "-f" or opt == "--config" or opt == "--cfg" or opt == "--file":
70 elif opt == "-h" or opt == "--host" or opt == "--url":
72 elif opt == "-m" or opt == "--method":
74 elif opt == "-u" or opt == "--username" or opt == "--user":
76 elif opt == "-p" or opt == "--password" or opt == "--pass" or opt == "--authstring":
78 elif opt == "-r" or opt == "--role":
83 # Do not parse options if called by a script
87 # If any XML-RPC options have been specified, do not try
88 # connecting directly to the DB.
92 # Otherwise, first try connecting directly to the DB. If this
93 # fails, try connecting to the API server via XML-RPC.
98 # Try connecting to the API server via XML-RPC
100 config = Config(config)
103 if int(config.PLC_API_PORT) == 443:
107 url += config.PLC_API_HOST + \
108 ":" + str(config.PLC_API_PORT) + \
109 "/" + config.PLC_API_PATH + "/"
111 server = xmlrpclib.ServerProxy(url, allow_none = 1)
113 # Default is to use capability authentication
114 if (method, user, password) == (None, None, None):
115 method = "capability"
117 if method == "capability":
119 user = config.PLC_API_MAINTENANCE_USER
121 password = config.PLC_API_MAINTENANCE_PASSWORD
127 if role == "anonymous" or method == "anonymous":
128 auth = {'AuthMethod': "anonymous"}
131 print "Error: must specify a username with -u"
136 password = getpass.getpass()
137 except (EOFError, KeyboardInterrupt):
141 auth = {'AuthMethod': method,
143 'AuthString': password}
148 # More convenient multicall support
156 raise Exception, "multicall already in progress"
166 results = system.multicall(calls)
167 for result in results:
168 if type(result) == type({}):
169 raise xmlrpclib.Fault(item['faultCode'], item['faultString'])
170 elif type(result) == type([]):
171 ret.append(result[0])
173 raise ValueError, "unexpected type in multicall result"
184 Wrapper to call a method either directly or remotely. Initialize
185 with no arguments to use as a dummy class to support tab
186 completion of API methods with dots in their names (e.g.,
190 def __init__(self, method = None):
193 if method is not None:
194 # Figure out if the function requires an authentication
195 # structure as its first argument.
199 func = api.callable(method)
200 if func.accepts and \
201 (isinstance(func.accepts[0], Auth) or \
202 (isinstance(func.accepts[0], Mixed) and \
203 filter(lambda param: isinstance(param, Auth), func.accepts[0]))):
206 traceback.print_exc()
207 # XXX Ignore undefined methods for now
210 if server is not None:
211 self.func = getattr(server, method)
215 def __call__(self, *args, **kwds):
217 Automagically add the authentication structure if the function
218 requires it and it has not been specified.
224 (not args or not isinstance(args[0], dict) or \
225 (not args[0].has_key('AuthMethod') and \
226 not args[0].has_key('session'))):
227 args = (auth,) + args
230 calls.append({'methodName': self.name, 'params': list(args)})
233 return self.func(*args, **kwds)
235 # Define all methods in the global namespace to support tab completion
236 for method in PLC.Methods.methods:
237 paths = method.split(".")
240 if first not in globals():
241 globals()[first] = Callable()
242 obj = globals()[first]
244 if not hasattr(obj, path):
245 if path == paths[-1]:
246 setattr(obj, path, Callable(method))
248 setattr(obj, path, Callable())
249 obj = getattr(obj, path)
251 globals()[method] = Callable(method)
256 Override builtin help() function to support calling help(method).
260 if isinstance(thing, Callable) and thing.name is not None:
261 pydoc.pager(system.methodHelp(thing.name))
271 # If called by a script
272 if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
273 # Pop us off the argument stack
275 execfile(sys.argv[0])
278 # Otherwise, create an interactive shell environment
281 print "PlanetLab Central Direct API Access"
283 elif auth['AuthMethod'] == "anonymous":
284 prompt = "[anonymous]"
285 print "Connected anonymously"
287 prompt = "[%s]" % auth['Username']
288 print "%s connected using %s authentication" % \
289 (auth['Username'], auth['AuthMethod'])
290 print 'Type "system.listMethods()" or "help(method)" for more information.'
292 # Readline and tab completion support
297 # Load command history
298 history_path = os.path.join(os.environ["HOME"], ".plcapi_history")
300 file(history_path, 'a').close()
301 readline.read_history_file(history_path)
302 atexit.register(readline.write_history_file, history_path)
306 # Enable tab completion
307 readline.parse_and_bind("tab: complete")
319 line = raw_input(prompt + sep)
321 except KeyboardInterrupt:
326 # Build up multi-line command
329 # Blank line or first line does not end in :
330 if line == "" or (command == line and line[-1] != ':'):
333 command += os.linesep
339 elif command in ["q", "quit", "exit"]:
344 # Try evaluating as an expression and printing the result
345 result = eval(command)
346 if result is not None:
349 # Fall back to executing as a statement
351 except Exception, err:
352 traceback.print_exc()