Syntax error when checking value of variable - ansible

This should be a very simple task but I'm unable to get past a syntax error.
In the below playbook, the first task gets the capabilities of Python and registers it in python_capability where python_capability.stdout becomes /usr/bin/python3.10 cap_sys_nice=ep.
In the next task, I split this string and set capabilities to everything following after the 0th item. In this case, capabilities becomes [cap_sys_nice=ep].
Then, I want to check the length of the above list and fail if it's > 1.
- hosts: tests
become: yes
pre_tasks:
- block:
- name: Ensure Python has CAP_SYS_NICE privileges
capabilities:
path: /usr/bin/python3.10
capability: cap_sys_nice+ep
state: present
register: python_capability
rescue:
- name: RESCUE | Get Python capabilities
set_fact:
capabilities: "{{ python_capability.stdout.split()[1:] }}"
- name: Get length
set_fact:
num_capabilities: "{{ capabilities | length }}"
- name: Failed when
failed_when: num_capabilities | int > 1
But I get the below syntax related error:
ERROR! no module/action detected in task.
The error appears to be in '/test.yml': line 25, column 11, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Failed when
^ here
What am I missing?

You need to change the last task to:
- name: Validate number of capabilities
ansible.builtin.fail:
msg: "There are too many capabilities"
when: num_capabilities | int > 1
This will stop the execution if the condition is met.

Related

Compare Kernel version of 2 managed nodes

I'm working on a cluster setup and need to compare if the kernel versions of both the machines are same if not end the play using "meta" but it is not functioning as expected and giving error :
- name: Cluster setup
hosts: cluster_nodes
tasks:
- name: Check kernel version of primary
shell: uname -r
when: inventory_hostname in groups['primary']
register: primary
- name: check kernel version of secondary
shell: uname -r
when: inventory_hostname in groups['secondary']
register: secondary
- meta: end_play
when: primary.stdout != secondary.stdout
ERROR:
ERROR! The conditional check 'primary.stdout != secondary.stdout' failed. The error was: error while evaluating conditional (primary.stdout != secondary.stdout): 'dict object' has no attribute 'stdout'
The error appears to be in '/var/lib/awx/projects/pacemaker_RHEL_7_ST/main_2.yml': line 55, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- meta: end_play
^ here
Please suggest how to write a when condition to stop the play if the OS versions are not RHEL7 and both are of same kernel version.
Why firing a shell when the information you need is directly inside the gathered facts?
Moreover, your above logic is wrong as:
all servers in your cluster_nodes group go through all tasks which are skipped when the condition is not met (hence why you do not get a stdout defined on your register on skipped servers)
you are only trying to compare 2 servers (one from each primary and secondary group) where your cluster can grow and contain many. So what you want to check IMO is that all kernel version are aligned for all nodes. If this is not exactly what you want, you can still adapt from the below example.
Here is how I would do that check.
- name: Cluster setup
hosts: cluster_nodes
vars:
# Get all kernel versions from all nodes into a list
# Note that this var will be undefined if facts are not gathered
# prior to using it.
kernels_list: "{{ groups['cluster_nodes']
| map('extract', hostvars, 'ansible_kernel') | list }}"
tasks:
# Make sure the kernel list has a single unique value
# (i.e. all kernel versions are identical)
# We check only once for all servers in the play.
- name: Make sure all kernel versions are aligned
assert:
that:
- kernels_list | unique | count == 1
fail_msg: "Node kernel versions are not aligned ({{ kernels_list | string }})"
run_once: true
- name: go on with install if assert was ok
debug:
msg: Go on.

Ansible ERROR! 'retries' is not a valid attribute for a TaskInclude

