ansible with_dict fails when provided with set_fact variable - dictionary

I am trying to dynamically provide dictionary name for interface variables.
My ansible task looks like this.
- name: Setting interface list
set_fact:
one_fact: "{{ host_name }}_interfaces"
- name: deb
debug: var={{ one_fact }}
- name: Managing Interfaces
ios_interface:
enabled: "{{ item['value']['enabled'] }}"
name: "{{ item['key'] }}"
state: "{{ item['value']['state'] }}"
with_dict: "{{ one_fact }}"
Dictionary looks something like this
---
h1_interfaces:
Ethernet1/1:
description: Firewall
enabled: true
speed: auto
state: present
Ethernet1/2:
description: asd
enabled: true
speed: auto
state: present
h2_interfaces:
Ethernet1/1:
description: Firewall
enabled: true
speed: auto
state: present
Ethernet1/2:
description: asd
enabled: true
speed: auto
state: present
When i set with_dict: {{ one_fact }} i get an error FAILED! => {"msg": "with_dict expects a dict"}
But when i provide with with_dict: {{ h1_interfaces }} it works like a charm. What am i doing wrong?

Apparently you have a variable host_name too, which is set to h1 or h2, and you want to access the dictionaries: h1_interfaces/h2_interfaces.
To construct dynamically the variable name and access its value, you should use the lookup plugin, please see below task:
- name: Setting interface list
set_fact:
one_fact: "{{ lookup('vars', myvar + '_interfaces') }}"
vars:
myvar: "{{ host_name }}"
and a slightly altered playbook to demonstrate the result:
playbook:
---
- hosts: localhost
gather_facts: false
vars:
host_name: h1
h1_interfaces:
Ethernet1/1:
description: Firewall
enabled: true
speed: auto
state: present
Ethernet1/2:
description: asd
enabled: true
speed: auto
state: present
h2_interfaces:
Ethernet1/1:
description: Firewall
enabled: true
speed: auto
state: present
Ethernet1/2:
description: asd
enabled: true
speed: auto
state: present
tasks:
- name: Setting interface list
set_fact:
one_fact: "{{ lookup('vars', myvar + '_interfaces') }}"
vars:
myvar: "{{ host_name }}"
- name: deb
debug: var=one_fact
- name: Managing Interfaces
debug:
msg: "enabled: {{ item['value']['enabled'] }}, name: {{ item['key'] }}, state: {{ item['value']['state'] }}"
with_dict: "{{ one_fact }}"
result:
TASK [Managing Interfaces] *********************************************************************************************************************************************************************************************
ok: [localhost] => (item={'key': 'Ethernet1/1', 'value': {'description': 'Firewall', 'enabled': True, 'speed': 'auto', 'state': 'present'}}) => {
"msg": "enabled: True, name: Ethernet1/1, state: present"
}
ok: [localhost] => (item={'key': 'Ethernet1/2', 'value': {'description': 'asd', 'enabled': True, 'speed': 'auto', 'state': 'present'}}) => {
"msg": "enabled: True, name: Ethernet1/2, state: present"
}
cheers

Related

Ansible filter to extract specific keys from a dict into another dict

