def test_connect_all(self): inventory = make_inventory(hosts=('@local', )) state = State(inventory, Config()) connect_all(state) assert len(state.active_hosts) == 1
def test_connect_all(self): inventory = make_inventory(hosts=('@docker/not-an-image', )) state = State(inventory, Config()) connect_all(state) self.assertEqual(len(state.active_hosts), 1)
def test_connect_all_error(self): inventory = make_inventory(hosts=('@docker/a-broken-image', )) state = State(inventory, Config()) with self.assertRaises(PyinfraError): connect_all(state)
def _main( inventory, operations, verbosity, chdir, ssh_user, ssh_port, ssh_key, ssh_key_password, ssh_password, winrm_username, winrm_password, winrm_port, winrm_transport, shell_executable, sudo, sudo_user, use_sudo_password, su_user, parallel, fail_percent, data, group_data, config_filename, dry, limit, no_wait, serial, quiet, debug, debug_facts, debug_operations, support=None, ): # Setup working directory # if chdir: os_chdir(chdir) # Setup logging # if not debug and not sys.warnoptions: warnings.simplefilter("ignore") log_level = logging.INFO if debug: log_level = logging.DEBUG elif quiet: log_level = logging.WARNING setup_logging(log_level) # Bootstrap any virtualenv init_virtualenv() # Check operations are valid and setup command # # Make a copy before we overwrite original_operations = operations # Debug (print) inventory + group data if operations[0] == "debug-inventory": command = "debug-inventory" # Get one or more facts elif operations[0] == "fact": command = "fact" operations = get_facts_and_args(operations[1:]) # Execute a raw command with server.shell elif operations[0] == "exec": command = "exec" operations = operations[1:] # Execute one or more deploy files elif all(cmd.endswith(".py") for cmd in operations): command = "deploy" filenames = [] for filename in operations[0:]: if path.exists(filename): filenames.append(filename) continue if chdir and filename.startswith(chdir): correct_filename = path.relpath(filename, chdir) logger.warning( ( "Fixing deploy filename under `--chdir` argument: " f"{filename} -> {correct_filename}" ), ) filenames.append(correct_filename) continue raise CliError( "No deploy file: {0}".format( path.join(chdir, filename) if chdir else filename, ), ) operations = filenames # Operation w/optional args (<module>.<op> ARG1 ARG2 ...) elif len(operations[0].split(".")) == 2: command = "op" operations = get_operation_and_args(operations) else: raise CliError( """Invalid operations: {0} Operation usage: pyinfra INVENTORY deploy_web.py [deploy_db.py]... pyinfra INVENTORY server.user pyinfra home=/home/pyinfra pyinfra INVENTORY exec -- echo "hello world" pyinfra INVENTORY fact os [users]...""".format( operations, ), ) # Setup state, config & inventory # cwd = getcwd() if cwd not in sys.path: # ensure cwd is present in sys.path sys.path.append(cwd) state = State() state.cwd = cwd ctx_state.set(state) if verbosity > 0: state.print_fact_info = True state.print_noop_info = True if verbosity > 1: state.print_input = state.print_fact_input = True if verbosity > 2: state.print_output = state.print_fact_output = True if not quiet: click.echo("--> Loading config...", err=True) config = Config() ctx_config.set(config) # Load up any config.py from the filesystem config_filename = path.join(state.cwd, config_filename) if path.exists(config_filename): exec_file(config_filename) # Lock the current config, this allows us to restore this version after # executing deploy files that may alter them. config.lock_current_state() # Arg based config overrides if sudo: config.SUDO = True if sudo_user: config.SUDO_USER = sudo_user if use_sudo_password: config.USE_SUDO_PASSWORD = use_sudo_password if su_user: config.SU_USER = su_user if parallel: config.PARALLEL = parallel if shell_executable: config.SHELL = None if shell_executable in ("None", "null") else shell_executable if fail_percent is not None: config.FAIL_PERCENT = fail_percent if not quiet: click.echo("--> Loading inventory...", err=True) override_data = {} for arg in data: key, value = arg.split("=", 1) override_data[key] = value override_data = {key: parse_cli_arg(value) for key, value in override_data.items()} for key, value in ( ("ssh_user", ssh_user), ("ssh_key", ssh_key), ("ssh_key_password", ssh_key_password), ("ssh_port", ssh_port), ("ssh_password", ssh_password), ("winrm_username", winrm_username), ("winrm_password", winrm_password), ("winrm_port", winrm_port), ("winrm_transport", winrm_transport), ): if value: override_data[key] = value # Load up the inventory from the filesystem inventory, inventory_group = make_inventory( inventory, cwd=state.cwd, override_data=override_data, group_data_directories=group_data, ) ctx_inventory.set(inventory) # Now that we have inventory, apply --limit config override initial_limit = None if limit: all_limit_hosts = [] for limiter in limit: try: limit_hosts = inventory.get_group(limiter) except NoGroupError: limit_hosts = [host for host in inventory if fnmatch(host.name, limiter)] if not limit_hosts: logger.warning("No host matches found for --limit pattern: {0}".format(limiter)) all_limit_hosts.extend(limit_hosts) initial_limit = list(set(all_limit_hosts)) # Initialise the state state.init(inventory, config, initial_limit=initial_limit) if command == "debug-inventory": print_inventory(state) _exit() # Connect to the hosts & start handling the user commands # if not quiet: click.echo(err=True) click.echo("--> Connecting to hosts...", err=True) connect_all(state) if command == "fact": if not quiet: click.echo(err=True) click.echo("--> Gathering facts...", err=True) state.print_fact_info = True fact_data = {} for i, command in enumerate(operations): fact_cls, args, kwargs = command fact_key = fact_cls.name if args or kwargs: fact_key = "{0}{1}{2}".format( fact_cls.name, args or "", " ({0})".format(get_kwargs_str(kwargs)) if kwargs else "", ) try: fact_data[fact_key] = get_facts( state, fact_cls, args=args, kwargs=kwargs, apply_failed_hosts=False, ) except PyinfraError: pass print_facts(fact_data) _exit() if command == "exec": state.print_output = True add_op( state, server.shell, " ".join(operations), _allow_cli_mode=True, ) elif command == "deploy": if not quiet: click.echo(err=True) click.echo("--> Preparing operations...", err=True) # Number of "steps" to make = number of files * number of hosts for i, filename in enumerate(operations): logger.info("Loading: {0}".format(click.style(filename, bold=True))) state.current_op_file_number = i load_deploy_file(state, filename) # Remove any config changes introduced by the deploy file & any includes config.reset_locked_state() elif command == "op": if not quiet: click.echo(err=True) click.echo("--> Preparing operation...", err=True) op, args = operations args, kwargs = args kwargs["_allow_cli_mode"] = True def print_host_ready(host): logger.info( "{0}{1} {2}".format( host.print_prefix, click.style("Ready:", "green"), click.style(original_operations[0], bold=True), ), ) kwargs["_after_host_callback"] = print_host_ready add_op(state, op, *args, **kwargs) # Print proposed changes, execute unless --dry, and exit # if not quiet: click.echo(err=True) click.echo("--> Proposed changes:", err=True) print_meta(state) # If --debug-facts or --debug-operations, print and exit if debug_facts or debug_operations: if debug_facts: print_state_facts(state) if debug_operations: print_state_operations(state) _exit() if dry: _exit() if not quiet: click.echo(err=True) if not quiet: click.echo("--> Beginning operation run...", err=True) run_ops(state, serial=serial, no_wait=no_wait) if not quiet: click.echo("--> Results:", err=True) print_results(state) _exit()