]> scripts.mit.edu Git - wizard.git/commitdiff
Convert migrate to logger, and misc refactoring.
authorEdward Z. Yang <edwardzyang@thewritingpot.com>
Wed, 17 Jun 2009 04:37:35 +0000 (00:37 -0400)
committerEdward Z. Yang <edwardzyang@thewritingpot.com>
Wed, 17 Jun 2009 04:37:35 +0000 (00:37 -0400)
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
README
TODO
bin/wizard
lib/wizard/__init__.py
lib/wizard/command/__init__.py
lib/wizard/command/info.py
lib/wizard/command/migrate.py
lib/wizard/command/summary.py
lib/wizard/shell.py

diff --git a/README b/README
index 4f95d6941dd80afcca5ea36ce26b0a22154e5376..fe3e9aa41d1c2681a30b47377c8310699f706af1 100644 (file)
--- a/README
+++ b/README
@@ -6,4 +6,4 @@ locations:
 - lib/wizard/command/__init__.py
     Add the line "import commandname"
 - lib/wizard/command/commandname.py
-    Implement your command there as main()
+    Implement your command there as commandname()
diff --git a/TODO b/TODO
index 2a1460c594088a78efbec5dd6efc92c60777d694..05284982688446a393c7a4f9c6733dc3e03ba340 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,6 +2,7 @@ The Git Autoinstaller
 
 TODO NOW:
 
+- For the long running programs, I definitely want to use logging.
 - Whiteboard the flow for performing an upgrade on a single
   install. How assisted does it need to be?
 - Conduct migration tool testing
index 916a292528aa7d21b6176063af0220b0f6e470ce..4a204e84d472cc706ac9f74c75fec780ce868d87 100755 (executable)
@@ -48,10 +48,10 @@ See '%prog help COMMAND' for more information on a specific command."""
             raise SystemExit(-1)
     # Dispatch commands
     try:
-        command_module = getattr(wizard.command, command)
+        command_fn = getattr(wizard.command, command)
     except AttributeError:
         parser.error("invalid action")
-    command_module.main(rest_argv, options)
+    command_fn(rest_argv, options)
 
 if __name__ == "__main__":
     main()
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b0c216c4c1cc1d80f887717d4b29306663517e1b 100644 (file)
@@ -0,0 +1,34 @@
+import logging
+import sys
+import optparse
+
+def makeLogger(options):
+    logger = logging.getLogger("main")
+    logger.setLevel(logging.INFO)
+    stdout = logging.StreamHandler(sys.stdout)
+    logger.addHandler(stdout)
+    if options.verbose:
+        logger.verbose = True
+        stdout.setLevel(logging.INFO)
+    else:
+        stdout.setLevel(logging.ERROR)
+    return logger
+
+class UserException(Exception):
+    """User friendly exceptions inherit from here"""
+    pass
+
+class NullLogHandler(logging.Handler):
+    """Log handler that doesn't do anything"""
+    def emit(self, record):
+        pass
+
+class WizardOptionParser(optparse.OptionParser):
+    """Configures some default user-level options"""
+    def __init__(self, *args, **kwargs):
+        optparse.OptionParser.__init__(self, *args, **kwargs)
+        self.add_option("-v", "--verbose", dest="verbose", action="store_true",
+                default=False, help="Turns on verbose output")
+    def parse_all(self, argv, logger):
+        options, numeric_args = self.parse_args(argv)
+        return options, numeric_args, makeLogger(options)
index 96e12e8bb358c3178ef57448de6f7cc538ed8257..c6e878d7f3e1c394b5df5223b05b18cd1750b10f 100644 (file)
@@ -1,3 +1,3 @@
-import info
-import migrate
-import summary
+from info import info
+from migrate import migrate
+from summary import summary
index a04880ac6a4a1ad2568b7f14d4e81b06993b7fc7..ec4331e9c75669cb94c7ef24545ee4c51a43bf2b 100644 (file)
@@ -11,7 +11,7 @@ def indent(text, indent):
     # There should be a built-in
     return "\n".join([" " * indent + x for x in text.split("\n")])
 
