#!/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 _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 COMMAND [ARGS] Wizard is a Git-based autoinstall management system for scripts. 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: 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() _, 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_name = args[0] except IndexError: parser.print_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: help_module = get_command(rest_argv[0]) except ImportError: parser.error("invalid action") except IndexError: parser.print_help() 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.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()