set_fact in dictionary with dynamic variable name - ansible-2.x

I have an ansible inventory list like this:
domaenenliste:
"01":
dhcp_start: 10.43.12.0
dhcp_ende: 10.43.15.254
server_id: 3
"15":
"16":
dhcp_start: 10.43.128.26
dhcp_ende: 10.43.131.255
server_id: 2
And I want to set facts within this structure, expected output:
domaenenliste:
"01":
dhcp_start: 10.43.12.0
dhcp_ende: 10.43.15.254
server_id: 3
"15":
partner: foobar
"16":
dhcp_start: 10.43.128.26
dhcp_ende: 10.43.131.255
server_id: 2
I tried to assign it like this:
- debug:
var: dom
- name: Test set
set_fact:
"{'{{domaenenliste[dom]}}':{'partner':'foobar'}}"
- debug:
var: domaenenliste[dom].partner
- debug:
var: domaenenliste['16']
Which creates the following output:
TASK [calculate_missing_inventory_variables : debug] ****************************************************************************
task path: /home/mpw/gits/ansible-ffms/roles/calculate_missing_inventory_variables/tasks/main.yml:5
ok: [des1] => {
"dom": "15"
}
TASK [calculate_missing_inventory_variables : Test set] *************************************************************************
task path: /home/mpw/gits/ansible-ffms/roles/calculate_missing_inventory_variables/tasks/main.yml:8
ok: [des1] => {
"ansible_facts": {
"_raw_params": {
"": {
"partner": "foobar"
}
}
},
"changed": false
}
TASK [calculate_missing_inventory_variables : debug] ****************************************************************************
task path: /home/mpw/gits/ansible-ffms/roles/calculate_missing_inventory_variables/tasks/main.yml:17
ok: [des1] => {
"domaenenliste[dom].partner": "VARIABLE IS NOT DEFINED!"
}
TASK [calculate_missing_inventory_variables : debug] ****************************************************************************
task path: /home/mpw/gits/ansible-ffms/roles/calculate_missing_inventory_variables/tasks/main.yml:20
ok: [des1] => {
"domaenenliste['16']": {
"dhcp_ende": "10.43.131.255",
"dhcp_start": "10.43.128.26",
"server_id": 2
}
}
As the debug output shows, the variable is still unassigned. Accessing this structure with this syntax works. But I can't make changes to it.
Does someone have an idea how to set this correctly?
Thanks for your help in advance!
Regards,
Matthias

You cannot assign subkey of a dictionary in Ansible, override whole variable with required modifications:
---
- hosts: localhost
gather_facts: no
vars:
domaenenliste:
"01":
dhcp_start: 10.43.12.0
dhcp_ende: 10.43.15.254
server_id: 3
"15":
"16":
dhcp_start: 10.43.128.26
dhcp_ende: 10.43.131.255
server_id: 2
tasks:
- set_fact:
domaenenliste: "{{ domaenenliste | combine(new_leaf) }}"
vars:
new_leaf:
"15":
partner: foobar
- debug:
msg: "{{ domaenenliste }}"

Related

Ansible playbook get values from dict

I want to retrieve the id from a json dict based on a certain name. In this case I would like to get the ID from the "WebLogic" and store it into a variable to use the ID in a next task.
The playbook:
- name: Get Tags
uri:
url: "https://xx.xx-xx.xx{{ uat_env }}api/config/v1/autoTags"
method: GET
headers:
Content-Type: application/json; charset=utf-8
Authorization: xxx
return_content: yes
register: data
- name: Print returned json dictionary
debug:
var: data.json
- debug:
msg: "{{ data.json['values'] | json_query(query) }}"
vars:
name: 'WebLogic'
query: "[?name=='{{ name }}'].id"
- name: TEST
set_fact:
test: "{{ data.json['values'] | json_query([?name=='WebLogic'].id) }}"
Test run:
PLAY [TEST] ********************************************************************
TASK [Get all UAT autoTags] ****************************************************
ok: [localhost]
TASK [Print returned json dictionary] ******************************************
ok: [localhost] => {
"data.json": {
"values": [
{
"id": "5c3849a4-a044-4a98-a67a-c1ea42d652ca",
"name": "Apache"
},
{
"id": "b37511f4-d4e8-4c77-a628-841dba5c65d8",
"name": "WebLogic"
}
]
}
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
"b37511f4-d4e8-4c77-a628-841dba5c65d8"
]
}
TASK [TEST] ********************************************************************
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: unexpected char '?' at 37. String: {{ data.json['values'] | json_query([?name=='WebLogic'].id) }}"}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The message returns empty.
the problem is at the data.json.values syntax, please replace with data.json["values"]
the two tasks to show the difference:
- debug:
msg: "{{ data.json.values | json_query(query) }}"
vars:
name: 'WebLogic'
query: "[?name=='{{ name }}'].id"
- debug:
msg: "{{ data.json['values'] | json_query(query) }}"
vars:
name: 'WebLogic'
query: "[?name=='{{ name }}'].id"
update:
To assign the value to a variable, below task should do it:
- set_fact:
my_var: "{{ data.json['values'] | json_query(query) }}"
vars:
name: 'WebLogic'
query: "[?name=='{{ name }}'].id"
- debug:
var: my_var