-def main(argv, global_options):
+def info(argv, global_options):
     usage = """usage: %prog info [ARGS] DIR
 
 Prints information about an autoinstalled directory,
index fb38ff7c7c9d96bd536a781277afa68d290ab257..dbf93d32c2d81e9678b116b8d96618cd087c9cdc 100644 (file)
@@ -2,70 +2,120 @@ import optparse
 import sys
 import os
 import shutil
+import logging.handlers
 
+from wizard import *
 import wizard.deploy as wd
 import wizard.shell as sh
 
-def main(argv, global_options):
+class PermissionsError(UserException):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: You don't have permissions to access this directory.
+Do you have tickets for AFS with your root instance, and
+is your root instance on scripts-security-upd?
+
+You can check by running the commands 'klist' and
+'blanche scripts-security-upd'.  We recommend getting
+root tickets using Nelson Elhage's krbroot script
+at /mit/nelhage/Public/krbroot (for which you run
+'krbroot shell' and then 'aklog').
+"""
+
+class NoSuchDirectoryError(UserException):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: No such directory... check your typing
+"""
+
+class AlreadyMigratedError(UserException):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Directory already contains a .git directory.
+Did you already migrate it?
+"""
+
+class NotAutoinstallError(UserException):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Could not find .scripts-version file. Are you sure
+this is an autoinstalled application?
+"""
+
+class NoRepositoryError(UserException):
+    def __init__(self, app):
+        self.app = app
+    def __str__(self):
+        return """
+
+ERROR: Could not find repository for this application. Have
+you converted the repository over? Is the name %s
+the same as the name of the .git folder?
+""" % self.app
+
+class NoTagError(UserException):
+    def __init__(self, version):
+        self.version = version
+    def __str__(self):
+        return """
+
+ERROR: Could not find tag v%s-scripts in repository
+for %s.  Double check and make sure
+the repository was prepared with all necessary tags!
+""" % (self.version.version, self.version.application.name)
+
+def migrate(argv, global_options, logger = None):
     usage = """usage: %prog migrate [ARGS] DIR
 
 Migrates a directory to our Git-based autoinstall format.
 Performs basic sanity checking and intelligently determines
 what repository and tag to use."""
-    parser = optparse.OptionParser(usage)
-    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
-            default=False, help="Print all commands and outputs")
+    parser = WizardOptionParser(usage)
     parser.add_option("--dry-run", dest="dry_run", action="store_true",
             default=False, help="Prints would would be run without changing anything")
-    options, args = parser.parse_args(argv)
+    options, args, logger = parser.parse_all(argv, logger)
     if len(args) > 1:
         parser.error("too many arguments")
     elif not args:
         parser.error("must specify directory")
     dir = args[0]
-    print "Changing working directory to autoinstall directory"
     try:
         os.chdir(dir)
     except OSError as e:
         if e.errno == 13:
-            print
-            print "ERROR: You don't have permissions to access this directory."
-            print "Do you have tickets for AFS with your root instance, and"
-            print "is your root instance on scripts-security-upd?"
-            print
-            print "You can check by running the commands 'klist' and"
-            print "'blanche scripts-security-upd'.  We recommend getting"
-            print "root tickets using Nelson Elhage's krbroot script"
-            print "at /mit/nelhage/Public/krbroot (for which you run"
-            print "'krbroot shell' and then 'aklog')."
-            raise SystemExit(-1)
+            raise PermissionsError(dir)
         elif e.errno == 2:
-            print
-            print "ERROR: No such directory... check your typing"
-            raise SystemExit(-1)
+            raise NoSuchDirectoryError(dir)
         else: raise e
+    if os.path.isdir(".git"):
+        raise AlreadyMigratedError(dir)
     try:
         deploy = wd.Deployment.fromDir(".")
         version = deploy.getAppVersion()
     except IOError as e:
         if e.errno == 2:
-            print
-            print "ERROR: Could not find .scripts-version file.  Are you sure"
-            print "this is an autoinstalled application?"
-            raise SystemExit(-1)
+            raise NotAutoinstallError(dir)
         else: raise e
     # calculate the repository we'll be pulling out of
     application = version.application
     app = application.name
     repo = os.path.join("/afs/athena.mit.edu/contrib/scripts/wizard/srv", app + ".git")
     if not os.path.isdir(repo):
-        print
-        print "ERROR: Could not find repository for this application. Have"
-        print "you converted the repository over? Is the name %s" % app
-        print "the same as the the name of the foo.git folder?"
-        raise SystemExit(-1)
+        raise NoRepositoryError(app)
     # begin the command line process
-    shell = sh.Shell(options.verbose, options.dry_run)
+    shell = sh.Shell(logger, options.dry_run)
     # check if the version we're trying to convert exists. We assume
     # a convention here, namely, v1.2.3-scripts is what we want. If
     # you broke the convention... shame on you.
@@ -73,11 +123,7 @@ what repository and tag to use."""
         tag = "v%s-scripts" % version.version
         shell.call("git", "--git-dir", repo, "rev-parse", tag)
     except sh.CalledProcessError:
