I'm trying to write a role that finds difference between records in DN servers and records in Git. Some of them were added manually via Web interface. For that purpose in my Ansible playbook I dynamically generate variables like this:
- hosts: dn_servers
vars:
domains:
int_domain: int.domain.com
ext_domain: domain.com
tasks:
- name: Generates list of hostnames for every domain present in var domain
ansible.builtin.set_fact:
"{{ item.key }}_git_records": "{{ list of A records generated with the help of item.value }}"
loop: "{{ domains | dict2items }}"
Then I try reuse these vars inside the Ansible filter rejectattr
:
- name: Find difference between remote hosts and Git file
ansible.builtin.set_fact:
"{{ item.key }}_inconsistent_hostnames": "{{ remote_records | selectattr('domain', '==', item.value) | rejectattr('hostname', 'in', (item.key ~ '_git_records')) }}"
loop: "{{ domains | dict2items }}"
But filter rejectattr
fail filtering. I'm sure that problem is here (item.key ~ '_gitlab_hostnames')
, because it works as expected after I hardcoded third argument.
Maybe the issue is that after concatenation of variable and string rejectattr
perceives third argument as a string instead of variable?
If yes, how it could be fixed?
UPD remote_records
list example:
- domain: int.domain.com
enabled: true
hostname: hostname1
record_type: A
value: 172.16.1.11
- domain: domain.com
enabled: true
hostname: hostname2
record_type: A
value: 172.16.1.22
You're looking for indirect addressing. The expression item.key ~ '_git_records'
creates the name of the variable, but doesn't deliver the value
rejectattr('hostname', 'in', (item.key ~ '_git_records'))
The third argument must be a list. For example, given the data
domains:
int_domain: int.domain.com
ext_domain: domain.com
remote_records:
- {domain: int.domain.com, hostname: h1}
- {domain: int.domain.com, hostname: h9}
int_domain_git_records: [h1, h2, h3]
ext_domain_git_records: [h6, h7, h8]
You have to use the vars lookup to address the lists int_domain_git_records, ext_domain_git_records, ... in the loop below
- debug:
msg: "{{ remote_records |
selectattr('domain', '==', item.value) |
rejectattr('hostname', 'in', lookup('vars', item.key ~ '_git_records')) }}"
loop: "{{ domains | dict2items }}"
gives (abridged)
msg:
- domain: int.domain.com
hostname: h9
msg: []
Example of a complete playbook for testing
- hosts: localhost
vars:
domains:
int_domain: int.domain.com
ext_domain: domain.com
remote_records:
- {domain: int.domain.com, hostname: h1}
- {domain: int.domain.com, hostname: h9}
int_domain_git_records: [h1, h2, h3]
ext_domain_git_records: [h6, h7, h8]
tasks:
- debug:
msg: "{{ lookup('vars', 'int_domain' ~ '_git_records') }}"
- debug:
msg: "{{ remote_records |
selectattr('domain', '==', item.value) |
rejectattr('hostname', 'in', lookup('vars', item.key ~ '_git_records')) }}"
loop: "{{ domains | dict2items }}"
Optionally, you can create a dictionary to speed up the searching
git_records: "{{ dict(domains.values() |
zip(q('vars', *q('varnames', domains.keys() | join('|'))))) }}"
gives
git_records:
domain.com: [h6, h7, h8]
int.domain.com: [h1, h2, h3]
Then, use this dictionary instead of the lookup. The iteration below gives the same result
- debug:
msg: "{{ remote_records |
selectattr('domain', '==', item.value ) |
rejectattr('hostname', 'in', git_records[item.value]) }}"
loop: "{{ domains | dict2items }}"
Ideally, replace the *_git_records with the dictionary git_records and forget all this lookup stuff.