How to use proper loop in Ansible uri module

I'm using the Ansible uri module to trigger the pfSense API.
Now I want to create firewall rules in a task (code is truncated).
---
# tasks file for creating firewall rules
- name: "Create firewall rules"
uri:
url: "https://{{ pf_hostname }}/api/v1/firewall/rule"
method: "POST"
body: "{ \
\"client-id\": \"{{ pf_user }}\",
\"client-token\": \"{{ pf_password }}\",
\"type\": \"{{ pf_fw_type_01 }}\",
\"interface\": \"{{ pf_fw_interface_01 }}\",
}"
The vars file looks like this.
---
# vars file for creating firewall rules
# Authentication
pf_hostname: "pfsense.local"
pf_user: "admin"
pf_password: "pfsense"
# Rule 01
pf_fw_type_01: "pass"
pf_fw_interface_01: "wan"
How can I now repeat the task without unnecessary redundancy (e.g. with loop) for other rules?
I only come up with the following idea, but it doesn't seem ideal to me.
loop:
- "{{ item.client-id: {{ pf_user }}, item.type: {{ pf_fw_type_01 }} }}"
- "{{ item.client-id: {{ pf_user }}, item.type: {{ pf_fw_type_02 }} }}"
How about putting the rules as a dynamic parameter in the list?
For example, here's like.
vars.yml
---
# vars file for creating firewall rules
# Authentication
pf_hostname: "pfsense.local"
pf_user: "admin"
pf_password: "pfsense"
rules:
- num: 01
type: "pass"
pf_fw_interface: "wan"
- num: 02
type: "pass"
pf_fw_interface: "wan"
playbook
---
- hosts: localhost
gather_facts: false
vars_files:
- vars.yml
tasks:
- debug:
msg: |
{
"client-id": "{{ pf_user }}",
"client-token": "{{ pf_password }}",
"type": "{{ item.type }}",
"interface": "{{ item.pf_fw_interface }}"
}
loop: "{{ rules }}"
result
$ ansible-playbook main.yml
(snip)
PLAY [localhost] *********************************************************************************************************************************************************************
TASK [debug] *************************************************************************************************************************************************************************
ok: [localhost] => (item={'type': 'pass', 'pf_fw_interface': 'wan'}) => {
"msg": {
"client-id": "admin",
"client-token": "pfsense",
"interface": "wan",
"type": "pass"
}
}
ok: [localhost] => (item={'type': 'pass', 'pf_fw_interface': 'wan'}) => {
"msg": {
"client-id": "admin",
"client-token": "pfsense",
"interface": "wan",
"type": "pass"
}
}
(snip)

how to loop over hostvars of register output

