Beispiel #1
0
    def _get_delegated_vars(self, play, task, existing_variables):
        # This method has a lot of code copied from ``TaskExecutor._get_loop_items``
        # if this is failing, and ``TaskExecutor._get_loop_items`` is not
        # then more will have to be copied here.
        # TODO: dedupe code here and with ``TaskExecutor._get_loop_items``
        #       this may be possible once we move pre-processing pre fork

        if not hasattr(task, 'loop'):
            # This "task" is not a Task, so we need to skip it
            return {}, None

        # we unfortunately need to template the delegate_to field here,
        # as we're fetching vars before post_validate has been called on
        # the task that has been passed in
        vars_copy = existing_variables.copy()

        # get search path for this task to pass to lookup plugins
        vars_copy['ansible_search_path'] = task.get_search_path()

        # ensure basedir is always in (dwim already searches here but we need to display it)
        if self._loader.get_basedir() not in vars_copy['ansible_search_path']:
            vars_copy['ansible_search_path'].append(self._loader.get_basedir())

        templar = Templar(loader=self._loader, variables=vars_copy)

        items = []
        has_loop = True
        if task.loop_with is not None:
            if task.loop_with in lookup_loader:
                fail = True
                if task.loop_with == 'first_found':
                    # first_found loops are special. If the item is undefined then we want to fall through to the next
                    fail = False
                try:
                    loop_terms = listify_lookup_plugin_terms(
                        terms=task.loop,
                        templar=templar,
                        loader=self._loader,
                        fail_on_undefined=fail,
                        convert_bare=False)

                    if not fail:
                        loop_terms = [
                            t for t in loop_terms if not templar.is_template(t)
                        ]

                    mylookup = lookup_loader.get(task.loop_with,
                                                 loader=self._loader,
                                                 templar=templar)

                    # give lookup task 'context' for subdir (mostly needed for first_found)
                    for subdir in ['template', 'var',
                                   'file']:  # TODO: move this to constants?
                        if subdir in task.action:
                            break
                    setattr(mylookup, '_subdir', subdir + 's')

                    items = wrap_var(
                        mylookup.run(terms=loop_terms, variables=vars_copy))

                except AnsibleTemplateError:
                    # This task will be skipped later due to this, so we just setup
                    # a dummy array for the later code so it doesn't fail
                    items = [None]
            else:
                raise AnsibleError(
                    "Failed to find the lookup named '%s' in the available lookup plugins"
                    % task.loop_with)
        elif task.loop is not None:
            try:
                items = templar.template(task.loop)
            except AnsibleTemplateError:
                # This task will be skipped later due to this, so we just setup
                # a dummy array for the later code so it doesn't fail
                items = [None]
        else:
            has_loop = False
            items = [None]

        # since host can change per loop, we keep dict per host name resolved
        delegated_host_vars = dict()
        item_var = getattr(task.loop_control, 'loop_var', 'item')
        cache_items = False
        for item in items:
            # update the variables with the item value for templating, in case we need it
            if item is not None:
                vars_copy[item_var] = item

            templar.available_variables = vars_copy
            delegated_host_name = templar.template(task.delegate_to,
                                                   fail_on_undefined=False)
            if delegated_host_name != task.delegate_to:
                cache_items = True
            if delegated_host_name is None:
                raise AnsibleError(
                    message="Undefined delegate_to host for task:",
                    obj=task._ds)
            if not isinstance(delegated_host_name, string_types):
                raise AnsibleError(
                    message=
                    "the field 'delegate_to' has an invalid type (%s), and could not be"
                    " converted to a string type." % type(delegated_host_name),
                    obj=task._ds)

            if delegated_host_name in delegated_host_vars:
                # no need to repeat ourselves, as the delegate_to value
                # does not appear to be tied to the loop item variable
                continue

            # now try to find the delegated-to host in inventory, or failing that,
            # create a new host on the fly so we can fetch variables for it
            delegated_host = None
            if self._inventory is not None:
                delegated_host = self._inventory.get_host(delegated_host_name)
                # try looking it up based on the address field, and finally
                # fall back to creating a host on the fly to use for the var lookup
                if delegated_host is None:
                    for h in self._inventory.get_hosts(
                            ignore_limits=True, ignore_restrictions=True):
                        # check if the address matches, or if both the delegated_to host
                        # and the current host are in the list of localhost aliases
                        if h.address == delegated_host_name:
                            delegated_host = h
                            break
                    else:
                        delegated_host = Host(name=delegated_host_name)
            else:
                delegated_host = Host(name=delegated_host_name)

            # now we go fetch the vars for the delegated-to host and save them in our
            # master dictionary of variables to be used later in the TaskExecutor/PlayContext
            delegated_host_vars[delegated_host_name] = self.get_vars(
                play=play,
                host=delegated_host,
                task=task,
                include_delegate_to=False,
                include_hostvars=True,
            )
            delegated_host_vars[delegated_host_name][
                'inventory_hostname'] = vars_copy.get('inventory_hostname')

        _ansible_loop_cache = None
        if has_loop and cache_items:
            # delegate_to templating produced a change, so we will cache the templated items
            # in a special private hostvar
            # this ensures that delegate_to+loop doesn't produce different results than TaskExecutor
            # which may reprocess the loop
            _ansible_loop_cache = items

        return delegated_host_vars, _ansible_loop_cache
