As a person who enjoys the subcommands found in git and perf, I decided to create a framework to support subcommands to help me navigate around autotest.
The approach I took was to try and keep the original autotest workflow and just quickly jump into a function to test for certains commands and process them. Otherwise, just return and do the normal autotest workflow. This patch implements the base CommandParser class that process the special commands 'list' and 'run'. Changes from v1: * Turned methods that do not depend on object state into class methods * Fixed general coding style issues and small python mistakes. With this now cmdparser.py is a 10/10 in pylint :) * If less not present, just use sys.stdout * Use os.path.curdir rather than os.environ['PWD'], as you won't have that env variable properly set when using things like sudo Signed-off-by: Don Zickus <[email protected]> Signed-off-by: Lucas Meneghel Rodrigues <[email protected]> --- client/bin/cmdparser.py | 172 +++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 172 insertions(+), 0 deletions(-) create mode 100644 client/bin/cmdparser.py diff --git a/client/bin/cmdparser.py b/client/bin/cmdparser.py new file mode 100644 index 0000000..2a8a489 --- /dev/null +++ b/client/bin/cmdparser.py @@ -0,0 +1,172 @@ +""" +Autotest command parser + +@copyright: Don Zickus <[email protected]> 2011 +""" + +import os, re, sys +from autotest_lib.client.bin import os_dep + +LOCALDIRTEST = "/tests" +GLOBALDIRTEST = "/opt/autotest/tests" +DEBUG = False + + +class CommandParser(object): + """ + A client-side command wrapper for the autotest client. + """ + def __init__(self): + self.cmdlist = ['help', 'list', 'run'] + + + @classmethod + def _print_control_list(cls, pipe, path): + """ + Print the list of control files available. + + @param pipe: Pipe opened to an output stream (may be a pager) + @param path: Path we'll walk through + """ + if not os.path.isdir(path): + pipe.write("Test directory not available\n") + return + + pipe.write(" %-50s %s\n" % ("[Control]", "[Description]")) + # The strategy here is to walk the root directory + # looking for "*control*" files in some directory + # and printing them out + for root, _, files in sorted(os.walk(path)): + for name in files: + if re.search("control", name): + # strip full path + basename = re.sub(path + "/", "", root) + text = "%s/%s" % (basename, name) + desc = "None" + + if name == "control": + # Imply /control by listing only directory name + text = "%s" % basename + + for line in open(root + "/" + name).readlines(): + if re.match("NAME", line): + # We have a description line + desc = re.split("=\s*", line, + maxsplit=1)[1].rstrip() + try: + desc = desc[1:-1] + except IndexError: + pass + break + pipe.write(' %-50s %s\n' % (text, desc)) + + + @classmethod + def help(cls): + """ + List the commands and their usage strings. + + @param args is not used here. + """ + print "help\t\t\tOutput a list of supported commands" + print "list\t\t\tOutput a list of available tests" + print "run <test> [<args>]\tFind given <test> in path and run with args" + raise SystemExit(0) + + + @classmethod + def list_tests(cls): + """ + List the available tests for users to choose from + """ + # One favorite feature from git :-) + try: + less_cmd = os_dep.command('less') + pipe = os.popen('%s -FRSX' % less_cmd, 'w') + except ValueError: + pipe = sys.stdout + + pipe.write("List of tests available\n") + pipe.write("Unless otherwise specified, outputs imply /control files\n") + pipe.write("\n") + + # Walk local ./tests directory + dirtest = os.path.curdir + LOCALDIRTEST + # Don't repeat autodirtest results + if not dirtest == os.environ['AUTODIRTEST']: + pipe.write("Local tests (%s)\n" % dirtest) + cls._print_control_list(pipe, dirtest) + pipe.write("\n") + + # Walk globaldirtests directory + dirtest = GLOBALDIRTEST + pipe.write("Globally imported tests (%s)\n" % dirtest) + cls._print_control_list(pipe, dirtest) + pipe.write("\n") + + # Walk autodirtest directory + dirtest = os.environ['AUTODIRTEST'] + pipe.write("Autotest prepackaged tests (%s)\n" % dirtest) + cls._print_control_list(pipe, dirtest) + + pipe.close() + raise SystemExit(0) + + + def parse_args(self, args): + """ + Process a client side command. + + @param args: Command line args. + """ + if len(args) and args[0] in self.cmdlist: + cmd = args.pop(0) + else: + # Do things the traditional way + return args + + # List is a python reserved word + if cmd == 'list': + cmd = 'list_tests' + try: + try: + args = getattr(self, cmd)(args) + except TypeError: + args = getattr(self, cmd)() + except SystemExit as return_code: + sys.exit(return_code) + except Exception, error_detail: + if DEBUG: + raise + sys.stderr.write("Command failed: %s -> %s\n" % (cmd, error_detail)) + self.help() + sys.exit(1) + + # Args are cleaned up, return to process the traditional way + return args + + + def run(self, args): + """ + Wrap args with a path and send it back to autotest. + """ + if not len(args): + self.help() + + test = args.pop(0) + + # Autotest works on control files + if not re.search("control", test): + test = test + "/control" + + localdir = os.environ['PWD'] + LOCALDIRTEST + globaldir = GLOBALDIRTEST + autodir = os.environ['AUTODIRTEST'] + + for dirtest in [localdir, globaldir, autodir]: + if os.path.isfile(dirtest + "/" + test): + args.insert(0, dirtest + "/" + test) + return args + + print "Can not find test %s" % test + raise SystemExit(1) -- 1.7.7.4 _______________________________________________ Autotest mailing list [email protected] http://test.kernel.org/cgi-bin/mailman/listinfo/autotest
