]> scripts.mit.edu Git - wizard.git/blobdiff - bin/wizard
Set admin e-mail address properly on MediaWiki >= 1.18.0
[wizard.git] / bin / wizard
index 958226644238013d2307ca4785b468b2bc43bcd0..57bf81ef4912cb45b8595c453d1fd720fd7d2513 100755 (executable)
 #!/usr/bin/env python
 
+"""
+Wizard is a next-generation autoinstall management system with an
+eye towards flexibility and scalability.
+
+Copyright (c) 2009-2010 the Wizard development team
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""
+
 import os
 import optparse
 import sys
+import logging
+import traceback
+
+# import some non-standard modules to make it fail out early
+import decorator
 
-# Add lib to path
-sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-import wizard.command
+_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.insert(0, _root_dir)
+sys.path.insert(0, os.path.join(_root_dir, "plugins/scripts")) # hack to load scripts plugins for now
+
+import wizard
+from wizard import command, prompt
 
 def main():
-    usage = """usage: %prog [-s|--versions] COMMAND [ARGS]
+    usage = """usage: %prog COMMAND [ARGS]
 
 Wizard is a Git-based autoinstall management system for scripts.
 
-Its commands are:
-    info            Reports information about an autoinstall
-    massmigrate     Performs mass migration of autoinstalls of an application
+User commands:
+    backup          Backup data not on filesystem (database, etc)
+    install         Installs an application
     migrate         Migrate autoinstalls from old format to Git-based format
-    summary         Generate statistics about autoinstalls
+    remove          Removes an autoinstall, databases and other files
+    restore         Restores files and database to previous version
     upgrade         Upgrades an autoinstall to the latest version
 
+Administrative commands:
+    prepare-upgrade Downloads and tests a software upgrade
+    blacklist       Marks an autoinstall to not try upgrades
+    errors          Lists all broken autoinstall metadata
+    list            Lists autoinstalls, with optional filtering
+    mass-migrate    Performs mass migration of autoinstalls of an application
+    mass-upgrade    Performs mass upgrade of autoinstalls of an application
+    research        Print statistics about a possible upgrade
+    summary         Generate statistics (see help for subcommands)
+
+Plumbing commands:
+    prepare-pristine Downloads and extracts pristine upstream files
+    prepare-config   Prepares configuration files for versioning
+    quota            Prints the usage and available quota of a directory
+
 See '%prog help COMMAND' for more information on a specific command."""
 
     parser = optparse.OptionParser(usage)
     parser.disable_interspersed_args()
-    parser.add_option("-s", "--versions", dest="versions",
-            default="/afs/athena.mit.edu/contrib/scripts/sec-tools/store/versions",
-            help="Location of parallel-find output directory, or a file containing a newline separated list of 'all autoinstalls' (for testing).")
-    # Find the end of the "global" options
-    options, args = parser.parse_args()
+    _, args = parser.parse_args() # no global options
     rest_argv = args[1:]
+    baton = command.OptionBaton()
+    baton.add("--versions-path", dest="versions_path", metavar="PATH",
+        default=getenvpath("WIZARD_VERSIONS_PATH") or "/afs/athena.mit.edu/contrib/scripts/sec-tools/store/versions",
+        help="Location of parallel-find output directory, or a file containing a newline separated list of 'all autoinstalls' (for development work).  Environment variable is WIZARD_VERSIONS_PATH.")
+    baton.add("--srv-path", dest="srv_path", metavar="PATH",
+        default=getenvpath("WIZARD_SRV_PATH") or "/afs/athena.mit.edu/contrib/scripts/git/autoinstalls",
+        help="Location of autoinstall Git repositories, such that $REPO_PATH/$APP.git is a repository (for development work).  Environment variable is WIZARD_SRV_PATH.")
+    baton.add("--dry-run", dest="dry_run", action="store_true",
+            default=False, help="Performs the operation without actually modifying any files.  Use in combination with --verbose to see commands that will be run.")
+    # common variables for mass commands
+    baton.add("--seen", dest="seen",
+            default=None, help="File to read/write paths of successfully modified installs;"
+            "these will be skipped on re-runs.  If --log-dir is specified, this is automatically enabled.")
+    baton.add("--no-parallelize", dest="no_parallelize", action="store_true",
+            default=False, help="Turn off parallelization")
+    baton.add("--max-processes", dest="max_processes", type="int", metavar="N",
+            default=5, help="Maximum subprocesses to run concurrently")
+    baton.add("--limit", dest="limit", type="int",
+            default=None, help="Limit the number of autoinstalls to look at.")
+    baton.add("--user", "-u", dest="user",
+            default=None, help="Only mass migrate a certain user's installs.  No effect if versions_path is a file.")
     try:
-        command = args[0]
+        command_name = args[0]
     except IndexError:
         parser.print_help()
-        raise SystemExit(-1)
-    if command == "help":
+        sys.exit(1)
+    baton.add("--log-dir", dest="log_dir",
+        default=getenvpath("WIZARD_LOG_DIR") or "/tmp/wizard-%s" % command_name,
+        help="Log files for Wizard children processes are placed here.")
+    if command_name == "help":
         try:
-            getattr(wizard.command, rest_argv[0]).main(['-h'], options)
-        except AttributeError:
+            help_module = get_command(rest_argv[0])
+        except ImportError:
             parser.error("invalid action")
         except IndexError:
             parser.print_help()
-            raise SystemExit(-1)
+            sys.exit(1)
+        help_module.main(['--help'], baton)
+    # This is a gigantic hack to handle the case of AFS + Scripts style
+    # permissions, where we usually don't have access to the HOME
+    # directory.  AFS will throttle you if you trigger too many
+    # lack of permissions, and since we run Git a lot and Git
+    # persistently stats the home directory, this can cause pretty
+    # big problems for our performance.
+    if not os.access(os.environ['HOME'], os.R_OK):
+        os.putenv('HOME', '/disabled')
     # Dispatch commands
+    command_module = get_command(command_name)
     try:
-        command_module = getattr(wizard.command, command)
-    except AttributeError:
-        parser.error("invalid action")
-    command_module.main(rest_argv, options)
+        command_module.main(rest_argv, baton)
+    except prompt.UserCancel as e:
+        print str(e)
+        sys.exit(1)
+    except Exception as e:
+        # log the exception
+        msg = traceback.format_exc()
+        if command.logging_setup:
+            outfun = logging.error
+        else:
+            outfun = sys.stderr.write
+        if isinstance(e, wizard.Error):
+            if e.quiet and not command.debug:
+                msg = str(e)
+                if command.logging_setup:
+                    msg = msg.replace("ERROR: ", "")
+            outfun(msg)
+            sys.exit(e.exitcode)
+        else:
+            outfun(msg)
+            sys.exit(1)
+
+def get_command(name):
+    name = name.replace("-", "_")
+    __import__("wizard.command." + name)
+    return getattr(wizard.command, name)
+
+def getenvpath(name):
+    val = os.getenv(name)
+    if val:
+        val = os.path.abspath(val)
+    return val
 
 if __name__ == "__main__":
     main()