def _find_start_docker_container(container_id): docker_info = local.shell( 'docker container inspect {0}'.format(container_id)) docker_info = json.loads(docker_info)[0] if docker_info['State']['Running'] is False: logger.info('Starting stopped container: {0}'.format(container_id)) local.shell('docker container start {0}'.format(container_id))
def connect(state, host, **kwargs): logger.info('{0}{1}'.format( host.print_prefix, click.style('Ready', 'green'), )) return True
def connect(self, for_fact=None, show_errors=True): self._check_state() if not self.connection: try: self.connection = self.executor.connect(self.state, self) except ConnectError as e: if show_errors: log_message = '{0}{1}'.format( self.print_prefix, click.style(e.args[0], 'red'), ) logger.error(log_message) else: log_message = '{0}{1}'.format( self.print_prefix, click.style('Connected', 'green'), ) if for_fact: log_message = '{0}{1}'.format( log_message, ' (for {0} fact)'.format(for_fact), ) logger.info(log_message) return self.connection
def connect(self, reason=None, show_errors=True): self._check_state() if not self.connection: self.state.trigger_callbacks('host_before_connect', self) try: self.connection = self.executor.connect(self.state, self) except ConnectError as e: if show_errors: log_message = '{0}{1}'.format( self.print_prefix, click.style(e.args[0], 'red'), ) logger.error(log_message) self.state.trigger_callbacks('host_connect_error', self, e) else: log_message = '{0}{1}'.format( self.print_prefix, click.style('Connected', 'green'), ) if reason: log_message = '{0}{1}'.format( log_message, ' ({0})'.format(reason), ) logger.info(log_message) self.state.trigger_callbacks('host_connect', self) return self.connection
def print_results(state): for hostname, results in six.iteritems(state.results): if hostname not in state.inventory.connected_hosts: logger.info('[{0}]\tNo connection'.format( colored(hostname, 'red', attrs=['bold']))) else: meta = state.meta[hostname] success_ops = results['success_ops'] error_ops = results['error_ops'] # If all ops got complete (even with ignored_errors) if results['ops'] == meta['ops']: # Yellow if ignored any errors, else green color = 'green' if error_ops == 0 else 'yellow' host_string = colored(hostname, color) # Ops did not complete! else: host_string = colored(hostname, 'red', attrs=['bold']) logger.info( '[{0}]\tSuccessful: {1}\t Errors: {2}\t Commands: {3}/{4}' .format( host_string, colored(success_ops, attrs=['bold']), error_ops if error_ops == 0 else colored( error_ops, 'red', attrs=['bold']), results['commands'], meta['commands']))
def _run_server_ops(state, host, progress=None): ''' Run all ops for a single server. ''' logger.debug('Running all ops on {0}'.format(host)) for op_hash in state.get_op_order(): op_meta = state.op_meta[op_hash] logger.info('--> {0} {1} on {2}'.format( click.style('--> Starting operation:', 'blue'), click.style(', '.join(op_meta['names']), bold=True), click.style(host.name, bold=True), )) result = _run_server_op(state, host, op_hash) # Trigger CLI progress if provided if progress: progress((host, op_hash)) if result is False: raise PyinfraError('Error in operation {0} on {1}'.format( ', '.join(op_meta['names']), host, )) if pyinfra.is_cli: print()
def _run_server_ops(state, host, progress=None): name = host.name logger.debug('Running all ops on {0}'.format(name)) for op_hash in state.op_order: op_meta = state.op_meta[op_hash] logger.info('--> {0} {1} on {2}'.format( click.style('--> Starting operation:', 'blue'), click.style(', '.join(op_meta['names']), bold=True), click.style(name, bold=True), )) result = _run_op(state, host, op_hash) # Trigger CLI progress if provided if progress: progress() if result is False: raise PyinfraError('Error in operation {0} on {1}'.format( ', '.join(op_meta['names']), name, )) if state.print_lines: print()
def connect(self, reason=None, show_errors=True, raise_exceptions=False): self._check_state() if not self.connection: self.state.trigger_callbacks("host_before_connect", self) try: self.connection = self.executor.connect(self.state, self) except ConnectError as e: if show_errors: log_message = "{0}{1}".format( self.print_prefix, click.style(e.args[0], "red"), ) logger.error(log_message) self.state.trigger_callbacks("host_connect_error", self, e) if raise_exceptions: raise else: log_message = "{0}{1}".format( self.print_prefix, click.style("Connected", "green"), ) if reason: log_message = "{0}{1}".format( log_message, " ({0})".format(reason), ) logger.info(log_message) self.state.trigger_callbacks("host_connect", self) return self.connection
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), ), )
def loop_hosts(): # This actually does the op build for host in inventory: pseudo_host.set(host) exec_file(arguments['deploy']) logger.info('{0} {1}'.format( '[{}]'.format(colored(host.name, attrs=['bold'])), colored('Ready', 'green')))
def get_vagrant_config(limit=None): global VAGRANT_CONFIG if VAGRANT_CONFIG is None: logger.info('Getting vagrant config...') VAGRANT_CONFIG = _get_vagrant_config(limit=limit) return VAGRANT_CONFIG
def get_mech_config(limit=None): logger.info('Getting Mech config...') if limit and not isinstance(limit, (list, tuple)): limit = [limit] # Note: There is no "--machine-readable" option to 'mech status' with progress_spinner({'mech ls'}) as progress: output = local.shell( 'mech ls', splitlines=True, ) progress('mech ls') targets = [] for line in output: address = '' data = line.split() target = data[0] if len(data) == 5: address = data[1] # Skip anything not in the limit if limit is not None and target not in limit: continue # For each vm that has an address, fetch it's SSH config in a thread if address != '' and address[0].isdigit(): targets.append(target) threads = [] config_queue = Queue() with progress_spinner(targets) as progress: for target in targets: thread = Thread( target=_get_mech_ssh_config, args=(config_queue, progress, target), ) threads.append(thread) thread.start() for thread in threads: thread.join() queue_items = list(config_queue.queue) lines = [] for output in queue_items: lines.extend(output) return lines
def connect(state, host, **kwargs): ''' Connect to a single host. Returns the SSH client if succesful. Stateless by design so can be run in parallel. ''' logger.debug('Connecting to: {0} ({1})'.format(host.name, kwargs)) name = host.name hostname = host.data.ssh_hostname or name try: # Create new client & connect to the host client = SSHClient() client.set_missing_host_key_policy(MissingHostKeyPolicy()) client.connect(hostname, **kwargs) # Enable SSH forwarding session = client.get_transport().open_session() AgentRequestHandler(session) # Log logger.info('{0}{1}'.format( host.print_prefix, click.style('Connected', 'green'), )) return client except AuthenticationException as e: auth_kwargs = {} for key, value in kwargs.items(): if key in ('username', 'password'): auth_kwargs[key] = value continue if key == 'pkey' and value: auth_kwargs['key'] = host.data.ssh_key auth_args = ', '.join('{0}={1}'.format(key, value) for key, value in auth_kwargs.items()) _log_connect_error(host, 'Authentication error', auth_args) except SSHException as e: _log_connect_error(host, 'SSH error', e) except gaierror: _log_connect_error(host, 'Could not resolve hostname', hostname) except socket_error as e: _log_connect_error(host, 'Could not connect', e) except EOFError as e: _log_connect_error(host, 'EOF error', e)
def load_file(local_host): with ctx_config.use(state.config.copy()): with ctx_host.use(local_host): exec_file(filename) logger.info( "{0}{1} {2}".format( local_host.print_prefix, click.style("Ready:", "green"), click.style(filename, bold=True), ), )
def print_meta(state, inventory): group_combinations = _get_group_combinations(inventory) for i, (groups, hosts) in enumerate(six.iteritems(group_combinations), 1): if groups: logger.info('Groups: {0}'.format( click.style(' / '.join(groups), bold=True), )) else: logger.info('Ungrouped:') for host in hosts: meta = state.meta[host.name] # Didn't connect to this host? if host.name not in state.connected_hosts: logger.info('[{0}]\tNo connection'.format( click.style(host.name, 'red', bold=True), )) continue logger.info( '[{0}]\tOperations: {1}\t Commands: {2}'.format( click.style(host.name, bold=True), meta['ops'], meta['commands'], ), ) if i != len(group_combinations): print()
def connect(state, host, **kwargs): ''' Connect to a single host. Returns the SSH client if succesful. Stateless by design so can be run in parallel. ''' logger.debug('Connecting to: {0} ({1})'.format(host.name, kwargs)) name = host.name hostname = host.data.ssh_hostname or name try: # Create new client & connect to the host client = SSHClient() client.set_missing_host_key_policy(MissingHostKeyPolicy()) client.connect(hostname, **kwargs) # Enable SSH forwarding session = client.get_transport().open_session() AgentRequestHandler(session) # Log logger.info('{0}{1}'.format( host.print_prefix, click.style('Connected', 'green'), )) return client except AuthenticationException as e: logger.error('Auth error on: {0}, {1}'.format(name, e)) except SSHException as e: logger.error('SSH error on: {0}, {1}'.format(name, e)) except gaierror: if hostname == name: logger.error('Could not resolve {0}'.format(name)) else: logger.error('Could not resolve for {0} (SSH host: {1})'.format( name, hostname)) except socket_error as e: logger.error( 'Could not connect: {0}:{1}, {2}'.format(name, kwargs.get('port', 22), e), ) except EOFError as e: logger.error('EOF error connecting to {0}: {1}'.format(name, e))
def get_vagrant_config(limit=None): logger.info("Getting Vagrant config...") if limit and not isinstance(limit, (list, tuple)): limit = [limit] with progress_spinner({"vagrant status"}) as progress: output = local.shell( "vagrant status --machine-readable", splitlines=True, ) progress("vagrant status") targets = [] for line in output: line = line.strip() _, target, type_, data = line.split(",", 3) # Skip anything not in the limit if limit is not None and target not in limit: continue # For each running container - fetch it's SSH config in a thread - this # is because Vagrant *really* slow to run each command. if type_ == "state" and data == "running": targets.append(target) threads = [] config_queue = Queue() with progress_spinner(targets) as progress: for target in targets: thread = Thread( target=_get_vagrant_ssh_config, args=(config_queue, progress, target), ) threads.append(thread) thread.start() for thread in threads: thread.join() queue_items = list(config_queue.queue) lines = [] for output in queue_items: lines.extend([ln.strip() for ln in output]) return lines
def _log_operation_start(op_meta): op_types = [] if op_meta['serial']: op_types.append('serial') if op_meta['run_once']: op_types.append('run once') logger.info('{0} {1} {2}'.format( click.style( '--> Starting{0}operation:'.format( ' {0} '.format(', '.join(op_types)) if op_types else ' ', ), 'blue'), click.style(', '.join(op_meta['names']), bold=True), tuple(op_meta['args']) if op_meta['args'] else '', ))
def disconnect(state, host): container_id = host.host_data['docker_container_id'][:12] with progress_spinner({'docker commit'}): image_id = local.shell( 'docker commit {0}'.format(container_id), splitlines=True, )[-1][7:19] # last line is the image ID, get sha256:[XXXXXXXXXX]... with progress_spinner({'docker rm'}): local.shell('docker rm -f {0}'.format(container_id), ) logger.info('{0}docker build complete, image ID: {1}'.format( host.print_prefix, click.style(image_id, bold=True), ))
def connect(state, host, for_fact=None): # Log log_message = '{0}{1}'.format( host.print_prefix, click.style('Connected', 'green'), ) if for_fact: log_message = '{0}{1}'.format( log_message, ' (for {0} fact)'.format(for_fact), ) logger.info(log_message) return True
def _run_server_ops(state, hostname): logger.debug('Running all ops on {}'.format(hostname)) for op_hash in state.op_order: op_meta = state.op_meta[op_hash] logger.info('{0} {1} on {2}'.format( colored('Starting operation:', 'blue'), colored(', '.join(op_meta['names']), attrs=['bold']), colored(hostname, attrs=['bold']))) result = _run_op(state, hostname, op_hash) if result is False: raise PyinfraError('Error in operation {0} on {1}'.format( ', '.join(op_meta['names']), hostname)) if state.print_lines: print()
def get_ansible_inventory(inventory_filename=None): if not inventory_filename: # pragma: no cover raise InventoryError('No Ansible inventory filename provided!') if not path.exists(inventory_filename): raise InventoryError(('Could not find Ansible inventory file: {0}' ).format(inventory_filename)) logger.info('Parsing Ansible inventory...') config = ConfigParser( delimiters=(' '), # we only handle the hostnames for now allow_no_value=True, # we don't by default have = values interpolation=None, # remove any Python interpolation ) config.read(inventory_filename) return config
def load_deploy_file(state, filename): state.current_deploy_filename = filename # Copy the inventory hosts (some might be removed during deploy) hosts = list(state.inventory.iter_active_hosts()) for host in hosts: pseudo_host.set(host) exec_file(filename) logger.info('{0}{1} {2}'.format( host.print_prefix, click.style('Ready:', 'green'), click.style(filename, bold=True), )) # Remove any pseudo host pseudo_host.reset()
def disconnect(state, host): container_id = host.host_data["docker_container_id"][:12] with progress_spinner({"docker commit"}): image_id = ssh.run_shell_command( state, host, "docker commit {0}".format(container_id))[1][-1][ 7:19] # last line is the image ID, get sha256:[XXXXXXXXXX]... with progress_spinner({"docker rm"}): ssh.run_shell_command( state, host, "docker rm -f {0}".format(container_id), ) logger.info( "{0}docker build complete, image ID: {1}".format( host.print_prefix, click.style(image_id, bold=True), ), )
def load_deploy_file(state, filename): # Copy the inventory hosts (some might be removed during deploy) hosts = list(state.inventory) for host in hosts: if not state.is_host_in_limit(host): continue pseudo_host.set(host) exec_file(filename) logger.info('{0}{1} {2}'.format( host.print_prefix, click.style('Ready:', 'green'), click.style(filename, bold=True), )) # Remove any pseudo host pseudo_host.reset()
def load_deploy_file(state, filename, progress): for host in state.inventory: pseudo_host.set(host) exec_file(filename) state.ready_host(host) progress() logger.info('{0} {1} {2}'.format( '[{}]'.format(click.style(host.name, bold=True)), click.style('Ready:', 'green'), click.style(filename, bold=True), )) # Remove any pseudo host pseudo_host.reset() # Un-ready the hosts - this is so that any hooks or callbacks during the # deploy can still use facts as expected. state.ready_host_names = set()
def load_ansible_ini_inventory(inventory_filename): logger.info('Trying Ansible inventory format...') config = ConfigParser( delimiters=(' '), # we only handle the hostnames for now allow_no_value=True, # we don't by default have = values interpolation=None, # remove any Python interpolation ) config.read(inventory_filename) groups = {} # First pass - load hosts/groups of hosts for section in config.sections(): if ':' in section: # ignore :children and :vars sections this time continue all_hosts = [] options = config.options(section) for option in options: all_hosts.extend(_parse_ansible_hosts(options)) groups[section] = all_hosts # Second pass - load any children groups for section in config.sections(): if not section.endswith( ':children'): # we only support :children for now continue group_name = section.replace(':children', '') all_hosts = [] options = config.options(section) for option in options: all_hosts.extend(groups[option]) groups[group_name] = all_hosts return groups
def load_deploy_file(state, filename): # Copy the inventory hosts (some might be removed during deploy) hosts = list(state.inventory) for host in hosts: # Don't load for anything within our (top level, --limit) limit if (isinstance(state.limit_hosts, list) and host not in state.limit_hosts): continue pseudo_host.set(host) exec_file(filename) logger.info('{0}{1} {2}'.format( host.print_prefix, click.style('Ready:', 'green'), click.style(filename, bold=True), )) # Remove any pseudo host pseudo_host.reset()
def log_operation_start(op_meta, op_types=None, prefix="--> "): op_types = op_types or [] if op_meta["serial"]: op_types.append("serial") if op_meta["run_once"]: op_types.append("run once") args = "" if op_meta["args"]: args = "({0})".format(", ".join(str(arg) for arg in op_meta["args"])) logger.info( "{0} {1} {2}".format( click.style( "{0}Starting{1}operation:".format( prefix, " {0} ".format(", ".join(op_types)) if op_types else " ", ), "blue", ), click.style(", ".join(op_meta["names"]), bold=True), args, ), )
def print_results(state, inventory): group_combinations = _get_group_combinations(inventory) for i, (groups, hosts) in enumerate(six.iteritems(group_combinations), 1): if groups: logger.info('Groups: {0}'.format( click.style(' / '.join(groups), bold=True), )) else: logger.info('Ungrouped:') for host in hosts: # Didn't conenct to this host? if host.name not in state.connected_hosts: logger.info('[{0}]\tNo connection'.format( click.style(host.name, 'red', bold=True), )) continue results = state.results[host.name] meta = state.meta[host.name] success_ops = results['success_ops'] error_ops = results['error_ops'] # If all ops got complete (even with ignored_errors) if results['ops'] == meta['ops']: # Yellow if ignored any errors, else green color = 'green' if error_ops == 0 else 'yellow' host_string = click.style(host.name, color) # Ops did not complete! else: host_string = click.style(host.name, 'red', bold=True) logger.info( '[{0}]\tSuccessful: {1}\t Errors: {2}\t Commands: {3}/{4}' .format( host_string, click.style(six.text_type(success_ops), bold=True), error_ops if error_ops == 0 else click.style( six.text_type(error_ops), 'red', bold=True), results['commands'], meta['commands'], )) if i != len(group_combinations): print()