X-Git-Url: https://scripts.mit.edu/gitweb/wizard.git/blobdiff_plain/6839a46260a70b1a992ee5a4b00f544e8879ce4d..17181564839ae64f01f7b00d3141e4410306b8d6:/bin/wizard diff --git a/bin/wizard b/bin/wizard index 51cfaa0..d652d19 100755 --- a/bin/wizard +++ b/bin/wizard @@ -1,29 +1,74 @@ #!/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 -sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# import some non-standard modules to make it fail out early +import decorator + +_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 +from wizard import command, prompt def main(): usage = """usage: %prog COMMAND [ARGS] Wizard is a Git-based autoinstall management system for scripts. -Its commands are: - configure Configures an autoinstall (database, etc) to work - errors Lists all broken autoinstall metadata +User commands: + backup Backup data not on filesystem (database, etc) install Installs an application + migrate Migrate autoinstalls from old format to Git-based format + 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: + 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 - migrate Migrate autoinstalls from old format to Git-based format - prepare-config Prepares configuration files for versioning + mass-upgrade Performs mass upgrade of autoinstalls of an application + research Print statistics about a possible upgrade summary Generate statistics (see help for subcommands) - upgrade Upgrades an autoinstall to the latest version + +Utility commands: + prepare-pristine Downloads and extracts pristine upstream files + prepare-new Prepares a new repository + 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.""" @@ -32,20 +77,34 @@ See '%prog help COMMAND' for more information on a specific command.""" _, args = parser.parse_args() # no global options rest_argv = args[1:] baton = command.OptionBaton() - baton.add("--versions-path", dest="versions_path", + 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", + 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("--log-dir", dest="log_dir", - default=getenvpath("WIZARD_LOG_DIR") or None, - help="Log files for Wizard children processes are placed here.") + 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_name = args[0] except IndexError: parser.print_help() - raise SystemExit(1) + 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: help_module = get_command(rest_argv[0]) @@ -53,14 +112,40 @@ See '%prog help COMMAND' for more information on a specific command.""" 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 = get_command(command_name) - except ImportError: - parser.error("invalid action") - command_module.main(rest_argv, baton) + 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("-", "_")