def test_gssapi_auth_with_credentials():
    with pytest.raises(AnsibleLookupError) as err:
        lookup_loader.get('laps_password').run(["host"], domain="test", auth="gssapi", username="******", password="******")

    assert str(err.value) == "Explicit credentials are not supported when auth='gssapi'. Call kinit outside of Ansible"
def test_not_encrypted_without_override():
    with pytest.raises(AnsibleLookupError) as err:
        lookup_loader.get('laps_password').run(["host"], domain="dc01", auth="simple", username="******", password="******")

    assert str(err.value) == "Current configuration will result in plaintext traffic exposing credentials. Set " \
                             "auth=gssapi, scheme=ldaps, start_tls=True, or allow_plaintext=True to continue"
def test_invalid_auth():
    with pytest.raises(AnsibleLookupError) as err:
        lookup_loader.get('laps_password').run(["host"], domain="test", auth="fail")

    assert str(err.value) == "Invalid auth value 'fail': expecting either 'gssapi', or 'simple'"
def test_simple_auth_without_credentials():
    with pytest.raises(AnsibleLookupError) as err:
        lookup_loader.get('laps_password').run(["host"], domain="test", auth="simple")

    assert str(err.value) == "The username and password values are required when auth=simple"
Beispiel #6
0
    def _lookup(self, name, *args, **kwargs):
        instance = lookup_loader.get(name, loader=self._loader, templar=self)

        if instance is None:
            raise AnsibleError("lookup plugin (%s) not found" % name)

        wantlist = kwargs.pop('wantlist', False)
        allow_unsafe = kwargs.pop('allow_unsafe',
                                  C.DEFAULT_ALLOW_UNSAFE_LOOKUPS)
        errors = kwargs.pop('errors', 'strict')

        loop_terms = listify_lookup_plugin_terms(terms=args,
                                                 templar=self,
                                                 loader=self._loader,
                                                 fail_on_undefined=True,
                                                 convert_bare=False)
        # safely catch run failures per #5059
        try:
            ran = instance.run(loop_terms,
                               variables=self._available_variables,
                               **kwargs)
        except (AnsibleUndefinedVariable, UndefinedError) as e:
            raise AnsibleUndefinedVariable(e)
        except AnsibleOptionsError as e:
            # invalid options given to lookup, just reraise
            raise e
        except AnsibleLookupError as e:
            # lookup handled error but still decided to bail
            msg = 'Lookup failed but the error is being ignored: %s' % to_native(
                e)
            if errors == 'warn':
                display.warning(msg)
            elif errors == 'ignore':
                display.display(msg, log_only=True)
            else:
                raise e
            return [] if wantlist else None
        except Exception as e:
            # errors not handled by lookup
            msg = u"An unhandled exception occurred while running the lookup plugin '%s'. Error was a %s, original message: %s" % \
                  (name, type(e), to_text(e))
            if errors == 'warn':
                display.warning(msg)
            elif errors == 'ignore':
                display.display(msg, log_only=True)
            else:
                display.vvv('exception during Jinja2 execution: {0}'.format(
                    format_exc()))
                raise AnsibleError(to_native(msg), orig_exc=e)
            return [] if wantlist else None

        if ran and allow_unsafe is False:
            if self.cur_context:
                self.cur_context.unsafe = True

            if wantlist:
                return wrap_var(ran)

            try:
                if isinstance(ran[0], NativeJinjaText):
                    ran = wrap_var(NativeJinjaText(",".join(ran)))
                else:
                    ran = wrap_var(",".join(ran))
            except TypeError:
                # Lookup Plugins should always return lists.  Throw an error if that's not
                # the case:
                if not isinstance(ran, Sequence):
                    raise AnsibleError(
                        "The lookup plugin '%s' did not return a list." % name)

                # The TypeError we can recover from is when the value *inside* of the list
                # is not a string
                if len(ran) == 1:
                    ran = wrap_var(ran[0])
                else:
                    ran = wrap_var(ran)

        return ran
