Ansible play is not able to take variable - ansible

C:\CYGWIN64\ETC\ANSIBLE\ANSIBLE-ACI-CONFIG
├───environments
│ ├───houston
│ └───munich
├───group_vars
├───plays
├───plugins
│ └───filter
│ └───__pycache__
└───roles
├───aci-fabric-onboarding
│ └───tasks
variable file:
oob_nodes:
- { node_id: "101", obb_address: "10.10.10.10", obb_cidr: "27" , obb_gateway: "10.10.10.1" }
- { node_id: "102", obb_address: "10.10.10.11", obb_cidr: "27" , obb_gateway: "10.10.10.1" }
- { node_id: "201", obb_address: "10.10.10.12", obb_cidr: "27" , obb_gateway: "10.10.10.1" }
play
========
- name: Setup ACI Fabric
hosts: "{{ target }}"
gather_facts: no
any_errors_fatal: true
tasks:
- include_vars:
file: "{{ ACI_SSoT_path }}/fabricsetup.yml"
- include_vars:
file: "{{ ACI_SSoT_path }}/oob.yml"
# Intent Statement
- include_role:
name: aci-fabric-onboarding
roles
==============
# Adding OBB address
- name: Add OBB address
delegate_to: localhost
aci_rest:
host: "{{ aci_ip }}"
username: ansible
private_key: ansible.key
certificate_name: ansible
use_ssl: yes
validate_certs: false
path: /api/node/mo/uni/tn-mgmt/mgmtp-default/oob-default/rsooBStNode-[topology/pod-1/node-"{{item.node_id}}"].json
method: post
content:
{
"mgmtRsOoBStNode":{
"attributes":{
"tDn":"topology/pod-1/node-101",
"addr":"25.96.131.61/27",
"gw":"25.96.131.33",
"status":"created"
},
"children":[
]
}
}
with_items: "{{ oob_nodes }}"
error:
TASK [aci-fabric-onboarding : Add OBB address] *****************************************************************************************************************************************************
task path: /etc/ansible/Ansible-Aci-config/roles/aci-fabric-onboarding/tasks/apply-oob-config.yml:4
fatal: [25.96.131.30]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'item' is undefined\n\nThe error appears to be in '/etc/ansible/Ansible-Aci-config/roles/aci-fabric-onboarding/tasks/apply-oob-config.yml': line 4, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n# Adding OBB address\n- name: Add OBB address\n ^ here\n"
}

That looks like an indention error to me. You have with_items with the same indention as aci_rest:
# Adding OBB address
- name: Add OBB address
delegate_to: localhost
aci_rest:
host: "{{ aci_ip }}"
username: ansible
private_key: ansible.key
certificate_name: ansible
use_ssl: yes
validate_certs: false
path: /api/node/mo/uni/tn-mgmt/mgmtp-default/oob-default/rsooBStNode-[topology/pod-1/node-"{{ item.node_id }}"].json
method: post
content:
{
"mgmtRsOoBStNode":{
"attributes":{
"tDn":"topology/pod-1/node-101",
"addr":"25.96.131.61/27",
"gw":"25.96.131.33",
"status":"created"
},
"children":[
]
}
}
with_items: "{{ oob_nodes }}"
Have a look at the documentation as well.

Related

ansible rabbitmq purge queue

I have ansible playbook where I want purge all queues in rabbitmq
- name: Get queues
uri:
url: http://localhost:15672/api/queues
method: GET
return_content: yes
status_code: 200,404
body_format: json
register: result
- name: Just the Names
debug: msg="{{ result.json | json_query(jmesquery)}}"
vars:
jmesquery: "[*].name"
register: queue_name
Return list queues
TASK [demo : Just the Names] ***
ok: [test] => {
"msg": [
"test1",
"test2"
]
}
How to do a loop for queues
- name: delete queues
uri:
url: http://localhost:15672/api/queues/aaa/{{ queue_name }}/contents"
method: DELETE
You could try:
- name: delete queues
uri:
url: "http://localhost:15672/api/queues/aaa/{{ item }}/contents"
method: DELETE
loop: "{{ result.json | json_query(jmesquery)}}"
vars:
jmesquery: "[*].name"

How to loop over a list of dict elements deep 3 in Ansible

