Ansible Strange conditional behavior - ansible

I'm facing a strange issue while trying to search inside a dictionary. In the code above, i'm trying to define a new var "OLD_VLANID_PP", with the value of the "name" key of the dictionary "VM_NETWORK_INFOS_PP".
If the dictionary is set and not empty, I want the var to be defined, otherwise just skip the task.
In order to test if the condition was true or false, I've added a debug task which should display the content of the dictionary.
When running my playbook, the debug task does indicates that the condition is not matched, and so doesn't display my dictionary, but Ansible keep trying to define my new var as shown in the output, and so fires an error "'item' is undefined"
---
- block:
#Search VCENTER for PPROD VM (If Windows to create => Search PG / EIther search Postgre)
- name: Search for PPROD VM already created for the CLIENT {{ CLIENTNAME }} to retrieve VLANID
set_fact:
OLD_VMNAME_PP: "{{ item.guest_name }}"
with_items: "{{ VM_EXIST_PP.virtual_machines }}"
when: item.guest_name == VMNAME_PP
delegate_to: localhost
when: ENV != "PROD" and DEPLOY != "ALL" and (VM_EXIST_PP is defined) and (VM_EXIST_PP|length > 0)
#If we successfully found the previous VM for the client, then get its network info
- name: Retrieve PPROD VM {{ VMNAME_PP }} VLAN ID
community.vmware.vmware_guest_network:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
datacenter: "{{ datacenter_name }}"
validate_certs: False
name: "{{ OLD_VMNAME_PP }}"
gather_network_info: true
register: VM_NETWORK_INFOS_PP
when: (OLD_VMNAME_PP is defined) and OLD_VMNAME_PP != ""
- block:
- debug: msg={{VM_NETWORK_INFOS_PP}}
#If we successfully found the network info, set the OLD_VLANID with previous VM VLANID
- set_fact:
OLD_VLANID_PP: "{{ (item.name) | replace('(HDS : Client)','') | replace('VLAN0','') | replace('VLAN','') | replace(' ','') }}"
with_items:
- "{{ VM_NETWORK_INFOS_PP.network_info }}"
when: item.name | regex_search('VLAN') and not item.name | regex_search('PVLAN')
when: (VM_NETWORK_INFOS_PP is defined) and VM_NETWORK_INFOS_PP != ""
Debug Output (Should display the dictionary content) :
ok: [127.0.0.1] => {
"msg": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
Error Output :
The error was: error while evaluating conditional (item.name | regex_search('VLAN') and not item.name | regex_search('PVLAN')): 'item' is undefined
Ansible Version : 2.10.7
Python Version : 3.7.3
Any help would be appreciated.
Thank you

Not sure if it's the right thing to do, but finally get ride of this issue using :
when: item is defined and item.name | regex_search('VLAN') and not item.name | regex_search('PVLAN')
note the item is defined before the regex search

Related

Ansible when conditional with variable and item

I'm having hard time trying to figure it out what I'm doing wrong with my Ansible playbook.
I've got a bunch of tasks, which define or not some variables according to context, depending of the result, some task will be ignored or not.
For this specific case, I check if a VlanID already exists, if it doesn't then I create one, and retrieve the new VlanID from the result.
Here is the playbook :
---
#Tasks for portGroup_add
- name: Get all portgroups in dvswitch vDS
community.vmware.vmware_dvs_portgroup_find:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
dvswitch: "{{ vcenter_dvSwitch }}"
validate_certs: False
register: portGroup_infos
when: (OLD_VLANID is not defined) or (OLD_VLANID|length < 1)
#Get last VLAN ID for HDS client, and set VLANID + 1
- name: get portGroup_infos
set_fact:
VLANID: "{{ item.vlan_id }}"
with_items: "{{ portGroup_infos.dvs_portgroups}}"
when:
- (portGroup_infos is defined) and (portGroup_infos|length > 0)
- item.name | regex_search("\(HDS :\s*")
While everything is working pretty well for most of the tasks, this one fires the following error :
The conditional check 'item.name | regex_search("\(HDS :\s*")' failed.
The error was: error while evaluating conditional (item.name | regex_search("\(HDS :\s*")): 'item' is undefined
Which is pretty obvious, because the dict portGroup_infos, is not defined.
In order to get the new VlanID, I'm using a "when" conditionnal , which check if in the item, the value "(HDS :" , is present.
But I don't want the task to launch if the portGroup_infos variable defined above is not set, I though I'd should use nested "when", but can't succeed.
Ansible version : 2.10.7
python version : 3.7.3
Thank you for your help.
Put both tasks into a block, e.g.
- block:
- name: Get all portgroups in dvswitch vDS
...
- name: get portGroup_infos
...
when: OLD_VLANID|default('')|length == 0

Ansible: tasks to append number to name tag

I'm trying to append a number to the end of the instance name tag, i have the following code but there's a problem with the second task which i cannot find, and i've not been able to find an example of anyone else trying to solve this problem.
I'm also relatively new to Ansible and cannot find the relevant documentation to do certain things like how to lookup a value in a list like how i'm doing with my until: which is probably invalid syntax
Ansible is 2.9 and runs on the instance itself
The Tasks I have are setup to do the following
Get a list of running EC2 instances that belong to the same launch template
Loop the same amount of times as the instance list until the desired name based on item index is not found in the name tags of the ec2 list
Set the updated tag name
Current Code:
---
- name: "{{ role_name }} | Name Instance: Gather facts about current LT instances"
ec2_instance_info:
tags:
"aws:autoscaling:groupName": "{{ tag_asg }}"
"aws:ec2launchtemplate:id": "{{ tag_lt }}"
Application: "{{ tag_application }}"
filters:
instance-state-name: [ "running" ]
no_log: false
failed_when: false
register: ec2_list
- name: "{{ role_name }} | Name Instance: Generate Name"
with_sequence: count="{{ ec2_list.instances|length }}"
until: not "{{ tag_default_name }} {{ '%02d' % (item + 1) }}" in ec2_list.instances[*].tags['Name']
when: tag_name == tag_default_name
no_log: false
failed_when: false
register: item
- name: "{{ role_name }} | Name Instance: Append Name Tag"
ec2_tag:
region: eu-west-1
resource: "{{ instance_id }}"
state: present
tags:
Name: "{{ tag_default_name }} {{ '%02d' % (item + 1) }}"
when: tag_name == tag_default_name
no_log: false
failed_when: false
As requested here's the error I am getting:
ERROR! no module/action detected in task.
The error appears to be in '/app/deploy/Ansible/roles/Boilerplate/tasks/name_instance.yml': line 14, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: "{{ role_name }} | Name Instance: Generate Name"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
The error is not with the name: as i constantly get that error when there's some other error within the task body
You don't appear to have a module listed in the second task. You might be able to use debug as the module, or use set_fact and skip the register line.
I think it might also be possible to combine the last two tasks with some more advanced looping, but I'd have to play around with it to give you a working example.
Thanks to bnabbs answer i realised the problem with that version was the lack of module, after fixing that i then finished creating and testing it and now have a fully working set of tasks which has resulted in the following code.
---
- name: "{{ role_name }} | Name Instance: Gather facts about current LT instances"
ec2_instance_info:
filters:
"tag:aws:autoscaling:groupName": "{{ tag_asg }}"
"tag:aws:ec2launchtemplate:id": "{{ tag_lt }}"
"tag:Application": "{{ tag_application }}"
instance-state-name: [ "pending", "running" ]
register: ec2_list
- name: "{{ role_name }} | Name Instance: Set Needed Naming Facts"
set_fact:
tag_name_seperator: " "
start_index: "{{ (ec2_list.instances | sort(attribute='instance_id') | sort(attribute='launch_time') | map(attribute='instance_id') | list).index(instance_id) }}"
name_tag_list: "{{ (ec2_list.instances | map(attribute='tags') | list) | map(attribute='Name') | list }}"
# Generate Name from starting index of array and mod to the length of the amount of instances to help prevent conflicts when multiple instances are launched at the same time
- name: "{{ role_name }} | Name Instance: Generate Name"
set_fact:
desired_tag_name: "{{ tag_default_name }}{{ tag_name_seperator }}{{ '%02d' % (((item|int) + (start_index|int) % (name_tag_list|length)) + 1) }}"
loop: "{{ name_tag_list }}"
until: not "{{ tag_default_name }}{{ tag_name_seperator }}{{ '%02d' % (((item|int) + (start_index|int) % (name_tag_list|length)) + 1) }}" in name_tag_list
- name: "{{ role_name }} | Name Instance: Append Name Tag"
ec2_tag:
region: eu-west-1
resource: "{{ instance_id }}"
state: present
tags:
Name: "{{ desired_tag_name }}"

Ansible - Getting List Values from within a Dictionary (Regsiter Variable)

So I'm working on some audit points using Ansible for many of the servers we support. In most cases, I have had to use the shell modules to get the data I want and then write some files based on pass/fail cases. In a lot of situations, this has been the easier way to work with the output data. First, I realize this isn't necessarily Ansible's forte. I guess at some point it was pitched to the company that it could do this pretty easily, and I would agree - it's easier in many ways than just writing a custom python/BASH script to do the same. So - I do realize I'm bending the concept of Ansible a bit here for reporting, rather than configuration/state management. However; I like the tool and want to show the company we can get a lot of value from it.
While I could do this section easily using the shell module, I would like to learn Ansible a bit better. So thought I would post this question.
I'm using the Yum module to just get a repolist on the target hosts. But I've been running into confusion on just how to extract the list values nested in the output dictionary. I have done some checking on the types and as far as I can tell - the 'results' variable is a dictionary, with the output in in a list. What I want to do, is get the key/values from the list and then perform some other tasks based on that output. But for the life of me - I can't figure out how to do this!
Ideally - I would like to either use some 'when' module statements based on the output (When the repo ID is.. do this.. for example) or at least be able to store them in a variable to work with the data. So from this output, I just want to get the repoid and if it's enabled. How can I get these values from the nested list?
Simple Playbook:
---
- hosts: localhost
become: yes
tasks:
- name: Section 1.1 - Check Yum Repos
yum:
list: repos
register: section1_1
- name: Debug
debug:
var: section1_1
Here is my output from the debug task in this playbook:
TASK [Debug] ****************************************************************************************************************************************************
ok: [localhost] => {
"section1_1": {
"changed": false,
"failed": false,
"results": [
{
"repoid": "ansible",
"state": "enabled"
},
{
"repoid": "epel",
"state": "enabled"
},
{
"repoid": "ol7_UEKR6",
"state": "enabled"
},
{
"repoid": "ol7_latest",
"state": "enabled"
}
]
}
}
I suspect this might be easy for someone out there. I've been trying this and that's for quite a while now and finally got to the point where I thought I would just ask :)
As the output of registered in section1_1 is a list of dictionaries. We can loop through each item, to get the dictionary keys.
Example:
- name: Get the first repo's repoid and state
debug:
msg: "Repo ID: {{ results[0]['repoid'] }}, is {{ results[0]['state'] }}"
# This will show -- Repo ID: ansible, is enabled
Similarly we can access other elements with their number.
Or we can loop through each element of array:
- name: loop through array and conditionally do something
debug:
msg: "Repo ID is {{ item.repoid }}, so I am going to write a playbook."
when: item.repoid == 'ansible'
loop: "{{ results }}"
Q: "Get the key/values from the list."
A: There are more options. Given the data below
section1_1:
changed: false
failed: false
results:
- repoid: ansible
state: enabled
- repoid: epel
state: enabled
- repoid: ol7_UEKR6
state: enabled
- repoid: ol7_latest
state: enabled
- repoid: test
state: disabled
1a) Get the keys and values, and create a dictionary
_keys1: "{{ section1_1.results|map(attribute='repoid')|list }}"
_vals1: "{{ section1_1.results|map(attribute='state')|list }}"
repos1: "{{ dict(_keys1|zip(_vals1)) }}"
gives
_keys1: [ansible, epel, ol7_UEKR6, ol7_latest, test]
_vals1: [enabled, enabled, enabled, enabled, disabled]
repos1:
ansible: enabled
epel: enabled
ol7_UEKR6: enabled
ol7_latest: enabled
test: disabled
1b) The filter items2dict gives the same result
repos2: "{{ section1_1.results|
items2dict(key_name='repoid', value_name='state') }}"
1c) The filter json_query gives also the same result
repos3: "{{ dict(section1_1.results|
json_query('[].[repoid, state]')) }}"
Iterate the dictionary
- debug:
msg: "Repo {{ item.key }} is {{ item.value }}"
loop: "{{ repos1|dict2items }}"
gives (abridged)
msg: Repo ansible is enabled
msg: Repo epel is enabled
msg: Repo ol7_UEKR6 is enabled
msg: Repo ol7_latest is enabled
msg: Repo test is disabled
The next option is the conversion of the values to boolean
_vals4: "{{ section1_1.results|
json_query('[].state.contains(#, `enabled`)') }}"
repos4: "{{ dict(_keys1|zip(_vals4)) }}"
gives
_vals4: [true, true, true, true, false]
repos4:
ansible: true
epel: true
ol7_UEKR6: true
ol7_latest: true
test: false
Iterate the dictionary
- debug:
msg: "Repo {{ item.key }} is enabled: {{ item.value }}"
loop: "{{ repos4|dict2items }}"
gives (abridged)
msg: 'Repo ansible is enabled: True'
msg: 'Repo epel is enabled: True'
msg: 'Repo ol7_UEKR6 is enabled: True'
msg: 'Repo ol7_latest is enabled: True'
msg: 'Repo test is enabled: False'
3a) The list of the enabled repos can be easily selected
- debug:
msg: "Repo {{ item.key }} is enabled"
loop: "{{ repos4|dict2items|selectattr('value') }}"
gives (abridged)
msg: Repo ansible is enabled
msg: Repo epel is enabled
msg: Repo ol7_UEKR6 is enabled
msg: Repo ol7_latest is enabled
3b), or rejected
- debug:
msg: "Repo {{ item.key }} is disabled"
loop: "{{ repos4|dict2items|rejectattr('value') }}"
gives (abridged)
msg: Repo test is disabled
Example of a complete playbook for testing
- hosts: localhost
vars:
section1_1:
changed: false
failed: false
results:
- {repoid: ansible, state: enabled}
- {repoid: epel, state: enabled}
- {repoid: ol7_UEKR6, state: enabled}
- {repoid: ol7_latest, state: enabled}
- {repoid: test, state: disabled}
_keys1: "{{ section1_1.results|map(attribute='repoid')|list }}"
_vals1: "{{ section1_1.results|map(attribute='state')|list }}"
repos1: "{{ dict(_keys1|zip(_vals1)) }}"
repos2: "{{ section1_1.results|
items2dict(key_name='repoid', value_name='state') }}"
repos3: "{{ dict(section1_1.results|
json_query('[].[repoid, state]')) }}"
_vals4: "{{ section1_1.results|
json_query('[].state.contains(#, `enabled`)') }}"
repos4: "{{ dict(_keys1|zip(_vals4)) }}"
tasks:
- debug:
var: section1_1
- debug:
var: _keys1|to_yaml
- debug:
var: _vals1|to_yaml
- debug:
var: repos1
- debug:
var: repos2
- debug:
var: repos3
- debug:
msg: "Repo {{ item.key }} is {{ item.value }}"
loop: "{{ repos1|dict2items }}"
- debug:
var: _vals4|to_yaml
- debug:
var: repos4
- debug:
msg: "Repo {{ item.key }} is enabled: {{ item.value }}"
loop: "{{ repos4|dict2items }}"
- debug:
msg: "Repo {{ item.key }} is enabled"
loop: "{{ repos4|dict2items|selectattr('value') }}"
- debug:
msg: "Repo {{ item.key }} is disabled"
loop: "{{ repos4|dict2items|rejectattr('value') }}"

