Reputation: 187
I have an Ansible host with variables like below. Lets assume that the 53dns rule was originally a group variable overwritten to null by a host variable; it would show up as None in Ansible.
firewall__rule_02loopback: |
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
firewall__rule_03icmp: |
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
firewall__rule_53dns: ~
I am looking for an Ansible/Jinja expression to convert these flat variables into a nested one such that the final product could look something like:
firewall__rules:
- name: 02loopback
content: |
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
- name: 03icmp
content: |
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
- name: 53dns
content: None
I have been extract the variables into a single nested list of dicts with this:
set_fact:
firewall__rules: '{{ hostvars[inventory_hostname]
| dict2items("name", "content")
| selectattr("name", "search", "^firewall__rule") }}'
However, the name
field of every dict is still prefixed with firewall__rule_
, how can I perform a transform on the name
value for every dict inside the list?
Upvotes: 1
Views: 139
Reputation: 68074
The set_fact below does the job
- set_fact:
firewall__rules: "{{ firewall__rules|default([]) +
[{'name': key, 'content': val}] }}"
loop: "{{ query('varnames', 'firewall__rule_*') }}"
vars:
key: "{{ item.split('_')|last }}"
val: "{{ lookup('vars', item) }}"
gives
firewall__rules:
- content: |-
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
name: 02loopback
- content: |-
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
name: 03icmp
- content: ''
name: 53dns
Example of a complete playbook for testing
- hosts: localhost
vars:
firewall__rule_02loopback: |
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
firewall__rule_03icmp: |
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
firewall__rule_53dns: ~
tasks:
- set_fact:
firewall__rules: "{{ firewall__rules|default([]) +
[{'name': key, 'content': val}] }}"
loop: "{{ query('varnames', 'firewall__rule_*') }}"
vars:
key: "{{ item.split('_')|last }}"
val: "{{ lookup('vars', item) }}"
- debug:
var: firewall__rules
You can avoid the iteration of set_fact and hide the logic in vars. Declare the variables
fr_vars: '^firewall__rule_.*$'
fr_keys: "{{ query('varnames', fr_vars)|map('split', '_')|map('last') }}"
fr_vals: "{{ lookup('vars', *q('varnames', fr_vars)) }}"
fr_dict: "{{ dict(fr_keys|zip(fr_vals)) }}"
give the list of the keys
fr_keys:
- 02loopback
- 03icmp
- 53dns
, the list of the values
fr_vals:
- |-
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
- |-
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
- null
, and the dictionary
fr_dict:
02loopback: |-
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
03icmp: |-
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
53dns: null
Convert the dictionary to list
fr_list: "{{ fr_dict|
dict2items(key_name='name', value_name='content') }}"
gives the expected result
fr_list:
- content: |-
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
name: 02loopback
- content: |-
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
name: 03icmp
- content: null
name: 53dns
Example of a complete playbook for testing
- hosts: localhost
vars:
firewall__rule_02loopback: |
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
firewall__rule_03icmp: |
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
firewall__rule_53dns: ~
fr_vars: '^firewall__rule_.*$'
fr_keys: "{{ query('varnames', fr_vars)|map('split', '_')|map('last') }}"
fr_vals: "{{ lookup('vars', *q('varnames', fr_vars)) }}"
fr_dict: "{{ dict(fr_keys|zip(fr_vals)) }}"
fr_list: "{{ fr_dict|dict2items(key_name='name', value_name='content') }}"
tasks:
- debug:
var: fr_keys
- debug:
var: fr_vals
- debug:
var: fr_dict
- debug:
var: fr_list
Upvotes: 1