#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # 2010-2011 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # # cdist is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # cdist is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # # import argparse import logging import os import re import sys import time log = logging.getLogger("cdist") # Ensure our /lib/ is included into PYTHON_PATH sys.path.insert(0, os.path.abspath( os.path.join(os.path.dirname(os.path.realpath(__file__)), '../lib'))) import cdist TYPE_PREFIX = "__" def commandline(): """Parse command line""" # Construct parser others can reuse parser = {} # Options _all_ parsers have in common parser['loglevel'] = argparse.ArgumentParser(add_help=False) parser['loglevel'].add_argument('-d', '--debug', help='Set log level to debug', action='store_true', default=False) parser['loglevel'].add_argument('-v', '--verbose', help='Set log level to info, be more verbose', action='store_true', default=False) # Main subcommand parser parser['main'] = argparse.ArgumentParser(description='cdist ' + cdist.VERSION, parents=[parser['loglevel']]) parser['main'].add_argument('-V', '--version', help='Show version', action='version', version='%(prog)s ' + cdist.VERSION) parser['sub'] = parser['main'].add_subparsers(title="Commands") # Banner parser['banner'] = parser['sub'].add_parser('banner', parents=[parser['loglevel']]) parser['banner'].set_defaults(func=cdist.banner.banner) # Config and install (common stuff) parser['configinstall'] = argparse.ArgumentParser(add_help=False) parser['configinstall'].add_argument('host', nargs='+', help='one or more hosts to operate on') parser['configinstall'].add_argument('-c', '--cdist-home', help='Change cdist home (default: .. from bin directory)', action='store') parser['configinstall'].add_argument('-i', '--initial-manifest', help='Path to a cdist manifest', dest='manifest', required=False) parser['configinstall'].add_argument('-p', '--parallel', help='Operate on multiple hosts in parallel', action='store_true', dest='parallel') parser['configinstall'].add_argument('-s', '--sequential', help='Operate on multiple hosts sequentially (default)', action='store_false', dest='parallel') # Config parser['config'] = parser['sub'].add_parser('config', parents=[parser['loglevel'], parser['configinstall']]) parser['config'].set_defaults(func=config) # Install parser['install'] = parser['sub'].add_parser('install', parents=[parser['loglevel'], parser['configinstall']]) parser['install'].set_defaults(func=install) for p in parser: parser[p].epilog = "Get cdist at http://www.nico.schottelius.org/software/cdist/" args = parser['main'].parse_args(sys.argv[1:]) # Loglevels are handled globally in here and debug wins over verbose if args.verbose: logging.root.setLevel(logging.INFO) if args.debug: logging.root.setLevel(logging.DEBUG) log.debug(args) args.func(args) def config(args): configinstall(args, mode=cdist.config.Config) def install(args): configinstall(args, mode=cdist.install.Install) def configinstall(args, mode): """Configure or install remote system""" process = {} time_start = time.time() import cdist.context for host in args.host: context = cdist.context.Context( target_host=host, initial_manifest=args.manifest, base_path=args.cdist_home, exec_path=sys.argv[0], debug=args.debug) c = mode(context) if args.parallel: log.debug("Creating child process for %s", host) process[host] = multiprocessing.Process(target=c.deploy_and_cleanup) process[host].start() else: c.deploy_and_cleanup() context.cleanup() if args.parallel: for p in process.keys(): log.debug("Joining process %s", p) process[p].join() # FIXME: error handling for parallel mode! time_end = time.time() log.info("Total processing time for %s host(s): %s", len(args.host), (time_end - time_start)) def emulator(): """Prepare and run emulator""" emulator = cdist.emulator.Emulator(sys.argv) emulator.run() if __name__ == "__main__": try: logging.basicConfig(format='%(levelname)s: %(message)s') if re.match(TYPE_PREFIX, os.path.basename(sys.argv[0])): import cdist.emulator emulator() else: import cdist.banner import cdist.config import cdist.install commandline() except KeyboardInterrupt: sys.exit(0) except cdist.Error as e: log.error(e) sys.exit(1)