- insert peers to object_types table
[plcapi.git] / Shell.py
index 0c49f92..a649e84 100755 (executable)
--- a/Shell.py
+++ b/Shell.py
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2005 The Trustees of Princeton University
 #
-# $Id: Shell.py,v 1.5 2006/10/03 19:34:05 mlhuang Exp $
+# $Id: Shell.py,v 1.12 2006/11/02 22:07:22 mlhuang Exp $
 #
 
 import os, sys
@@ -16,6 +16,9 @@ import pg
 import xmlrpclib
 import getpass
 
+# Append PLC to the system path
+sys.path.append(os.path.dirname(os.path.realpath(sys.argv[0])))
+
 from PLC.API import PLCAPI
 from PLC.Parameter import Mixed
 from PLC.Auth import Auth
@@ -30,56 +33,61 @@ 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"
-    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 "     -x, --xmlrpc            Use XML-RPC interface"
-    print "     --help                  This message"
-    sys.exit(1)
-
-# Get options
-try:
-    (opts, argv) = getopt.getopt(sys.argv[1:],
-                                 "f:h:m:u:p:r:x",
-                                 ["config=", "cfg=", "file=",
-                                  "host=",
-                                  "method=",
-                                  "username=", "user=",
-                                  "password=", "pass=", "authstring=",
-                                  "role=",
-                                  "xmlrpc",
-                                  "help"])
-except getopt.GetoptError, err:
-    print "Error: " + err.msg
-    usage()
-
-for (opt, optval) in opts:
-    if opt == "-f" or opt == "--config" or opt == "--cfg" or opt == "--file":
-        config = optval
-    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":
+xmlrpc = False
+
+if len(sys.argv) < 2 or not os.path.exists(sys.argv[1]):
+    # Parse options if called interactively
+    def usage():
+        print "Usage: %s [OPTION]..." % sys.argv[0]
+        print "Options:"
+        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 "     -x, --xmlrpc            Use XML-RPC interface"
+        print "     --help                  This message"
+        sys.exit(1)
+
+    try:
+        (opts, argv) = getopt.getopt(sys.argv[1:],
+                                     "f:h:m:u:p:r:x",
+                                     ["config=", "cfg=", "file=",
+                                      "host=",
+                                      "method=",
+                                      "username=", "user=",
+                                      "password=", "pass=", "authstring=",
+                                      "role=",
+                                      "xmlrpc",
+                                      "help"])
+    except getopt.GetoptError, err:
+        print "Error: ", err.msg
         usage()
 
+    for (opt, optval) in opts:
+        if opt == "-f" or opt == "--config" or opt == "--cfg" or opt == "--file":
+            config = optval
+        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 == "-x" or opt == "--xmlrpc":
+            xmlrpc = True
+        elif opt == "--help":
+            usage()
+
 try:
     # If any XML-RPC options have been specified, do not try
     # connecting directly to the DB.
-    if opts:
+    if (url, method, user, password, role, xmlrpc) != \
+       (None, None, None, None, None, False):
         raise Exception
         
     # Otherwise, first try connecting directly to the DB. If this
@@ -120,10 +128,6 @@ elif method is None:
 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()
@@ -137,8 +141,44 @@ else:
 
     auth = {'AuthMethod': method,
             'Username': user,
-            'AuthString': password,
-            'Role': role}
+            'AuthString': password}
+
+    if role is not None:
+        auth['Role'] = role
+
+# More convenient multicall support
+multi = False
+calls = []
+
+def begin():
+    global multi, calls
+
+    if calls:
+        raise Exception, "multicall already in progress"
+
+    multi = True
+
+def commit():
+    global multi, calls
+
+    if calls:
+        ret = []
+        multi = False
+        results = system.multicall(calls)
+        for result in results:
+            if type(result) == type({}):
+                raise xmlrpclib.Fault(item['faultCode'], item['faultString'])
+            elif type(result) == type([]):
+                ret.append(result[0])
+            else:
+                raise ValueError, "unexpected type in multicall result"
+    else:
+        ret = None
+
+    calls = []
+    multi = False
+
+    return ret
 
 class Callable:
     """
@@ -155,16 +195,16 @@ class Callable:
             # Figure out if the function requires an authentication
             # structure as its first argument.
             self.auth = False
-           func = api.callable(method)
             
            try:
-                #func = api.callable(method)
+                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
             except:
+                traceback.print_exc()
                 # XXX Ignore undefined methods for now
                 pass
 
@@ -179,19 +219,22 @@ class Callable:
         requires it and it has not been specified.
         """
 
+        global multi, calls
+
         if self.auth and \
-           (not args or not isinstance(args[0], dict) or not args[0].has_key('AuthMethod')):
-            return self.func(auth, *args, **kwds)
+           (not args or not isinstance(args[0], dict) or \
+            (not args[0].has_key('AuthMethod') and \
+             not args[0].has_key('session'))):
+            args = (auth,) + args
+
+        if multi:
+            calls.append({'methodName': self.name, 'params': list(args)})
+            return None
         else:
-            return self.func(*args)
-
-if server is not None:
-    methods = server.system.listMethods()
-else:
-    methods = api.call(None, "system.listMethods")
+            return self.func(*args, **kwds)
 
 # Define all methods in the global namespace to support tab completion
-for method in methods:
+for method in PLC.Methods.methods:
     paths = method.split(".")
     if len(paths) > 1:
         first = paths.pop(0)
@@ -226,9 +269,11 @@ def help(thing):
     # help(...)
     pyhelp(thing)
 
-# If a file is specified
-if argv:
-    execfile(argv[0])
+# If called by a script
+if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
+    # Pop us off the argument stack
+    sys.argv.pop(0)
+    execfile(sys.argv[0])
     sys.exit(0)
 
 # Otherwise, create an interactive shell environment
@@ -240,9 +285,9 @@ 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'])
+    prompt = "[%s]" % auth['Username']
+    print "%s connected using %s authentication" % \
+          (auth['Username'], auth['AuthMethod'])
 print 'Type "system.listMethods()" or "help(method)" for more information.'
 
 # Readline and tab completion support
@@ -301,7 +346,7 @@ try:
                 result = eval(command)
                 if result is not None:
                     print result
-            except:
+            except SyntaxError:
                 # Fall back to executing as a statement
                 exec command
         except Exception, err: