diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 28e1d923eb8..d3c0aa53006 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -402,6 +402,7 @@ class PlayBook(object): play_vars=task.play_vars, play_file_vars=task.play_file_vars, role_vars=task.role_vars, + role_params=task.role_params, default_vars=task.default_vars, extra_vars=self.extra_vars, private_key_file=self.private_key_file, diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index b793247826a..0dcbca86841 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -221,6 +221,14 @@ class Play(object): raise errors.AnsibleError("too many levels of recursion while resolving role dependencies") for role in roles: role_path,role_vars = self._get_role_path(role) + + # save just the role params for this role, which exclude the special + # keywords 'role', 'tags', and 'when'. + role_params = role_vars.copy() + for item in ('role', 'tags', 'when'): + if item in role_params: + del role_params[item] + role_vars = utils.combine_vars(passed_vars, role_vars) vars = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(role_path, 'vars'))) @@ -249,6 +257,13 @@ class Play(object): for dep in dependencies: allow_dupes = False (dep_path,dep_vars) = self._get_role_path(dep) + + # save the dep params, just as we did above + dep_params = dep_vars.copy() + for item in ('role', 'tags', 'when'): + if item in dep_params: + del dep_params[item] + meta = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(dep_path, 'meta'))) if os.path.isfile(meta): meta_data = utils.parse_yaml_from_file(meta, vault_password=self.vault_password) @@ -332,13 +347,13 @@ class Play(object): dep_vars['when'] = tmpcond self._build_role_dependencies([dep], dep_stack, passed_vars=dep_vars, level=level+1) - dep_stack.append([dep,dep_path,dep_vars,dep_defaults_data]) + dep_stack.append([dep, dep_path, dep_vars, dep_params, dep_defaults_data]) # only add the current role when we're at the top level, # otherwise we'll end up in a recursive loop if level == 0: self.included_roles.append(role) - dep_stack.append([role,role_path,role_vars,defaults_data]) + dep_stack.append([role, role_path, role_vars, role_params, defaults_data]) return dep_stack def _load_role_vars_files(self, vars_files): @@ -400,12 +415,12 @@ class Play(object): # make role_path available as variable to the task for idx, val in enumerate(roles): this_uuid = str(uuid.uuid4()) - roles[idx][-2]['role_uuid'] = this_uuid - roles[idx][-2]['role_path'] = roles[idx][1] + roles[idx][-3]['role_uuid'] = this_uuid + roles[idx][-3]['role_path'] = roles[idx][1] role_names = [] - for (role,role_path,role_vars,default_vars) in roles: + for (role, role_path, role_vars, role_params, default_vars) in roles: # special vars must be extracted from the dict to the included tasks special_keys = [ "sudo", "sudo_user", "when", "with_items" ] special_vars = {} @@ -438,13 +453,13 @@ class Play(object): role_names.append(role_name) if os.path.isfile(task): - nt = dict(include=pipes.quote(task), vars=role_vars, default_vars=default_vars, role_name=role_name) + nt = dict(include=pipes.quote(task), vars=role_vars, role_params=role_params, default_vars=default_vars, role_name=role_name) for k in special_keys: if k in special_vars: nt[k] = special_vars[k] new_tasks.append(nt) if os.path.isfile(handler): - nt = dict(include=pipes.quote(handler), vars=role_vars, role_name=role_name) + nt = dict(include=pipes.quote(handler), vars=role_vars, role_params=role_params, role_name=role_name) for k in special_keys: if k in special_vars: nt[k] = special_vars[k] @@ -509,7 +524,7 @@ class Play(object): # ************************************************* - def _load_tasks(self, tasks, vars=None, default_vars=None, sudo_vars=None, + def _load_tasks(self, tasks, vars=None, role_params=None, default_vars=None, sudo_vars=None, additional_conditions=None, original_file=None, role_name=None): ''' handle task and handler include statements ''' @@ -521,6 +536,8 @@ class Play(object): additional_conditions = [] if vars is None: vars = {} + if role_params is None: + role_params = {} if default_vars is None: default_vars = {} if sudo_vars is None: @@ -572,11 +589,15 @@ class Play(object): included_additional_conditions.append(x[k]) elif type(x[k]) is list: included_additional_conditions.extend(x[k]) - elif k in ("include", "vars", "default_vars", "sudo", "sudo_user", "role_name", "no_log"): + elif k in ("include", "vars", "role_params", "default_vars", "sudo", "sudo_user", "role_name", "no_log"): continue else: include_vars[k] = x[k] + # get any role parameters specified + role_params = x.get('role_params', {}) + + # get any role default variables specified default_vars = x.get('default_vars', {}) if not default_vars: default_vars = self.default_vars @@ -609,7 +630,7 @@ class Play(object): for y in data: if isinstance(y, dict) and 'include' in y: y['role_name'] = new_role - loaded = self._load_tasks(data, mv, default_vars, included_sudo_vars, list(included_additional_conditions), original_file=include_filename, role_name=new_role) + loaded = self._load_tasks(data, mv, role_params, default_vars, included_sudo_vars, list(included_additional_conditions), original_file=include_filename, role_name=new_role) results += loaded elif type(x) == dict: task = Task( @@ -618,6 +639,7 @@ class Play(object): play_vars=self.vars, play_file_vars=self.vars_file_vars, role_vars=self.role_vars, + role_params=role_params, default_vars=default_vars, additional_conditions=list(additional_conditions), role_name=role_name diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index ebe43f63c1c..783f488fa10 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -26,7 +26,7 @@ class Task(object): __slots__ = [ 'name', 'meta', 'action', 'when', 'async_seconds', 'async_poll_interval', - 'notify', 'module_name', 'module_args', 'module_vars', 'play_vars', 'play_file_vars', 'role_vars', 'default_vars', + 'notify', 'module_name', 'module_args', 'module_vars', 'play_vars', 'play_file_vars', 'role_vars', 'role_params', 'default_vars', 'play', 'notified_by', 'tags', 'register', 'role_name', 'delegate_to', 'first_available_file', 'ignore_errors', 'local_action', 'transport', 'sudo', 'remote_user', 'sudo_user', 'sudo_pass', @@ -45,7 +45,7 @@ class Task(object): 'su', 'su_user', 'su_pass', 'no_log', 'run_once', ] - def __init__(self, play, ds, module_vars=None, play_vars=None, play_file_vars=None, role_vars=None, default_vars=None, additional_conditions=None, role_name=None): + def __init__(self, play, ds, module_vars=None, play_vars=None, play_file_vars=None, role_vars=None, role_params=None, default_vars=None, additional_conditions=None, role_name=None): ''' constructor loads from a task or handler datastructure ''' # meta directives are used to tell things like ansible/playbook to run @@ -123,6 +123,7 @@ class Task(object): self.play_vars = play_vars self.play_file_vars = play_file_vars self.role_vars = role_vars + self.role_params = role_params self.default_vars = default_vars self.play = play @@ -226,6 +227,7 @@ class Task(object): all_vars = utils.combine_vars(all_vars, self.play_file_vars) all_vars = utils.combine_vars(all_vars, self.role_vars) all_vars = utils.combine_vars(all_vars, self.module_vars) + all_vars = utils.combine_vars(all_vars, self.role_params) self.async_seconds = ds.get('async', 0) # not async by default self.async_seconds = template.template_from_string(play.basedir, self.async_seconds, all_vars) diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 1d236f5f11e..0d167462552 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -137,6 +137,7 @@ class Runner(object): play_vars=None, # play_file_vars=None, # role_vars=None, # + role_params=None, # default_vars=None, # extra_vars=None, # extra vars specified with he playbook(s) is_playbook=False, # running from playbook or not? @@ -182,6 +183,7 @@ class Runner(object): self.play_vars = utils.default(play_vars, lambda: {}) self.play_file_vars = utils.default(play_file_vars, lambda: {}) self.role_vars = utils.default(role_vars, lambda: {}) + self.role_params = utils.default(role_params, lambda: {}) self.default_vars = utils.default(default_vars, lambda: {}) self.extra_vars = utils.default(extra_vars, lambda: {}) @@ -645,6 +647,8 @@ class Runner(object): # followed by vars_cache things (set_fact, include_vars, and # vars_files which had host-specific templating done) inject = utils.combine_vars(inject, self.vars_cache.get(host, {})) + # role parameters next + inject = utils.combine_vars(inject, self.role_params) # and finally -e vars are the highest priority inject = utils.combine_vars(inject, self.extra_vars) # and then special vars