Beispiel #7
0
 def setUp(self):
     self.lookup = lookup_loader.get("community.general.tss")
 def setUp(self):
     self.lookup = lookup_loader.get("conjur_variable")
Beispiel #9
0
def hashi_vault_lookup_module():
    return lookup_loader.get('community.hashi_vault.hashi_vault')
Beispiel #10
0
def vault_login_lookup():
    return lookup_loader.get('community.hashi_vault.vault_login')
Beispiel #11
0
 def setUp(self):
     etcd3.HAS_ETCD = True
     self.lookup = lookup_loader.get('community.general.etcd3')
def test_invalid_lookup(dummy_credentials):
    avi_lookup = lookup_loader.get('community.network.avi')
    avi_mock = MagicMock()
    with pytest.raises(AnsibleError):
        with patch.object(avi, 'ApiSession', avi_mock):
            avi_lookup.run([], {}, avi_credentials=dummy_credentials)
Beispiel #13
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = {}

        result = super().run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        root_key = ""

        if self._task.args:
            if "root_key" in self._task.args:
                n = self._task.args.get("root_key")
                n = self._templar.template(n)
                if not isidentifier(n):
                    raise AnsibleActionFail(
                        f"The argument 'root_key' value of '{n}' is not valid. Keys must start with a letter or underscore character, "
                        "and contain only letters, numbers and underscores.")
                root_key = n

            if "templates" in self._task.args:
                t = self._task.args.get("templates")
                if isinstance(t, list):
                    template_list = t
                else:
                    raise AnsibleActionFail(
                        "The argument 'templates' is not a list")
            else:
                raise AnsibleActionFail("The argument 'templates' must be set")
        else:
            raise AnsibleActionFail("The argument 'templates' must be set")

        output = {}

        template_lookup_module = lookup_loader.get('ansible.builtin.template',
                                                   loader=self._loader,
                                                   templar=self._templar)

        template_vars = task_vars

        for template_item in template_list:
            template = template_item.get('template')
            if not template:
                raise AnsibleActionFail("Invalid template data")

            template_options = template_item.get('options', {})
            list_merge = template_options.get('list_merge', 'append')
            strip_empty_keys = template_options.get('strip_empty_keys', True)

            if root_key:
                template_vars[root_key] = output
            else:
                template_vars = combine(task_vars, output, recursive=True)

            template_output = template_lookup_module.run([template],
                                                         template_vars)
            template_output_data = yaml.safe_load(template_output[0])
            if strip_empty_keys:
                template_output_data = strip_null_from_data(
                    template_output_data)
            if template_output_data:
                output = combine(output,
                                 template_output_data,
                                 recursive=True,
                                 list_merge=list_merge)

        if root_key:
            result['ansible_facts'] = {root_key: output}
        else:
            result['ansible_facts'] = output
        return result
