I am trying to create a ansible role which filter out host based on dictionary(passed to role) in comparison with host variables. If host variables contains key:value from dictionary it will added to filtered_host list. This is what I found till point but not getting desired output:
Ansible role:
---
- name: Filtering host
debug:
msg: "Here is the response {{ filters }} {{groups['all']}}"
- name: Setting filter
set_fact:
input_filter: "{{ filters }}"
- name: Setting filtered hosts
set_fact:
filtered_hosts: "{{ groups['all'] | map('extract', hostvars) | select('@ in filters') | map(attribute='inventory_hostname') | list }}"
vars:
input_filter: "{{ filters | to_json }}"
- name: Print filtered hosts
debug:
var: filtered_hosts
Here filters is a dictionary(filter) passed from playbook to test but actually i want to pass a dictionary, and this role should return list of hosts which contains key:value from this dictionary.
Sample dictionary:
{
"os_type":"linux",
"datacenter" : "REM",
"location" : "IND"
}
Sample Host variables:
HOST A variables:
{
"os_type":"linux",
"datacenter" : "REM",
"location" : "IND"
}
HOST B variables:
{
"os_type":"linux",
"datacenter" : "REM",
"location" : "USA",
"status" : "success"
}
HOST C variables:
{
"os_type":"linux",
"datacenter" : "REM",
"location" : "IND",
"status" : "success"
}
OUTPUT:
[Host A, Host C]
because both host contains key:value passed through dictionary. Also number of keys differ from different hosts.
Actual Scenario: I created a dynamic inventory which will fetch all host and host vars from DB based on some filters and assign it to inventory. Groups are created using some general info based on host vars. Now templates are created for different organizations/product who require to filter host based on some conditions(dynamically) out of the host vars. Since we cant create groups for everything, we decided to have a common ansible role which will act as a filter.
Within playbook there are multiple task will be performed on different host and to get those specific host some conditions are generated in the form of dictionary. These dictionary will be passed to ansible role to filter further based on the dynamic condition. The filter in given scenario is comparing 2 dictionaries to find out proper host for that task. A simplified example is what I have shown in the question.
Given the dictionary
filters:
os_type: linux
datacenter: REM
location: IND
In the vars, select the keys
fkeys: "{{ filters.keys() }}"
Select and join the values
fvals: "{{ filters.values()|join(',') }}"
gives
fvals: linux,REM,IND
In the tasks, create the variable my_fvals
- set_fact:
my_fvals: "{{ lookup('vars', *fkeys, default='UNDEF') }}"
- debug:
var: my_fvals
gives (abridged)
ok: [Host_A] =>
my_fvals: linux,REM,IND
ok: [Host_B] =>
my_fvals: linux,REM,USA
ok: [Host_C] =>
my_fvals: linux,REM,IND
Select hosts that match the criteria
filtered_hosts: "{{ hostvars|dict2items|
selectattr('value.my_fvals', '==', fvals)|
map(attribute='key') }}"
gives
filtered_hosts: [Host_A, Host_C]
- hosts: all
vars:
filters:
os_type: linux
datacenter: REM
location: IND
fkeys: "{{ filters.keys() }}"
fvals: "{{ filters.values()|join(',') }}"
filtered_hosts: "{{ hostvars|dict2items|
selectattr('value.my_fvals', '==', fvals)|
map(attribute='key') }}"
tasks:
- debug:
var: fvals
run_once: true
- set_fact:
my_fvals: "{{ lookup('vars', *fkeys, default='UNDEF') }}"
- debug:
var: my_fvals
- debug:
var: filtered_hosts|to_yaml
run_once: true
shell> tree roles
roles/
└── filtered_hosts
├── defaults
│ └── main.yml
└── tasks
└── main.yml
3 directories, 2 files
shell> cat roles/filtered_hosts/defaults/main.yml
fkeys: "{{ filters.keys() }}"
fvals: "{{ filters.values()|join(',') }}"
shell> cat roles/filtered_hosts/tasks/main.yml
- set_fact:
my_fvals: "{{ lookup('vars', *fkeys, default='UNDEF') }}"
- set_fact:
filtered_hosts: "{{ hostvars|dict2items|
selectattr('value.my_fvals', '==', fvals)|
map(attribute='key') }}"
run_once: true
The playbook
shell> cat pb.yml
- hosts: all
vars:
filters:
os_type: linux
datacenter: REM
location: IND
roles:
- filtered_hosts
tasks:
- debug:
var: filtered_hosts|to_yaml
run_once: true
gives
shell> ansible-playbook pb.yml
PLAY [all] ***********************************************************************************
TASK [filtered_hosts : set_fact] *************************************************************
ok: [Host_A]
ok: [Host_C]
ok: [Host_B]
TASK [filtered_hosts : set_fact] *************************************************************
ok: [Host_A]
TASK [debug] *********************************************************************************
ok: [Host_A] =>
filtered_hosts|to_yaml: |-
[Host_A, Host_C]
PLAY RECAP ***********************************************************************************
Host_A: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Host_B: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Host_C: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shell> tree inventory
inventory/
├── 01-hosts
└── 02-constructed.yml
0 directories, 2 files
shell> cat inventory/01-hosts
Host_A
Host_B
Host_C
shell> cat inventory/02-constructed.yml
plugin: ansible.builtin.constructed
use_extra_vars: true
use_vars_plugins: true
strict: true
compose:
fkeys: filters.keys()
fvals: filters.values()|join(',')
my_fvals: lookup('vars', *filters.keys(), default='UNDEF')
groups:
filtered_hosts: my_fvals == fvals
because in this inventory plugin lookups were disabled from templating
shell> ansible-inventory -i inventory -e @filters.yml --list --yaml
[WARNING]: * Failed to parse /export/scratch/tmp7/test-477/inventory/02-constructed.yml with auto plugin: failed to parse /export/scratch/tmp7/test-477/inventory/02-constructed.yml: Could not set my_fvals for host Host_A: The lookup
vars
was found, however lookups were disabled from templating . Could not set my_fvals for host Host_A: The lookupvars
was found, however lookups were disabled from templating
[WARNING]: * Failed to parse /export/scratch/tmp7/test-477/inventory/02-constructed.yml with yaml plugin: Plugin configuration YAML file, not YAML inventory
[WARNING]: * Failed to parse /export/scratch/tmp7/test-477/inventory/02-constructed.yml with ini plugin: Invalid host pattern 'plugin:' supplied, ending in ':' is not allowed, this character is reserved to provide a port.
[WARNING]: Unable to parse /export/scratch/tmp7/test-477/inventory/02-constructed.yml as an inventory source
all:
children:
ungrouped:
hosts:
Host_A:
datacenter: REM
filters: &id001
datacenter: REM
location: IND
os_type: linux
fkeys:
- os_type
- datacenter
- location
fvals: linux,REM,IND
location: IND
os_type: linux
Host_B:
datacenter: REM
filters: *id001
location: USA
os_type: linux
status: success
Host_C:
datacenter: REM
filters: *id001
location: IND
os_type: linux
status: success