Conditional set_fact only with selectattr?

I am trying to use set_fact, but only when an item is defined. I'm having trouble figuring out how to do this.
Playbook:
---
- hosts: localhost
vars:
apps:
- name: app1
role: "app1-role"
- name: app2
role: "app2-role"
- name: app4
role: "app4-role"
tasks:
- name: "Find a matching app, and print the values"
set_fact:
app: "{{ apps | selectattr('name', 'match', app_name) | first }}"
The task fails when there is no match, for example: app_name=app3, with this message:
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.
I have tried a few different conditionals, but I'm not quite sure how to structure this.
when: (apps | selectattr('name', 'match', app_name)) is undefined
This condition always evaluates as False - skipping: [localhost] => {"changed": false, "skip_reason": "Conditional result was False"}.
I tried this :
when: "{{ apps | selectattr('name', 'match', app_name) | list }}"
It seems that without the list filter, selectattr returning a generator object does not seem to allow converting to a boolean for the test evaluation. I'm not 100% sure about this interpretation though.
Note that having is defined or not would not change anything, it would be totally equivalent.
Try
- set_fact:
app: "{{ mylist|first }}"
vars:
mylist: "{{ apps|selectattr('name', 'match', app_name)|list }}"
when: mylist|length > 0
Next option is json_query instead of selectattr
- set_fact:
app: "{{ mylist|first }}"
vars:
query: "[?name=='{{ app_name }}']"
mylist: "{{ apps|json_query(query) }}"
when: mylist|length > 0

