Changing list value in Ansible - ansible

I have inventory with a very complicated structure. For my specific installation I want to override only some values. For example, I have structure:
---
System:
atr1: 47
config:
- nodes:
- logger:
id: 'all'
svr: 'IEW'
- Database:
constr: 'login/pass#db'
atr2: 'some value'
I want to override severity of the logger, i.e. add statistic information
svr: 'IEWS'. I want to provide an override within --extra-vars parameter.
In ansible.cfg -> hash_behaviour = merge
I don't want to use something like - svr: "{{ svr_custom | default('IEW') }}", because there are too many parameters, and thus it will be difficult to write the entire inventory in such way.
I read about combine filter, but I can't use it, when I had to override only one item in hash.
How can I achieve my goal?

The way you found is the simplest one. It's verbose to write but very easy to debug and to fix.
If you REALLY want to shrink this job, you can write your own lookup plugin. (https://docs.ansible.com/ansible/2.5/plugins/lookup.html).
From my experience, I really want to say that direct and dumb approach (write verbose) is much better for overall maintainability. A next person will see a dumb dump (pun intended) which is easy to fix, not a some obscure python snippet.
To make life easier you may want to store this configuration as a separate file (with all jinja pieces) and use lookup (st right from docs):
# Since 2.4, you can pass in variables during evaluation
- debug: msg="{{ lookup('template', './some_template.j2', template_vars=dict(x=42)) }} is evaluated with x=42"
Moreover, you can use Jinja's |from_yaml (or from_json) to convert loaded and processed template into data structure.

I read about combine filter, but I can't use it, when I had to override only one item in hash.
Why is that? Wouldn't new_svr defined in --extra-vars achieve what you want?
- set_fact:
System: "{{ System | combine({'config':[{'nodes':[{'logger':{'svr':new_svr }}]}]}, recursive=True) }}"

Related

Ansible regex_replace replace multiple characters in URI

Long story short -
Task 1:
Through URI module I am querying Website to obtain json data. Output is saved to variable.
Task 2:
Json_search is used to filter data.
But result value comes with square brackets which I need to get rid off as it needs to be inserted into url address.
I am using not elegant replace module twice to solve this problem. This works but I more than sure you can engage regex_replace (or json_query in more intelligent way) to achieve same result.
Expected output: https://site1/api/host/1
Received output before using replace 2x:
https://site1/api/host/[1]
Snippet from playbook:
…
- name: display url
debug:
msg: {{ json_output_var.json|json_search('result[].id')|replace('[','')|replace(']','')

GitLab CI yml check if value is within array at workflow rules

I have to validate a pipeline before triggering it. One criterion of the validation is if a CI/CD variable has one of the accepted values. Is there a way to find if it is matching the correct values?
I tried to create an array of values then to check it in the workflow rules but it is not clear from the other questions how to do that.
So it should be looking like this:
#WARNING: invalid yml!
variables:
ValidValues: ["Value1", "Value2", "SomeOtherValue"]
workflow:
rules:
- if: ValidValues contains $GivenValue
when: always
Searching on this issue, I found that I can add the allowed values to a regex on which I can check at the workflow rules. In the end it looks like this:
workflow:
rules:
- if: $GivenValue =~ /\b(Value1|Value2|SomeOtherValue)\b/
when: always
- when: never
Unfortunately I did not found a solution on my initial approach (adding the allowed values to an array, then looking for them) but this works as well.

ansible. Run handler on specific host

I need to collect some information during run of ansible and print this information in the end.
I've tried to define empty list variable in role. I added in playbook handler which add new value to list ant print value of this variable in the end of playbook.
set_fact:
manual_tasks: "{{ manual_tasks + ['restart apache'] }}"
I miss that set_fact host related. So this solution stop working as soon as I start using different hosts. Delegate_to is not solve problem as well. Is there way to make this list global? Or any other solution exist?
Q: Add new value to list and print it at the end of the playbook. Is there way to make this list global?
A: No. It is not. "Global scope is set by config, environment variables and the command line." A host can't set_fact in the play or global scope.
You might want to take a look at ansible-runner. See Sending Runner Status and Events to External Systems.

The right way to extract a list of attributes from a list of maps in Ansible

I'm looking for a non-ugly way to extract a list of particular values from a list of maps in Ansible. I can find some ways to do it, for example here: here, but everything I have seen is very ugly, and it feels like there should be a way where it is clearer what is being done to someone reading it in future. I could write a filter, but it feels like this should be unnecessary since this must come up relatively regularly.
I have a data structure like so in Ansible:
interfaces:
- name: eth0
subnet: 192.168.2
netmask: 255.255.255.0
static_dhcp_hosts:
- {name: "hosta", mac: "00:01:02:03:04:05", ip: "192.168.2.20"}
- name: eth1
subnet: 192.168.5
netmask: 255.255.255.0
static_dhcp_hosts:
- {name: "hostb", mac: "00:02:03:04:05:06", ip: "192.168.5.20"}
- {name: "hostc", mac: "00:03:04:05:06:07", ip: "192.168.5.21"}
I'd like to get a space separated list of the interface names, so
eth0 eth1
Obviously this is just example data, the actual top level list has 10 elements for one host. I know that I can use the join filter to get from a list of interfaces to the string I want and how to do that.
Can anyone suggest a nice way to make the list, that is readable to future maintainers (code/configuration should be self-documenting as far as possible (and no further))?
I am looking to do something like
{% for interface in interfaces %}{{ interface.name }} {% endfor %}
or
" ".join([ interface['name'] for interface in interfaces ])
in Python.
but, AFAIK, you can't, or it is considered bad practice to, use jinja2 loops like this in a role's task/main.yml, and, as I said, it feels like it shouldn't be necessary to use a custom filter for this.
(This role isn't just configuring a DHCP server, so please don't just suggest a pre-existing role that does that, that wouldn't solve my issue).
Any non-ugly way to do this would be much appreciated, as would confirmation from people that there is no non-ugly way.
I am using Ansible 2.3, but I'm interested in answers still even if they only work in later versions.
Edit:
The following:
"{{ internal_interfaces | items2dict(key_name='name',value_name='name') | list | join(\" \") }}"
works, and is the least ugly I can think of. It makes a dictionary from the list, with both the key and values being from the name attribute of the dictionaries in the list, and then casts this dictionary to a list, which just gives a list of keys. I'd still like something less obtuse and ugly if anyione can think of anything, or for any Ansible gurus to reply if they think there is nothing nicer.
Map and join is what you require:
- set_fact:
interface_names: "{{ interfaces | map(attribute='name') | join(' ') }}"
OK. I am being stupid. There is a nice way to do this:
"{{ interfaces | map(attribute='name') | join(\" \") }}"
The output from map is a generator which generates a list of the interface names, and join turns this into the string I want. Perfect.

How to access value from YAML key/value pair list item?

If I have the following YAML list item (I think that's what it's called) which is expressed as a key/value pair, how do I read the value? I've been looking through the YAML documentation and didn't see this situation discussed.
---
apache_vhosts:
- servername: localhost
documentroot: "/var/www/html"
I want to do something like this and set install_path to "/var/www/html":
...
# This doesn't work
install_path: "{{ apache_vhosts.servername[documentroot] }}"
apache_vhosts is a list of mappings. You are trying to access the key servername from this list, but lists don't have keys.
Also I don't know what you want to achieve with .servername[documentroot].
My guess is you need:
apache_vhosts[0].documentroot (the first element of apache_vhosts -> key documentroot)
Please have a look at the Jinja2 Template Docs.
Edit: Probably better for your case: Ansible Templating

Resources