class InventoryWrapper:
    def __init__(self, host_list):
        self.loader = DataLoader()
        # this code is a bit ugly, because Ansible 2.4 switched the order
        # of the object and the pattern (InventoryManager is a argument to
        # VariableManager, and vice-versa on version <2.3)
        if ANSIBLE_24:
            self.im = InventoryManager(loader=self.loader,
                                       sources=[host_list, ])
            self.vm = VariableManager(loader=self.loader, inventory=self.im)
        else:
            self.vm = VariableManager()
            self.im = Inventory(self.loader, self.vm, host_list)

    def get_loader(self):
        return self.loader

    def get_variable_manager(self):
        return VariableManagerWrapper(self.vm)

    def get_groups(self):
        if ANSIBLE_24:
            return self.im.groups
        return self.im.get_groups()

    def get_hosts(self, group):
        if ANSIBLE_24:
            return self.im.get_hosts(pattern=group)
        return self.im.get_hosts(group)

    def get_host(self, host):
        return self.im.get_host(host)

    def refresh_inventory(self):
        return self.im.refresh_inventory()
Example #2
0
def find_host_in_inventory(base, host_name):
  loader = DataLoader()
  var_manager = VariableManager()
  host_vars = None
  host_info = []

  for i in get_inventory_files(base):
    try:
      inv = Inventory(host_list='%s/%s' %(base, i), loader=loader, variable_manager=var_manager)
      host_vars = inv.get_vars(host_name)
    except AnsibleError as err:
#      print '%s in inventory %s' %(err, i)
      inv.host_list = None
      inv.refresh_inventory()
      continue

  if host_vars:
    ssh_host = host_vars.get('ansible_ssh_host', None)
    ssh_user = host_vars.get('ansible_ssh_user', None)
    ssh_key  = host_vars.get('ansible_ssh_private_key_file', None)
    ssh_port = host_vars.get('ansible_ssh_port', 22)
  else:
    raise Exception('Host %s is not present in any inventory %s' %(host_name, ' '.join(get_inventory_files(base))))

  if not ssh_host or not ssh_user:
    raise Exception('ansible_ssh_host and ansible_ssh_user required.')

  host_info.append(str('%s@%s' %(ssh_user, ssh_host)))

  if ssh_key:
    ssh_key = os.path.expanduser(ssh_key)

    if not os.path.isabs(ssh_key):
      ssh_key = os.path.abspath(ssh_key)

    if os.path.exists(ssh_key):
      host_info += ['-i', str(ssh_key)]
    else:
      print ("SSH key %s doesn't exists please check path." %(ssh_key))

  if ssh_port:
    if not int(ssh_port) == min(max(int(ssh_port), 0), 65535):
      raise Exception('Incorrect port number given')
    host_info += ['-p', str(ssh_port)]

  return host_info
Example #3
0
def extract_list_hosts_git(revision, path):
    """ Extract the hosts list from git, after deduplciating it
        and resolving variables """
    result = []
    if revision == '0000000000000000000000000000000000000000':
        return result
    try:
        host_content = subprocess.check_output(
            ['git', 'show', '%s:hosts' % revision], cwd=path)
    # usually, this is done when we can't check the list of hosts
    except subprocess.CalledProcessError:
        return result

    # beware, not portable on windows
    tmp_dir = tempfile.mkdtemp()
    tmp_file = tempfile.NamedTemporaryFile('w+', dir=tmp_dir)
    tmp_file.write(host_content)
    tmp_file.flush()
    os.fsync(tmp_file.fileno())

    variable_manager = VariableManager()
    loader = DataLoader()

    inventory = Inventory(loader=loader,
                          variable_manager=variable_manager,
                          host_list=tmp_file.name)
    for group in inventory.get_groups():
        for host in inventory.get_hosts(group):
            vars_host = variable_manager.get_vars(loader, host=host)
            result.append({
                'name':
                vars_host.get('ansible_ssh_host', host.name),
                'ssh_args':
                vars_host.get('ansible_ssh_common_args', ''),
                'connection':
                vars_host.get('ansible_connection', 'ssh')
            })

    # for some reason, there is some kind of global cache that need to be
    # cleaned
    inventory.refresh_inventory()
    shutil.rmtree(tmp_dir)

    return result