My requirement is to run the script stop-all as many times (5 retries) until the output of ps -fu user1 |wc -l becomes less than 2.
I wrote the below ansible playbook for the same:
cat stop.yml
- hosts: dest_nodes
tasks:
- name: Start service
include_tasks: "{{ playbook-dir }}/inner.yml"
retries: 5
delay: 4
until: stopprocesscount.stdout is version('2', '<')
cat inner.yml
- name: Start service
shell: ~/stop-all
register: stopprocess
- name: Start service
shell: ps -fu user1 |wc -l
register: stopprocesscount
However, I get the below error running the playbook.
ERROR! 'retries' is not a valid attribute for a TaskInclude
The error appears to be in '/app/playbook/stop.yml': line 19, column 9, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Start service
^ here
Can you please suggest?
First of all, correct the indentation of tasks in inner.yml. Secondly, remove retries, delay and until from stop.yml and move them to specific task as these are task level parameters.
Since you need to retry one task based on another task, you can just combine the script and command and extract the result of wc -l command like below:
Since stdout_lines will contain list of strings and version requires int hence the conversion.
inner.yml
- name: Start service
shell: ~/stop-all; ps -fu user1 | wc -l
register: stopprocesscount
retries: 5
delay: 4
until: stopprocesscount.stdout_lines[stopprocesscount.stdout_lines | length - 1] | int is version('2', '<')
stop.yml
- hosts: dest_nodes
tasks:
- name: Start service
include_tasks: "{{ playbook-dir }}/inner.yml"
Not all task attributes work with all task (here TaskInclude task).
There is no clear documentation, such a compatibility matrix, but the error message here is quite clear "is not a valid attribute".
By instance:
You cant loop a Block: 'with_items' is not a valid attribute for a Block
You cant async a TaskInclude, see https://github.com/ansible/ansible/issues/57051.

Unable to print ansible_failed_task.name in rescue block

Following is the piece of code, which is retrieving list of local zones and trying to enter rescue block if any one of the local zones is not in RUNNING state.
But the rescue block is failing, at 'Check if the zones are in running state'
Expectation is to send an email with the task name. But it is working well with other failed tasks.Can anyone please guide?
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/playbooks/misc/test1404.yml': line 23, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n rescue:\n - name: Sending an e-mail from Ansible controller node\n ^ here\n"}
- '{{ host }}'
tasks:
- block:
- name: Retrieve list of local zones
shell: /usr/sbin/zoneadm list | grep -v global
register: lzones
tags:
- local_zone_list
- debug:
msg: "{{ item }}"
with_items: "{{lzones.stdout_lines}}"
- name: Check if the zones are in running state
shell: /usr/sbin/zoneadm list | grep -v global | grep "{{ item }}" | awk '{print$3}'
register: status
with_items: "{{lzones.stdout_lines}}"
failed_when: status.stdout.find('running') == -1
< few other tasks>
rescue:
- name: Sending an e-mail from Ansible controller node
mail:
host: localhost
port: 25
to: xyz#abc.com
subject: Reboot Failed
body: Reboot FAILED at TASK - {{ ansible_failed_task.name }} with ERROR {{ ansible_failed_result }}
delegate_to: localhost
Well that was an absolutely fascinating rabbit hole.
Because in ansible jinja2 templates are rendered recursively, when it tries to render the failed message, which contains the failed template, it tries to re-render the failed template again, re-causing the error
This appears to be impacting you by the inclusion of the ansible_failed_task variable, since -- inexplicably -- it appears to be safe to include ansible_failed_result in the body
As best I can tell by experimenting with ansible 2.9.6, one must determine if it is possible to safely output the a*_task flavor variable before touching it, because I was wholly unable to find any combination of | string or |regex_replace or anything that allowed jinja2 to touch that variable so long as it contains the bogus variable reference:
- block:
- debug:
msg: this explodes {{ nope_not_a_var }}
rescue:
- set_fact:
is_undefined_error: '{{ "undefined variable" in ansible_failed_result.msg }}'
- name: variable is unsafe version
debug:
msg: >-
failed task action has an undefined variable in the task,
so we cannot show you the task, but here is the result: {{ ansible_failed_result }}
when: is_undefined_error
- name: variable is safe to output version
debug:
msg: Reboot FAILED at TASK - {{ ansible_failed_task.name }} with ERROR {{ ansible_failed_result }}
when: not is_undefined_error
It may be safe to inline that test of "..." in ansible_failed_result.msg into the when: lines directly, but since it (should) produce the same answer both times, there's no really good reason to evaluate it twice
This appears to me to be an ansible bug, but I don't have the emotional energy to take it up with their community -- however, I encourage you to file a bug with them