Using the nios lookup modules, I can get a list of dicts of records
- set_fact:
records: "{{ lookup('community.general.nios', 'record:a', filter={'name~': 'abc.com'}) }}"
This returns something like
- ref: record:a/someBase64:name/view
name: abc.com
ipv4addr: 1.2.3.4
view: default
- ref: record:a/someBase64:name/view
name: def.abc.com
ipv4addr: 1.2.3.5
view: default
- ref: record:a/someBase64:name/view
name: ghi.abc.com
ipv4addr: 1.2.3.6
view: default
I want to convert this into a dict of dicts of {name}: a: {ipv4addr}
abc.com:
a: 1.2.3.4
def.abc.com:
a: 1.2.3.5
ghi.abc.com:
a: 1.2.3.6
So that I can then run a similar lookup to get other record types (e.g. cname) and combine them into the same dict. The items2dict filter seems halfway there, but I want the added a: key underneath.
If you just wanted a dictionary that maps name to an ipv4 address, like:
{
"abc.com": "1.2.3.4",
...
}
You could use a simple json_query expression. Take a look at the
set_fact task in the following example:
- hosts: localhost
gather_facts: false
vars:
data:
- ref: record:a/someBase64:name/view
name: abc.com
ipv4addr: 1.2.3.4
view: default
- ref: record:a/someBase64:name/view
name: def.abc.com
ipv4addr: 1.2.3.5
view: default
- ref: record:a/someBase64:name/view
name: ghi.abc.com
ipv4addr: 1.2.3.6
view: default
tasks:
- set_fact:
name_map: "{{ dict(data|json_query('[].[name, ipv4addr]')) }}"
- debug:
var: name_map
Running that playbook will output:
PLAY [localhost] ***************************************************************
TASK [set_fact] ****************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"name_map": {
"abc.com": "1.2.3.4",
"def.abc.com": "1.2.3.5",
"ghi.abc.com": "1.2.3.6"
}
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You could use a similar structure to extract other data (e.g. cname records). This would get you dictionary per type of data, rather than merging everything together in a single dictionary as you've requested, but this might end up being easier to work with.
To get exactly the structure you want, you can use set_fact in a loop, like this:
- hosts: localhost
vars:
data:
- ref: record:a/someBase64:name/view
name: abc.com
ipv4addr: 1.2.3.4
view: default
- ref: record:a/someBase64:name/view
name: def.abc.com
ipv4addr: 1.2.3.5
view: default
- ref: record:a/someBase64:name/view
name: ghi.abc.com
ipv4addr: 1.2.3.6
view: default
gather_facts: false
tasks:
- set_fact:
name_map: "{{ name_map|combine({item.name: {'a': item.ipv4addr}}) }}"
loop: "{{ data }}"
vars:
name_map: {}
- debug:
var: name_map
This will produce:
PLAY [localhost] ***************************************************************
TASK [set_fact] ****************************************************************
ok: [localhost] => (item={'ref': 'record:a/someBase64:name/view', 'name': 'abc.com', 'ipv4addr': '1.2.3.4', 'view': 'default'})
ok: [localhost] => (item={'ref': 'record:a/someBase64:name/view', 'name': 'def.abc.com', 'ipv4addr': '1.2.3.5', 'view': 'default'})
ok: [localhost] => (item={'ref': 'record:a/someBase64:name/view', 'name': 'ghi.abc.com', 'ipv4addr': '1.2.3.6', 'view': 'default'})
TASK [debug] *******************************************************************
ok: [localhost] => {
"name_map": {
"abc.com": {
"a": "1.2.3.4"
},
"def.abc.com": {
"a": "1.2.3.5"
},
"ghi.abc.com": {
"a": "1.2.3.6"
}
}
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Network Interface not enabling when provisioning VM from Ansible

I'm provisioning VM from ansible-playbook using VMware template, I can see the VM is created successfully but the network interface is not enabled automatically. I have to manually go to the VMware console and then edit the settings of the VM to enable the Network Interface. Kindly check the below playbook tasks and suggest what correction I need to do to enable the Network Interface when running the playbook
tasks:
- name: Create VM from template
vmware_guest:
validate_certs: False
hostname: "{{ vcenter_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
esxi_hostname: "{{ esxhost }}"
datacenter: "{{ datacenter_name }}"
name: "{{ name }}"
folder: TEST
template: "{{ vmtemplate }}"
disk:
- size_gb: "{{ disk_size | default(32) }}"
type: thin
datastore: "{{ datastore }}"
networks:
- name: VM Network
ip: 172.17.254.223
netmask: 255.255.255.0
gateway: 172.17.254.1
device_type: vmxnet3
state: present
wait_for_ip_address: True
hardware:
memory_mb: "{{ vm_memory | default(2000) }}"
state: present
register: newvm
- name: Changing network adapter
vmware_guest_network:
hostname: "{{ vcenter_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
datacenter: "{{ datacenter_name }}"
name: "{{ name }}"
validate_certs: no
networks:
- name: "VM Network"
device_type: vmxnet3
state: present
According to the documentation you can "connect" the network interface via connected: true. There is also a parameter start_connected. So add both to your networks dictionary
networks:
- name: VM Network
ip: 172.17.254.223
netmask: 255.255.255.0
gateway: 172.17.254.1
device_type: vmxnet3
connected: true
start_connected: true
I can't see a default value in the documentation, but I assume - they are per default false.
Also - there is no state parameter in networks dict list.
I have had this issue before. This happen because you're using VDS and on the vm-template side the open-vm-tools is installed instead of vmware-tools.
I was able to fix this issue by applying this workaround:
First install vmware-tools in the template instead of open-vm-tools.
Make sure in the playbook that the VM got those parameters:
connected: true /
start_connected: true
In case there is a need for open-vm-tools after, you can simply run a small playbook which uninstall vmware-tools and reinstall the open-vmtools instead.

Ansible - How to loop with a dict and a module's values

I'm trying to create a task with Ansible (=> 2.5) that will configure network interfaces such as that:
- name: Set up network interfaces addr
interfaces_file:
dest: "/etc/network/interfaces.d/{{ item.device }}"
iface: "{{ item.device }}"
state: present
option: address
value: "{{ item.addr }}"
with_items:
- "{{ network }}"
when: item.addr is defined
notify: Restart interface
- name: Set up network interfaces netmask
interfaces_file:
dest: "/etc/network/interfaces.d/{{ item.device }}"
iface: "{{ item.device }}"
state: present
option: netmask
value: "{{ item.netmask }}"
with_items:
- "{{ network }}"
when: item.netmask is defined
notify: Restart interface
- name: Set up network interfaces dns
interfaces_file:
dest: "/etc/network/interfaces.d/{{ item.device }}"
iface: "{{ item.device }}"
state: present
option: dns-nameservers
value: "{{ item.dns }}"
with_items:
- "{{ network }}"
when: item.dns is defined
notify: Restart interface
- name: Set up network interfaces dns-search
interfaces_file:
dest: "/etc/network/interfaces.d/{{ item.device }}"
iface: "{{ item.device }}"
state: present
option: dns-search
value: "{{ item.dns_search }}"
with_items:
- "{{ network }}"
when: item.dns_search is defined
notify: Restart interface
This works.
But from my point of view, that's not so clean ..
So I'm trying to use 2 loops ... Which is not working obviously.
- name: Set up network interfaces
interfaces_file:
dest: "/etc/network/interfaces.d/{{ item.iDunnoWhatToPutHere }}"
iface: "{{ item.iDunnoWhatToPutHere }}"
state: present
option: {{ item.option }}
value: "{{ item.value }}"
with_together:
- "{{ network }}"
- { option: address, value: item.0.addr }
- { option: netmask, value: item.0.netmask }
- { option: dns-nameservers, value: item.0.dns }
when: item.dns_search is defined
notify: Restart interface
[...]
Edit: This is good but it's strict. I should loop on vars which should loop on each option and its value for any options. Because I also have options for bridge such as "vlan_raw_device, bridge_ports, bridge_stp ...". So it should just loop blindly on a dict of options and values.
Edit2: With variable network
network:
- name: admin
device: admin
method: static
address: X.X.X.X/X
netmask: X.X.X.X
up:
net: X.X.X.X/X
gateway: X.X.X.X/X
down:
net: X.X.X.X/X
gateway: X.X.X.X/X
Why I'm trying all this ?
Because I need to change all the values if it has to be changed.
Because I want to restart (ifup, ifdown) only the interface that
Because I'm surprised that I have to use multiple times the same module.
Can you guys help me find out how to use that ?
Maybe it's not possible ?
Thanks folks !
here is a task that will hopefully meet your needs. i have replaced the interfaces_file with debug module, just to print the variables you need to actually use in the interfaces_file module. for the sake of the demo, i added a second interface in the network variable:
playbook with the variable and the task:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
network:
- name: admin
device: admin
method: static
address: 10.10.10.22
netmask: 255.255.255.0
up:
net: X.X.X.X/X
gateway: X.X.X.X/X
down:
net: X.X.X.X/X
gateway: X.X.X.X/X
- name: admin22
device: admin22
method: static
address: 20.20.20.22
netmask: 255.255.255.192
up:
net: X.X.X.X/X
gateway: X.X.X.X/X
down:
net: X.X.X.X/X
gateway: X.X.X.X/X
tasks:
- name: process network config
debug:
msg: "dest: {{ item[0].name }}, option: {{ item[1].option }}, value: {{ item[0][item[1].value] }}"
with_nested:
- "{{ network }}"
- [{ option: address, value: address }, { option: netmask, value: netmask }]
result:
TASK [process network config] ******************************************************************************************************************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "dest: admin, option: address, value: 10.10.10.22"
}
ok: [localhost] => (item=None) => {
"msg": "dest: admin, option: netmask, value: 255.255.255.0"
}
ok: [localhost] => (item=None) => {
"msg": "dest: admin22, option: address, value: 20.20.20.22"
}
ok: [localhost] => (item=None) => {
"msg": "dest: admin22, option: netmask, value: 255.255.255.192"
}
hope it helps