Beispiel #14
0
 def setUp(self):
     dsv.sdk_is_missing = False
     self.lookup = lookup_loader.get("community.general.dsv")
 def run_rules(self):
     """ run the plugin for rules """
     pfsense_lookup = lookup_loader.get('pfsensible.core.pfsense')
     self.rules = pfsense_lookup.run(['dummy.yml', 'rules'], {})[0]
Beispiel #16
0
    def _get_delegated_vars(self, play, task, existing_variables):
        if not hasattr(task, 'loop'):
            # This "task" is not a Task, so we need to skip it
            return {}, None

        # we unfortunately need to template the delegate_to field here,
        # as we're fetching vars before post_validate has been called on
        # the task that has been passed in
        vars_copy = existing_variables.copy()
        templar = Templar(loader=self._loader, variables=vars_copy)

        items = []
        has_loop = True
        if task.loop_with is not None:
            if task.loop_with in lookup_loader:
                try:
                    loop_terms = listify_lookup_plugin_terms(terms=task.loop, templar=templar,
                                                             loader=self._loader, fail_on_undefined=True, convert_bare=False)
                    items = wrap_var(lookup_loader.get(task.loop_with, loader=self._loader, templar=templar).run(terms=loop_terms, variables=vars_copy))
                except AnsibleTemplateError:
                    # This task will be skipped later due to this, so we just setup
                    # a dummy array for the later code so it doesn't fail
                    items = [None]
            else:
                raise AnsibleError("Failed to find the lookup named '%s' in the available lookup plugins" % task.loop_with)
        elif task.loop is not None:
            try:
                items = templar.template(task.loop)
            except AnsibleTemplateError:
                # This task will be skipped later due to this, so we just setup
                # a dummy array for the later code so it doesn't fail
                items = [None]
        else:
            has_loop = False
            items = [None]

        delegated_host_vars = dict()
        item_var = getattr(task.loop_control, 'loop_var', 'item')
        cache_items = False
        for item in items:
            # update the variables with the item value for templating, in case we need it
            if item is not None:
                vars_copy[item_var] = item

            templar.available_variables = vars_copy
            delegated_host_name = templar.template(task.delegate_to, fail_on_undefined=False)
            if delegated_host_name != task.delegate_to:
                cache_items = True
            if delegated_host_name is None:
                raise AnsibleError(message="Undefined delegate_to host for task:", obj=task._ds)
            if not isinstance(delegated_host_name, string_types):
                raise AnsibleError(message="the field 'delegate_to' has an invalid type (%s), and could not be"
                                           " converted to a string type." % type(delegated_host_name),
                                   obj=task._ds)
            if delegated_host_name in delegated_host_vars:
                # no need to repeat ourselves, as the delegate_to value
                # does not appear to be tied to the loop item variable
                continue

            # a dictionary of variables to use if we have to create a new host below
            # we set the default port based on the default transport here, to make sure
            # we use the proper default for windows
            new_port = C.DEFAULT_REMOTE_PORT
            if C.DEFAULT_TRANSPORT == 'winrm':
                new_port = 5986

            new_delegated_host_vars = dict(
                ansible_delegated_host=delegated_host_name,
                ansible_host=delegated_host_name,  # not redundant as other sources can change ansible_host
                ansible_port=new_port,
                ansible_user=C.DEFAULT_REMOTE_USER,
                ansible_connection=C.DEFAULT_TRANSPORT,
            )

            # now try to find the delegated-to host in inventory, or failing that,
            # create a new host on the fly so we can fetch variables for it
            delegated_host = None
            if self._inventory is not None:
                delegated_host = self._inventory.get_host(delegated_host_name)
                # try looking it up based on the address field, and finally
                # fall back to creating a host on the fly to use for the var lookup
                if delegated_host is None:
                    if delegated_host_name in C.LOCALHOST:
                        delegated_host = self._inventory.localhost
                    else:
                        for h in self._inventory.get_hosts(ignore_limits=True, ignore_restrictions=True):
                            # check if the address matches, or if both the delegated_to host
                            # and the current host are in the list of localhost aliases
                            if h.address == delegated_host_name:
                                delegated_host = h
                                break
                        else:
                            delegated_host = Host(name=delegated_host_name)
                            delegated_host.vars = combine_vars(delegated_host.vars, new_delegated_host_vars)
            else:
                delegated_host = Host(name=delegated_host_name)
                delegated_host.vars = combine_vars(delegated_host.vars, new_delegated_host_vars)

            # now we go fetch the vars for the delegated-to host and save them in our
            # master dictionary of variables to be used later in the TaskExecutor/PlayContext
            delegated_host_vars[delegated_host_name] = self.get_vars(
                play=play,
                host=delegated_host,
                task=task,
                include_delegate_to=False,
                include_hostvars=False,
            )

        _ansible_loop_cache = None
        if has_loop and cache_items:
            # delegate_to templating produced a change, so we will cache the templated items
            # in a special private hostvar
            # this ensures that delegate_to+loop doesn't produce different results than TaskExecutor
            # which may reprocess the loop
            _ansible_loop_cache = items

        return delegated_host_vars, _ansible_loop_cache
    def _get_delegated_vars(self, play, task, existing_variables):
        # we unfortunately need to template the delegate_to field here,
        # as we're fetching vars before post_validate has been called on
        # the task that has been passed in
        vars_copy = existing_variables.copy()
        templar = Templar(loader=self._loader, variables=vars_copy)

        items = []
        if task.loop is not None:
            if task.loop in lookup_loader:
                try:
                    loop_terms = listify_lookup_plugin_terms(
                        terms=task.loop_args,
                        templar=templar,
                        loader=self._loader,
                        fail_on_undefined=True,
                        convert_bare=False)
                    items = lookup_loader.get(task.loop,
                                              loader=self._loader,
                                              templar=templar).run(
                                                  terms=loop_terms,
                                                  variables=vars_copy)
                except AnsibleUndefinedVariable:
                    # This task will be skipped later due to this, so we just setup
                    # a dummy array for the later code so it doesn't fail
                    items = [None]
            else:
                raise AnsibleError(
                    "Unexpected failure in finding the lookup named '%s' in the available lookup plugins"
                    % task.loop)
        else:
            items = [None]

        delegated_host_vars = dict()
        for item in items:
            # update the variables with the item value for templating, in case we need it
            if item is not None:
                vars_copy['item'] = item

            templar.set_available_variables(vars_copy)
            delegated_host_name = templar.template(task.delegate_to,
                                                   fail_on_undefined=False)
            if delegated_host_name is None:
                raise AnsibleError(
                    message="Undefined delegate_to host for task:",
                    obj=task._ds)
            if delegated_host_name in delegated_host_vars:
                # no need to repeat ourselves, as the delegate_to value
                # does not appear to be tied to the loop item variable
                continue

            # a dictionary of variables to use if we have to create a new host below
            # we set the default port based on the default transport here, to make sure
            # we use the proper default for windows
            new_port = C.DEFAULT_REMOTE_PORT
            if C.DEFAULT_TRANSPORT == 'winrm':
                new_port = 5986

            new_delegated_host_vars = dict(
                ansible_delegated_host=delegated_host_name,
                ansible_host=
                delegated_host_name,  # not redundant as other sources can change ansible_host
                ansible_port=new_port,
                ansible_user=C.DEFAULT_REMOTE_USER,
                ansible_connection=C.DEFAULT_TRANSPORT,
            )

            # now try to find the delegated-to host in inventory, or failing that,
            # create a new host on the fly so we can fetch variables for it
            delegated_host = None
            if self._inventory is not None:
                delegated_host = self._inventory.get_host(delegated_host_name)
                # try looking it up based on the address field, and finally
                # fall back to creating a host on the fly to use for the var lookup
                if delegated_host is None:
                    if delegated_host_name in C.LOCALHOST:
                        delegated_host = self._inventory.localhost
                    else:
                        for h in self._inventory.get_hosts(
                                ignore_limits=True, ignore_restrictions=True):
                            # check if the address matches, or if both the delegated_to host
                            # and the current host are in the list of localhost aliases
                            if h.address == delegated_host_name:
                                delegated_host = h
                                break
                        else:
                            delegated_host = Host(name=delegated_host_name)
                            delegated_host.vars = combine_vars(
                                delegated_host.vars, new_delegated_host_vars)
            else:
                delegated_host = Host(name=delegated_host_name)
                delegated_host.vars = combine_vars(delegated_host.vars,
                                                   new_delegated_host_vars)

            # now we go fetch the vars for the delegated-to host and save them in our
            # master dictionary of variables to be used later in the TaskExecutor/PlayContext
            delegated_host_vars[delegated_host_name] = self.get_vars(
                play=play,
                host=delegated_host,
                task=task,
                include_delegate_to=False,
                include_hostvars=False,
            )
        return delegated_host_vars
