From 494043947d174296335f4b649412344cd90148c3 Mon Sep 17 00:00:00 2001 From: smoothify Date: Tue, 20 Aug 2013 10:11:39 +0100 Subject: [PATCH 1/3] Add support for role defaults. These are variables on a per role basis with lowest precedence. --- lib/ansible/playbook/play.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 03216fe1f1..aeb9555811 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -183,6 +183,20 @@ class Play(object): dep_stack.append([role,role_path,role_vars]) return dep_stack + def _load_role_defaults(self, defaults_files): + # process default variables + default_vars = {} + for filename in defaults_files: + if os.path.exists(filename): + new_default_vars = utils.parse_yaml_from_file(filename) + if new_default_vars: + if type(new_default_vars) != dict: + raise errors.AnsibleError("%s must be stored as dictonary/hash: %s" % (filename, type(new_default_vars))) + + default_vars = utils.combine_vars(default_vars, new_default_vars) + + return default_vars + def _load_roles(self, roles, ds): # a role is a name that auto-includes the following if they exist # /tasks/main.yml @@ -199,6 +213,7 @@ class Play(object): new_tasks = [] new_handlers = [] new_vars_files = [] + defaults_files = [] pre_tasks = ds.get('pre_tasks', None) if type(pre_tasks) != list: @@ -222,10 +237,13 @@ class Play(object): task_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'tasks')) handler_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'handlers')) vars_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'vars')) + defaults_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'defaults')) task = self._resolve_main(task_basepath) handler = self._resolve_main(handler_basepath) vars_file = self._resolve_main(vars_basepath) + defaults_file = self._resolve_main(defaults_basepath) + library = utils.path_dwim(self.basedir, os.path.join(role_path, 'library')) if not os.path.isfile(task) and not os.path.isfile(handler) and not os.path.isfile(vars_file) and not os.path.isdir(library): @@ -244,6 +262,8 @@ class Play(object): new_handlers.append(nt) if os.path.isfile(vars_file): new_vars_files.append(vars_file) + if os.path.isfile(defaults_file): + defaults_files.append(defaults_file) if os.path.isdir(library): utils.plugins.module_finder.add_directory(library) @@ -275,6 +295,11 @@ class Play(object): ds['handlers'] = new_handlers ds['vars_files'] = new_vars_files + defaults = self._load_role_defaults(defaults_files) + # merge default vars with self.vars, with vars taking precedence. + if defaults: + self.vars = utils.combine_vars(defaults, self.vars) + return ds # ************************************************* From 637d3070dc1cd7ad801635df77b1b5d1fb530cd3 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Thu, 29 Aug 2013 17:21:28 -0500 Subject: [PATCH 2/3] Allow default variables to be overridden by inventory variables --- lib/ansible/playbook/__init__.py | 4 ++-- lib/ansible/playbook/play.py | 9 +++------ lib/ansible/playbook/task.py | 7 ++++--- lib/ansible/runner/__init__.py | 4 ++++ 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 3f9130e153..b8bc24b059 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -307,7 +307,7 @@ class PlayBook(object): remote_pass=self.remote_pass, module_path=self.module_path, timeout=self.timeout, remote_user=task.play.remote_user, remote_port=task.play.remote_port, module_vars=task.module_vars, - private_key_file=self.private_key_file, + default_vars=task.default_vars, private_key_file=self.private_key_file, setup_cache=self.SETUP_CACHE, basedir=task.play.basedir, conditional=task.only_if, callbacks=self.runner_callbacks, sudo=task.sudo, sudo_user=task.sudo_user, @@ -447,7 +447,7 @@ class PlayBook(object): remote_pass=self.remote_pass, remote_port=play.remote_port, private_key_file=self.private_key_file, setup_cache=self.SETUP_CACHE, callbacks=self.runner_callbacks, sudo=play.sudo, sudo_user=play.sudo_user, transport=play.transport, sudo_pass=self.sudo_pass, is_playbook=True, module_vars=play.vars, - check=self.check, diff=self.diff + default_vars=play.default_vars, check=self.check, diff=self.diff ).run() self.stats.compute(setup_results, setup=True) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index d204980b9d..68563d655a 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -28,7 +28,7 @@ import os class Play(object): __slots__ = [ - 'hosts', 'name', 'vars', 'vars_prompt', 'vars_files', + 'hosts', 'name', 'vars', 'default_vars', 'vars_prompt', 'vars_files', 'handlers', 'remote_user', 'remote_port', 'sudo', 'sudo_user', 'transport', 'playbook', 'tags', 'gather_facts', 'serial', '_ds', '_handlers', '_tasks', @@ -70,7 +70,7 @@ class Play(object): self.tags = [] ds = self._load_roles(self.roles, ds) - self.vars_files = ds.get('vars_files', []) + self.vars_files = ds.get('vars_files', []) self._update_vars_files_for_host(None) @@ -295,10 +295,7 @@ class Play(object): ds['handlers'] = new_handlers ds['vars_files'] = new_vars_files - defaults = self._load_role_defaults(defaults_files) - # merge default vars with self.vars, with vars taking precedence. - if defaults: - self.vars = utils.combine_vars(defaults, self.vars) + self.default_vars = self._load_role_defaults(defaults_files) return ds diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index a4f4911fba..6ff409eab0 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -24,7 +24,7 @@ class Task(object): __slots__ = [ 'name', 'meta', 'action', 'only_if', 'when', 'async_seconds', 'async_poll_interval', - 'notify', 'module_name', 'module_args', 'module_vars', + 'notify', 'module_name', 'module_args', 'module_vars', 'default_vars', 'play', 'notified_by', 'tags', 'register', 'delegate_to', 'first_available_file', 'ignore_errors', 'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass', @@ -100,8 +100,9 @@ class Task(object): elif not x in Task.VALID_KEYS: raise errors.AnsibleError("%s is not a legal parameter in an Ansible task or handler" % x) - self.module_vars = module_vars - self.play = play + self.module_vars = module_vars + self.play = play + self.default_vars = play.default_vars # load various attributes self.name = ds.get('name', None) diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 45e6cb8d07..63e99c7a94 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -131,6 +131,7 @@ class Runner(object): sudo=False, # whether to run sudo or not sudo_user=C.DEFAULT_SUDO_USER, # ex: 'root' module_vars=None, # a playbooks internals thing + default_vars=None, # ditto is_playbook=False, # running from playbook or not? inventory=None, # reference to Inventory object subset=None, # subset pattern @@ -159,6 +160,7 @@ class Runner(object): self.inventory = utils.default(inventory, lambda: ansible.inventory.Inventory(host_list)) self.module_vars = utils.default(module_vars, lambda: {}) + self.default_vars = utils.default(default_vars, lambda: {}) self.always_run = None self.connector = connection.Connection(self) self.conditional = conditional @@ -405,6 +407,7 @@ class Runner(object): port = self.remote_port inject = {} + inject = utils.combine_vars(inject, self.default_vars) inject = utils.combine_vars(inject, host_variables) inject = utils.combine_vars(inject, self.module_vars) inject = utils.combine_vars(inject, self.setup_cache[host]) @@ -413,6 +416,7 @@ class Runner(object): inject['group_names'] = host_variables.get('group_names', []) inject['groups'] = self.inventory.groups_list() inject['vars'] = self.module_vars + inject['defaults'] = self.default_vars inject['environment'] = self.environment if self.inventory.basedir() is not None: From 25e3eed519b723a0c8017c67dee1eda35bcbb0b6 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Fri, 30 Aug 2013 00:32:20 -0500 Subject: [PATCH 3/3] Fixing a bug in variable precedence for roles and dependencies --- lib/ansible/playbook/play.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 68563d655a..8a13902cb0 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -69,9 +69,17 @@ class Play(object): elif type(self.tags) != list: self.tags = [] - ds = self._load_roles(self.roles, ds) + # We first load the vars files from the datastructure + # so we have the default variables to pass into the roles self.vars_files = ds.get('vars_files', []) + self._update_vars_files_for_host(None) + # now we load the roles into the datastructure + ds = self._load_roles(self.roles, ds) + + # and finally re-process the vars files as they may have + # been updated by the included roles + self.vars_files = ds.get('vars_files', []) self._update_vars_files_for_host(None) # template everything to be efficient, but do not pre-mature template @@ -153,6 +161,13 @@ 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) + role_vars = utils.combine_vars(role_vars, passed_vars) + vars = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(role_path, 'vars'))) + vars_data = {} + if os.path.isfile(vars): + vars_data = utils.parse_yaml_from_file(vars) + if vars_data: + role_vars = utils.combine_vars(vars_data, role_vars) # the meta directory contains the yaml that should # hold the list of dependencies (if any) meta = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(role_path, 'meta'))) @@ -162,17 +177,14 @@ class Play(object): dependencies = data.get('dependencies',[]) for dep in dependencies: (dep_path,dep_vars) = self._get_role_path(dep) + dep_vars = utils.combine_vars(passed_vars, dep_vars) + dep_vars = utils.combine_vars(role_vars, dep_vars) vars = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(dep_path, 'vars'))) vars_data = {} if os.path.isfile(vars): vars_data = utils.parse_yaml_from_file(vars) - dep_vars.update(role_vars) - for k in passed_vars.keys(): - if not k in dep_vars: - dep_vars[k] = passed_vars[k] - for k in vars_data.keys(): - if not k in dep_vars: - dep_vars[k] = vars_data[k] + if vars_data: + dep_vars = utils.combine_vars(vars_data, dep_vars) if 'role' in dep_vars: del dep_vars['role'] self._build_role_dependencies([dep], dep_stack, passed_vars=dep_vars, level=level+1)