From e131de4de08955a3b36e40d532398c9d46c2d24b Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Mon, 14 Oct 2013 21:01:38 -0400 Subject: [PATCH] Added "debug: var=variableName" capability. --- CHANGELOG.md | 1 + lib/ansible/callbacks.py | 8 ++++---- lib/ansible/runner/action_plugins/debug.py | 18 +++++++++++++----- lib/ansible/utils/__init__.py | 20 +++++++++++++++++--- library/utilities/debug | 10 +++++++++- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cfbab86460..4a1a5286816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Highlighted new features: * The roles search path is now configurable in ansible.cfg. 'roles_path' in the config setting. * Includes with parameters can now be done like roles for consistency: - { include: song.yml, year:1984, song:'jump' } * The name of each role is now shown before each task if roles are being used +* Adds a "var=" option to the debug module for debugging variable data. "debug: var=hostvars['hostname']" and "debug: var=foo" are all valid syntax. New modules and plugins: diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py index 4f94c81e5fb..0ab0db1dd6a 100644 --- a/lib/ansible/callbacks.py +++ b/lib/ansible/callbacks.py @@ -484,7 +484,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): host_result2 = host_result.copy() host_result2.pop('invocation', None) - verbose_always = host_result2.pop('verbose_always', None) + verbose_always = host_result2.pop('verbose_always', False) changed = host_result.get('changed', False) ok_or_changed = 'ok' if changed: @@ -493,7 +493,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): # show verbose output for non-setup module results if --verbose is used msg = '' if (not self.verbose or host_result2.get("verbose_override",None) is not - None) and verbose_always is None: + None) and not verbose_always: if item: msg = "%s: [%s] => (item=%s)" % (ok_or_changed, host, item) else: @@ -502,10 +502,10 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): else: # verbose ... if item: - msg = "%s: [%s] => (item=%s) => %s" % (ok_or_changed, host, item, utils.jsonify(host_result2)) + msg = "%s: [%s] => (item=%s) => %s" % (ok_or_changed, host, item, utils.jsonify(host_result2, format=verbose_always)) else: if 'ansible_job_id' not in host_result or 'finished' in host_result2: - msg = "%s: [%s] => %s" % (ok_or_changed, host, utils.jsonify(host_result2)) + msg = "%s: [%s] => %s" % (ok_or_changed, host, utils.jsonify(host_result2, format=verbose_always)) if msg != '': if not changed: diff --git a/lib/ansible/runner/action_plugins/debug.py b/lib/ansible/runner/action_plugins/debug.py index ac6bfa3d74f..f076b429355 100644 --- a/lib/ansible/runner/action_plugins/debug.py +++ b/lib/ansible/runner/action_plugins/debug.py @@ -38,13 +38,21 @@ class ActionModule(object): kv = utils.parse_kv(module_args) args.update(kv) - if not 'msg' in args: + + if not 'msg' in args and not 'var' in args: args['msg'] = 'Hello world!' - if 'fail' in args and utils.boolean(args['fail']): - result = dict(failed=True, msg=args['msg']) - else: - result = dict(msg=args['msg']) + result = {} + if 'msg' in args: + if 'fail' in args and utils.boolean(args['fail']): + result = dict(failed=True, msg=args['msg']) + else: + result = dict(msg=args['msg']) + elif 'var' in args: + (intermediate, exception) = utils.safe_eval(args['var'], inject, include_exceptions=True, template_call=True) + if exception is not None: + intermediate = "failed to evaluate: %s" % str(exception) + result[args['var']] = intermediate # force flag to make debug output module always verbose result['verbose_always'] = True diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 28c4b4be71a..8a9240192d3 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -896,16 +896,20 @@ def is_list_of_strings(items): return False return True -def safe_eval(str): +def safe_eval(str, locals=None, include_exceptions=False, template_call=False): ''' this is intended for allowing things like: - with_items: {{ a_list_variable }} + with_items: a_list_variable where Jinja2 would return a string but we do not want to allow it to call functions (outside of Jinja2, where the env is constrained) ''' # FIXME: is there a more native way to do this? + if template_call: + # for the debug module in Ansible, allow debug of the form foo.bar.baz versus Python dictionary form + str = template.template(None, "{{ %s }}" % str, locals) + def is_set(var): return not var.startswith("$") and not '{{' in var @@ -922,8 +926,18 @@ def safe_eval(str): if re.search(r'import \w+', str): return str try: - return eval(str) + result = None + if not locals: + result = eval(str) + else: + result = eval(str, None, locals) + if include_exceptions: + return (result, None) + else: + return result except Exception, e: + if include_exceptions: + return (str, e) return str diff --git a/library/utilities/debug b/library/utilities/debug index 9f3378ad192..629b3d85006 100644 --- a/library/utilities/debug +++ b/library/utilities/debug @@ -35,7 +35,10 @@ options: message. required: false default: "Hello world!" -author: Dag Wieers + var: + description: + - A variable name to debug. Mutually exclusive with the 'msg' option. +author: Dag Wieers, Michael DeHaan ''' EXAMPLES = ''' @@ -44,4 +47,9 @@ EXAMPLES = ''' - debug: msg="System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}" when: ansible_default_ipv4.gateway is defined + +- shell: /usr/bin/uptime + register: result +- debug: var=result + '''