Use a Ansible Array variable with loop in Jinja2 template [duplicate]

This question already has answers here:
How to use Ansible's with_item with a variable?
(2 answers)
Closed 5 years ago.
This is my configuration array.
tomcatsconfs:
- {instance: tc-p1_i01_3001, port: 30011, connector: ajp-nio-, connector_port: 30012}
- {instance: tc-p1_i02_3002, port: 30021, connector: ajp-nio-, connector_port: 30022}
- {instance: tc-p1_i03_3003, port: 30031, connector: ajp-nio-, connector_port: 30032}
Now I woul like to create a nrpe.cfg with a jinja2 template with these task:
- name: copy nrpe.conf from template
template: src=nrpe.cfg.j2 dest=/etc/nagios/nrpe.cfg mode=0644 owner=root group=root
with_items:
- tomcatsconfs
Ansible transfers this array as a dictionary.
+[{u'connector': u'ajp-nio-', u'instance': u'tc-p1_i01_3001', u'connector_port': 30012, u'port': 30011}, {u'connector': u'ajp-nio-', u'instance': u'tc-p1_i02_3002', u'connector_port': 30022, u'port': 30021}, {u'connector': u'ajp-nio-', u'instance': u'tc-p1_i03_3003', u'connector_port': 30032, u'port': 30031}]
And I try to iterate this dictionary with this loop
{% for key value in tomcatconfs.iteritems() %}
key value
{% endfor %}
But I get the error message:
failed: [host] (item=tomcatconfs) => {"failed": true, "item": "tomcatconfs", "msg": "AnsibleUndefinedVariable: 'list object' has no attribute 'iteritems'"}
How I can iterate this dictionary in this template?
Greetings niesel
I used this.
---
- name: Run Ansible
hosts: 127.0.0.1
connection: local
gather_facts: true
vars:
tomcatsconfs:
- {instance: tc-p1_i01_3001, port: 30011, connector: ajp-nio-, connector_port: 30012}
- {instance: tc-p1_i02_3002, port: 30021, connector: ajp-nio-, connector_port: 30022}
- {instance: tc-p1_i03_3003, port: 30031, connector: ajp-nio-, connector_port: 30032}
tasks:
- name: Testing Iteration
copy:
dest: /tmp/testtemp
content: |
{% for var in tomcatsconfs %}
instance: {{ var.instance }}
port: {{ var.port }}
connector: {{ var.connector }}
connector_port: {{ var.connector_port }}
{% endfor %}
OUTPUT:
instance: tc-p1_i01_3001
port: 30011
connector: ajp-nio-
connector_port: 30012
instance: tc-p1_i02_3002
port: 30021
connector: ajp-nio-
connector_port: 30022
instance: tc-p1_i03_3003
port: 30031
connector: ajp-nio-
connector_port: 30032
I think all you need to change is how you are passing the list to with_items. Try changing
- name: copy nrpe.conf from template
template: src=nrpe.cfg.j2 dest=/etc/nagios/nrpe.cfg mode=0644 owner=root group=root
with_items:
- tomcatsconfs
to
- name: copy nrpe.conf from template
template: src=nrpe.cfg.j2 dest=/etc/nagios/nrpe.cfg mode=0644 owner=root group=root
with_items: "{{ tomcatsconfs }}"
I think what is going on is that you are giving with_items a list of one list. If you change it to what I have in my example, you are just giving it the list.
This fixed it with my simplified sample playbook:
---
- hosts: localhost
connection: local
vars:
tomcatsconfs:
- {instance: tc-p1_i01_3001, port: 30011, connector: ajp-nio-, connector_port: 30012}
- {instance: tc-p1_i02_3002, port: 30021, connector: ajp-nio-, connector_port: 30022}
- {instance: tc-p1_i03_3003, port: 30031, connector: ajp-nio-, connector_port: 30032}
tasks:
- debug: var="{{item}}"
with_items:
- tomcatsconfs
- debug: var="{{item['port']}}"
with_items: "{{ tomcatsconfs }}"