I have the following variables in my var file:
repo_type:
hosted:
data:
- name: hosted_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: hosted_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
proxy:
data:
- name: proxy_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: proxy_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
group:
data:
- name: group_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: group_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
I want to configure a task to to loop over (hosted,proxy and group) and body over data dict.
Here is the task:
- name: Create pypi hosted Repos
uri:
url: "{{ nexus_api_scheme }}://{{ nexus_api_hostname }}:{{ nexus_api_port }}\
{{ nexus_api_context_path }}{{ nexus_rest_api_endpoint }}/repositories/pypi/{{ item.key}}"
user: "{{ nexus_api_user }}"
password: "{{ nexus_default_admin_password }}"
headers:
accept: "application/json"
Content-Type: "application/json"
body_format: json
method: POST
force_basic_auth: yes
validate_certs: "{{ nexus_api_validate_certs }}"
body: "{{ item }}"
status_code: 201
no_log: no
with_dict: "{{ repo_type}}"
I have tried with_items, with_dict and with_nested but nothing helped.
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'data'
Any help would be appreciated!
If your goal is to loop over the contents of the data keys as a flat list, you could do it like this:
- debug:
msg: "repo {{ item.name }} write_policy {{ item.storage.write_policy }}"
loop_control:
label: "{{ item.name }}"
loop: "{{ repo_type | json_query('*.data[]') }}"
That uses a JMESPath expression to get the data key from each
top-level dictionary, and then flatten the resulting nested list. In
other words, it transforms you original structure into:
- name: hosted_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: hosted_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: proxy_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: proxy_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: group_repo1
online: true
storage:
blobstarage: default
write_policy: allow_once
- name: group_repo2
online: true
storage:
blobstarage: default
write_policy: allow_once
When run using your example data, this produces as output:
TASK [debug] *********************************************************************************************************
ok: [localhost] => (item=hosted_repo1) => {
"msg": "repo hosted_repo1 write_policy allow_once"
}
ok: [localhost] => (item=hosted_repo2) => {
"msg": "repo hosted_repo2 write_policy allow_once"
}
ok: [localhost] => (item=proxy_repo1) => {
"msg": "repo proxy_repo1 write_policy allow_once"
}
ok: [localhost] => (item=proxy_repo2) => {
"msg": "repo proxy_repo2 write_policy allow_once"
}
ok: [localhost] => (item=group_repo1) => {
"msg": "repo group_repo1 write_policy allow_once"
}
ok: [localhost] => (item=group_repo2) => {
"msg": "repo group_repo2 write_policy allow_once"
}
If you're trying to do something else, please update your question so
that it clearly shows the values you expect for each iteration of your
loop.
As reported by #larsk, you actually didn't manage to explain clearly how you are trying to loop over your data and what your api call is actually expecting.
But you are in luck this time since I have messed around with Nexus quite a bit (and I think I actually recognize those variable names and overall task layout)
The Nexus Repository POST /v1/repositories/pypi/[hosted|proxy|group] API endpoints are expecting one call for each of your repos in data. To implement your requirement you need to loop over the keys in repo_type to select the appropriate endpoint and then loop again over each element in data to send the repo definition to create.
This is actually possible combining a dict2items and subelements filters in your loop as in the below playbook (not directly tested).
The basics of the transformation are as follow:
transform your dict to a key/value list using dict2items e.g. (shortened example)
- key: hosted
value:
data:
- <repo definition 1>
- <repo definition 2>
[...]
use the subelements filter to combine each top element with each element in value.data e.g.:
- # <- This is the entry for first repo i.e. `item` in your loop
- key: hosted # <- this is the start of the corresponding top element i.e. `item.0` in your loop
value:
data:
- <repo definition 1>
- <repo definition 2>
- <repo definition 1> # <- this is the sub-element i.e. `item.1` in your loop
- # <- this is the entry for second repo
- key: hosted
value:
data:
- <repo definition 1>
- <repo definition 2>
- <repo definition 2>
[...]
Following one of your comments and from my experience, I added to my example an explicit json serialization of the repo definition using the to_json filter.
Putting it all together this gives:
- name: Create pypi hosted Repos
uri:
url: "{{ nexus_api_scheme }}://{{ nexus_api_hostname }}:{{ nexus_api_port }}\
{{ nexus_api_context_path }}{{ nexus_rest_api_endpoint }}/repositories/pypi/{{ item.0.key }}"
user: "{{ nexus_api_user }}"
password: "{{ nexus_default_admin_password }}"
headers:
accept: "application/json"
Content-Type: "application/json"
body_format: json
method: POST
force_basic_auth: yes
validate_certs: "{{ nexus_api_validate_certs }}"
body: "{{ item.1 | to_json }}"
status_code: 201
no_log: no
loop: "{{ repo_type | dict2items | subelements('value.data') }}"

Ansible loop built from variable sets

