Merge pull request #1575 from dhozac/parameterized-playbook-include

Add parameterized playbook includes
This commit is contained in:
Michael DeHaan 2012-11-12 05:17:38 -08:00
commit c890ae18e7
8 changed files with 81 additions and 22 deletions

View file

@ -22,6 +22,7 @@ from ansible import utils
from ansible import errors from ansible import errors
import ansible.callbacks import ansible.callbacks
import os import os
import shlex
import collections import collections
from play import Play from play import Play
@ -123,7 +124,7 @@ class PlayBook(object):
# ***************************************************** # *****************************************************
def _load_playbook_from_file(self, path): def _load_playbook_from_file(self, path, vars={}):
''' '''
run top level error checking on playbooks and allow them to include other playbooks. run top level error checking on playbooks and allow them to include other playbooks.
''' '''
@ -135,21 +136,45 @@ class PlayBook(object):
if type(playbook_data) != list: if type(playbook_data) != list:
raise errors.AnsibleError("parse error: playbooks must be formatted as a YAML list") raise errors.AnsibleError("parse error: playbooks must be formatted as a YAML list")
basedir = os.path.dirname(path)
for play in playbook_data: for play in playbook_data:
if type(play) != dict: if type(play) != dict:
raise errors.AnsibleError("parse error: each play in a playbook must a YAML dictionary (hash), recieved: %s" % play) raise errors.AnsibleError("parse error: each play in a playbook must a YAML dictionary (hash), recieved: %s" % play)
if 'include' in play: if 'include' in play:
if len(play.keys()) == 1: if len(play.keys()) <= 2:
included_path = utils.path_dwim(self.basedir, play['include']) tokens = shlex.split(play['include'])
(plays, basedirs) = self._load_playbook_from_file(included_path)
accumulated_plays.extend(plays) items = ['']
play_basedirs.extend(basedirs) for k in play.keys():
if not k.startswith("with_"):
continue
plugin_name = k[5:]
if plugin_name not in utils.plugins.lookup_loader:
raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name))
terms = utils.template_ds(basedir, play[k], vars)
items = utils.plugins.lookup_loader.get(plugin_name, basedir=basedir, runner=None).run(terms, inject=vars)
break
for item in items:
incvars = vars.copy()
incvars['item'] = item
for t in tokens[1:]:
(k,v) = t.split("=", 1)
incvars[k] = utils.template_ds(basedir, v, incvars)
included_path = utils.path_dwim(basedir, tokens[0])
(plays, basedirs) = self._load_playbook_from_file(included_path, incvars)
for p in plays:
if 'vars' not in p:
p['vars'] = {}
p['vars'].update(incvars)
accumulated_plays.extend(plays)
play_basedirs.extend(basedirs)
else: else:
raise errors.AnsibleError("parse error: top level includes cannot be used with other directives: %s" % play) raise errors.AnsibleError("parse error: playbook includes cannot be used with other directives: %s" % play)
else: else:
accumulated_plays.append(play) accumulated_plays.append(play)
play_basedirs.append(os.path.dirname(path)) play_basedirs.append(basedir)
return (accumulated_plays, play_basedirs) return (accumulated_plays, play_basedirs)

View file

@ -111,7 +111,7 @@ class Play(object):
plugin_name = k[5:] plugin_name = k[5:]
if plugin_name not in utils.plugins.lookup_loader: if plugin_name not in utils.plugins.lookup_loader:
raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name)) 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) terms = utils.template_ds(self.basedir, x[k], task_vars)
items = utils.plugins.lookup_loader.get(plugin_name, basedir=self.basedir, runner=None).run(terms, inject=task_vars) items = utils.plugins.lookup_loader.get(plugin_name, basedir=self.basedir, runner=None).run(terms, inject=task_vars)
for item in items: for item in items:
@ -119,7 +119,7 @@ class Play(object):
mv['item'] = item mv['item'] = item
for t in tokens[1:]: for t in tokens[1:]:
(k,v) = t.split("=", 1) (k,v) = t.split("=", 1)
mv[k] = utils.varReplaceWithItems(self.basedir, v, mv) mv[k] = utils.template_ds(self.basedir, v, mv)
include_file = utils.template(self.basedir, tokens[0], mv) include_file = utils.template(self.basedir, tokens[0], mv)
data = utils.parse_yaml_from_file(utils.path_dwim(self.basedir, include_file)) data = utils.parse_yaml_from_file(utils.path_dwim(self.basedir, include_file))
for y in data: for y in data:

View file

