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.7 2006/10/19 19:58:50 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, allow_none = 1)
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 username with -u"
129 password = getpass.getpass()
130 except (EOFError, KeyboardInterrupt):
134 auth = {'AuthMethod': method,
136 'AuthString': password}
143 Wrapper to call a method either directly or remotely. Initialize
144 with no arguments to use as a dummy class to support tab
145 completion of API methods with dots in their names (e.g.,
149 def __init__(self, method = None):
152 if method is not None:
153 # Figure out if the function requires an authentication
154 # structure as its first argument.
158 func = api.callable(method)
159 if func.accepts and \
160 (isinstance(func.accepts[0], Auth) or \
161 (isinstance(func.accepts[0], Mixed) and \
162 filter(lambda param: isinstance(param, Auth), func.accepts[0]))):
165 traceback.print_exc()
166 # XXX Ignore undefined methods for now
169 if server is not None:
170 self.func = getattr(server, method)
174 def __call__(self, *args, **kwds):
176 Automagically add the authentication structure if the function
177 requires it and it has not been specified.
181 (not args or not isinstance(args[0], dict) or not args[0].has_key('AuthMethod')):
182 return self.func(auth, *args, **kwds)
184 return self.func(*args, **kwds)
186 if server is not None:
187 methods = server.system.listMethods()
189 methods = api.call(None, "system.listMethods")
191 # Define all methods in the global namespace to support tab completion
192 for method in methods:
193 paths = method.split(".")
196 if first not in globals():
197 globals()[first] = Callable()
198 obj = globals()[first]
200 if not hasattr(obj, path):
201 if path == paths[-1]:
202 setattr(obj, path, Callable(method))
204 setattr(obj, path, Callable())
205 obj = getattr(obj, path)
207 globals()[method] = Callable(method)
212 Override builtin help() function to support calling help(method).
216 if isinstance(thing, Callable) and thing.name is not None:
217 pydoc.pager(system.methodHelp(thing.name))
227 # If a file is specified
232 # Otherwise, create an interactive shell environment
235 print "PlanetLab Central Direct API Access"
237 elif auth['AuthMethod'] == "anonymous":
238 prompt = "[anonymous]"
239 print "Connected anonymously"
241 prompt = "[%s]" % auth['Username']
242 print "%s connected using %s authentication" % \
243 (auth['Username'], auth['AuthMethod'])
244 print 'Type "system.listMethods()" or "help(method)" for more information.'
246 # Readline and tab completion support
251 # Load command history
252 history_path = os.path.join(os.environ["HOME"], ".plcapi_history")
254 file(history_path, 'a').close()
255 readline.read_history_file(history_path)
256 atexit.register(readline.write_history_file, history_path)
260 # Enable tab completion
261 readline.parse_and_bind("tab: complete")
273 line = raw_input(prompt + sep)
275 except KeyboardInterrupt:
280 # Build up multi-line command
283 # Blank line or first line does not end in :
284 if line == "" or (command == line and line[-1] != ':'):
287 command += os.linesep
293 elif command in ["q", "quit", "exit"]:
298 # Try evaluating as an expression and printing the result
299 result = eval(command)
300 if result is not None:
303 # Fall back to executing as a statement
305 except Exception, err:
306 traceback.print_exc()