Skip to content

Instantly share code, notes, and snippets.

@dvarrazzo
Last active November 2, 2015 16:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dvarrazzo/7418a89b7278ff69267c to your computer and use it in GitHub Desktop.
Save dvarrazzo/7418a89b7278ff69267c to your computer and use it in GitHub Desktop.
Ansible Plugin implementing the include_defaults action.
"""
Ansible Plugin implementing the include_defaults action.
Like include_vars, but the included defaults can be overridden by the
inventory or by group vars. Can be used to read the defaults from another role.
Usage: drop it into a directory called ``action_plugins`` in your playbook
directory. Then you can use it with::
- name: get the defaults from the web server role
action: include_defaults roles/web/defaults/main.yml
Proposed for inclusion in <https://github.com/ansible/ansible/pull/8808>.
"""
# (c) 2014 Daniele Varrazzo <daniele.varrazzo@gmail.com>
import os
from ansible.utils import template
from ansible import utils
from ansible import errors
from ansible.runner.return_data import ReturnData
class ActionModule(object):
TRANSFERS_FILES = False
def __init__(self, runner):
self.runner = runner
def run(self, conn, tmp, module_name, module_args, inject, complex_args=None, **kwargs):
if not module_args:
result = dict(failed=True, msg="No source file given")
return ReturnData(conn=conn, comm_ok=True, result=result)
source = module_args
source = template.template(self.runner.basedir, source, inject)
if '_original_file' in inject:
source = utils.path_dwim_relative(inject['_original_file'], 'defaults', source, self.runner.basedir)
else:
source = utils.path_dwim(self.runner.basedir, source)
if os.path.exists(source):
data = utils.parse_yaml_from_file(source, vault_password=self.runner.vault_pass)
if data and type(data) != dict:
raise errors.AnsibleError("%s must be stored as a dictionary/hash" % source)
elif data is None:
data = {}
inject['defaults'].update(data)
return ReturnData(conn=conn, comm_ok=True, result={})
else:
result = dict(failed=True, msg="Source file not found.", file=source)
return ReturnData(conn=conn, comm_ok=True, result=result)
@dvarrazzo
Copy link
Author

Warning! unfortunately this implementation of include_defaults has an issue: because it changes some data structures in-place it doesn't work when ansible runs in parallel on many hosts, because the process forks and the modified variables get lost.

In my case, I have solved the issue by adding a vars_files list to the group_vars files: the list usually contains the name of the file in vars_files itself but it can contain further customization (can create configurations "inheriting" from a base one and tweaking a few things). I've then converted every task file where I was using include_defaults into include_vars, with a final include of vars_files too, in order to have the values specified in vars_files to override the "defaults" (which become too powerful otherwise). E.g. if a web client should know the port of the web server, you can have:

  • file roles/web_server/defaults/main.yml:
http_port: 80
  • file group_files/testsite:
http_port: 8080
vars_files:
  - group_files/testsite
  • file roles/web_client/tasks/main.yml:
- include_vars: "{{item}}"
  with_items:
    - roles/web_server/defaults/main.yml
    - {{vars_files}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment