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.8 2006/10/20 18:25:39 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"
38 print "Usage: %s [OPTION]..." % sys.argv[0]
40 print " -f, --config=FILE PLC configuration file"
41 print " -h, --url=URL API URL"
42 print " -m, --method=METHOD API authentication method"
43 print " -u, --user=EMAIL API user name"
44 print " -p, --password=STRING API password"
45 print " -r, --role=ROLE API role"
46 print " -x, --xmlrpc Use XML-RPC interface"
47 print " --help This message"
52 (opts, argv) = getopt.getopt(sys.argv[1:],
54 ["config=", "cfg=", "file=",
58 "password=", "pass=", "authstring=",
62 except getopt.GetoptError, err:
63 print "Error: " + err.msg
66 for (opt, optval) in opts:
67 if opt == "-f" or opt == "--config" or opt == "--cfg" or opt == "--file":
69 elif opt == "-h" or opt == "--host" or opt == "--url":
71 elif opt == "-m" or opt == "--method":
73 elif opt == "-u" or opt == "--username" or opt == "--user":
75 elif opt == "-p" or opt == "--password" or opt == "--pass" or opt == "--authstring":
77 elif opt == "-r" or opt == "--role":
83 # If any XML-RPC options have been specified, do not try
84 # connecting directly to the DB.
88 # Otherwise, first try connecting directly to the DB. If this
89 # fails, try connecting to the API server via XML-RPC.
94 # Try connecting to the API server via XML-RPC
96 config = Config(config)
99 if int(config.PLC_API_PORT) == 443:
103 url += config.PLC_API_HOST + \
104 ":" + str(config.PLC_API_PORT) + \
105 "/" + config.PLC_API_PATH + "/"
107 server = xmlrpclib.ServerProxy(url, allow_none = 1)
109 # Default is to use capability authentication
110 if (method, user, password) == (None, None, None):
111 method = "capability"
113 if method == "capability":
115 user = config.PLC_API_MAINTENANCE_USER
117 password = config.PLC_API_MAINTENANCE_PASSWORD
123 if role == "anonymous" or method == "anonymous":
124 auth = {'AuthMethod': "anonymous"}
127 print "Error: must specify a username with -u"
132 password = getpass.getpass()
133 except (EOFError, KeyboardInterrupt):
137 auth = {'AuthMethod': method,
139 'AuthString': password}
146 Wrapper to call a method either directly or remotely. Initialize
147 with no arguments to use as a dummy class to support tab
148 completion of API methods with dots in their names (e.g.,
152 def __init__(self, method = None):
155 if method is not None:
156 # Figure out if the function requires an authentication
157 # structure as its first argument.
161 func = api.callable(method)
162 if func.accepts and \
163 (isinstance(func.accepts[0], Auth) or \
164 (isinstance(func.accepts[0], Mixed) and \
165 filter(lambda param: isinstance(param, Auth), func.accepts[0]))):
168 traceback.print_exc()
169 # XXX Ignore undefined methods for now
172 if server is not None:
173 self.func = getattr(server, method)
177 def __call__(self, *args, **kwds):
179 Automagically add the authentication structure if the function
180 requires it and it has not been specified.
184 (not args or not isinstance(args[0], dict) or \
185 (not args[0].has_key('AuthMethod') and \
186 not args[0].has_key('session'))):
187 return self.func(auth, *args, **kwds)
189 return self.func(*args, **kwds)
191 if server is not None:
192 methods = server.system.listMethods()
194 methods = api.call(None, "system.listMethods")
196 # Define all methods in the global namespace to support tab completion
197 for method in methods:
198 paths = method.split(".")
201 if first not in globals():
202 globals()[first] = Callable()
203 obj = globals()[first]
205 if not hasattr(obj, path):
206 if path == paths[-1]:
207 setattr(obj, path, Callable(method))
209 setattr(obj, path, Callable())
210 obj = getattr(obj, path)
212 globals()[method] = Callable(method)
217 Override builtin help() function to support calling help(method).
221 if isinstance(thing, Callable) and thing.name is not None:
222 pydoc.pager(system.methodHelp(thing.name))
232 # If a file is specified
237 # Otherwise, create an interactive shell environment
240 print "PlanetLab Central Direct API Access"
242 elif auth['AuthMethod'] == "anonymous":
243 prompt = "[anonymous]"
244 print "Connected anonymously"
246 prompt = "[%s]" % auth['Username']
247 print "%s connected using %s authentication" % \
248 (auth['Username'], auth['AuthMethod'])
249 print 'Type "system.listMethods()" or "help(method)" for more information.'
251 # Readline and tab completion support
256 # Load command history
257 history_path = os.path.join(os.environ["HOME"], ".plcapi_history")
259 file(history_path, 'a').close()
260 readline.read_history_file(history_path)
261 atexit.register(readline.write_history_file, history_path)
265 # Enable tab completion
266 readline.parse_and_bind("tab: complete")
278 line = raw_input(prompt + sep)
280 except KeyboardInterrupt:
285 # Build up multi-line command
288 # Blank line or first line does not end in :
289 if line == "" or (command == line and line[-1] != ':'):
292 command += os.linesep
298 elif command in ["q", "quit", "exit"]:
303 # Try evaluating as an expression and printing the result
304 result = eval(command)
305 if result is not None:
308 # Fall back to executing as a statement
310 except Exception, err:
311 traceback.print_exc()