Example #4
0
    def default(self):
        verbose = self.app.pargs.verbose

        init(autoreset=True)

        if len(self.app.pargs.extra_arguments) == 0:
            self.app.args.print_help()
            print(Fore.RED + 'Error! You must specify a playbook file to run')
            sys.exit(1)

        if str(self.app.pargs.become_method).upper() not in [
                'SUDO', 'SU', 'PBRUN', 'PFEXEC', 'DOAS', 'DZDO', 'KSU', 'RUNAS'
        ]:
            print(
                Fore.RED +
                'Error! Become method must be one of sudo, su, pbrun, pfexec, doas, dzdo, ksu or runas.'
            )
            sys.exit(1)

        #if len(self.app.pargs.extra_arguments) > 0:

        playbook_path = self.app.pargs.extra_arguments[0] if len(
            self.app.pargs.extra_arguments) > 0 else "site.yml"

        if not os.path.isfile(playbook_path):
            print(Fore.RED + 'Error! The playbook file does not exist')
            sys.exit(1)

        inventory_path = self.app.pargs.inventory

        if not os.path.isfile(inventory_path):
            print(Fore.RED + 'Error! The inventory file does not exist.')
            sys.exit(1)

        # Most of the code from here down is straight copy & paste from ansible source code,
        # with a few tweaks/hacks to clean up variables that we don't want to emit in the generated
        # YAML.
        loader = DataLoader()

        pb_cli = PlaybookCLI(sys.argv[1:])
        pb_cli.parse()

        # !!!!! WARNING: THESE WILL BE INCLUDED IN THE GENERATED YAML FILE !!!!!
        (ssh_pass, become_pass) = pb_cli.ask_passwords()
        passwords = {'conn_pass': ssh_pass, 'become_pass': become_pass}

        vault_pass = None

        if self.app.pargs.ask_vault_pass:
            vault_pass = pb_cli.ask_vault_passwords()
        else:
            vault_pass = pb_cli.read_vault_password_file(
                self.app.pargs.vault_password_file, loader)

        if vault_pass is not None:
            loader.set_vault_password(vault_pass)

        # create the inventory, and filter it based on the subset specified (if any)
        host_list = self.app.pargs.inventory if self.app.pargs.inventory else constants.DEFAULT_HOST_LIST

        # FIXME: This should parse any arguments provided via -e or --extra-vars. Currently it seems
        # to parse the argument value wrongly and returns
        # '_raw_params': '<last character in variable>'. This prevents the ability to use
        # --extra-vars when executing plays.
        variable_manager = VariableManager()

        options = Options(
            verbosity=self.app.pargs.verbose,
            inventory=self.app.pargs.inventory,
            subset=self.app.pargs.limit,
            extra_vars=[self.app.pargs.extra_vars],
            forks=self.app.pargs.forks,
            ask_vault_pass=self.app.pargs.ask_vault_pass,
            vault_password_files=self.app.pargs.vault_password_file,
            tags=self.app.pargs.tags,
            skip_tags=self.app.pargs.skip_tags,
            become=self.app.pargs.become,
            become_method=self.app.pargs.become_method,
            become_user=self.app.pargs.become_user,
            become_ask_pass=self.app.pargs.ask_become_pass,
            ask_pass=self.app.pargs.ask_pass,
            private_key_file=self.app.pargs.private_key,
            remote_user=self.app.pargs.user,
            connection=self.app.pargs.connection,
            timeout=self.app.pargs.timeout,
            ssh_common_args=self.app.pargs.ssh_common_args,
            sftp_extra_args=self.app.pargs.sftp_extra_args,
            ssh_extra_args=self.app.pargs.ssh_extra_args,
            scp_extra_args=self.app.pargs.scp_extra_args,
            flush_cache=self.app.pargs.flush_cache,
            module_path=self.app.pargs.module_path)

        inventory = Inventory(loader=loader,
                              variable_manager=variable_manager,
                              host_list=host_list)

        variable_manager.extra_vars = load_extra_vars(loader=loader,
                                                      options=options)
        #variable_manager.options_vars = load_options_vars(options)

        if self.app.pargs.flush_cache:
            inventory.refresh_inventory()

        no_hosts = False

        if len(inventory.list_hosts()) == 0:
            print(
                Fore.YELLOW +
                "Warning! Provided hosts list is empty, only localhost is available."
            )
            no_hosts = True

        # FIXME: Limit is currently ignored.
        inventory.subset(self.app.pargs.limit)

        if len(inventory.list_hosts()) == 0 and no_hosts is False:
            # Invalid limit
            print(Fore.RED +
                  "Error! Specified --limit does not match any hosts.")
            sys.exit(1)

        play_data = loader.load_from_file(playbook_path)

        host_list = inventory.get_hosts(
            pattern=self.app.pargs.limit
        ) if self.app.pargs.limit else inventory.get_hosts()

        playbook = Playbook(loader=loader).load(
            playbook_path, variable_manager=variable_manager, loader=loader)

        pbex = PlaybookExecutor(playbooks=playbook.get_plays(),
                                inventory=inventory,
                                variable_manager=variable_manager,
                                loader=loader,
                                options=options,
                                passwords=passwords)

        # Ansible variables matching these strings will be excluded from the generated yaml content.
        ignored_elements = [
            'ansible_playbook_python', 'ansible_version', 'group_names',
            'inventory_hostname', 'inventory_hostname_short', 'omit',
            'playbook_dir', 'role_names'
        ]

        json_data = {}

        for host in host_list:
            host_vars = host.get_vars()
            host_name = host_vars[
                'ansible_host'] if 'ansible_host' in host_vars.keys(
                ) else host.get_name()

            for play in playbook.get_plays():
                loader.set_vault_password(vault_pass)
                all_vars = variable_manager.get_vars(loader=loader,
                                                     play=play,
                                                     host=host)

                json_data[host_name] = all_vars['vars']

                json_data[host_name]['ansible_groups'] = all_vars[
                    'group_names']
                json_data[host_name]['ansible_roles'] = all_vars['role_names']

                for elem in ignored_elements:
                    del json_data['{}'.format(host)][elem]

                json_data[host_name][
                    'ansible_become_user'] = self.app.pargs.become_user

                if passwords['become_pass'] is not None:
                    json_data[host_name]['ansible_become_pass'] = passwords[
                        'become_pass']

                json_data[host_name]['ansible_become'] = self.app.pargs.become
                json_data[host_name][
                    'ansible_become_method'] = self.app.pargs.become_method

                if self.app.pargs.ssh_extra_args:
                    json_data[host_name][
                        'ansible_ssh_extra_args'] = self.app.pargs.ssh_extra_args

                if self.app.pargs.scp_extra_args:
                    json_data[host_name][
                        'ansible_scp_extra_args'] = self.app.pargs.scp_extra_args

                if self.app.pargs.sftp_extra_args:
                    json_data[host_name][
                        'ansible_sftp_extra_args'] = self.app.pargs.sftp_extra_args

                if self.app.pargs.ssh_common_args:
                    json_data[host_name][
                        'ansible_ssh_common_args'] = self.app.pargs.ssh_common_args

                json_data[host_name][
                    'ansible_connection_timeout'] = self.app.pargs.timeout
                json_data[host_name][
                    'ansible_connection_method'] = self.app.pargs.connection

                if self.app.pargs.private_key:
                    json_data[host_name][
                        'ansible_private_key'] = self.app.pargs.private_key

                json_data[host_name][
                    'ansible_ask_pass'] = self.app.pargs.ask_pass

                if self.app.pargs.limit:
                    json_data[host_name][
                        'ansible_limit'] = self.app.pargs.limit

                # FIXME: Extra vars needs to be processed by Ansible instead of dumping them
                # here as a "dumb" string.
                if self.app.pargs.extra_vars:
                    json_data[host_name][
                        'ansible_extra_vars'] = self.app.pargs.extra_vars

            for key, value in json_data[host_name].iteritems():
                if type(value) is AnsibleVaultEncryptedUnicode:
                    json_data[host_name][key] = str(value)

        # Convert the processed python dictionary to a valid json string
        json_obj = json.dumps(json_data)

        # Take the json string, and convert it to a valid yaml string
        yml = YAML(typ="safe", pure=True)
        yml.default_flow_style = False

        yml_obj = yml.load(json_obj)

        if self.app.pargs.output is None:
            yml.dump(yml_obj, sys.stdout)
        else:
            with open(self.app.pargs.output, 'w') as outfile:
                yml.dump(yml_obj, outfile)

            print(Fore.GREEN +
                  "Ansible variable dictionary written to {}".format(
                      self.app.pargs.output))

        sys.exit(1)