def vault_json_list(value): """Run code with a new Vault-protected JSON list. After the code is finished, read back the value of the file and store it in `value`. :param value: the starting value of the Vault-protected JSON list. The final value will be written to this. """ with tempfile.NamedTemporaryFile(delete=False) as temp_list: # Write the value to the temporary file this_vault = vault.Vault(TEST_VAULT_PASSWORD) this_vault.dump_as_json(value, stream=temp_list) temp_list.flush() # Run the code, making the file path available try: yield temp_list.name finally: # Read the (potentially modified) list file if os.path.isfile(temp_list.name): value[:] = this_vault.load_as_json(temp_list.name) os.unlink(temp_list.name) else: # The code might have deleted the file value[:] = []
def inventory_scan(hosts_yml_path, facts_to_collect, report_path, vault_pass, base_name, forks=None, scan_dirs=None, log_path=None, verbosity=0): """Run an inventory scan. :param hosts_yml_path: path to an Ansible inventory file to scan. :param facts_to_collect: a list of facts to collect. :param report_path: the path to write a report to. :param vault_pass: the vault password used to protect user data :param base_name: the base name of the output files :param forks: the number of Ansible forks, or None for default. :param scan_dirs: the directories on the remote host to scan, or None for default. :param log_path: path to log to, or None for default. :param verbosity: number of v's of Ansible verbosity. :returns: True if scan completed successfully, False if not. """ hosts_yml = base_name + utilities.PROFILE_HOSTS_SUFIX hosts_yml_path = utilities.get_config_path(hosts_yml) vault = vault_module.Vault(vault_pass) hosts_dict = vault.load_as_yaml(hosts_yml_path) host_groups = hosts_by_group(hosts_dict) variables_prefix = os.path.join(tempfile.gettempdir(), 'rho-fact-temp-' + str(time.time()) + '-') if os.path.isfile(utilities.PLAYBOOK_DEV_PATH): playbook = utilities.PLAYBOOK_DEV_PATH elif os.path.isfile(utilities.PLAYBOOK_RPM_PATH): playbook = utilities.PLAYBOOK_RPM_PATH else: print(t("rho scan playbook not found locally or in '%s'") % playbook) sys.exit(1) log_path = log_path or utilities.SCAN_LOG_PATH my_env = os.environ.copy() my_env["ANSIBLE_HOST_KEY_CHECKING"] = "False" my_env["ANSIBLE_NOCOLOR"] = "True" facts_out = [] total_hosts_count = 0 for group in host_groups.keys(): hosts = host_groups.get(group, []) total_hosts_count += len(hosts) utilities.log.info('Starting scan of %d systems broken into %d groups.', total_hosts_count, len(host_groups.keys())) print('\nStarting scan of %d systems broken into %d groups.' % (total_hosts_count, len(host_groups.keys()))) for group in host_groups.keys(): variables_path = variables_prefix + group hosts = host_groups.get(group, []) ansible_vars = { 'facts_to_collect': list(facts_to_collect), 'scan_dirs': ' '.join(scan_dirs or []), 'variables_path': variables_path } cmd_string = ('ansible-playbook {playbook} ' '--limit {group},localhost ' '-i {inventory} -f {forks} ' '--ask-vault-pass ' '--extra-vars \'{vars}\'').format( group=group, playbook=playbook, inventory=hosts_yml_path, forks=forks, vars=json.dumps(ansible_vars)) rho_host_scan_timeout = os.getenv('RHO_HOST_SCAN_TIMEOUT', DEFAULT_HOST_SCAN_TIMEOUT) try: rho_host_scan_timeout = int(rho_host_scan_timeout) except ValueError: rho_host_scan_timeout = DEFAULT_HOST_SCAN_TIMEOUT host_scan_timeout = ((len(hosts) // int(forks)) + 1) \ * rho_host_scan_timeout utilities.log.info( 'Starting scan for group "%s" with %d systems' ' with timeout of %d minutes.', group, len(hosts), host_scan_timeout) print('\nStarting scan for group "%s" with %d systems' ' with timeout of %d minutes.\n' % (group, len(hosts), host_scan_timeout)) try: ansible_utils.run_with_vault( cmd_string, vault_pass, env=my_env, log_path=log_path, log_to_stdout=utilities.process_host_scan, ansible_verbosity=verbosity, timeout=host_scan_timeout * 60, print_before_run=True) except ansible_utils.AnsibleProcessException as ex: print( t("An error has occurred during the scan. Please review" + " the output to resolve the given issue: %s" % str(ex))) sys.exit(1) except ansible_utils.AnsibleTimeoutException as ex: utilities.log.warning( 'Scan for group "%s" timed out. Hosts \n' '%s\nwill be skipped. The rest of the scan ' 'is not affected.', group, host_groups[group]) continue if os.path.isfile(variables_path): with open(variables_path, 'r') as variables_file: vars_by_host = {} update_json = json.load(variables_file) for host in hosts: host_facts = update_json.get(host, {}) vars_by_host[host] = host_facts os.remove(variables_path) utilities.log.info('Processing scan data for %d more systems.', len(hosts)) print('\nProcessing scan data for %d more systems.' % (len(hosts))) group_facts = process_host_vars(facts_to_collect, vars_by_host) facts_out += group_facts utilities.log.info('Completed scanning %d systems.', len(facts_out)) print('Completed scanning %d systems.\n' % (len(facts_out))) else: utilities.log.error( 'Error collecting data for group %s.' 'output file %s not found.', group, variables_path) if facts_out == []: print( t("An error has occurred during the scan. " + "No data was collected for any groups. " + "Please review the output to resolve the given issues")) sys.exit(1) write_fact_report(facts_to_collect, facts_out, report_path)