I am pretty new to Ansible as a network engineer and have found it breaking my brain. I've used basic loops in some Ansible playbooks. Now I'm trying something a bit more complex and I'm sure I'm missing something because it feels like it should be simple.
I want to take these variables in a playbook:
vars:
smb_tcp_ports:
- '139'
- '445'
smb_udp_ports:
- '137'
- '138'
smb_ips:
- '172.16.13.130'
- '172.16.13.0/26'
- '200X:8X0:fX4a:X053::/64'
- '200X:8X0:fX4a:X050::130'
and loop through them so I build a new variable like this:
vars:
smb_allowed_ips_tcp:
- { ip: "172.16.13.130", port: ['139','445'] }
- { ip: "172.16.13.0/26", port: ['139','445'] }
- { ip: "X001:8X0:fX4a:X053::/64", port: ['139','445'] }
- { ip: "X001:8X0:fX4a:X050::130", port: ['139','445'] }
smb_allowed_ips_udp:
- { ip: "172.16.13.130", port: ['137','138'] }
- { ip: "172.16.13.0/26", port: ['137','138'] }
- { ip: "200X:8X0:fX4a:X053::/64", port: ['137','138'] }
- { ip: "200X:8X0:fX4a:X050::130", port: ['137','138'] }
^^^ the above bit that I want to generate is the bit that I'm struggling with ^^^
Then I can send it to this:
- name: Allow SMB TCP
ufw:
rule: allow
src: '{{ item.0.ip }}'
port: '{{ item.1 }}'
proto: tcp
with_subelements:
- "{{ smb_allowed_ips_tcp }}"
- port
when: "'smbserver' in group_names"
- name: Allow SMB UDP
ufw:
rule: allow
src: '{{ item.0.ip }}'
port: '{{ item.1 }}'
proto: udp
with_subelements:
- "{{ smb_allowed_ips_udp }}"
- port
when: "'smbserver' in group_names"
The question used to have a lot of words here. I deleted it. Thanks Larsks. I hope this is clearer?
I tried set_facts, but there is loads of stuff I don't understand in examples i see, like adding | symbols and writing list, product etc, and I always end up breaking. It also doesnt seem to add as an array, it overwrites.
Answered here: using https://ansibledaily.com/process-complex-variables-with-set_fact-and-with_items/
---
- hosts: myhosts
gather_facts: true
become: true
vars:
smb_tcp_ports:
- '139'
- '445'
smb_udp_ports:
- '137'
- '138'
smb_ips:
- '172.16.13.130'
- '172.16.13.0/26'
- '200X:8X0:fX4a:X053::/64'
- '200X:8X0:fX4a:X050::130'
smb_ips_tcp: {}
tasks:
- name: Populate IPs in dict
set_fact:
smb_ips_tcp: "{{ smb_ips_tcp | combine({'ip': item}) }}"
with_items:
- "{{ smb_ips }}"
register: smbout
- name: Populate ports in dict
set_fact:
smb_ips_tcp: "{{ item | combine({'port': smb_tcp_ports}) }}"
with_items:
- "{{ smbout.results | map(attribute='ansible_facts.smb_ips_tcp') | list }}"
register: smbout
- name: smbout results
set_fact:
smb_ips_tcp: "{{ smbout.results | map(attribute='ansible_facts.smb_ips_tcp') | list }}"
- name: Allow SMB TCP
ufw:
rule: allow
src: '{{ item.0.ip }}'
port: '{{ item.1 }}'
proto: tcp
with_subelements:
- "{{ smb_ips_tcp }}"
- port
I think I had missed the register bit. So when I tried the register facts bit before it kept leaving me with one key value pair. Which was useless. The register though is allowing me to keep all the key values and use them again.
Unsure if this is a duplicate question now.
It sounds like you may have resolved your question, but I thought you might be interested in an alternative implementation. I would probably solve this using the product filter, which produces the cartesian product of two lists. For example, to produce smb_allowed_ips_tcp, I would write:
- name: create smb_allowed_ips_tcp
set_fact:
smb_allowed_ips_tcp: "{{ smb_allowed_ips_tcp + [{'ip': item.0, 'port': item.1}] }}"
loop: "{{ smb_ips|product(smb_tcp_ports)|list }}"
vars:
smb_allowed_ips_tcp: []
This produces a data structure that looks like:
TASK [debug] ******************************************************************************************
ok: [localhost] => {
"smb_allowed_ips_tcp": [
{
"ip": "172.16.13.130",
"port": "139"
},
{
"ip": "172.16.13.130",
"port": "445"
},
{
"ip": "172.16.13.0/26",
"port": "139"
},
{
"ip": "172.16.13.0/26",
"port": "445"
},
{
"ip": "200X:8X0:fX4a:X053::/64",
"port": "139"
},
{
"ip": "200X:8X0:fX4a:X053::/64",
"port": "445"
},
{
"ip": "200X:8X0:fX4a:X050::130",
"port": "139"
},
{
"ip": "200X:8X0:fX4a:X050::130",
"port": "445"
}
]
}
We can feed that to the ufw module like this:
- name: Allow SMB TCP
ufw:
rule: allow
src: '{{ item.ip }}'
port: '{{ item.port }}'
proto: tcp
loop: "{{ smb_allowed_ips_tcp }}"
There are fewer tasks required for this solution, and I think the logic is a little easier to follow.