How to define an Ansible playbook environment with passed in environment dictionary

My problem is that I'm unable to set the environment for the entire playbook by passing in a dict to be set as the environment. Is that possible?
For example, here is my sample ansible playbook:
- hosts: localhost
vars:
env_vars: "{{ PLAY_ENVS }}"
environment: "{{ env_vars }}"
tasks:
- name: Here is what you passed in
debug: msg="env_vars == {{ env_vars }}"
- name: What is FAKE_ENV
debug: msg="FAKE_ENV == {{ lookup('env', 'FAKE_ENV') }}"
And I'm passing the command:
/bin/ansible-playbook sample_playbook.yml --extra-vars '{PLAY_ENVS: {"FAKE_ENV":"/path/to/fake/destination"}}'
The response I'm getting is the following:
PLAY [localhost] ***************************************************************
TASK [setup] *******************************************************************
ok: [localhost]
TASK [Here is what you passed in] **********************************************
ok: [localhost] => {
"msg": "env_vars == {u'FAKE_ENV': u'/path/to/fake/destination'}"
}
TASK [What is FAKE_ENV] ********************************************************
ok: [localhost] => {
"msg": "FAKE_ENV == "
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
As you can see 'FAKE_ENV' is not being set in the environment. What am I doing wrong?
Lookups in Ansible are executed in a context of parent ansible process.
You should check your environment with a spawned process, like this:
- hosts: localhost
vars:
env_vars:
FAKE_ENV: foobar
environment: "{{ env_vars }}"
tasks:
- name: Test with spawned process
shell: echo $FAKE_ENV
And get expected result: "stdout": "foobar",

Resources