... | ... |
@@ -16,6 +16,7 @@ Highlighted new features: |
16 | 16 |
* The roles search path is now configurable in ansible.cfg. 'roles_path' in the config setting. |
17 | 17 |
* Includes with parameters can now be done like roles for consistency: - { include: song.yml, year:1984, song:'jump' } |
18 | 18 |
* The name of each role is now shown before each task if roles are being used |
19 |
+* Adds a "var=" option to the debug module for debugging variable data. "debug: var=hostvars['hostname']" and "debug: var=foo" are all valid syntax. |
|
19 | 20 |
|
20 | 21 |
New modules and plugins: |
21 | 22 |
|
... | ... |
@@ -484,7 +484,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): |
484 | 484 |
|
485 | 485 |
host_result2 = host_result.copy() |
486 | 486 |
host_result2.pop('invocation', None) |
487 |
- verbose_always = host_result2.pop('verbose_always', None) |
|
487 |
+ verbose_always = host_result2.pop('verbose_always', False) |
|
488 | 488 |
changed = host_result.get('changed', False) |
489 | 489 |
ok_or_changed = 'ok' |
490 | 490 |
if changed: |
... | ... |
@@ -493,7 +493,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): |
493 | 493 |
# show verbose output for non-setup module results if --verbose is used |
494 | 494 |
msg = '' |
495 | 495 |
if (not self.verbose or host_result2.get("verbose_override",None) is not |
496 |
- None) and verbose_always is None: |
|
496 |
+ None) and not verbose_always: |
|
497 | 497 |
if item: |
498 | 498 |
msg = "%s: [%s] => (item=%s)" % (ok_or_changed, host, item) |
499 | 499 |
else: |
... | ... |
@@ -502,10 +502,10 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): |
502 | 502 |
else: |
503 | 503 |
# verbose ... |
504 | 504 |
if item: |
505 |
- msg = "%s: [%s] => (item=%s) => %s" % (ok_or_changed, host, item, utils.jsonify(host_result2)) |
|
505 |
+ msg = "%s: [%s] => (item=%s) => %s" % (ok_or_changed, host, item, utils.jsonify(host_result2, format=verbose_always)) |
|
506 | 506 |
else: |
507 | 507 |
if 'ansible_job_id' not in host_result or 'finished' in host_result2: |
508 |
- msg = "%s: [%s] => %s" % (ok_or_changed, host, utils.jsonify(host_result2)) |
|
508 |
+ msg = "%s: [%s] => %s" % (ok_or_changed, host, utils.jsonify(host_result2, format=verbose_always)) |
|
509 | 509 |
|
510 | 510 |
if msg != '': |
511 | 511 |
if not changed: |
... | ... |
@@ -38,13 +38,21 @@ class ActionModule(object): |
38 | 38 |
|
39 | 39 |
kv = utils.parse_kv(module_args) |
40 | 40 |
args.update(kv) |
41 |
- if not 'msg' in args: |
|
41 |
+ |
|
42 |
+ if not 'msg' in args and not 'var' in args: |
|
42 | 43 |
args['msg'] = 'Hello world!' |
43 | 44 |
|
44 |
- if 'fail' in args and utils.boolean(args['fail']): |
|
45 |
- result = dict(failed=True, msg=args['msg']) |
|
46 |
- else: |
|
47 |
- result = dict(msg=args['msg']) |
|
45 |
+ result = {} |
|
46 |
+ if 'msg' in args: |
|
47 |
+ if 'fail' in args and utils.boolean(args['fail']): |
|
48 |
+ result = dict(failed=True, msg=args['msg']) |
|
49 |
+ else: |
|
50 |
+ result = dict(msg=args['msg']) |
|
51 |
+ elif 'var' in args: |
|
52 |
+ (intermediate, exception) = utils.safe_eval(args['var'], inject, include_exceptions=True, template_call=True) |
|
53 |
+ if exception is not None: |
|
54 |
+ intermediate = "failed to evaluate: %s" % str(exception) |
|
55 |
+ result[args['var']] = intermediate |
|
48 | 56 |
|
49 | 57 |
# force flag to make debug output module always verbose |
50 | 58 |
result['verbose_always'] = True |
... | ... |
@@ -896,16 +896,20 @@ def is_list_of_strings(items): |
896 | 896 |
return False |
897 | 897 |
return True |
898 | 898 |
|
899 |
-def safe_eval(str): |
|
899 |
+def safe_eval(str, locals=None, include_exceptions=False, template_call=False): |
|
900 | 900 |
''' |
901 | 901 |
this is intended for allowing things like: |
902 |
- with_items: {{ a_list_variable }} |
|
902 |
+ with_items: a_list_variable |
|
903 | 903 |
where Jinja2 would return a string |
904 | 904 |
but we do not want to allow it to call functions (outside of Jinja2, where |
905 | 905 |
the env is constrained) |
906 | 906 |
''' |
907 | 907 |
# FIXME: is there a more native way to do this? |
908 | 908 |
|
909 |
+ if template_call: |
|
910 |
+ # for the debug module in Ansible, allow debug of the form foo.bar.baz versus Python dictionary form |
|
911 |
+ str = template.template(None, "{{ %s }}" % str, locals) |
|
912 |
+ |
|
909 | 913 |
def is_set(var): |
910 | 914 |
return not var.startswith("$") and not '{{' in var |
911 | 915 |
|
... | ... |
@@ -922,8 +926,18 @@ def safe_eval(str): |
922 | 922 |
if re.search(r'import \w+', str): |
923 | 923 |
return str |
924 | 924 |
try: |
925 |
- return eval(str) |
|
925 |
+ result = None |
|
926 |
+ if not locals: |
|
927 |
+ result = eval(str) |
|
928 |
+ else: |
|
929 |
+ result = eval(str, None, locals) |
|
930 |
+ if include_exceptions: |
|
931 |
+ return (result, None) |
|
932 |
+ else: |
|
933 |
+ return result |
|
926 | 934 |
except Exception, e: |
935 |
+ if include_exceptions: |
|
936 |
+ return (str, e) |
|
927 | 937 |
return str |
928 | 938 |
|
929 | 939 |
|
... | ... |
@@ -35,7 +35,10 @@ options: |
35 | 35 |
message. |
36 | 36 |
required: false |
37 | 37 |
default: "Hello world!" |
38 |
-author: Dag Wieers |
|
38 |
+ var: |
|
39 |
+ description: |
|
40 |
+ - A variable name to debug. Mutually exclusive with the 'msg' option. |
|
41 |
+author: Dag Wieers, Michael DeHaan |
|
39 | 42 |
''' |
40 | 43 |
|
41 | 44 |
EXAMPLES = ''' |
... | ... |
@@ -44,4 +47,9 @@ EXAMPLES = ''' |
44 | 44 |
|
45 | 45 |
- debug: msg="System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}" |
46 | 46 |
when: ansible_default_ipv4.gateway is defined |
47 |
+ |
|
48 |
+- shell: /usr/bin/uptime |
|
49 |
+ register: result |
|
50 |
+- debug: var=result |
|
51 |
+ |
|
47 | 52 |
''' |