get child elements of a dictonairy - ansible

I have a network with different hosts and different networks and I want to use ansible to ping each host in each network.
In my host_vars directory all informations about every host and its network are stored in its own file. A hostfile looks like this:
bond:
- conn_name: net1
ip4: '10.10.44.21'
gw4: '10.10.44.1'
- conn_name: net2
ip4: '10.10.47.21'
gw4: '10.10.47.1'
- conn_name: net3
ip4: '10.10.46.21'
gw4: '10.10.46.1'
bond_slave:
- conn_name: eno1
ifname: eno1
master: net1
- conn_name: eno2
ifname: eno2
master: net2
- conn_name: ens2f1
ifname: ens2f1
master: net2
- conn_name: ens2f0
ifname: ens2f0
master: net2
- conn_name: ens4f1
ifname: ens4f1
master: net3
- conn_name: ens4f0
ifname: ens4f0
master: net3
My purpose is to get every ip4-value and ping it from every host. what i can do is include all files from host_vars directory and ping the ip-adress of the first bond item of each file.
---
- hosts: all
- name: Include information about all hosts
include_vars: ../../host_vars/{{ item }}.yaml
with_items: hosts
register: "r"
- name: execute ping
command: ping {{ bond[0].ip4 }} -c 3
with_items: "{{ r.results }}"
But the bond can have from 2 to 4 networks. How do i loop through each bond to access all ip-address?
Solution
Even though the snippet of Konstantin Suvorov wasn't working for me, it helped me to find the right solution:
- name: execute ping
command: ping {{ item.ip4 }} -c 3
with_items: "{{ r.results | map(attribute='ansible_facts.bond') | list }}"

If you have bond var defined for each host via host_vars, you can use hostvars magic variable (no need to do this ugly include task):
- name: ping everything
command: ping {{ item }} -c 3
with_items: "{{ play_hosts | map('extract',hostvars,'bond') | list }}"
Actually this Jinja2 expression will return a list of lists, but with_items flattends them, so you end up with long list all elements from every bond of every host in current play.

Related

Ansible how to loop with_dict

I want to run these mikrotik commands using ansible
interface bridge port add bridge=bridge1 ingress-filtering=no interface=ether3
interface bridge port add bridge=bridge1 ingress-filtering=no interface=ether4
BUT NOT
interface bridge port add bridge=bridge1 ingress-filtering=no interface=ether3,ether4
my playbook run wrong command
How to fix that
tq
`# cat main.yml
- hosts: mikrotiks
gather_facts: no
connection: network_cli
vars:
ansible_network_os: routeros
vlans:
- 10:
interfaces:
- ether3
- ether4
untagged_interfaces: ether2
tagged_interfaces: bridge1,ether3,ether4
network: 10.0.10.0
netmask: 24
ip_address1: 10.0.10.1
ip_address2: 10.0.10.2
dns: 192.168.88.1
dhcp_pool1: 10.0.10.101-10.0.10.150
dhcp_pool2: 10.0.10.151-10.0.10.200
leasetime: 1d
- 20:
interfaces:
- ether3
- ether4
untagged_interfaces: ether2
tagged_interfaces: bridge1,ether3,ether4
network: 10.0.20.0
netmask: 24
ip_address1: 10.0.20.1
ip_address2: 10.0.20.2
dns: 192.168.88.1
dhcp_pool1: 10.0.20.101-10.0.20.150
dhcp_pool2: 10.0.20.151-10.0.20.200
leasetime: 1d
tasks:
- import_tasks: vlan.yml
cat vlan.yml
- name: create vlans on R1
routeros_command:
commands:
- "interface bridge port add bridge=bridge1 ingress-filtering=no interface={{ item.value.interfaces }}"
- "interface bridge port add bridge=bridge1 frame-types=admit-only-untagged-and-priority-tagged pvid=10 interface={{ item.value.interfaces }}"
with_dict: "{{ vlans }}"\`
EDIT: my mikrotik commands should be like this
R1 /interface bridge port add bridge=bridge1 ingress-filtering=no interface=ether3 add bridge=bridge1 ingress-filtering=no interface=ether4 add bridge=bridge1 frame-types=admit-only-untagged-and-priority-tagged interface=ether2 pvid=10 /interface bridge vlan add bridge=bridge1 tagged=bridge1,ether3,ether4 untagged=ether2 vlan-ids=10 add bridge=bridge1 tagged=bridge1,ether3,ether4 vlan-ids=20
when I use these
\`routeros_command:
commands:
- "/interface bridge port add bridge=bridge1 ingress-filtering=no interface={{ item.1 }}"
- "/interface bridge port add bridge=bridge1 frame-types=admit-only-untagged-and-priority-tagged pvid=10 interface={{ item.1 }}"
with_subelements:
- "{{ vlans }}"
- interfaces`
I got this error
`fatal: [R1]: FAILED! => {
"msg": "could not find 'interfaces' key in iterated item '{10: {'interfaces': ['ether3', 'ether4'], 'untagged_interfaces': 'ether2', 'tagged_interfaces': 'bridge1,ether3,ether4', 'network': '10.0.10.0', 'netmask': 24, 'ip_address1': '10.0.10.1', 'ip_address2': '10.0.10.2', 'dns': '192.168.88.1', 'dhcp_pool1': '10.0.10.101-10.0.10.150', 'dhcp_pool2': '10.0.10.151-10.0.10.200', 'leasetime': '1d'}}'"`
Iterate with_subelements, e.g.
- debug:
msg: "{{ item.0.keys()|first }} ... interface={{ item.1 }}"
with_subelements:
- "{{ vlans }}"
- interfaces
gives
msg: 10 ... interface=ether3
msg: 10 ... interface=ether4
msg: 20 ... interface=ether3
msg: 20 ... interface=ether4