How to generate json files in the same task with templates in Ansible

I need to get JMX metrics from the Hazelcast product. I have created a Logstash process that connects to the JMX port. This process has to read a json where is the information of the hostname, port, cluster, environment, etc of Hazelcast JMX. I need to deploy on the Logstash machines the json file for each Hazelcast machine / port. In this case there are three Hazelcast machines and a total of 6 processes with different ports.
Example data:
Hazelcast Hostnames: hazelcast01, hazelcast02, hazelcast03
Hazelcast Ports: 6661, 6662, 6663, 6664, 6665
Logstash Hostnames: logstash01, logstash02, logstash03
Dictionary of Hazelcast information in Ansible:
logstash_hazelcast_jmx:
- hazelcast_pre:
name: hazelcast_pre
port: 15554
cluster: PRE
- hazelcast_dev:
name: hazelcast_dev
port: 15555
cluster: DEV
Example of task in Ansible:
- name: Deploy HAZELCAST JMX config
template:
src: "hazelcast_jmx.json.j2"
dest: "{{ logstash_directory_jmx_hazelcast }}/hazelcast_jmx_{{ item }}_{{ item.value.cluster }}.json"
owner: "{{ logstash_system_user }}"
group: "{{ logstash_system_group }}"
mode: 0640
with_dict:
- "{{ groups['HAZELCAST'] }}"
- logstash_hazelcast_jmx
The final result should be as follows:
/opt/logstash/jmx/hazelcast/hazelcast_jmx_hazelcast01_DEV.json
/opt/logstash/jmx/hazelcast/hazelcast_jmx_hazelcast01_PRE.json
/opt/logstash/jmx/hazelcast/hazelcast_jmx_hazelcast02_DEV.json
...
Here is an example of the json content:
{
"host" : "{{ hostname of groups['HAZELCAST' }}",
"port" : {{ item.value.port }},
"alias" : "{{ hostname of groups['HAZELCAST' }}_{{ item.value.cluster }}",
"queries" : [
{
"object_name" : "com.hazelcast:instance=_hz_{{ item.value.cluster }},type=XXX,name=YYY",
"attributes" : [ "size", "localHits" ],
"object_alias" : "Hazelcast_map"
} ,{
"object_name" : "com.hazelcast:instance=_hz_{{ item.value.cluster }},type=IMap,name=user",
"attributes" : [ "size", "localHits" ],
"object_alias" : "Hazelcast_map"
}
]
}
I think the problem I have is that the with_dict option does not allow using a listing of inventory hosts and a dictionary.
How can I get this generation of json files for each machine / port?
If you run your playbook against logstash hosts, you can use with_nested:
---
- hosts: logstash_hosts
tasks:
- name: Deploy HAZELCAST JMX config
template:
src: "hazelcast_jmx.json.j2"
dest: "{{ logstash_directory_jmx_hazelcast }}/hazelcast_jmx_{{ helper_host }}_{{ helper_cluster }}.json"
owner: "{{ logstash_system_user }}"
group: "{{ logstash_system_group }}"
mode: 0640
with_nested:
- "{{ groups['HAZELCAST'] }}"
- "{{ logstash_hazelcast_jmx }}"
vars:
helper_host: "{{ item.0 }}"
helper_cluster: "{{ item.1.cluster }}"
helper_port: "{{ item.1.port }}"
I also used helper variables with more meaningful names. You should also modify your template with either helper vars or item.0, item.1 – where item.0 is a host from HAZELCAST group, and item.1 is an item from logstash_hazelcast_jmx list.

ansible 2.0.0-0.3.beta1 ec2_vpc_route_table ec2_vpc_subnet

I am creating an ansible playbook using version 2.0.0-0.3.beta1
I'd like to get the subnet id after i create a subnet. I'm refering the official ansible docs : http://docs.ansible.com/ansible/ec2_vpc_route_table_module.html
- name: Create VPC Public Subnet
ec2_vpc_subnet:
state: present
resource_tags: '{"Name":"{{ prefix }}_subnet_public_0"}'
vpc_id: "{{ vpc.vpc_id }}"
az: "{{ az0 }}"
cidr: 172.16.0.0/24
register: public
- name: Create Public Subnet Route Table
ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc_id }}"
region: "{{ region }}"
tags:
Name: Public
subnets:
- "{{ public.subnet_id }}"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ igw.gateway_id }}"
after running the playbook i received following error:
fatal: [localhost]: FAILED! => {"failed": true, "msg": "ERROR! 'dict object' has no attribute 'subnet_id'"}
Try using: public.subnet.id instead of public.subnet-id
Its useful to debug by running this task:
- debug: msg="{{ public }}"

Resources