diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 021d62890d..3bd1b64308 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -179,7 +179,46 @@ class PlayBook(object): # ***************************************************** - def _load_playbook_from_file(self, path, vars={}): + def _get_playbook_vars(self, play_ds, existing_vars): + ''' + Gets the vars specified with the play and blends them + with any existing vars that have already been read in + ''' + new_vars = existing_vars.copy() + if 'vars' in play_ds: + if isinstance(play_ds['vars'], dict): + new_vars.update(play_ds['vars']) + elif isinstance(play_ds['vars'], list): + for v in play_ds['vars']: + new_vars.update(v) + return new_vars + + # ***************************************************** + + def _get_include_info(self, play_ds, basedir, existing_vars={}): + ''' + Gets any key=value pairs specified with the included file + name and returns the merged vars along with the path + ''' + new_vars = existing_vars.copy() + tokens = shlex.split(play_ds.get('include', '')) + for t in tokens[1:]: + (k,v) = t.split("=", 1) + new_vars[k] = template(basedir, v, new_vars) + + return (new_vars, tokens[0]) + + # ***************************************************** + + def _get_playbook_vars_files(self, play_ds, existing_vars_files): + new_vars_files = list(existing_vars_files) + if 'vars_files' in play_ds: + new_vars_files = utils.list_union(new_vars_files, play_ds['vars_files']) + return new_vars_files + + # ***************************************************** + + def _load_playbook_from_file(self, path, vars={}, vars_files=[]): ''' run top level error checking on playbooks and allow them to include other playbooks. ''' @@ -201,36 +240,25 @@ class PlayBook(object): # a playbook (list of plays) decided to include some other list of plays # from another file. The result is a flat list of plays in the end. - tokens = shlex.split(play['include']) + play_vars = self._get_playbook_vars(play, vars) + play_vars_files = self._get_playbook_vars_files(play, vars_files) + inc_vars, inc_path = self._get_include_info(play, basedir, play_vars) + play_vars.update(inc_vars) - incvars = vars.copy() - if 'vars' in play: - if isinstance(play['vars'], dict): - incvars.update(play['vars']) - elif isinstance(play['vars'], list): - for v in play['vars']: - incvars.update(v) - - # allow key=value parameters to be specified on the include line - # to set variables - - for t in tokens[1:]: - - (k,v) = t.split("=", 1) - incvars[k] = template(basedir, v, incvars) - - included_path = utils.path_dwim(basedir, template(basedir, tokens[0], incvars)) - (plays, basedirs) = self._load_playbook_from_file(included_path, incvars) + included_path = utils.path_dwim(basedir, template(basedir, inc_path, play_vars)) + (plays, basedirs) = self._load_playbook_from_file(included_path, vars=play_vars, vars_files=play_vars_files) for p in plays: # support for parameterized play includes works by passing # those variables along to the subservient play if 'vars' not in p: p['vars'] = {} if isinstance(p['vars'], dict): - p['vars'].update(incvars) + p['vars'].update(play_vars) elif isinstance(p['vars'], list): # nobody should really do this, but handle vars: a=1 b=2 - p['vars'].extend([{k:v} for k,v in incvars.iteritems()]) + p['vars'].extend([{k:v} for k,v in play_vars.iteritems()]) + # now add in the vars_files + p['vars_files'] = utils.list_union(p.get('vars_files', []), play_vars_files) accumulated_plays.extend(plays) play_basedirs.extend(basedirs) diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 55e1c7d536..b1916371fc 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -1005,6 +1005,22 @@ def is_list_of_strings(items): return False return True +def _listify(a): + if not isinstance(a, (list, tuple)): + return [a,] + else: + return a + +def list_union(a, b): + set_a = set(_listify(a)) + set_b = set(_listify(b)) + return list(set_a.union(set_b)) + +def list_intersection(a, b): + set_a = set(_listify(a)) + set_b = set(_listify(b)) + return list(set_a.intersection(set_b)) + def safe_eval(expr, locals={}, include_exceptions=False): ''' this is intended for allowing things like: