Search Dictionary Values in Ansible - ansible

Having a dictionary like this:
ossec_datacenter:
atlanta:
hostname: 'server1.fakedomain.net'
ip: '192.168.12.170'
port: '1515'
miami:
hostname: 'server2.fakedomain.net'
ip: '192.168.20.31'
port: '1514'
dallas:
hostname: 'server2.fakedomain.net'
ip: '192.168.20.20'
port: '1515'
How would I search for all values in this dictionary in my when clause?
I can access variables using ossec_datacenter[ossec_dc]['hostname']
But I want so search all values to make sure no matches are present.
In other words I don't want the inventory_hostname nor the IP to be found anywhere in that data structure.

If you want to use json_query (requires ansible 2.2) you can do this to search ip and hostname:
- name: find inventory_hostname
set_fact:
found: True
with_items:
- "{{ ossec_datacenter | json_query('*.ip') }}"
- "{{ ossec_datacenter | json_query('*.hostname') }}"
when: "inventory_hostname == item"
or if you want to search any of the keys in the datacenters (ip, hostname, or port):
- name: find inventory_hostname
set_fact:
found: True
with_items: "{{ ossec_datacenter | json_query('*.*') }}"
when: "inventory_hostname == item"
and then test the found var.

Here's a condition for hostname:
when: inventory_hostname not in (ossec_datacenter.values() | map(attribute='hostname') | list)
Use ansible_default_ipv4.address or some other fact about IP address and reduce your dict with map(attribute='ip') to search for IP addresses.

Related

Ansible: Delete interfaces that are not in my host_vars

I've an ansible playbook that creates l3_subinterfaces on a Palo Alto firewall, the creating is based on the host_vars of the firewall.
- l3_subinterfaces:
- tag: "9"
vr_name: "vr_production"
ip: "10.0.9.2/24"
comment: "VLAN9 Subinterface"
parent_if: "ethernet1/1"
zone: "Infrastructuur"
- tag: "13"
vr_name: "vr_production"
ip: "10.0.13.2/24"
comment: "VLAN13 Subinterface"
parent_if: "ethernet1/2"
zone: "Infrastructuur"
And the playbook task which create the interfaces:
- name: Configure l3_subinterfaces
panos_l3_subinterface:
provider: "{{ panos_provider }}"
name: "{{ item.parent_if }}.{{ item.tag }}"
tag: "{{ item.tag }}"
ip: ["{{ item.ip }}"]
vr_name: "{{ item.vr_name }}"
zone_name: "{{ item.zone }}"
comment: "{{ item.comment }}"
enable_dhcp: false
with_items:
- "{{ l3_subinterfaces }}"
when: l3_subinterfaces is defined
So at this point everything is working fine. However the thing I'm trying to achieve is holding the state of the firewall in the Ansible inventory.
So for example I'm now delete the l3_subinterface with tag 13 and run the task again, it still have the l3_subinterface with tag 13 configured on the Palo Alto firewall.
I'm trying to figure out how I can delete the l3_subinterfaces which exists on the firewall, but doesn't exists in my host_vars. I think I need to compare something like te facts with the host_vars, but really have no clue how to do it.
Actually I've already found my own answer. The solution is to compare the list l3_subinterfaces against the palo alto interfaces:
- name: Get interfaces facts
panos_facts:
provider: '{{ panos_provider }}'
gather_subset: ['interfaces']
- name: Delete unused l3_subinterfaces
panos_l3_subinterface:
provider: "{{ panos_provider }}"
name: "{{ item }}"
tag: "{{ item|regex_search('\\d+$') }}"
state: "absent"
with_items:
- "{{ ansible_net_interfaces|selectattr('tag', 'defined')|map(attribute='name')|list | difference(l3_subinterfaces|map(attribute='name')|list) }}"

Ansible to get the netmask in CIDR

In order to get the value "24" corresponding to an IP address/networkmask, I have this working piece of code:
- set_fact:
ip: "{{ ansible_default_ipv4.address }}/{{ansible_default_ipv4.netmask }}"
- set_fact:
mask_cidr: "{{ ip | ipaddr('prefix') }}"
Where ansible_default_ipv4.address = 172.16.1.67 and ansible_default_ipv4.netmask is 255.255.255.0 as per gather_facts or setup module.
I've tried different things to make this code "smarter" so I only need to set 1 fact instead of 2, but my filtering abilities are not strong.
Any ideas in how to convert these two facts in a smarter fact that do both things?
Easiest and the most clean way is through a helper variable:
- set_fact:
mask_cidr: "{{ ip | ipaddr('prefix') }}"
vars:
ip: "{{ ansible_default_ipv4.address }}/{{ansible_default_ipv4.netmask }}"
If you insist on writing a single template:
- sef_fact:
ip: "{{ (ansible_default_ipv4.address + '/' + ansible_default_ipv4.netmask) | ipaddr('prefix') }}"
I'm using this:
mynetwork = {{ (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ipaddr('network/prefix') }}

Omit os_subnet's variable dns_nameservers in Ansible

I would like to omit the dns_nameservers variable from the following Openstack function if the value does not appear in the variable file:
os_subnet:
cloud: "{{ item.cloud }}"
state: present
validate_certs: no
no_gateway_ip: yes
dns_nameservers:
- "{{ item.dns | default(None) }}"
enable_dhcp: yes
name: "{{ item.subnet }}"
network_name: "{{ item.network }}"
cidr: "{{ item.cidr }}"
allocation_pool_start: "{{ item.allocation_pool_start }}"
allocation_pool_end: "{{ item.allocation_pool_end }}"
host_routes: "{{ item.host_routes | default(omit) }}"
with_items:
- "{{ subnets }}"
tags: subnets
Until now, I have tried to omit it with | default(omit) and | default(None), but it is not working. Is any filter that might help or any other way?
EDIT:
Variable file:
- cloud: tenant1
network: nw
subnet: nw_subnet
cidr: 172.12.17.64/26
dns:
- 8.8.8.8
- 8.8.8.9
allocation_pool_start: 172.12.17.68
allocation_pool_end: 172.12.17.70
host_routes:
- destination: 0.0.0.0/0
nexthop: 172.12.17.65
I am getting the following error:
Reason: '[u'8.8.8.8', u'8.8.8.9']' is not a valid
nameserver. '[u'8.8.8.8', u'8.8.8.9']' is not a valid
IP address.\", \"type\": \"HTTPBadRequest\", \"detail\": \"\"}}"}
You want to either pass a list with a single element or pass an omit keyword (placeholder object), which tells Ansible not to pass the whole parameter (dns_nameservers here) to the module:
dns_nameservers: "{{ [item.dns] if item.dns is defined else omit }}"
In your example, if item.dns was undefined, you passed a list with a single element being an omit placeholder. In such case the dns_nameservers parameter is defined (that list which is hardcoded in the code) and behaviour is undefined (likely depends on module).

