Ansible variable from output - ansible

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.

Related

Is there any option in Ansible systemd module to print details of Cgroup?

Is there any option in Ansible systemd module to print details of Cgroup?
Below snippet does not print any Cgroup details of particular service.
- name: service
systemd:
name: test.service
register: service_status
- name: print
debug:
msg: "{{ service_status.status.ControlGroup }}"
Above code will print only below output
msg: /system.slice/test.service
whereby CLI command
sudo systemctl status test.service
would print below Cgroup PID details
Cgroup: /system.slice/test.service
1000 processname1
1002 processname2
1003 processname3
Ansible is mainly a Configuration Management tool with which you declare a Desired State. By using a service module (to) Manage services like systemd module (to) Manage systemd units you can make sure that a service is in a certain state.
Even it seems to be possible to gather the service state by using (annot.: not using service_facts module – Return service state information as fact data).
---
- hosts: localhost
become: true
gather_facts: false
tasks:
- name: Get current service state
systemd:
name: "{{ SERVICE_NAME }}"
register: result
- name: Show result
debug:
msg: "{{ result }}"
it will result in to the output of
...
ControlPID: '0'
ExecMainPID: '123'
...
MainPID: '123'
...
and the main PID only.
Looking into the Source Code of the module systemd.py this for intension
def parse_systemctl_show(lines):
# The output of 'systemctl show' can contain values that span multiple lines. At first glance it
...
# part of that value. Cryptically, this would lead to Ansible reporting that the service file
# couldn't be found.
#
# To avoid this issue, the following code only accepts multi-line values for keys whose names
# start with Exec (e.g., ExecStart=), since these are the only keys whose values are known to
# span multiple lines.
...
For monitoring a service or processes and sub-processes you may use an other approach or provide more details and information regarding your use case and what you try to achieve.
You may also have a look into pids module – Retrieves process IDs list if the process is running otherwise return empty list and
- name: Get current service PIDs
pids:
name: "{{ SERVICE_NAME }}"
register: result
- name: Show result
debug:
msg: "{{ result.pids }}"
which would result into an additional output of
TASK [Show result] ******
ok: [localhost] =>
msg:
- 124
and the sub-processes of the example service here.

stdout filter and conditional in ansible

Need assistance how to filter output and run additional tasks based on the previous task in ansible.
I am writing a task which check metrics configuration is enabled/disabled. If disabled need to run enable task.
- name: Verify insights configuration
shell: dsetool -h `hostname` -a username -b {{ password }} insights_config --
show_config
register: insight_output
- name: Print status
debug:
msg: "{{ insight_output.stdout }}"
- name: Filter Mode
shell: dsetool -h `hostname` -a username -b {{ password }} insights_config --
show_config | python -m json.tool | jq '.mode' | tr -d '"'
register: mode_status
Output of print status will be like below
msg:
config_refresh_interval_in_seconds: 30
data_dir_max_size_in_mb: 1024
metric_sampling_interval_in_seconds: 30
mode: ENABLED_WITH_LOCAL_STORAGE
node_system_info_report_period: PT1H
TASK [cassandra : Print mode]
msg: ENABLED_WITH_LOCAL_STORAGE
How can I accomplish running additional tasks if output shows DISABLED.
How can I accomplish running additional tasks if output shows DISABLED.
You may have a look into the following minimal example playbook with Conditionals.
---
- hosts: localhost
become: false
gather_facts: false
vars:
mode_status:
mode: ENABLED_WITH_LOCAL_STORAGE
tasks:
- name: Show status
debug:
msg: "DISABLED"
when: "'DISABLED' in mode_status.mode"
Further Q&A
... which might be interesting for future use
Ansible: Test what a variable begins with
Ansible Conditionals - Wildcard match string
How do you search for substring?

How can I put the discovered values into loop variables so that they are on one line

How can I put the discovered values into loop variables so that they are on one line using Ansible task? I have now task like this
- name: Updating test.conf
lineinfile:
path: "/root/test.conf"
regexp: "test="
line: "test={{ hostvars[item]['ansible_env'].SSH_CONNECTION.split(' ')[2] }}"
state: present
with_nested:
- "{{groups['app']}}"
It needs that when invoking the job, it takes the IP addresses from the servers that are in the app group and puts them on a single line. Currently, it performs such a substitution twice with which they are replaced and finally there is only one address in the test parameter.
I need format after task like this:
test=1.1.1.1, 2.2.2.2
While jinja2 is heavily inspired from python, its does not allow you to do all the same operations. To join a list your would have to do something like:
- debug:
msg: "{{ myvar | join(',') }}"
vars:
myvar:
- foo
- bar
When in doubt, always use a simple playwook with a debug task to validate your jinja code.

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.

ansible output printing unwanted things. how to format and display only specific data's

I am using ansible 2.4 in centos, trying to run the below script in remote servers and getting the output. Here the problem is yum info output is showing with json format also. But i need to display only the output. How to remove the json format.
---
- hosts: GeneralServer
tasks:
- name: Checking the service status
shell: systemctl status {{ item }}
with_items:
- httpd
- crond
- postfix
- sshd
register: service
- debug: var=service
- name: Checking the package info
shell : yum info {{ item }}
with_items:
- httpd
- postfix
register: info
- debug: var=info
- name: Executing the mysql running scripts in mysql
shell: mysql -u username --password mysql -Ns -e 'show databases;'
register: databases
- debug: var=databases
Also i am new in callback Module. Please help me to resolve this issue.
Is it possibile to display only stdout_lines values only.
You can try to play with different callback plugins to alter your output, e.g.:
$ ANSIBLE_STDOUT_CALLBACK=oneline ansible-playbook myplaybook.yml
$ ANSIBLE_STDOUT_CALLBACK=minimal ansible-playbook myplaybook.yml
But generally you would not avoid JSON, as it's how Ansible interprets data.
To reduce amount of info, you can use different technics. For example json_query filter.
Something like this:
- debug:
msg: "{{ info.results | json_query('[].stdout_lines[]') }}"

Resources