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.3 2006/09/08 00:29:34 mlhuang Exp $
19 from PLC.API import PLCAPI
20 from PLC.Parameter import Mixed
21 from PLC.Auth import Auth
22 from PLC.Config import Config
23 from PLC.Method import Method
27 config = "/etc/planetlab/plc_config"
35 print "Usage: %s [OPTION]..." % sys.argv[0]
37 print " -f, --config=FILE PLC configuration file"
38 print " -h, --url=URL API URL"
39 print " -m, --method=METHOD API authentication method"
40 print " -u, --user=EMAIL API user name"
41 print " -p, --password=STRING API password"
42 print " -r, --role=ROLE API role"
43 print " -x, --xmlrpc Use XML-RPC interface"
44 print " --help This message"
49 (opts, argv) = getopt.getopt(sys.argv[1:],
51 ["config=", "cfg=", "file=",
55 "password=", "pass=", "authstring=",
59 except getopt.GetoptError, err:
60 print "Error: " + err.msg
63 for (opt, optval) in opts:
64 if opt == "-f" or opt == "--config" or opt == "--cfg" or opt == "--file":
66 elif opt == "-h" or opt == "--host" or opt == "--url":
68 elif opt == "-m" or opt == "--method":
70 elif opt == "-u" or opt == "--username" or opt == "--user":
72 elif opt == "-p" or opt == "--password" or opt == "--pass" or opt == "--authstring":
74 elif opt == "-r" or opt == "--role":
80 # If any XML-RPC options have been specified, do not try
81 # connecting directly to the DB.
85 # Otherwise, first try connecting directly to the DB. If this
86 # fails, try connecting to the API server via XML-RPC.
91 # Try connecting to the API server via XML-RPC
93 config = Config(config)
96 if int(config.PLC_API_PORT) == 443:
100 url += config.PLC_API_HOST + \
101 ":" + str(config.PLC_API_PORT) + \
102 "/" + config.PLC_API_PATH + "/"
104 server = xmlrpclib.ServerProxy(url)
106 # Default is to use capability authentication
107 if (method, user, password) == (None, None, None):
108 method = "capability"
110 if method == "capability":
112 user = config.PLC_API_MAINTENANCE_USER
114 password = config.PLC_API_MAINTENANCE_PASSWORD
120 if role == "anonymous" or method == "anonymous":
121 auth = {'AuthMethod': "anonymous"}
124 print "Error: must specify a role with -r"
128 print "Error: must specify a username with -u"
133 password = getpass.getpass()
134 except (EOFError, KeyboardInterrupt):
138 auth = {'AuthMethod': method,
140 'AuthString': password,
145 Wrapper to call a method either directly or remotely. Initialize
146 with no arguments to use as a dummy class to support tab
147 completion of API methods with dots in their names (e.g.,
151 def __init__(self, method = None):
154 if method is not None:
155 # Figure out if the function requires an authentication
156 # structure as its first argument.
160 func = api.callable(method)
161 if func.accepts and \
162 (isinstance(func.accepts[0], Auth) or \
163 (isinstance(func.accepts[0], Mixed) and \
164 filter(lambda param: isinstance(param, Auth), func.accepts[0]))):
167 # XXX Ignore undefined methods for now
170 if server is not None:
171 self.func = getattr(server, method)
175 def __call__(self, *args):
177 Automagically add the authentication structure if the function
178 requires it and it has not been specified.
182 (not args or not isinstance(args[0], dict) or not args[0].has_key('AuthMethod')):
183 return self.func(auth, *args)
185 return self.func(*args)
187 if server is not None:
188 methods = server.system.listMethods()
190 methods = api.call(None, "system.listMethods")
192 # Define all methods in the global namespace to support tab completion
193 for method in methods:
194 paths = method.split(".")
197 if first not in globals():
198 globals()[first] = Callable()
199 obj = globals()[first]
201 if not hasattr(obj, path):
202 if path == paths[-1]:
203 setattr(obj, path, Callable(method))
205 setattr(obj, path, Callable())
206 obj = getattr(obj, path)
208 globals()[method] = Callable(method)
213 Override builtin help() function to support calling help(method).
217 if isinstance(thing, Callable) and thing.name is not None:
218 pydoc.pager(system.methodHelp(thing.name))
228 # If a file is specified
233 # Otherwise, create an interactive shell environment
236 print "PlanetLab Central Direct API Access"
238 elif auth['AuthMethod'] == "anonymous":
239 prompt = "[anonymous]"
240 print "Connected anonymously"
242 prompt = "[%s %s]" % (auth['Username'], auth['Role'])
243 print "%s connected as %s using %s authentication" % \
244 (auth['Username'], auth['Role'], auth['AuthMethod'])
245 print 'Type "system.listMethods()" or "help(method)" for more information.'
247 # Readline and tab completion support
252 # Load command history
253 history_path = os.path.join(os.environ["HOME"], ".plcapi_history")
255 file(history_path, 'a').close()
256 readline.read_history_file(history_path)
257 atexit.register(readline.write_history_file, history_path)
261 # Enable tab completion
262 readline.parse_and_bind("tab: complete")
274 line = raw_input(prompt + sep)
276 except KeyboardInterrupt:
281 # Build up multi-line command
284 # Blank line or first line does not end in :
285 if line == "" or (command == line and line[-1] != ':'):
288 command += os.linesep
294 elif command in ["q", "quit", "exit"]:
299 # Try evaluating as an expression and printing the result
300 result = eval(command)
301 if result is not None:
304 # Fall back to executing as a statement
306 except Exception, err:
307 traceback.print_exc()