diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 349618faa19..06a4a8ed30e 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -104,10 +104,16 @@ class Play(object): if 'include' in x: task_vars = self.vars.copy() tokens = shlex.split(x['include']) - if 'with_items' in x: - items = utils.varReplaceWithItems(self.basedir, x['with_items'], task_vars) - else: - items = [''] + items = [''] + for k in x: + if not k.startswith("with_"): + continue + plugin_name = k[5:] + if plugin_name not in self.playbook.lookup_plugins_list: + raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name)) + terms = utils.varReplaceWithItems(self.basedir, x[k], task_vars) + items = self.playbook.lookup_plugins_list[plugin_name].LookupModule(None).run(terms) + for item in items: mv = task_vars.copy() mv['item'] = item diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index e9e15e991f7..f085b0590eb 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -24,7 +24,7 @@ class Task(object): __slots__ = [ 'name', 'action', 'only_if', 'async_seconds', 'async_poll_interval', 'notify', 'module_name', 'module_args', 'module_vars', - 'play', 'notified_by', 'tags', 'register', 'with_items', + 'play', 'notified_by', 'tags', 'register', 'delegate_to', 'first_available_file', 'ignore_errors', 'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass', 'items_lookup_plugin', 'items_lookup_terms' @@ -32,7 +32,7 @@ class Task(object): # to prevent typos and such VALID_KEYS = [ - 'name', 'action', 'only_if', 'async', 'poll', 'notify', 'with_items', + 'name', 'action', 'only_if', 'async', 'poll', 'notify', 'first_available_file', 'include', 'tags', 'register', 'ignore_errors', 'delegate_to', 'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass' @@ -49,7 +49,7 @@ class Task(object): ds.pop(x) # code to allow "with_glob" and to reference a lookup plugin named glob - elif x.startswith("with_") and x != 'with_items': + elif x.startswith("with_"): plugin_name = x.replace("with_","") if plugin_name in play.playbook.lookup_plugins_list: ds['items_lookup_plugin'] = plugin_name @@ -112,7 +112,6 @@ class Task(object): self.async_poll_interval = int(ds.get('poll', 10)) # default poll = 10 seconds self.notify = ds.get('notify', []) self.first_available_file = ds.get('first_available_file', None) - self.with_items = ds.get('with_items', None) self.items_lookup_plugin = ds.get('items_lookup_plugin', None) self.items_lookup_terms = ds.get('items_lookup_terms', None) @@ -142,19 +141,14 @@ class Task(object): self.action = utils.template(None, self.action, self.module_vars) # handle mutually incompatible options - incompatibles = [ x for x in [ self.with_items, self.first_available_file, self.items_lookup_plugin ] if x is not None ] + incompatibles = [ x for x in [ self.first_available_file, self.items_lookup_plugin ] if x is not None ] if len(incompatibles) > 1: - raise errors.AnsibleError("with_items, with_(plugin), and first_available_file are mutually incompatible in a single task") + raise errors.AnsibleError("with_(plugin), and first_available_file are mutually incompatible in a single task") # make first_available_file accessable to Runner code if self.first_available_file: self.module_vars['first_available_file'] = self.first_available_file - # process with_items so it can be used by Runner code - if self.with_items is None: - self.with_items = [ ] - self.module_vars['items'] = self.with_items - if self.items_lookup_plugin is not None: self.module_vars['items_lookup_plugin'] = self.items_lookup_plugin self.module_vars['items_lookup_terms'] = self.items_lookup_terms diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 6b3e909dd2c..03856c4dbe6 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -280,22 +280,17 @@ class Runner(object): inject['group_names'] = host_variables.get('group_names', []) inject['groups'] = self.inventory.groups_list() - # allow with_items to work in playbooks... - # apt and yum are converted into a single call, others run in a loop - items = self.module_vars.get('items', []) - if isinstance(items, basestring) and items.startswith("$"): - items = utils.varReplaceWithItems(self.basedir, items, inject) - - # if we instead said 'with_foo' and there is a lookup module named foo... + # allow with_foo to work in playbooks... + items = [] items_plugin = self.module_vars.get('items_lookup_plugin', None) if items_plugin is not None: items_terms = self.module_vars.get('items_lookup_terms', '') if items_plugin in self.lookup_plugins: - items_terms = utils.template(self.basedir, items_terms, inject) + items_terms = utils.varReplaceWithItems(self.basedir, items_terms, inject) items = self.lookup_plugins[items_plugin].run(items_terms) if type(items) != list: - raise errors.AnsibleError("with_items only takes a list: %s" % items) + raise errors.AnsibleError("lookup plugins have to return a list: %r" % items) if len(items) and self.module_name in [ 'apt', 'yum' ]: # hack for apt and soon yum, with_items maps back into a single module call diff --git a/lib/ansible/runner/lookup_plugins/items.py b/lib/ansible/runner/lookup_plugins/items.py new file mode 100644 index 00000000000..2a4757db0ee --- /dev/null +++ b/lib/ansible/runner/lookup_plugins/items.py @@ -0,0 +1,29 @@ +# (c) 2012, Michael DeHaan +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +import os + +class LookupModule(object): + + def __init__(self, runner): + self.runner = runner + + def run(self, terms): + return terms + + +