Ansible: need to combine information from 2 files for a task later

I'm running Ansible 2.7.9 on Red Hat Linux 7.4. In my playbook, I need to combine information in two yml files into one data structure that I can then iterate over in a task.
For example, in one .yml file I might have:
---
network_interfaces:
big_computer:
- name: eth0.10
- name: eth1.20
- name: ens224
- name: bond1
type: bond
device: intel
In another .yml file called definitions.yml I might have:
---
eth0.10:
type: vlan
slave: true
master: bond1
subnet: 192.168.10.0/24
eth1.20:
type: vlan
slave: true
master: bond1
subnet: 192.168.20.0/24
ens224:
type: ethernet
subnet: 172.19.22.0/23
bond1:
type: bond
I am able to set, for example, a variable network_interfaces_list from network_interfaces[computer_type]
When computer_type is big_computer, then, I want to take the names of network_interfaces_list, the data from network_interfaces_listand the data from definitions.yml, and create this combined data structure. Then use that data structure in a task. Note that some information may be included in the network_interfaces file, but some of it is found in the definitions file. Or it may remain undefined- there is no "slave" or "master" setting for the bond1 interface, for example.
network_interfaces_list should end up looking like this:
- eth0.10:
type: vlan
slave: true
master: bond1
subnet: 192.168.10.0/24
- eth1.20:
type: vlan
slave: true
master: bond1
subnet: 192.168.20.0/24
- ens224:
type: ethernet
subnet: 172.19.22.0/23
- bond1:
type: bond
device: intel
Entries missing in a dictionary for any given list item are considered unimportant for that item and are essentially null.
I've been wracking my brains all day on this one. I can debug either entry, but trying to put them together- especially without Ansible giving me errors about missing dictionary keys- I'm finding impossible to do.
Essentially what I want is a short file of information, and for those items that are considered unimportant at first glance, I want to put them in another layer that we can peruse to gather more detail, if we wish.
I have done this successfully:
- debug:
msg: "interface_list: {{ network_interfaces_list }}"
- name: MIKE debug
debug:
msg: "thingy: {{ interface['name'] }} \
{{ interface['type']|default('NULL') }} \
{{ interface['device']|default('NULL') }}"
loop: "{{ network_interfaces_list }}"
loop_control:
loop_var: interface
- name: MIKE2 debug
debug:
msg: "thing2: {{ lookup ( 'vars', interface['name'], default='XXX' ) }}"
loop: "{{ network_interfaces_list }}"
loop_control:
loop_var: interface
~~~
But I'm stuck in trying to get merge the members of `interface['name']` from the two data structures.
For example
- set_fact:
network_interfaces_list: "{{ network_interfaces_list|default([]) +
[{item: _val}] }}"
loop: "{{ _keys }}"
vars:
computer_type: big_computer
_keys: "{{ network_interfaces[computer_type]|map(attribute='name')|list }}"
_vals: "{{ network_interfaces[computer_type] }}"
_dict: "{{ dict(_keys|zip(_vals)) }}"
_val: "{{ _dict[item]|combine(definitions[item]) }}"
gives
network_interfaces_list:
- eth0.10:
master: bond1
name: eth0.10
slave: true
subnet: 192.168.10.0/24
type: vlan
- eth1.20:
master: bond1
name: eth1.20
slave: true
subnet: 192.168.20.0/24
type: vlan
- ens224:
name: ens224
subnet: 172.19.22.0/23
type: ethernet
- bond1:
device: intel
name: bond1
type: bond
Notes
The dictionaries keep the redundant attribute name
The names of the variables in definitions.yml are not valid. See Creating valid variable names. Include them in a dictionary, e.g.
- include_vars:
file: definitions.yml
name: definitions
gives
definitions:
bond1:
type: bond
ens224:
subnet: 172.19.22.0/23
type: ethernet
eth0.10:
master: bond1
slave: true
subnet: 192.168.10.0/24
type: vlan
eth1.20:
master: bond1
slave: true
subnet: 192.168.20.0/24
type: vlan
In some cases a dictionary might be a better structure, e.g.
- set_fact:
network_interfaces_dict: "{{ network_interfaces_dict|default({})|
combine({item: _val}) }}"
...
would give
network_interfaces_dict:
bond1:
device: intel
name: bond1
type: bond
ens224:
name: ens224
subnet: 172.19.22.0/23
type: ethernet
eth0.10:
master: bond1
name: eth0.10
slave: true
subnet: 192.168.10.0/24
type: vlan
eth1.20:
master: bond1
name: eth1.20
slave: true
subnet: 192.168.20.0/24
type: vlan