@ -273,7 +273,7 @@ class Runner(object):
items_plugin = self.module_vars.get('items_lookup_plugin', None) items_plugin = self.module_vars.get('items_lookup_plugin', None)
if items_plugin is not None and items_plugin in utils.plugins.lookup_loader: if items_plugin is not None and items_plugin in utils.plugins.lookup_loader:
items_terms = self.module_vars.get('items_lookup_terms', '') items_terms = self.module_vars.get('items_lookup_terms', '')
items_terms = utils.varReplaceWithItems(self.basedir, items_terms, inject) items_terms = utils.template_ds(self.basedir, items_terms, inject)
items = utils.plugins.lookup_loader.get(items_plugin, runner=self, basedir=self.basedir).run(items_terms, inject=inject) items = utils.plugins.lookup_loader.get(items_plugin, runner=self, basedir=self.basedir).run(items_terms, inject=inject)
if type(items) != list: if type(items) != list:
raise errors.AnsibleError("lookup plugins have to return a list: %r" % items) raise errors.AnsibleError("lookup plugins have to return a list: %r" % items)

View file

@ -183,8 +183,8 @@ def _varReplaceLookups(basedir, raw, vars):
return ''.join(done) return ''.join(done)
def varReplaceWithItems(basedir, varname, vars): def template_ds(basedir, varname, vars):
''' helper function used by with_items ''' ''' templates a data structure by traversing it and substituting for other data structures '''
if isinstance(varname, basestring): if isinstance(varname, basestring):
m = _varFind(varname, vars) m = _varFind(varname, vars)
@ -192,17 +192,17 @@ def varReplaceWithItems(basedir, varname, vars):
return varname return varname
if m['start'] == 0 and m['end'] == len(varname): if m['start'] == 0 and m['end'] == len(varname):
if m['replacement'] is not None: if m['replacement'] is not None:
return varReplaceWithItems(basedir, m['replacement'], vars) return template_ds(basedir, m['replacement'], vars)
else: else:
return varname return varname
else: else:
return template(basedir, varname, vars) return template(basedir, varname, vars)
elif isinstance(varname, (list, tuple)): elif isinstance(varname, (list, tuple)):
return [varReplaceWithItems(basedir, v, vars) for v in varname] return [template_ds(basedir, v, vars) for v in varname]
elif isinstance(varname, dict): elif isinstance(varname, dict):
d = {} d = {}
for (k, v) in varname.iteritems(): for (k, v) in varname.iteritems():
d[k] = varReplaceWithItems(basedir, v, vars) d[k] = template_ds(basedir, v, vars)
return d return d
else: else:
return varname return varname

View file

@ -204,6 +204,27 @@ class TestPlaybook(unittest.TestCase):
assert len(EVENTS) == 44 assert len(EVENTS) == 44
def test_includes(self):
pb = os.path.join(self.test_dir, 'playbook-includer.yml')
actual = self._run(pb)
# if different, this will output to screen
print "**ACTUAL**"
print utils.jsonify(actual, format=True)
expected = {
"localhost": {
"changed": 0,
"failures": 0,
"ok": 5,
"skipped": 0,
"unreachable": 0
}
}
print "**EXPECTED**"
print utils.jsonify(expected, format=True)
assert utils.jsonify(expected, format=True) == utils.jsonify(actual,format=True)
def test_playbook_vars(self): def test_playbook_vars(self):
test_callbacks = TestCallbacks() test_callbacks = TestCallbacks()
playbook = ansible.playbook.PlayBook( playbook = ansible.playbook.PlayBook(

View file

@ -286,9 +286,9 @@ class TestUtils(unittest.TestCase):
assert res == u'hello world world' assert res == u'hello world world'
##################################### #####################################
### varReplaceWithItems function tests ### template_ds function tests
def test_varReplaceWithItems_basic(self): def test_template_ds_basic(self):
vars = { vars = {
'data': { 'data': {
'var': [ 'var': [
@ -313,19 +313,19 @@ class TestUtils(unittest.TestCase):
} }
template = '${data.var}' template = '${data.var}'
res = ansible.utils.varReplaceWithItems(None, template, vars) res = ansible.utils.template_ds(None, template, vars)
assert sorted(res) == sorted(vars['data']['var']) assert sorted(res) == sorted(vars['data']['var'])
template = '${data.types}' template = '${data.types}'
res = ansible.utils.varReplaceWithItems(None, template, vars) res = ansible.utils.template_ds(None, template, vars)
assert sorted(res) == sorted(vars['data']['types']) assert sorted(res) == sorted(vars['data']['types'])
template = '${data.alphas}' template = '${data.alphas}'
res = ansible.utils.varReplaceWithItems(None, template, vars) res = ansible.utils.template_ds(None, template, vars)
assert sorted(res) == sorted(vars['alphas']) assert sorted(res) == sorted(vars['alphas'])
template = '${data.nonexisting}' template = '${data.nonexisting}'
res = ansible.utils.varReplaceWithItems(None, template, vars) res = ansible.utils.template_ds(None, template, vars)
assert res == template assert res == template
##################################### #####################################

View file

@ -0,0 +1,5 @@
---
- hosts: all
gather_facts: False
tasks:
- action: debug msg="$variable"

View file

@ -0,0 +1,8 @@
---
- include: playbook-included.yml variable=foobar
- include: playbook-included.yml variable=foofoo
- include: playbook-included.yml variable=$item
with_items:
- foo
- bar
- baz