def test_invalid_cert_mapping():
    with pytest.raises(AnsibleLookupError) as err:
        lookup_loader.get('laps_password').run(["host"], domain="test", validate_certs="incorrect")

    assert str(err.value) == "Invalid validate_certs value 'incorrect': valid values are 'allow', 'demand', " \
                             "'never', 'try'"
Beispiel #19
0
    def _get_delegated_vars(self, play, task, existing_variables):
        # we unfortunately need to template the delegate_to field here,
        # as we're fetching vars before post_validate has been called on
        # the task that has been passed in
        vars_copy = existing_variables.copy()
        templar = Templar(loader=self._loader, variables=vars_copy)

        items = []
        if task.loop is not None:
            if task.loop in lookup_loader:
                try:
                    loop_terms = listify_lookup_plugin_terms(terms=task.loop_args, templar=templar,
                                                             loader=self._loader, fail_on_undefined=True, convert_bare=False)
                    items = lookup_loader.get(task.loop, loader=self._loader, templar=templar).run(terms=loop_terms, variables=vars_copy)
                except AnsibleUndefinedVariable:
                    # This task will be skipped later due to this, so we just setup
                    # a dummy array for the later code so it doesn't fail
                    items = [None]
            else:
                raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % task.loop)
        else:
            items = [None]

        delegated_host_vars = dict()
        for item in items:
            # update the variables with the item value for templating, in case we need it
            if item is not None:
                vars_copy['item'] = item

            templar.set_available_variables(vars_copy)
            delegated_host_name = templar.template(task.delegate_to, fail_on_undefined=False)
            if delegated_host_name is None:
                raise AnsibleError(message="Undefined delegate_to host for task:", obj=task._ds)
            if delegated_host_name in delegated_host_vars:
                # no need to repeat ourselves, as the delegate_to value
                # does not appear to be tied to the loop item variable
                continue

            # a dictionary of variables to use if we have to create a new host below
            # we set the default port based on the default transport here, to make sure
            # we use the proper default for windows
            new_port = C.DEFAULT_REMOTE_PORT
            if C.DEFAULT_TRANSPORT == 'winrm':
                new_port = 5986

            new_delegated_host_vars = dict(
                ansible_delegated_host=delegated_host_name,
                ansible_host=delegated_host_name,  # not redundant as other sources can change ansible_host
                ansible_port=new_port,
                ansible_user=C.DEFAULT_REMOTE_USER,
                ansible_connection=C.DEFAULT_TRANSPORT,
            )

            # now try to find the delegated-to host in inventory, or failing that,
            # create a new host on the fly so we can fetch variables for it
            delegated_host = None
            if self._inventory is not None:
                delegated_host = self._inventory.get_host(delegated_host_name)
                # try looking it up based on the address field, and finally
                # fall back to creating a host on the fly to use for the var lookup
                if delegated_host is None:
                    if delegated_host_name in C.LOCALHOST:
                        delegated_host = self._inventory.localhost
                    else:
                        for h in self._inventory.get_hosts(ignore_limits=True, ignore_restrictions=True):
                            # check if the address matches, or if both the delegated_to host
                            # and the current host are in the list of localhost aliases
                            if h.address == delegated_host_name:
                                delegated_host = h
                                break
                        else:
                            delegated_host = Host(name=delegated_host_name)
                            delegated_host.vars = combine_vars(delegated_host.vars, new_delegated_host_vars)
            else:
                delegated_host = Host(name=delegated_host_name)
                delegated_host.vars = combine_vars(delegated_host.vars, new_delegated_host_vars)

            # now we go fetch the vars for the delegated-to host and save them in our
            # master dictionary of variables to be used later in the TaskExecutor/PlayContext
            delegated_host_vars[delegated_host_name] = self.get_vars(
                play=play,
                host=delegated_host,
                task=task,
                include_delegate_to=False,
                include_hostvars=False,
            )
        return delegated_host_vars
