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

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.

Related

Syntax error when checking value of variable

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.

Write task output to file while using "until"

I have an ansible task that fails about 20% of the time. It almost always succeeds if retried a couple of times. I'd like to use until to loop until the task succeeds and store the output of each attempt to a separate log file on the local machine. Is there a good way to achieve this?
For example, my task currently looks like this:
- name: Provision
register: prov_ret
until: prov_ret is succeeded
retries: 2
command: provision_cmd
I can see how to store the log output from the last retry when it succeeds, but I'd like to store it from each retry. To store from the last attempt to run the command I use:
- name: Write Log
local_action: copy content={{ prov_ret | to_nice_json }} dest="/tmp/ansible_logs/provision.log"
It's not possible as of 2.9. The until loop doesn't preserve results as loop does. Once a task terminates all variables inside this task will be gone except the register one.
To see what's going on in the loop write a log inside the command at the remote host. For example, the command provision_cmd writes a log to /scratch/provision_cmd.log. Run it in the block and display the log in the rescue section.
- block:
- name: Provision
command: provision_cmd
register: prov_ret
until: prov_ret is succeeded
retries: 2
rescue:
- name: Display registered variable
debug:
var: prov_ret
- name: Read the log
slurp:
src: /scratch/provision_cmd.log
register: provision_cmd_log
- name: Display log
debug:
msg: "{{ msg.split('\n') }}"
vars:
msg: "{{ provision_cmd_log.content|b64decode }}"

Evaluate Ansible stdout for end of output

I have a shell task that gets the defrag configuration, and stores it in a variable, like:
- name: Check if hugepages is disabled
shell: cat /sys/kernel/mm/transparent_hugepage/defrag
register: hugepages_status
changed_when: False
In the following task, I want to evaluate the hugepages_status, to see if the last word in it is "never". How can I read the hugepages_status to evaluate just the last word in the string?
Something like:
- name: Disable hugepages
shell: echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
when: swap_status.stdout != "*never"
You can use is search that will search for the string irrespective of the position. I think this would suffice for this scenario. Let me know in case you are looking to search at the end only.
---
- name: play
hosts: localhost
tasks:
- name: Check if hugepages is disabled
shell: cat /sys/kernel/mm/transparent_hugepage/defrag
register: hugepages_status
- name: display the output
debug:
var: hugepages_status.stdout_lines
when: hugepages_status.stdout is search('never')

Ansible variable from output

I would like to extract some words from an Ansible output. For example, I would like to extract from sudo monit status command the bolded words(** **) and store them in 2 variables(let's say variable 'A' for OK and variable 'B' for the uptime period) which will be used later on into a Jinja2 template:
[ansible#server ~]$ sudo monit status
Monit 5.25.1 uptime: 3m
System 'server'
status **OK**
monitoring status Monitored
monitoring mode active
on reboot start
load average [0.03] [0.07] [0.11]
cpu 0.1%us 0.2%sy 0.0%wa
memory usage 338.1 MB [18.4%]
swap usage 0 B [0.0%]
uptime **29m**
boot time Fri, 30 Mar 2018 11:56:12
data collected Fri, 30 Mar 2018 12:25:24
To accomplish this, I have started an Ansible playbook, but in this way, I'm taking all information from the output:
--- #Health check
- hosts: appserver
connection: ssh
sudo: yes
user: ansible
tasks:
- name: Monitor the status
shell: "{{item}}"
with_items:
- monit status
register: monitinfo
tags: basic_monitoring
- debug: var=monitinfo
Any idea how this can be accomplished?
Thank you,
Romain
I see two methods to solve your issue:
Method 1: Converting the shell output to YAML or JSON and parse it with from_yaml or from_json
In your particular case, as the output is already pre-formatted in a key-value way, it would be most convenient to just convert it to a format that Ansible understands (e.g. YAML/JSON). For example, you could convert the output to YAML by deleting the irrelevant lines with tail and then use the regex_replace filter for the rest. Afterwards, use the from_yaml filter to parse everything into Ansible variables. Example:
- name: Fetch the monit status
shell: "monit status | tail -n +3"
register: monit_status_raw
- name: Extract monit variables
set_fact:
monit_status: "{{ monit_status_raw.stdout | regex_replace('\s\s([a-z]+\s[a-z]*)\s+', '\\1: ') | regex_replace('\s:', ':') | regex_replace('([a-z])\s([a-z])', '\\1_\\2') | from_yaml }}"
- name: Show server status
debug: var=monit_status['status']
- name: Show server uptime
debug: var=monit_status['uptime']
If everything went fine, the monit_status variable should contain a dictionary of all variables that the monit status command provided. You can access the variables you want as shown in the debug commands within the example above. In case the code does not work already, you could debug the regex_replace filter with
- debug: msg="{{ monit_status_raw.stdout | regex_replace('\s\s([a-z]+\s[a-z]*)\s+', '\\1: ') | regex_replace('\s:', ':') | regex_replace('([a-z])\s([a-z])', '\\1_\\2') | from_yaml }}"
and check whether the output is really YAML already. In case not, the regular expression has to be adjusted so that in the end, the output is YAML-conform.
Method 2: Searching for the values you need with the regex_search filter
In similar cases where just a single variable has to be extracted or if the output cannot be easily converted to JSON or YAML with regex_replace, the regex_search filter would be the right choice. In your case, the regex_search solution would look similar to this:
- name: Fetch the monit status
shell: "monit status | tail -n +3"
register: monit_status_raw
- name: Extract monit variables
set_fact:
monit_status: "{{ monit_status_raw.stdout | regex_search('\s\sstatus\s+(.*)') }}"
monit_uptime: "{{ monit_status_raw.stdout | regex_search('\s\suptime\s+(.*)') }}"
- name: Show server status
debug: var=monit_status
- name: Show server uptime
debug: var=monit_uptime
Hint: Online regex testers like regex101.com can help you to find the right expression.

conditionally run tasks when given multiple input to a variable

I have written a ansible script which runs fine when there is only 1 input to a variable:
---
- hosts: ListA
vars:
app-dir: /tmp
service_name: exampleAAA
roles:
- prechecks
Below is the task i am using and working when only one service defined for service_name:
---
- name: check service status
command: "{{app_dir}}/app-name {{item}} status"
with_items: '{{service_name}}'
ignore_errors: yes
register: service_status
- name: starting service if it's in failed state
set_fact: serviceTostart={{item}}
with_items: '{{service_name}}'
when: service_status | failed
- shell: "{{app_dir}}/app-name {{serviceTostart}} start"
when: service_status | failed
As per my usecase i need this to work for below:
vars:
service_name:
- exampleAAA
- exampleBBB
- exampleCCC
When i run the playbook after defining multiple service_name. it shows failed status of service in step check service status but it says ok in rest of the steps. When i check the status of services there is no change. How can i make it work for multiple service_names ???
So here i what the script should do(I am stuck with points 2 & 3, can someone please let me know what need to be done to make it work):
The script will check the status of all the services mentioned (it is doing this correctly)
If one of the service status shows as stop. It will go the tasks which will run the command to bring back that particular service.
If after one start the service still does not come up then script should fail ( I am yet to write code for this part).
Honestly the answer to your question is in the documentation: Using register with a loop.
- name: check service status
command: "{{app_dir}}/app-name {{item}} status"
with_items: "{{service_name}}"
ignore_errors: yes
register: service_status
- shell: "{{app_dir}}/app-name {{item.item}} start"
when: item | failed
with_items: "{{service_status.results}}"

Resources