def _main( inventory, commands, verbosity, user, port, key, key_password, password, sudo, sudo_user, su_user, parallel, fail_percent, dry, limit, no_wait, serial, debug, debug_data, debug_state, facts=None, operations=None, ): print() print('### {0}'.format(click.style('Welcome to pyinfra', bold=True))) print() # Setup logging log_level = logging.DEBUG if debug else logging.INFO setup_logging(log_level) deploy_dir = getcwd() potential_deploy_dirs = [] # This is the most common case: we have a deploy file so use it's # pathname - we only look at the first file as we can't have multiple # deploy directories. if commands[0].endswith('.py'): deploy_file_dir, _ = path.split(commands[0]) above_deploy_file_dir, _ = path.split(deploy_file_dir) deploy_dir = deploy_file_dir potential_deploy_dirs.extend(( deploy_file_dir, above_deploy_file_dir, )) # If we have a valid inventory, look in it's path and it's parent for # group_data or config.py to indicate deploy_dir (--fact, --run). if inventory.endswith('.py') and path.isfile(inventory): inventory_dir, _ = path.split(inventory) above_inventory_dir, _ = path.split(inventory_dir) potential_deploy_dirs.extend(( inventory_dir, above_inventory_dir, )) for potential_deploy_dir in potential_deploy_dirs: logger.debug('Checking potential directory: {0}'.format( potential_deploy_dir, )) if any(( path.isdir(path.join(potential_deploy_dir, 'group_data')), path.isfile(path.join(potential_deploy_dir, 'config.py')), )): logger.debug( 'Setting directory to: {0}'.format(potential_deploy_dir)) deploy_dir = potential_deploy_dir break # List facts if commands[0] == 'fact': command = 'fact' fact_names = commands[1:] facts = [] for name in fact_names: args = None if ':' in name: name, args = name.split(':', 1) args = args.split(',') if not is_fact(name): raise CliError('No fact: {0}'.format(name)) facts.append((name, args)) commands = facts # Execute a raw command with server.shell elif commands[0] == 'exec': command = 'exec' commands = commands[1:] # Deploy files(s) elif all(cmd.endswith('.py') for cmd in commands): command = 'deploy' commands = commands[0:] # Check each file exists for file in commands: if not path.exists(file): raise CliError('No deploy file: {0}'.format(file)) # Operation w/optional args elif len(commands) == 2: command = 'op' commands = get_operation_and_args( commands[0], commands[1], ) else: raise CliError('''Invalid commands: {0} Command 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(commands)) print('--> Loading config...') # Load up any config.py from the filesystem config = load_config(deploy_dir) # Load any hooks/config from the deploy file if command == 'deploy': load_deploy_config(commands[0], config) # Arg based config overrides if sudo: config.SUDO = True if sudo_user: config.SUDO_USER = sudo_user if su_user: config.SU_USER = su_user if parallel: config.PARALLEL = parallel if fail_percent is not None: config.FAIL_PERCENT = fail_percent print('--> Loading inventory...') # Load up the inventory from the filesystem inventory, inventory_group = make_inventory( inventory, deploy_dir=deploy_dir, limit=limit, ssh_user=user, ssh_key=key, ssh_key_password=key_password, ssh_password=password, ssh_port=port, ) # If --debug-data dump & exit if debug_data: print_inventory(inventory) _exit() # Attach to pseudo inventory pseudo_inventory.set(inventory) # Create/set the state state = State(inventory, config) state.is_cli = True state.print_lines = True state.deploy_dir = deploy_dir # Setup printing on the new state print_output = verbosity > 0 print_fact_output = verbosity > 1 state.print_output = print_output # -v state.print_fact_info = print_output # -v state.print_fact_output = print_fact_output # -vv # Attach to pseudo state pseudo_state.set(state) # Setup the data to be passed to config hooks hook_data = FallbackAttrData( state.inventory.get_override_data(), state.inventory.get_group_data(inventory_group), state.inventory.get_data(), ) # Run the before_connect hook if provided run_hook(state, 'before_connect', hook_data) # Connect to all the servers print('--> Connecting to hosts...') with progress_spinner(state.inventory) as progress: connect_all(state, progress=progress) # Run the before_connect hook if provided run_hook(state, 'before_facts', hook_data) # Just getting a fact? # if command == 'fact': print() print('--> Gathering facts...') # Print facts as we get them state.print_fact_info = True # Print fact output with -v state.print_fact_output = print_output fact_data = {} with progress_spinner(commands) as progress: for i, (name, args) in enumerate(commands): fact_data[name] = get_facts( state, name, args=args, ) progress() print_facts(fact_data) _exit() # Prepare the deploy! # # Execute a raw command with server.shell if command == 'exec': # Print the output of the command state.print_output = True add_op( state, server.shell, ' '.join(commands), ) # Deploy files(s) elif command == 'deploy': print() print('--> Preparing operations...') # Number of "steps" to make = number of files * number of hosts prepare_steps = len(commands) * len(state.inventory) with progress_spinner(prepare_steps) as progress: for filename in commands: load_deploy_file(state, filename, progress=progress) progress() # Operation w/optional args elif command == 'op': print() print('--> Preparing operation...') op, args = commands add_op(state, op, *args[0], **args[1]) # Always show meta output print() print('--> Proposed changes:') print_meta(state, inventory) # If --debug-state, dump state (ops, op order, op meta) now & exit if debug_state: dump_state(state) _exit() # Run the operations we generated with the deploy file if dry: _exit() print() # Run the before_deploy hook if provided run_hook(state, 'before_deploy', hook_data) print('--> Beginning operation run...') # Number of "steps" to make = number of operations * number of hosts operation_steps = len(state.op_order) * len(state.inventory) with progress_spinner(operation_steps) as progress: run_ops( state, serial=serial, no_wait=no_wait, progress=progress, ) # Run the after_deploy hook if provided run_hook(state, 'after_deploy', hook_data) print('--> Results:') print_results(state, inventory) _exit()
def test_op(self): state = State(make_inventory(), Config()) # Enable printing on this test to catch any exceptions in the formatting state.print_output = True state.print_fact_info = True state.print_fact_output = True state.print_lines = True connect_all(state) add_op(state, files.file, '/var/log/pyinfra.log', user='******', group='pyinfra', mode='644', sudo=True, sudo_user='******', su_user='******', ignore_errors=True, env={ 'TEST': 'what', }) # Ensure we have an op self.assertEqual(len(state.op_order), 1) first_op_hash = state.op_order[0] # Ensure the op name self.assertEqual(state.op_meta[first_op_hash]['names'], {'Files/File'}) # Ensure the commands self.assertEqual(state.ops['somehost'][first_op_hash]['commands'], [ 'touch /var/log/pyinfra.log', 'chmod 644 /var/log/pyinfra.log', 'chown pyinfra:pyinfra /var/log/pyinfra.log' ]) # Ensure the meta meta = state.op_meta[first_op_hash] self.assertEqual(meta['sudo'], True) self.assertEqual(meta['sudo_user'], 'test_sudo') self.assertEqual(meta['su_user'], 'test_su') self.assertEqual(meta['ignore_errors'], True) # Ensure run ops works run_ops(state) # Ensure ops completed OK self.assertEqual(state.results['somehost']['success_ops'], 1) self.assertEqual(state.results['somehost']['ops'], 1) self.assertEqual(state.results['anotherhost']['success_ops'], 1) self.assertEqual(state.results['anotherhost']['ops'], 1) # And w/o errors self.assertEqual(state.results['somehost']['error_ops'], 0) self.assertEqual(state.results['anotherhost']['error_ops'], 0) # And with the different modes run_ops(state, serial=True) run_ops(state, no_wait=True)
# Create/set the state state = State(inventory, config) state.is_cli = True state.deploy_dir = deploy_dir # Setup printing on the new state print_output = arguments['verbose'] > 0 if arguments['deploy'] is None and arguments['op'] is None: print_fact_output = print_output else: print_fact_output = arguments['verbose'] > 1 state.print_output = print_output # -v state.print_fact_info = print_output # -v state.print_fact_output = print_fact_output # -vv state.print_lines = True # Attach to pseudo state pseudo_state.set(state) # Setup the data to be passed to config hooks hook_data = FallbackAttrData( state.inventory.get_override_data(), state.inventory.get_group_data(inventory_group), state.inventory.get_data() ) # Run the before_connect hook if provided run_hook(state, 'before_connect', hook_data) # Connect to all the servers