diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index fd80d7833e..ba948f18f3 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -624,6 +624,7 @@ class Runner(object): inject['defaults'] = self.default_vars inject['environment'] = self.environment inject['playbook_dir'] = os.path.abspath(self.basedir) + inject['omit'] = OMIT_PLACE_HOLDER # template this one is available, callbacks use this delegate_to = self.module_vars.get('delegate_to') @@ -867,11 +868,8 @@ class Runner(object): # allow module args to work as a dictionary # though it is usually a string - if type(module_args) == dict: - new_args = [] - for (k, v) in module_args.iteritems(): - new_args.append("%s='%s'" % (k, v)) - module_args = ' '.join(new_args) + if isinstance(module_args, dict): + module_args = utils.serialize_args(module_args) # render module_args and complex_args templates try: @@ -893,13 +891,16 @@ class Runner(object): except jinja2.exceptions.UndefinedError, e: raise errors.AnsibleUndefinedVariable("One or more undefined variables: %s" % str(e)) - # filter omitted arguments out - new_complex_args = {} - for key, value in complex_args.iteritems(): - if value == OMIT_PLACE_HOLDER: - continue - new_complex_args[key] = value - complex_args = new_complex_args + def not_omitted(item): + return item[1] != OMIT_PLACE_HOLDER + + if module_name not in ['shell', 'command']: + # filter omitted arguments out from complex_args + complex_args = dict(filter(not_omitted, complex_args.iteritems())) + # filter omitted arguments out from module_args + module_kv = utils.parse_kv(module_args) + module_kv = dict(filter(not_omitted, module_kv.iteritems())) + module_args = utils.serialize_args(module_kv) result = handler.run(conn, tmp, module_name, module_args, inject, complex_args) # Code for do until feature diff --git a/lib/ansible/runner/filter_plugins/core.py b/lib/ansible/runner/filter_plugins/core.py index 1096cf378e..7935cf19b6 100644 --- a/lib/ansible/runner/filter_plugins/core.py +++ b/lib/ansible/runner/filter_plugins/core.py @@ -32,7 +32,6 @@ from ansible.utils import md5s, OMIT_PLACE_HOLDER from distutils.version import LooseVersion, StrictVersion from random import SystemRandom from jinja2.filters import environmentfilter -from jinja2.runtime import Undefined def to_nice_yaml(*a, **kw): @@ -239,12 +238,6 @@ def rand(environment, end, start=None, step=None): raise errors.AnsibleFilterError('random can only be used on sequences and integers') -def default_omit(a): - if isinstance(a, Undefined): - return OMIT_PLACE_HOLDER - return a - - class FilterModule(object): ''' Ansible core jinja2 filters ''' @@ -316,6 +309,4 @@ class FilterModule(object): # random numbers 'random': rand, - - 'default_omit': default_omit, } diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 8030fa6318..7a50606d20 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -787,6 +787,10 @@ def _validate_both_dicts(a, b): "failed to combine variables, expected dicts but got a '%s' and a '%s'" % (type(a).__name__, type(b).__name__) ) +def serialize_args(args): + ''' convert a dict to a string of key/value items ''' + return ' '.join("%s='%s'" % item for item in args.iteritems()) + def merge_hash(a, b): ''' recursively merges hash b into a keys from b take precedence over keys from a ''' diff --git a/test/integration/roles/test_good_parsing/tasks/main.yml b/test/integration/roles/test_good_parsing/tasks/main.yml index 6461d8e772..63895df468 100644 --- a/test/integration/roles/test_good_parsing/tasks/main.yml +++ b/test/integration/roles/test_good_parsing/tasks/main.yml @@ -172,3 +172,32 @@ assert: that: - nested_include_var is undefined + +- name: test omit in complex args + set_fact: + foo: bar + spam: "{{ omit }}" + should_not_omit: "prefix{{ omit }}" + +- assert: + that: + - foo == 'bar' + - spam is undefined + - should_not_omit == "prefix{{ omit }}" + +- name: test omit in module args + set_fact: > + yo=whatsup + eggs="{{ omit }}" + default_omitted="{{ not_exists|default(omit) }}" + should_not_omit_1="prefix{{ omit }}" + should_not_omit_2="{{ omit }}suffix" + +- assert: + that: + - yo == 'whatsup' + - eggs is undefined + - default_omitted is undefined + - should_not_omit_1 == "prefix{{ omit }}" + - should_not_omit_2 == "{{ omit }}suffix" +