(Related to Callbacks or hooks, and reusable series of tasks, in Ansible roles):
Is there any better way to append to a list or add a key to a dictionary in Ansible than (ab)using a jina2 template expression?
I know you can do something like:
- name: this is a hack
shell: echo "{% originalvar.append('x') %}New value of originalvar is {{originalvar}}"
but is there really no sort of meta task or helper to do this?
It feels fragile, seems to be undocumented, and relies on lots of assumptions about how variables work in Ansible.
My use case is multiple roles (database server extensions) that each need to supply some configuration to a base role (the database server). It's not as simple as appending a line to the db server config file; each change applies to the same line, e.g. the extensions bdr
and pg_stat_statements
must both appear on a target line:
shared_preload_libaries = 'bdr, pg_stat_statements'
Is the Ansible way to do this to just process the config file multiple times (once per extension) with a regexp that extracts the current value, parses it, and then rewrites it? If so, how do you make that idempotent across multiple runs?
What if the config is harder than this to parse and it's not as simple as appending another comma-separated value? Think XML config files.
Since Ansible v2.x you can do these:
all the above is documented in: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#combining-hashes-dictionaries
You can merge two lists in a variable with
+
. Say you have agroup_vars
file with this content:And it's used in a template
pgsql.conf.j2
like:You can then append extensions to the testing database servers like this:
When the role is run in any of the testing servers, the aditional extensions will be added.
I'm not sure this works for dictionaries as well, and also be careful with spaces and leaving a dangling comma at the end of the line.
you need to split the loop into 2
and addhost.yml
Almost all answers here require changes in tasks, but I needed to dynamically merge dictionaries in vars definition, not during run.
E.g. I want to define some shared vars in
all
group_vars
and then I want to extend them in some othergroup
orhost_vars
. Very useful when working for roles.If you try to use the
combine
orunion
filters overwriting the original variable in var files, you will end in infinite loop during templating, so I created this workaround (it's not solution).You can define multiple variables based on some name pattern and then automatically load them in role.
group_vars/all.yml
group_vars/group1.yml
role code snippet
do_some_stuff.yml
It's just a snippet, but you should get the idea how it works. note: lookup('varnames','') is available since ansible 2.8
I guess it would be also possible to merge all variables
dictionary_of_bla.*
into one dictionary during runtime using the same lookup.The advantage of this approach is that you don't need to set exact lists of variable names, but only the pattern and user can set it dynamically.
Not sure when they added this, but at least for dictionaries/hashes (NOT lists/arrays), you can set the variable hash_behaviour, like so:
hash_behaviour = merge
in youransible.cfg
.Took me quite a few hours to accidentally stumble upon this setting :S
Ansible
is an automation system, and, concerning configuration file management, it's not very different fromapt
. The reason more and more software offers the feature to read configuration snippets from aconf.d
directory is to enable such automation systems to have different packages/roles add configuration to the software. I believe that it is not the philosophy ofAnsible
to do what you have in mind, but instead to use theconf.d
trick. If the software being configured does not offer this functionality, you may be in trouble.Since you mention XML configuration files, I take the opportunity to do some whining. There's a reason for the Unix tradition of using plain text configuration files. Binary configuration files do not lend themselves well to system automation, so any kind of binary format will give you trouble and will likely require you to create a program to handle the configuration. (If anyone thinks XML is a plain text format, they should go have their brains examined.)
Now, on your specific
PostgreSQL
problem.PostgreSQL
does support theconf.d
trick. First, I would check whethershared_preload_libraries
can be specified multiple times. I did not find any hint in the documentation that it can, but I would still try it. If it cannot be specified multiple times, I would explain my problem to thePostgreSQL
guys in case they have ideas; this is aPostgreSQL
issue and not anAnsible
issue. If there is no solution and I really couldn't merge the different roles into one, I'd implement a system to compile the configuration on the managed host. In this case, I'd probably create a script/usr/local/sbin/update_postgresql_config
which would compile/etc/postgresql/postgresql.conf.jinja
into/etc/postgresql/9.x/main/postgresql.conf
. The script would read the shared preload libraries from/etc/postgresql/shared_preload_libraries.txt
, one library per line, and provide them to jinja.It is not uncommon for automation systems to do this. An example is the Debian
exim4
package.