def dump_debug_info(): def dump_external_debug_info(title, args): dump_file.write(f'\n\n*****{title}******\n') p = subprocess.Popen(args, stdout=subprocess.PIPE) out, err = p.communicate() lines = filter(lambda x: x.strip(), out.decode("utf-8").splitlines(keepends=True)) dump_file.writelines(lines) try: logger = Log('dump_debug_info') config = Config() timestr = time.strftime("%Y%m%d-%H%M%S") dump_path = os.getcwd() + f'/epicli_error_{timestr}.dump' dump_file = open(dump_path, 'w') dump_file.write('*****EPICLI VERSION******\n') dump_file.write(f'{VERSION}') dump_file.write('\n\n*****EPICLI ARGS******\n') dump_file.write(' '.join([*['epicli'], *sys.argv[1:]])) dump_file.write('\n\n*****EPICLI CONFIG******\n') for attr in config.__dict__: if attr.startswith('_'): dump_file.write('%s = %r\n' % (attr[1:], getattr(config, attr))) dump_file.write('\n\n*****SYSTEM******\n') system_data = { 'platform': platform.system(), 'release': platform.release(), 'type': platform.uname().system, 'arch': platform.uname().machine, 'cpus': json.dumps(os.cpu_count()), 'hostname': socket.gethostname() } dump_file.write(json.dumps(dict(system_data), indent=2)) dump_file.write('\n\n*****ENVIROMENT VARS******\n') dump_file.write(json.dumps(dict(os.environ), indent=2)) dump_file.write('\n\n*****PYTHON******\n') dump_file.write(f'python_version: {platform.python_version()}\n') dump_file.write(f'python_build: {platform.python_build()}\n') dump_file.write(f'python_revision: {platform.python_revision()}\n') dump_file.write(f'python_compiler: {platform.python_compiler()}\n') dump_file.write(f'python_branch: {platform.python_branch()}\n') dump_file.write( f'python_implementation: {platform.python_implementation()}\n') dump_external_debug_info('ANSIBLE VERSION', ['ansible', '--version']) dump_external_debug_info('ANSIBLE CONFIG', ['ansible-config', 'dump']) dump_external_debug_info('ANSIBLE-VAULT VERSION', ['ansible-vault', '--version']) dump_external_debug_info('TERRAFORM VERSION', ['terraform', '--version']) dump_external_debug_info('SKOPEO VERSION', ['skopeo', '--version']) dump_external_debug_info('RUBY VERSION', ['ruby', '--version']) dump_external_debug_info('RUBY GEM VERSION', ['gem', '--version']) dump_external_debug_info('RUBY INSTALLED GEMS', ['gem', 'query', '--local']) dump_file.write('\n\n*****LOG******\n') log_path = os.path.join(get_output_path(), config.log_file) dump_file.writelines([l for l in open(log_path).readlines()]) finally: dump_file.close() logger.info(f'Error dump has been written to: {dump_path}') logger.warning( 'This dump might contain sensitive information. Check before sharing.' )
class TerraformCommand: def __init__(self, working_directory=os.path.dirname(__file__)): self.logger = Log(__name__) self.APPLY_COMMAND = "apply" self.DESTROY_COMMAND = "destroy" self.PLAN_COMMAND = "plan" self.INIT_COMMAND = "init" self.working_directory = working_directory def apply(self, auto_approve=False, env=os.environ.copy()): self.run(self, self.APPLY_COMMAND, auto_approve=auto_approve, env=env, auto_retries=3) def destroy(self, auto_approve=False, env=os.environ.copy()): self.run(self, self.DESTROY_COMMAND, auto_approve=auto_approve, env=env) def plan(self, env=os.environ.copy()): self.run(self, self.PLAN_COMMAND, env=env) def init(self, env=os.environ.copy()): self.run(self, self.INIT_COMMAND, env=env) @staticmethod def run(self, command, env, auto_approve=False, auto_retries=1): cmd = ['terraform', command] if auto_approve: cmd.append('--auto-approve') if command == self.APPLY_COMMAND or command == self.DESTROY_COMMAND: cmd.append(f'-state={self.working_directory}/terraform.tfstate') cmd.append('-no-color') cmd.append(self.working_directory) cmd = ' '.join(cmd) self.logger.info(f'Running: "{cmd}"') if Config().debug > 0: env['TF_LOG'] = terraform_verbosity[Config().debug] retries = 1 do_retry = True while ((retries <= auto_retries) and do_retry): logpipe = LogPipe(__name__) with subprocess.Popen(cmd, stdout=logpipe, stderr=logpipe, env=env, shell=True) as sp: logpipe.close() retries = retries + 1 do_retry = next( (True for s in logpipe.stderrstrings if 'RetryableError' in s), False) if do_retry and retries <= auto_retries: self.logger.warning( f'Terraform failed with "RetryableError" error. Retry: ' + str(retries) + '/' + str(auto_retries)) if sp.returncode != 0: raise Exception(f'Error running: "{cmd}"') else: self.logger.info(f'Done running "{cmd}"')
class AnsibleCommand: def __init__(self, working_directory=os.path.dirname(__file__)): self.logger = Log(__name__) self.working_directory = working_directory def __init__(self): self.logger = Log(__name__) def run_task(self, hosts, inventory, module, args=None): cmd = ['ansible'] cmd.extend(["-m", module]) if args is not None and len(args) > 0: cmd.extend(["-a", args]) if inventory is not None and len(inventory) > 0: cmd.extend(["-i", inventory]) cmd.append(hosts) if Config().debug > 0: cmd.append(ansible_verbosity[Config().debug]) self.logger.info('Running: "' + ' '.join(module) + '"') logpipe = LogPipe(__name__) with subprocess.Popen(cmd, stdout=logpipe, stderr=logpipe) as sp: logpipe.close() if sp.returncode != 0: raise Exception('Error running: "' + ' '.join(cmd) + '"') else: self.logger.info('Done running "' + ' '.join(cmd) + '"') def run_task_with_retries(self, inventory, module, hosts, retries, timeout=10, args=None): for i in range(retries): try: self.run_task(hosts=hosts, inventory=inventory, module=module, args=args) break except Exception as e: self.logger.error(e) self.logger.warning('Retry running task: ' + str(i + 1) + '/' + str(retries)) time.sleep(timeout) else: raise Exception( f'Failed running task after {str(retries)} retries') def run_playbook(self, inventory, playbook_path, vault_file=None): cmd = ['ansible-playbook'] if inventory is not None and len(inventory) > 0: cmd.extend(["-i", inventory]) if vault_file is not None: cmd.extend(["--vault-password-file", vault_file]) cmd.append(playbook_path) if Config().debug > 0: cmd.append(ansible_verbosity[Config().debug]) self.logger.info('Running: "' + ' '.join(playbook_path) + '"') logpipe = LogPipe(__name__) with subprocess.Popen(cmd, stdout=logpipe, stderr=logpipe) as sp: logpipe.close() if sp.returncode != 0: raise Exception('Error running: "' + ' '.join(cmd) + '"') else: self.logger.info('Done running "' + ' '.join(cmd) + '"') def run_playbook_with_retries(self, inventory, playbook_path, retries, timeout=10): for i in range(retries): try: self.run_playbook(inventory=inventory, playbook_path=playbook_path) break except Exception as e: self.logger.error(e) self.logger.warning('Retry running playbook: ' + str(i + 1) + '/' + str(retries)) time.sleep(timeout) else: raise Exception( f'Failed running playbook after {str(retries)} retries')