Ansible Pass multiple vaules with single defined Variable

I need to add a server to service group every time I create a new server using the following task.
Task
- name: Create a service group
a10_service_group_v3:
validate_certs: no
host: "{{ item.0.a10_host }}"
state: "{{ item.1.service_state }}"
username: "{{ item.0.user }}"
password: "{{ item.0.pass }}"
service_group: "{{ item.1.group_name }}"
reset_on_server_selection_fail: yes
servers:
- name: "{{ item.1.server_name1 }}"
port: "{{ item.1.server_port1 }}"
overwrite: yes
write_config: yes
ignore_errors: yes
with_nested:
- "{{ a10 }}"
- "{{ service_group }}"
Variables:
service_group:
- group_name: bif_sg
service_state: present
server_name1: bif01
server_port1: 80
I need help with passing variables for server_name and server_port, let's say If I have 3 servers to add to service group in the task I need to add 3 times server_name1, server_port1
server_name2, server_port2 ......
Everytime I add server I need to update in the task as well :(
Is there a way to pass multiple times sever_name and serer_port with single defined value in the task.
I you expect server_group to have a list of servers, refactor your variable to have a list of servers and not a bunch of separate subkeys:
service_group:
- group_name: bif_sg
service_state: present
servers:
- name: bif01
port: 80
- name: bif02
port: 8080
And in your task:
...
servers: "{{ item.1.servers }}"
...

ansibl inventory : create temporary inventory with multiple groups with add_host or group_by

I have an assignment to provision {{ create new hosts }} according to a given
invonteroy file,
example
[SET1]
1.1.1.1 new_ip=1.1.1.1
[SET2]
1.1.1.2 new_ip=1.1.1.2
[SET3]
1.1.1.3 new_ip=1.1.1.3
Here is the problem. The inventory file contains set of ip addresses that do not exists yet.
What I want to do
- connect to vsphere host(5.5)
- create new machines based on amount of hosts in the inventory
- change the machines ip address to match the one in the given inventory
modules like
vmware_shell
vmware_guest
requires administrative permissions which I don't have.
The only way to hack around this issue is to try and create an in memory or temporary inventory during provisioning task.
Here is my role:
- vsphere_guest:
vcenter_hostname: {{ vcenter }}
validate_certs: no
username: "{{ vsphere.username }}"
password: "{{ vsphere.password }}"
guest: "{{ item }}"
from_template: yes
template_src: Centos_base
resource_pool: "/Resources"
esxi:
datacenter: Remote
hostname: {{ esx_host }}
with_items:
- "{{ play_hosts }}"
- name: wait for power on to finish
pause: seconds=5
- name: get facts from vm
vsphere_guest:
validate_certs: False
vcenter_hostname: {{ vcenter }}
username: "{{ vsphere.username }}"
password: "{{ vsphere.password }}"
guest: "{{ item }}"
vmware_guest_facts: yes
with_items:
- "{{ play_hosts }}"
register: vm_facts
until: vm_facts.ansible_facts.hw_eth0.ipaddresses[0] is defined
retries: 10
delay: 10
- name: grab new ipaddresses
set_fact: myIp="{{ inventory_hostname }}"
- name: Add production VMs to inventory
add_host: hostname={{item.ansible_facts.hw_eth0.ipaddresses[0] }} groups=temp
new_ip={{ myIp }}
with_items: "{{ vm_facts.results }}"
ignore_errors: yes
my question:
is there any way to create a temp inventory with multiple groups and mutilple variables that matches the given inventory!?
update
I hacked a way around it:
- name: Add production VMs to inventory
add_host: hostname={{item.ansible_facts.hw_eth0.ipaddresses[0] }} groups=temp
new_ip="{{ item.ansible_facts.hw_name }}"
with_items: "{{ vm_facts.results }}"
ignore_errors: yes
this works since the machine name is equal to {{ play_host }}

Resources