From bb45c841240e9248682597575be09e937bf2fe69 Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Fri, 8 Sep 2006 00:29:34 +0000 Subject: [PATCH] - add XML-RPC support - now more like plcsh; specifying authentication parameter as first argument is optional (automagically added if required and not specified) --- Shell.py | 193 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 166 insertions(+), 27 deletions(-) diff --git a/Shell.py b/Shell.py index b76e869e..f2f45929 100755 --- a/Shell.py +++ b/Shell.py @@ -5,71 +5,195 @@ # Mark Huang # Copyright (C) 2005 The Trustees of Princeton University # -# $Id: Shell.py,v 1.1 2006/09/06 15:33:59 mlhuang Exp $ +# $Id: Shell.py,v 1.2 2006/09/06 19:17:25 mlhuang Exp $ # import os, sys import traceback import getopt import pydoc +import pg +import xmlrpclib +import getpass from PLC.API import PLCAPI +from PLC.Parameter import Mixed +from PLC.Auth import Auth +from PLC.Config import Config from PLC.Method import Method +import PLC.Methods # Defaults config = "/etc/planetlab/plc_config" +url = None +method = None +user = None +password = None +role = None def usage(): print "Usage: %s [OPTION]..." % sys.argv[0] print "Options:" - print " -f, --config=FILE PLC configuration file (default: %s)" % config - print " -h, --help This message" + print " -f, --config=FILE PLC configuration file" + print " -h, --url=URL API URL" + print " -m, --method=METHOD API authentication method" + print " -u, --user=EMAIL API user name" + print " -p, --password=STRING API password" + print " -r, --role=ROLE API role" + print " --help This message" sys.exit(1) # Get options try: - (opts, argv) = getopt.getopt(sys.argv[1:], "f:h", ["config=", "help"]) + (opts, argv) = getopt.getopt(sys.argv[1:], + "f:h:m:u:p:r:", + ["config=", "cfg=", "file=", + "host=", + "method=", + "username=", "user=", + "password=", "pass=", "authstring=", + "role=", + "help"]) except getopt.GetoptError, err: print "Error: " + err.msg usage() for (opt, optval) in opts: - if opt == "-f" or opt == "--config": + if opt == "-f" or opt == "--config" or opt == "--cfg" or opt == "--file": config = optval - elif opt == "-h" or opt == "--help": + elif opt == "-h" or opt == "--host" or opt == "--url": + url = optval + elif opt == "-m" or opt == "--method": + method = optval + elif opt == "-u" or opt == "--username" or opt == "--user": + user = optval + elif opt == "-p" or opt == "--password" or opt == "--pass" or opt == "--authstring": + password = optval + elif opt == "-r" or opt == "--role": + role = optval + elif opt == "--help": usage() -api = PLCAPI(config) +try: + # Try connecting directly to the DB + api = PLCAPI(config) + config = api.config + server = None +except: + # Try connecting to the API server via XML-RPC + api = PLCAPI(None) + config = Config(config) + + if url is None: + if int(config.PLC_API_PORT) == 443: + url = "https://" + else: + url = "http://" + url += config.PLC_API_HOST + \ + ":8000" + str(config.PLC_API_PORT) + \ + "/" + config.PLC_API_PATH + "/" + + server = xmlrpclib.ServerProxy(url) + +# Default is to use capability authentication +if (method, user, password) == (None, None, None): + method = "capability" + +if method == "capability": + if user is None: + user = config.PLC_API_MAINTENANCE_USER + if password is None: + password = config.PLC_API_MAINTENANCE_PASSWORD + if role is None: + role = "admin" +elif method is None: + method = "password" + +if role == "anonymous" or method == "anonymous": + auth = {'AuthMethod': "anonymous"} +else: + if role is None: + print "Error: must specify a role with -r" + usage() + + if user is None: + print "Error: must specify a username with -u" + usage() + + if password is None: + try: + password = getpass.getpass() + except (EOFError, KeyboardInterrupt): + print + sys.exit(0) -auth = {'AuthMethod': "capability", - 'Username': api.config.PLC_API_MAINTENANCE_USER, - 'AuthString': api.config.PLC_API_MAINTENANCE_PASSWORD, - 'Role': "admin"} + auth = {'AuthMethod': method, + 'Username': user, + 'AuthString': password, + 'Role': role} -class Dummy: +class Callable: """ - Dummy class to support tab completion of API methods with dots in - their names (e.g., system.listMethods). + Wrapper to call a method either directly or remotely. Initialize + with no arguments to use as a dummy class to support tab + completion of API methods with dots in their names (e.g., + system.listMethods). """ - pass - + + def __init__(self, method = None): + self.name = method + + if method is not None: + # Figure out if the function requires an authentication + # structure as its first argument. + func = api.callable(method) + if func.accepts and \ + (isinstance(func.accepts[0], Auth) or \ + (isinstance(func.accepts[0], Mixed) and \ + filter(lambda param: isinstance(param, Auth), func.accepts[0]))): + self.auth = True + else: + self.auth = False + + if server is not None: + self.func = getattr(server, method) + else: + self.func = func + + def __call__(self, *args): + """ + Automagically add the authentication structure if the function + requires it and it has not been specified. + """ + + if self.auth and \ + (not args or not isinstance(args[0], dict) or not args[0].has_key('AuthMethod')): + return self.func(auth, *args) + else: + return self.func(*args) + +if server is not None: + methods = server.system.listMethods() +else: + methods = api.call(None, "system.listMethods") + # Define all methods in the global namespace to support tab completion -for method in api.methods: +for method in methods: paths = method.split(".") if len(paths) > 1: first = paths.pop(0) if first not in globals(): - globals()[first] = Dummy() + globals()[first] = Callable() obj = globals()[first] for path in paths: if not hasattr(obj, path): if path == paths[-1]: - setattr(obj, path, api.callable(method)) + setattr(obj, path, Callable(method)) else: - setattr(obj, path, Dummy()) + setattr(obj, path, Callable()) obj = getattr(obj, path) else: - globals()[method] = api.callable(method) + globals()[method] = Callable(method) pyhelp = help def help(thing): @@ -77,10 +201,17 @@ def help(thing): Override builtin help() function to support calling help(method). """ - if isinstance(thing, Method): - return pydoc.pager(thing.help()) - else: - return pyhelp(thing) + # help(method) + if isinstance(thing, Callable) and thing.name is not None: + pydoc.pager(system.methodHelp(thing.name)) + return + + # help(help) + if thing == help: + thing = pyhelp + + # help(...) + pyhelp(thing) # If a file is specified if argv: @@ -89,8 +220,16 @@ if argv: # Otherwise, create an interactive shell environment -print "PlanetLab Central Direct API Access" -prompt = "" +if server is None: + print "PlanetLab Central Direct API Access" + prompt = "" +elif auth['AuthMethod'] == "anonymous": + prompt = "[anonymous]" + print "Connected anonymously" +else: + prompt = "[%s %s]" % (auth['Username'], auth['Role']) + print "%s connected as %s using %s authentication" % \ + (auth['Username'], auth['Role'], auth['AuthMethod']) print 'Type "system.listMethods()" or "help(method)" for more information.' # Readline and tab completion support -- 2.47.0