Assign item to a var with_items in ansible

I am trying to create a playbook to find out on which openstack server vm is running on. I have created a list of openstack servers in vars and used delegate_to with with_items to iterate through until find vm. I am using wc -l at the end of command and 1 will be success. The aim is, once os-server is found, store servername into a var so this can be used for rest of tasks in playbook. I am unable to get the os server name in a var from the list. I am not an ansible expert. Can anyone help to achieve this? Thanks
- hosts: localhost
vars:
openstack:
- reg1
- reg2
- reg3
- reg4
tasks:
- name: Command to find os server where vm exists
shell: somecommand-to-check-if-vm-exist | wc -l
delegate_to: "{{ item }}"
with_items: "{{ openstack }}"
register: found_server
retries: 1
delay: 1
until: found_server.stdout != "1"
- debug: var=found_server
- name: set fact
set_fact: os-server = "{{ item.item }}"
when: item.stdout == "1"
with_items: "{{ found_server.results }}"
register: var2
- name: debug var
debug: var=var2
- debug: var=os-server
There's no need to retry/until here and for the second loop as well.
Try this:
- hosts: localhost
vars:
openstack: [reg1, reg2, reg3, reg4]
tasks:
- name: Command to find os server where vm exists
shell: somecommand-to-check-if-vm-exist | wc -l
delegate_to: "{{ item }}"
with_items: "{{ openstack }}"
register: vm_check
- name: set fact
set_fact:
os_server: "{{ (vm_check.results | selectattr('stdout','equalto','1') | list | first).item }}"
- name: debug var
debug:
msg: "{{ os_server }}"
This will register results from every server into vm_check.results, and then just select elements with stdout set to 1, take first element of it it (I suppose you always have one server with VM), and get .item of this element which contains the item of original loop (in our case it is server's name).

ansible cisco modifying multiple object-groups with single playbook lines

I have following playbook to modify ASA object-group:
---
- hosts: us_asa
connection: local
gather_facts: false
tasks:
- name: change config
asa_config:
auth_pass: "{{ ansible_ssh_password }}"
username: "{{ ansible_ssh_user }}"
password: "{{ ansible_ssh_password }}"
authorize: yes
timeout: 45
lines:
- network-object host 1.2.3.4
- network-object host 2.3.2.3
parents: ['object-group network BAD_IPs']
This works fine for single group.
Any suggestion how to modify multiple groups with same connection? If I add another object-group after parents: ['object-group network BAD_IPs'] example:
---
- hosts: us_asa
connection: local
gather_facts: false
tasks:
- name: change config
asa_config:
auth_pass: "{{ ansible_ssh_password }}"
username: "{{ ansible_ssh_user }}"
password: "{{ ansible_ssh_password }}"
authorize: yes
timeout: 45
lines:
- network-object host 1.2.3.4
- network-object host 2.3.2.3
parents: ['object-group network BAD_IPs']
- network-object host 4.4.4.4
parents: ['object-group network Good_IPs']
This fails
The offending line appears to be:
parents: ['object-group network BAD_IPs']
- network-object host 4.4.4.4
^ here
Any recommendation on syntax I should use?
Thank you in advance!
You just have a basic YAML syntax error there. A YAML dictionary key with a list value looks either like this:
key: [item1, item2, item3]
Or like this:
key:
- item1
- item2
- item3
You have some weird combination of the two:
parents: ['object-group network BAD_IPs']
- network-object host 4.4.4.4
I don't know exactly what structure you want, but what you have there is simply invalid.

iterate over the hostname stored in var_files

I have two type of server host names added in the ansible main.yml var file:
main.yml file:
foo_server1: 10.10.1.1
foo_server2: 10.10.1.2
bar_server1: 192.168.1.3
bar_server2: 192.168.1.4
bar_server3: 192.168.1.5
I am having an ansible playbook which essentially runs on foo_server1 and initializes/formats all other servers in the list one at a time - starting with foo_server2 then bar_server1, bar_server2 and so on...
---
- name: Reading variables from var files
hosts: localhost
connection: local
vars_files:
- main.yml
tasks:
- name: Initialize foo server2
command: initialize --host1 {{foo_server1}} to --host2 {{foo_server2}}
- name: Initialize bar server1
command: initialize --host1 {{foo_server1}} to --host2 {{bar_server1}}
- name: Initialize bar server2
command: initialize --host1 {{foo_server1}} to --host2 {{bar_server2}}
- name: Initialize bar server3
command: initialize --host1 {{foo_server1}} to --host2 {{bar_server3}}
I dont want to add multiple lines in the playbook for each server rather wants to loop over the host names from the variable file. I am not sure how i would get this done..i am trying to loop over the hostname.. tried something below but no luck as i am getting undefined variable name..
---
server_list:
foo_server1: 10.10.1.1
foo_server2: 10.10.1.2
bar_server1: 192.168.1.3
bar_server2: 192.168.1.4
bar_server3: 192.168.1.5
Ansible playbook...
---
- hosts: localhost
gather_facts: no
vars_files:
- input.yml
tasks:
- name: Enable replication
local_action: shell initialize --host1 {{item.foo_server1}} --host2 {{item.foo_server2}}
with_items:
- "{{ server_list }}"
Can some one please suggest how can i run the same command on multiple servers. Would appreciate any help offered..
Here is an example for you:
---
- hosts: localhost
gather_facts: no
vars:
servers:
foo_server1: 10.10.1.1
foo_server2: 10.10.1.2
bar_server1: 192.168.1.3
bar_server2: 192.168.1.4
bar_server3: 192.168.1.5
tasks:
- debug:
msg: shell initialize --host1 {{ servers.foo_server1 }} --host2 {{ item.value }}
when: item.key != 'foo_server1'
with_dict: "{{ servers }}"

Resources