Beispiel #20
0
    def _lookup(self, name, *args, **kwargs):
        instance = lookup_loader.get(name, loader=self._loader, templar=self)

        if instance is not None:
            wantlist = kwargs.pop('wantlist', False)
            allow_unsafe = kwargs.pop('allow_unsafe',
                                      C.DEFAULT_ALLOW_UNSAFE_LOOKUPS)
            errors = kwargs.pop('errors', 'strict')

            from ansible.utils.listify import listify_lookup_plugin_terms
            loop_terms = listify_lookup_plugin_terms(terms=args,
                                                     templar=self,
                                                     loader=self._loader,
                                                     fail_on_undefined=True,
                                                     convert_bare=False)
            # safely catch run failures per #5059
            try:
                ran = instance.run(loop_terms,
                                   variables=self._available_variables,
                                   **kwargs)
            except (AnsibleUndefinedVariable, UndefinedError) as e:
                raise AnsibleUndefinedVariable(e)
            except Exception as e:
                if self._fail_on_lookup_errors:
                    msg = u"An unhandled exception occurred while running the lookup plugin '%s'. Error was a %s, original message: %s" % \
                          (name, type(e), to_text(e))
                    if errors == 'warn':
                        display.warning(msg)
                    elif errors == 'ignore':
                        display.display(msg, log_only=True)
                    else:
                        raise AnsibleError(to_native(msg))
                ran = [] if wantlist else None

            if ran and not allow_unsafe:
                if wantlist:
                    ran = wrap_var(ran)
                else:
                    try:
                        if self.jinja2_native and isinstance(
                                ran[0], NativeJinjaText):
                            ran = wrap_var(NativeJinjaText(",".join(ran)))
                        else:
                            ran = wrap_var(",".join(ran))
                    except TypeError:
                        # Lookup Plugins should always return lists.  Throw an error if that's not
                        # the case:
                        if not isinstance(ran, Sequence):
                            raise AnsibleError(
                                "The lookup plugin '%s' did not return a list."
                                % name)

                        # The TypeError we can recover from is when the value *inside* of the list
                        # is not a string
                        if len(ran) == 1:
                            ran = wrap_var(ran[0])
                        else:
                            ran = wrap_var(ran)

                if self.cur_context:
                    self.cur_context.unsafe = True
            return ran
        else:
            raise AnsibleError("lookup plugin (%s) not found" % name)