I'm struggling to loop over hostvars of the registered output of all the hosts in dynamic inventory.
Here is the code.
$cat collect.yaml
---
- hosts: "{{ env }}"
become: True
tasks:
- name: Get dockerinfo
docker_host_info:
containers: yes
register: result
- name: Debug dockerInfo
debug:
var: result.containers
- name: dynamic grouping
add_host:
name: "{{ item[0] }}"
groups: "{{ item[1].Image | regex_replace('.*?/(.*?):.*', '\\1') }}"
loops:
- "{{ ansible_play_batch }}"
- "{{ hostvars[item].result.containers }}"
Error i get is item not defined. I would want the hosts refer to their respective result.containers. Not sure on how to use hostvars for the host to refer their respective result.containers.
Here is result.container output.
TASK [Debug dockerInfo]
ok: [vm1.nodekite.com] => {
"result.containers": [
{
"Image": "ca.docker/webproxy:1.0.0",
},
{
"Image": "docker.local/egacustomer:1.0.1",
},
]}
ok: [vm2.nodekite.com ] => {
"result.containers": [
{
"Image": "ca.docker/webproxyui:1.0.0",
},
{
"Image": "cna-docker-local/lega-customer:1.0.1",
},
]}
Here is the what i'm trying to achieve
changed: [vm1.nodekite.com] => {
"add_host": {
"groups": [
"webproxy"
],
"host_name": "vm1.nodekite.com",
},
changed: [vm1.nodekite.com] => {
"add_host": {
"groups": [
"egacustomer"
],
"host_name": "vm1.nodekite.com",
},
changed: [vm2.nodekite.com] => {
"add_host": {
"groups": [
"webproxy" >> this should be webproxyui
],
"host_name": "vm2.nodekite.com",
},
changed: [vm2.nodekite.com] => {
"add_host": {
"groups": [
"egacustomer" >> this should be lega-customer
],
"host_name": "vm2.nodekite.com",
},
Any help would be greatly appreciated.
I would run this task using Images Names
- hosts: "{{ group }}"
gather_facts: false
become: true
become_method: sudo
tasks:
- name: stop or restart docker containers
command: "docker {{ state }} {{ container_name }}"
How about just group_by paired with the loop?
- hosts: "{{ env }}"
become: True
tasks:
- name: Get dockerinfo
docker_host_info:
containers: yes
register: result
- debug:
var: result.containers
- group_by:
key: "container_{{ item.image | regex_replace('.*?/(.*?):.*', '\\1') }}"
loop: "{{ result.containers }}"
- debug:
var: group_names
You don't need to add the prefix. But this would add each host to groups with their container image prefixes. You should be able to use the group later in the play or playbook.

Ansible: How to loop over registered output of dynamic hosts in the next task

I am struggling to loop over registered results.containers for all the hosts in the dynamic inventory.
Here is the code.
$cat collect.yaml
---
- hosts: "{{ env }}"
become: True
tasks:
- name: Get dockerinfo
docker_host_info:
containers: yes
register: result
- name: Debug dockerInfo
debug:
var: result.containers
- name: dynamic grouping
add_host:
name: "{{ item[0] }}"
groups: "{{ item[1].Image | regex_replace('.*?/(.*?):.*', '\\1') }}"
container_name: '{{ item[1].Names[0] | regex_replace("^/", "") }}'
with_nested:
- "{{ ansible_play_batch }}"
- "{{ result.containers }}"
Here is result.containers output.
TASK [Debug dockerInfo]
ok: [vm1.nodekite.com] => {
"result.containers": [
{
"Image": "ca.docker/webproxy:1.0.0",
"Names": [
"/customer1"
],
},
{
"Image": "docker.local/egacustomer:1.0.1",
"Names": [
"/webproxy"
],
},
]}
ok: [vm2.nodekite.com ] => {
"result.containers": [
{
"Image": "ca.docker/webproxy:1.0.0",
"Names": [
"/webproxyui"
],
},
{
"Image": "cna-docker-local/lega-customer:1.0.1",
"Names": [
"/webproxy"
],
},
]}
ok: [vm3.nodekite.com ] => {
"result.containers": [
{
"Image": "ca.docker/webproxy:1.0.0",
"Names": [
"/webproxy"
],
},
{
"Image": "local.docker/saga-customer:1.0.1",
"Names": [
"/customerr
],
},
]}
Right now item[1].Image and item[1].Names[0] is only taken from first host's(vm1.nodekite.com) results.containers output. I would like to loop over for every hosts. So that, I could create dynamic group for all the hosts with their respective containers. With my code, hosts vm1,vm2,vm3 all are referring to vm1.nodekite.com's result.containers but i want the hosts to refer to their respective containers. Any help would be greatly appreciated.
I have update dynamic grouping task ouput for clarification.
changed: [vm1.nodekite.com] => {
"add_host": {
"groups": [
"webproxy"
],
"host_name": "vm1.nodekite.com",
"host_vars": {
"container_name": "customer1" }
},
changed: [vm1.nodekite.com] => {
"add_host": {
"groups": [
"egacustomer"
],
"host_name": "vm1.nodekite.com",
"host_vars": {
"container_name": "webproxy" }
},
changed: [vm2.nodekite.com] => {
"add_host": {
"groups": [
"webproxy" >> this should be webproxy
],
"host_name": "vm2.nodekite.com",
"host_vars": {
"container_name": "customer1" } >> this should be webproxyui
},
changed: [vm2.nodekite.com] => {
"add_host": {
"groups": [
"egacustomer" >> this should be lega-customer
],
"host_name": "vm2.nodekite.com",
"host_vars": {
"container_name": "webproxy" } >> this should be webproxy
},
if you see vm2 is still referring to vm1's result.containers output.
when i try this...i get item not defined error.
- name: adding it to groups using images
add_host:
name: "{{ item[0] }}"
groups: "{{ item[1].Image | regex_replace('.*?/(.*?):.*', '\\1') }}"
container_name: '{{ item[1].Names[0] | regex_replace("^/", "") }}'
loop:
- "{{ ansible_play_batch }}"
- "{{ myresult.containers }}"
vars:
myresult: "{{ hostvars[item].result }}"
run_once: true
Q: "Hosts shall refer to their respective containers."
A: Use hostvars. For example
- name: dynamic grouping
debug:
msg:
- "name: {{ item }}"
- "groups: {{ my_result.containers|
map(attribute='Image')|
map('regex_replace', '.*?/(.*?):.*', '\\1')|
list }}"
- "container_names: {{ my_result.containers|
map(attribute='Names')|
map('regex_replace', '\/', '')|
list }}"
loop: "{{ ansible_play_batch }}"
vars:
my_result: "{{ hostvars[item].result }}"
run_once: true
gives
ok: [vm1.nodekite.com] => (item=vm1.nodekite.com) =>
msg:
- 'name: vm1.nodekite.com'
- 'groups: [''webproxy'', ''egacustomer'']'
- 'container_names: ["[''customer1'']", "[''webproxy'']"]'
ok: [vm1.nodekite.com] => (item=vm2.nodekite.com) =>
msg:
- 'name: vm2.nodekite.com'
- 'groups: [''webproxy'', ''lega-customer'']'
- 'container_names: ["[''webproxyui'']", "[''webproxy'']"]'
ok: [vm1.nodekite.com] => (item=vm3.nodekite.com) =>
msg:
- 'name: vm3.nodekite.com'
- 'groups: [''webproxy'', ''saga-customer'']'
- 'container_names: ["[''webproxy'']", "[''customer'']"]'
(Feel free to fit the code to your needs.)
I was having an issue of getting the item passed into the name below to be a plain string of: item='nginx' and not item='[u'/nginx]'
To get around this, I did the following:
- name: Get docker containers
become: docker
community.docker.docker_container
containers: yes
register: docker_info
- name: Stop running containers
become: docker
community.docker.docker_container
name: "{{ item }}"
state: stopped
loop: "{{ docker_info.containers | sum(attribute='Names', start=[]) | map('regex_replace','\\/','') | list }}"
when: item in apps.split(,)
In this case the apps is a comma deliminated string variable I passed into the ansible playbook to limit which apps to stop.
The sum piece, flattens the Names of all the apps running into a single list.
The regex piece removes the / in the Names parameter

Combining the output of multiple loops in Ansible

I am working with Ansible and in my playbook I am running the following task, in which I have multiple loops so as to retrieve different tags from a specific xml:
- name: Retrieve multiple xml tags valuei
xml:
xmlstring: "{{ item.string }}"
xpath: "{{ item.path }}"
content: text
loop:
- { path: "/rpc-reply/vlan-instance-information/vlan-instance-group/vlan-member/vlan-tag", string: "{{topology.xml}}" }
- { path: "/rpc-reply/vlan-instance-information/vlan-instance-group/vlan-member/vlan-member-interface", string: "{{topology.xml}}" }
register: tags_value
- debug:
msg: "{{ item.matches }}"
loop: "{{ tags_value.results }}"
loop_control:
label: "{{ item.matches }}"
So I am getting the following:
ok: [sss-sd1-02] => (item=[{u'vlan-member-interface': u'et-0/0/0.0*'}, {u'vlan-member-interface': u'et-0/0/1.0*'}]) => {
"msg": [
{
"vlan-member-interface": "et-0/0/0.0*"
},
{
"vlan-member-interface": "et-0/0/1.0*"
}
]
}
and this
ok: [sss-sd1-02] => (item=[{u'vlan-tag': u'4071'}, {u'vlan-tag': u'4072'}]) => {
"msg": [
{
"vlan-tag": "4071"
},
{
"vlan-tag": "4072"
}
]
}
Is there a way to group "vlan-member-interface": "et-0/0/0.0*" and "vlan-tag": "4071" in 1 result , either in this task or in a different one ? And also is there a way to create a list with only {4071, 4072} ? Because i cannot handle it as it is right now !!!
The question can be simplified. Let's have the data
results:
- [A: a1, A: a2]
- [B: b1, B: b2]
Q1: "Group "A:a1" and "B:b1" in 1 result"
A: Use zip function. For example
- set_fact:
my_groups: "{{ results.0|zip(results.1)|list }}"
- debug:
var: my_groups
gives
"my_groups": [
[
{
"A": "a1"
},
{
"B": "b1"
}
],
[
{
"A": "a2"
},
{
"B": "b2"
}
]
]
}
Q2: "Create a list with only [b1, b2]"
A: Use dict2items and json_query functions. For example
- set_fact:
my_values: "{{ results.1|map('dict2items')|list|json_query('[].value') }}"
- debug:
var: my_values
gives
"my_values": [
"b1",
"b2"
]
Question 1 is substitution of: Group "vlan-member-interface": "et-0/0/0.0*" and "vlan-tag": "4071" in 1 result
Question 2 is substitution of: Create a list with only {4071, 4072}
Variable results is substitution of tags_value.results

Resources