Trying to use 'selectattr in' filter with Ansible playbook but it fails as per the playbook below

I am trying to use textfsm to parse the data for admin show platform in that anything with a state of 'IOS XR RUN' 'READY' or 'OK' will pass & anything else will report failed. I'm using the selectattr in option but get the following error - "msg": "The task includes an option with an undefined variable. The error was: 'intf_tests_pass' is undefined. Any ideas why this is failing as the working_state variable is defined.
name: Collect admin show platform info
iosxr_command:
commands:
- admin show platform
provider: "{{ cli }}"
register: platform_result
when: device_os == 'cisco-ios-xr'
name: retrieve status to be returned
set_fact:
working_state: ['IOS XR RUN', 'READY', 'OK']
name: parse platform_result
textfsm_parser:
file: templates_textfsm/{{ device_os }}/admin_show_platform.template
content: "{{ platform_result.stdout.0 }}"
name: platform_state
when:
platform_result.stdout is defined
platform_result.stdout[0] != none
platform_result.stdout[0] != ""
name: identify platform_result that passed
set_fact:
platform_tests_pass: "{{ ansible_facts.platform_state |
selectattr('STATE', 'in', 'working_state') | list }}"
when: ansible_facts.platform_state is defined
name: identify platform_result that failed
set_fact:
platform_tests_fail: "{{ ansible_facts.platform_state |
difference(platform_tests_pass) | list}}"
when: ansible_facts.platform_state is defined
debug:
msg:
"{{ intf_tests_pass }}"
"{{ intf_tests_fail }}"
Thanks,
Brian
Based on the output you posted, you saved facts to platform_tests_pass and platform_tests_fail, not intf_tests_pass and intf_tests_fail, so those variables are never actually defined.

Resources