From c04d5ba739392d2b7db8bb665ac4acac75565c14 Mon Sep 17 00:00:00 2001 From: Daniel Hokka Zakrisson Date: Thu, 28 Feb 2013 12:09:29 +0100 Subject: [PATCH 1/3] Allow specifying args directly to actions using module: syntax Makes things like - name: do complex things with complex module complex: setting_a: true setting_b: - foo - bar possible. Fixes #2228. --- lib/ansible/playbook/task.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 9150d5683f..7b0d5460af 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -47,6 +47,13 @@ class Task(object): if x in utils.plugins.module_finder: if 'action' in ds: raise errors.AnsibleError("multiple actions specified in task %s" % (ds.get('name', ds['action']))) + if isinstance(ds[x], dict): + if 'args' in ds: + raise errors.AnsibleError("can't combine args: and a dict for %s: in task %s" % (x, ds.get('name', "%s: %s" % (x, ds[x])))) + ds['args'] = ds[x] + ds[x] = '' + elif ds[x] is None: + ds[x] = '' if not isinstance(ds[x], basestring): raise errors.AnsibleError("action specified for task %s has invalid type %s" % (ds.get('name', "%s: %s" % (x, ds[x])), type(ds[x]))) ds['action'] = x + " " + ds[x] From 297259725a1cf9ad6f152c73e9ae03c0058463be Mon Sep 17 00:00:00 2001 From: Daniel Hokka Zakrisson Date: Thu, 28 Feb 2013 14:12:36 +0100 Subject: [PATCH 2/3] Allow (local_)action: to be a dict Use the key module: to set which module to invoke. --- lib/ansible/playbook/task.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 7b0d5460af..a74971e673 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -115,6 +115,14 @@ class Task(object): self.delegate_to = ds.get('delegate_to', None) self.transport = ds.get('connection', ds.get('transport', play.transport)) + if isinstance(self.action, dict): + if 'module' not in self.action: + raise errors.AnsibleError("'module' attribute missing from action in task \"%s\"" % ds.get('name', '%s' % self.action)) + if self.args: + raise errors.AnsibleError("'args' cannot be combined with dict 'action' in task \"%s\"" % ds.get('name', '%s' % self.action)) + self.args = self.action + self.action = self.args.pop('module') + # delegate_to can use variables if not (self.delegate_to is None): # delegate_to: localhost should use local transport From ae8d6ac303e9dc2e8d62543c76f760a4ae7cc97e Mon Sep 17 00:00:00 2001 From: Daniel Hokka Zakrisson Date: Thu, 28 Feb 2013 14:27:42 +0100 Subject: [PATCH 3/3] Ensure complex_args is considered in all action_plugins --- lib/ansible/module_common.py | 3 ++- lib/ansible/runner/action_plugins/add_host.py | 5 ++++- lib/ansible/runner/action_plugins/async.py | 2 +- lib/ansible/runner/action_plugins/copy.py | 9 ++++++--- lib/ansible/runner/action_plugins/debug.py | 5 ++++- lib/ansible/runner/action_plugins/fail.py | 5 ++++- lib/ansible/runner/action_plugins/fetch.py | 5 ++++- lib/ansible/runner/action_plugins/group_by.py | 5 ++++- lib/ansible/runner/action_plugins/pause.py | 5 ++++- lib/ansible/runner/action_plugins/template.py | 9 ++++++--- 10 files changed, 39 insertions(+), 14 deletions(-) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index 82b0b5371d..635c87925f 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -379,7 +379,8 @@ class AnsibleModule(object): return changed try: # FIXME: support English modes - mode = int(mode, 8) + if not isinstance(mode, int): + mode = int(mode, 8) except Exception, e: self.fail_json(path=path, msg='mode needs to be something octalish', details=str(e)) diff --git a/lib/ansible/runner/action_plugins/add_host.py b/lib/ansible/runner/action_plugins/add_host.py index a599500061..23d5e22366 100644 --- a/lib/ansible/runner/action_plugins/add_host.py +++ b/lib/ansible/runner/action_plugins/add_host.py @@ -39,7 +39,10 @@ class ActionModule(object): if self.runner.check: return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not supported for this module')) - args = parse_kv(module_args) + args = {} + if complex_args: + args.update(complex_args) + args.update(parse_kv(module_args)) if not 'hostname' in args and not 'name' in args: raise ae("'name' is a required argument.") diff --git a/lib/ansible/runner/action_plugins/async.py b/lib/ansible/runner/action_plugins/async.py index 72266d3a1f..dcee36684d 100644 --- a/lib/ansible/runner/action_plugins/async.py +++ b/lib/ansible/runner/action_plugins/async.py @@ -33,7 +33,7 @@ class ActionModule(object): module_name = 'command' module_args += " #USE_SHELL" - (module_path, is_new_style, shebang) = self.runner._copy_module(conn, tmp, module_name, module_args, inject) + (module_path, is_new_style, shebang) = self.runner._copy_module(conn, tmp, module_name, module_args, inject, complex_args=complex_args) self.runner._low_level_exec_command(conn, "chmod a+rx %s" % module_path, tmp) return self.runner._execute_module(conn, tmp, 'async_wrapper', module_args, diff --git a/lib/ansible/runner/action_plugins/copy.py b/lib/ansible/runner/action_plugins/copy.py index d7cd001c7d..60e29ff885 100644 --- a/lib/ansible/runner/action_plugins/copy.py +++ b/lib/ansible/runner/action_plugins/copy.py @@ -32,7 +32,10 @@ class ActionModule(object): ''' handler for file transfer operations ''' # load up options - options = utils.parse_kv(module_args) + options = {} + if complex_args: + options.update(complex_args) + options.update(utils.parse_kv(module_args)) source = options.get('src', None) dest = options.get('dest', None) @@ -93,7 +96,7 @@ class ActionModule(object): # run the copy module module_args = "%s src=%s" % (module_args, tmp_src) - return self.runner._execute_module(conn, tmp, 'copy', module_args, inject=inject) + return self.runner._execute_module(conn, tmp, 'copy', module_args, inject=inject, complex_args=complex_args) else: # no need to transfer the file, already correct md5, but still need to call @@ -103,7 +106,7 @@ class ActionModule(object): module_args = "%s src=%s" % (module_args, tmp_src) if self.runner.check: module_args = "%s CHECKMODE=True" % module_args - return self.runner._execute_module(conn, tmp, 'file', module_args, inject=inject) + return self.runner._execute_module(conn, tmp, 'file', module_args, inject=inject, complex_args=complex_args) def _get_diff_data(self, conn, tmp, inject, destination, source): peek_result = self.runner._execute_module(conn, tmp, 'file', "path=%s diff_peek=1" % destination, inject=inject, persist_files=True) diff --git a/lib/ansible/runner/action_plugins/debug.py b/lib/ansible/runner/action_plugins/debug.py index 2b1c50e385..61ed449624 100644 --- a/lib/ansible/runner/action_plugins/debug.py +++ b/lib/ansible/runner/action_plugins/debug.py @@ -29,7 +29,10 @@ class ActionModule(object): self.runner = runner def run(self, conn, tmp, module_name, module_args, inject, complex_args=None, **kwargs): - args = utils.parse_kv(module_args) + args = {} + if complex_args: + args.update(complex_args) + args.update(utils.parse_kv(module_args)) if not 'msg' in args: args['msg'] = 'Hello world!' diff --git a/lib/ansible/runner/action_plugins/fail.py b/lib/ansible/runner/action_plugins/fail.py index ce8cbdd8d3..7c3a58cfb9 100644 --- a/lib/ansible/runner/action_plugins/fail.py +++ b/lib/ansible/runner/action_plugins/fail.py @@ -33,7 +33,10 @@ class ActionModule(object): # note: the fail module does not need to pay attention to check mode # it always runs. - args = utils.parse_kv(module_args) + args = {} + if complex_args: + args.update(complex_args) + args.update(utils.parse_kv(module_args)) if not 'msg' in args: args['msg'] = 'Failed as requested from task' diff --git a/lib/ansible/runner/action_plugins/fetch.py b/lib/ansible/runner/action_plugins/fetch.py index 96c7706724..58aa627b0e 100644 --- a/lib/ansible/runner/action_plugins/fetch.py +++ b/lib/ansible/runner/action_plugins/fetch.py @@ -40,7 +40,10 @@ class ActionModule(object): return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not (yet) supported for this module')) # load up options - options = utils.parse_kv(module_args) + options = {} + if complex_args: + options.update(complex_args) + options.update(utils.parse_kv(module_args)) source = options.get('src', None) dest = options.get('dest', None) if source is None or dest is None: diff --git a/lib/ansible/runner/action_plugins/group_by.py b/lib/ansible/runner/action_plugins/group_by.py index a4f9df67c2..72df4e626c 100644 --- a/lib/ansible/runner/action_plugins/group_by.py +++ b/lib/ansible/runner/action_plugins/group_by.py @@ -37,7 +37,10 @@ class ActionModule(object): # the group_by module does not need to pay attention to check mode. # it always runs. - args = parse_kv(self.runner.module_args) + args = {} + if complex_args: + args.update(complex_args) + args.update(utils.parse_kv(module_args)) if not 'key' in args: raise ae("'key' is a required argument.") diff --git a/lib/ansible/runner/action_plugins/pause.py b/lib/ansible/runner/action_plugins/pause.py index 3a7708ce76..109f41bfce 100644 --- a/lib/ansible/runner/action_plugins/pause.py +++ b/lib/ansible/runner/action_plugins/pause.py @@ -53,7 +53,10 @@ class ActionModule(object): # flag, it always runs hosts = ', '.join(self.runner.host_set) - args = parse_kv(template(self.runner.basedir, module_args, inject)) + args = {} + if complex_args: + args.update(complex_args) + args.update(parse_kv(template(self.runner.basedir, module_args, inject))) # Are 'minutes' or 'seconds' keys that exist in 'args'? if 'minutes' in args or 'seconds' in args: diff --git a/lib/ansible/runner/action_plugins/template.py b/lib/ansible/runner/action_plugins/template.py index 2eed900479..dc44485679 100644 --- a/lib/ansible/runner/action_plugins/template.py +++ b/lib/ansible/runner/action_plugins/template.py @@ -37,7 +37,10 @@ class ActionModule(object): raise errors.AnsibleError("in current versions of ansible, templates are only usable in playbooks") # load up options - options = utils.parse_kv(module_args) + options = {} + if complex_args: + options.update(complex_args) + options.update(utils.parse_kv(module_args)) source = options.get('src', None) dest = options.get('dest', None) @@ -106,9 +109,9 @@ class ActionModule(object): if self.runner.check: return ReturnData(conn=conn, comm_ok=True, result=dict(changed=True), diff=dict(before_header=dest, after_header=source, before=dest_contents, after=resultant)) else: - res = self.runner._execute_module(conn, tmp, 'copy', module_args, inject=inject) + res = self.runner._execute_module(conn, tmp, 'copy', module_args, inject=inject, complex_args=complex_args) res.diff = dict(before=dest_contents, after=resultant) return res else: - return self.runner._execute_module(conn, tmp, 'file', module_args, inject=inject) + return self.runner._execute_module(conn, tmp, 'file', module_args, inject=inject, complex_args=complex_args)