-        print
-        print "ERROR: Could not find tag v%s-scripts for" % version.version
-        print "this application's version.  Double check and make sure"
-        print "the repository was prepared with all necessary tags!"
-        raise SystemExit(-1)
+        raise NoTagError(version)
     did_git_init = False
     did_git_checkout_scripts = False
     try:
@@ -98,16 +144,14 @@ what repository and tag to use."""
         did_git_checkout_scripts = True
         # XXX: setup .scripts/version???
         # for verbose purposes, give us a git status and git diff
+        raise NotImplementedError
         if options.verbose:
             shell.call("git", "status")
             shell.call("git", "diff")
     except:
-        print
-        print "ERROR: Exception detected! Rolling back..."
+        logger.error("ERROR: Exception detected! Rolling back...")
         if did_git_init:
-            print "Deleting .git directory"
             shell.call("rm", "-Rf", ".git")
         if did_git_checkout_scripts:
-            print "Deleting .scripts directory"
             shell.call("rm", "-Rf", ".scripts")
         raise
index cc05bb63c8fd4bbf7c135b5346e3dba500a0d46f..c84348b271aec20a0ddb01a1e9525b92539ddc77 100644 (file)
@@ -34,7 +34,7 @@ class Printer(object):
             self._hang()
             print str
 
-def main(argv, global_options):
+def summary(argv, global_options):
     usage = """usage: %prog summary [ARGS] APPS
 
 Scans all of the collected data from parallel-find.pl, and
index a7dee83f6a811ebd9911e9e33b24aac3737bf1c8..40b09e6abe5024b7379a5d91072715dc5de0a3e9 100644 (file)
@@ -1,24 +1,28 @@
 import subprocess
-from subprocess import CalledProcessError
+from subprocess import CalledProcessError, PIPE, STDOUT
 import sys
 
 class Shell(object):
     """An advanced shell, with the ability to do dry-run and log commands"""
-    def __init__(self, verbose = False, dry = False):
-        """ `verbose`   Whether or not to print the command and outputs
+    def __init__(self, logger = False, dry = False):
+        """ `logger`    The logger
             `dry`       Whether or not to not run any commands, and just print"""
-        self.verbose = verbose
+        self.logger = logger
         self.dry = dry
     def call(self, *args):
-        if self.dry or self.verbose:
-            print "$ " + ' '.join(args)
+        if self.dry or self.logger:
+            self.logger.info("$ " + ' '.join(args))
         if self.dry: return
         proc = None
-        if self.verbose:
-            proc = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr)
+        if self.logger:
+            if hasattr(self.logger, "verbose"):
+                proc = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr)
+            else:
+                proc = subprocess.Popen(args, stdout=PIPE, stderr=STDOUT)
         else:
             proc = subprocess.Popen(args)
-        proc.communicate()
+        stdout, _ = proc.communicate()
+        if self.logger and stdout: self.logger.info(stdout)
         if proc.returncode:
             raise CalledProcessError(proc.returncode, args)