Unable to retrieve some Ansible facts about Windows clients

I currently want to select specifically the Windows adapter name ONLY from the ansible facts.
So my problem is that I cannot retrieve this value only.
Ansible 2.8.2_1 with Winrm & Kerberos Authentication are running on the server.
I've tried to launch this playbook :
- hosts: win_clients
gather_facts: true
strategy: free
tasks:
- name: Get Ansible network facts
debug:
msg: "{{ ansible_facts['interfaces'] }}"
and it works fine but I have all the informations about the interfaces. I just want the "connection_name".
When I put this line in the playbook :
msg: "{{ ansible_facts['interfaces']['connection_name'] }}"
It shows this message at the execution :
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'connection_name'\n\nThe error appears to be in '/home/sopra/git/rnd-windows/automation/playbooks/Dns/test.yaml': line 5, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Get Ansible network facts\n ^ here\n"}
I don't understand because the variable "connection_name" is well defined.
Can somebody help me? Thanks.
Have a good day !
If you want to list the connection_name, use below, as ansible_facts['interfaces'] is an array
- hosts: win_clients
gather_facts: true
strategy: free
tasks:
- name: Get Ansible network facts
debug:
msg: "{{ item.connection_name }}"
with_items:
- "{{ ansible_facts['interfaces'] }}"
Thank you very much for your support. I did resolve my problem.
Basically, my playbook consists in changing the DNS configuration if the occurrence of one (old) DNS's IP if found.
# tasks file for configureDnsServerAddresses
# Get the configuration state about DNS
# If one occurrence of 'old_dnsserver' is found...
- name: Get DNS configuration
win_shell: ipconfig /all | findstr {{ old_dnsserver }}
register: check_old_dns
changed_when: false
# '.rc' means the return code
failed_when: check_old_dns.rc != 0 and check_old_dns.rc != 1
# ... it will be replaced by 2 new ones
- name: Change the DNS only if the IP {{ old_dnsserver }} is found
win_dns_client:
adapter_names: "{{ item.connection_name }}"
ipv4_addresses:
- "{{ dnsserver1 }}"
- "{{ dnsserver2 }}"
# Array based on Ansible facts
with_items:
- "{{ ansible_facts['interfaces'] }}"
# Apply only if 'check_old_dns' is not empty, which means that the old DNS is found
when: check_old_dns.stdout | length > 0
This playbook is role-based, so the variables are stored in a "defaults" folder.
The code mentioned above was intended for testing purposes.

Ansible - Loop based on dict with stat not working

I'm currently creating a possibility to auto-obtain a Letsencrypt certificate using my ansible-playbook.
I'd like to check if the /etc/letsencrypt/domain.tld directory exists, when it doesn't, I need to obtain a certificate.
---
- name: LETSENCRYPT | Checking for existing certificates
stat:
path: /etc/letsencrypt/live/{{ item.value.server_name }}
register: le_cert_exists
with_dict: "{{ sites }}"
when: item.value.letsencrypt | default(false) | bool
- name: DEBUG | Output result of le cert exists
debug:
var: le_cert_exists
- name: LETSENCRYPT | Output sites that need a new certificate
debug:
msg: Obtain certificate here
var: item.item
with_items: le_cert_exists.results
when: item.stat.exists is defined and not item.stat.exists
So far it is working, except for the last function. The last task just keeps getting skipped or fails with the following error:
fatal: [-]: FAILED! => {"msg": "The conditional check 'item.stat.exists is defined and not item.stat.exists' failed. The error was: error while evaluating conditional (item.stat.exists is defined and not item.stat.exists): 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'stat'\n\nThe error appears to have been in '/path/to/main.yml': line 13, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: LETSENCRYPT | Output sites that need a new certificate\n ^ here\n"}
Does anyone has an example of how this could be done easily?
I just need to execute a command when a directory does not exists.
This with_items: le_cert_exists.results is wrong,
this with_items: "{{ le_cert_exists.results }}" is correct.

Resources