how to extract string from ansible regsiter variable in ansible - ansible

I have written the following ansible playbook to find the disk failure on the raid
- name: checking raid status
shell: "cat /proc/mdstat | grep nvme"
register: "array_check"
- debug:
msg: "{{ array_check.stdout_lines }}"
Following is the output I got
"msg": [
"md0 : active raid1 nvme0n1p1[0] nvme1n1p1[1]",
"md2 : active raid1 nvme1n1p3[1](F) nvme0n1p3[0]",
"md1 : active raid1 nvme1n1p2[1] nvme0n1p2[0]"
]
I want to extract the disk name which is failed from the register variable array_check.
How do I do this in the ansible? Can I use set_fact module in ansible? Can I use grep, awk, sed command on the register variable array_check
This is the playbook I am using to check the health status of a drive using smartctl
- name: checking the smartctl logs
shell: "smartctl -H /dev/{{ item }}"
with_items:
- nvme0
- nvme1
And I am facing the following error
(item=nvme0) => {"changed": true, "cmd": "smartctl -H /dev/nvme0", "delta": "0:00:00.090760", "end": "2019-09-05 11:21:17.035173", "failed": true, "item": "nvme0", "rc": 127, "start": "2019-09-05 11:21:16.944413", "stderr": "/bin/sh: 1: smartctl: not found", "stdout": "", "stdout_lines": [], "warnings": []}
(item=nvme1) => {"changed": true, "cmd": "smartctl -H /dev/nvme1", "delta": "0:00:00.086596", "end": "2019-09-05 11:21:17.654036", "failed": true, "item": "nvme1", "rc": 127, "start": "2019-09-05 11:21:17.567440", "stderr": "/bin/sh: 1: smartctl: not found", "stdout": "", "stdout_lines": [], "warnings": []}
The desired output should be something like this,
=== START OF SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED

Below is the complete playbook including the logic to execute multiple commands in a single task using with_items,
---
- hosts: raid_host
remote_user: ansible
become: yes
become_method: sudo
tasks:
- name: checking raid status
shell: "cat /proc/mdstat | grep 'F' | cut -d' ' -f6 | cut -d'[' -f1"
register: "array_check"
- debug:
msg: "{{ array_check.stdout_lines }}"
- name: checking the samrtctl logs for the drive
shell: "/usr/sbin/smartctl -H /dev/{{ item }} | tail -2|awk -F' ' '{print $6}'"
with_items:
- "nvme0"
- "nvme1"
register: "smartctl_status"

Related

How to repeat the same command in creating the openstack backup?

I took volumes 'in-use' of OpenStack instance and filtered those volume ids into a file from which it has to make a backup
shell: openstack volume list | grep 'in-use' | awk '{print $2}' > /home/volumeid
shell: openstack volume backup create {{ item }}
with_items:
- /home/volumeid
error shows like
**failed: [controller2] (item=volumeid) => {"ansible_loop_var": "item", "changed": true, "cmd": "openstack volume backup create volumeid", "delta": "0:00:03.682611", "end": "2022-09-26 12:01:59.961613", "item": "volumeid", "msg": "non-zero return code", "rc": 1, "start": "2022-09-26 12:01:56.279002", "stderr": "No volume with a name or ID of 'volumeid' exists.", "stderr_lines": ["No volume with a name or ID of 'volumeid' exists."], "stdout": "", "stdout_lines": []}
failed: [controller1] (item=volumeid) => {"ansible_loop_var": "item", "changed": true, "cmd": "openstack volume backup create volumeid", "delta": "0:00:04.020051", "end": "2022-09-26 12:02:00.280130", "item": "volumeid", "msg": "non-zero return code", "rc": 1, "start": "2022-09-26 12:01:56.260079", "stderr": "No volume with a name or ID of 'volumeid' exists.", "stderr_lines": ["No volume with a name or ID of 'volumeid' exists."], "stdout": "", "stdout_lines": []}**
Can someone say how to create the volume backup from that file (which has volume ids) in the ansible playbook?
Currently, you are supplying only one element to the with_items, that is, /home/volumeid, meaning your loop will iterate only once for the file name and not its contents.
You need to use the file lookup if you are on localhost or the slurp module on the remote host. Example:
For the localhost:
- name: Show the volume id from the file
debug:
msg: "{{ item }}"
loop: "{{ lookup('file', '/home/volumeid').splitlines() }}"
For the remote host:
- name: Alternate if the file is on remote host
ansible.builtin.slurp:
src: /home/volumeid
register: vol_data
- name: Show the volume id from the file
debug:
msg: "{{ item }}"
loop: "{{ (vol_data['content'] | b64decode).splitlines() }}"
Just one line shell command:
openstack volume list --status in-use -c ID -f value | xargs -n1 openstack volume backup create
One advice, don't use the hardcode command like this grep 'in-use' or awk '{print $2}', openstack has it's optional arguments and output formatters, check it by openstack command [sub command] -h.

Cant pass content of txt file to ansible shell command

I'm trying to pass the content of a text file I have in the local folder to an Ansible shell command and for whatever reason, I can't and I'm sure its something silly that I'm missing.
Any help would be really appreciated
Here is the ansible line:
- name: Connect K8s cluster to Shipa
shell: shipa cluster-add {{ cluster_name }} --addr $(< {{ cluster_name }}.txt) --cacert {{ cluster_name }}.crt --pool {{ cluster_name }} --ingress-service-type=LoadBalancer
args:
executable: /bin/bash
no_log: False
Whenever I run this, this is what I get in the log:
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "shipa cluster-add scale2 --addr $(< scale2.txt) --cacert scale2.crt --pool scale2 --ingress-service-type=LoadBalancer", "delta": "0:00:00.096162", "end": "2020-04-30 21:32:54.245781", "msg": "non-zero return code", "rc": 1, "start": "2020-04-30 21:32:54.149619", "stderr": "Error: 500 Internal Server Error: parse \"https://\\x1b[0;33mhttps://192.196.192.156\\x1b[0m\": net/url: invalid control character in URL", "stderr_lines": ["Error: 500 Internal Server Error: parse \"https://\\x1b[0;33mhttps://192.196.192.156\\x1b[0m\": net/url: invalid control character in URL"], "stdout": "", "stdout_lines": []}
Here is the content of the file:
# cat scale2.txt
https://192.196.192.156
Here is what Im trying to pass in the command:
shipa cluster-add scale2 --addr https://192.196.192.156 --cacert scale2.crt --pool scale2 --ingress-service-type=LoadBalancer
EDIT:
I also tried the following:
- name: Get K8s cluster address
shell: kubectl cluster-info | grep 'Kubernetes master' | awk '/http/ {print $NF}'
args:
executable: /bin/bash
register: cluster_address
no_log: False
- debug: var=cluster_address
- name: Save to file
copy:
content: "{{ ''.join(cluster_address.stdout_lines) | replace('\\u001b[0;33m', '')| replace('\\u001b[0m', '') }}"
dest: "cluster_address.json"
- name: Connect K8s cluster to Shipa
shell: shipa cluster-add {{ cluster_name }} --addr {{ lookup('file', 'cluster_address.json') }} --cacert {{ cluster_name }}.crt --pool {{ cluster_name }} --ingress-service-type=LoadBalancer
args:
executable: /bin/bash
no_log: False
But it saves the following content to the file:
# vi cluster_address.json
^[[0;33mhttps://192.192.56.192^[[0m
Here is the output of the ansible execution:
TASK [debug] **********************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"cluster_address": {
"changed": true,
"cmd": "kubectl cluster-info | grep 'Kubernetes master' | awk '/http/ {print $NF}'",
"delta": "0:00:00.144193",
"end": "2020-04-30 22:54:21.970460",
"failed": false,
"rc": 0,
"start": "2020-04-30 22:54:21.826267",
"stderr": "",
"stderr_lines": [],
"stdout": "\u001b[0;33mhttps://192.192.56.192\u001b[0m",
"stdout_lines": [
"\u001b[0;33mhttps://192.192.56.192\u001b[0m"
]
}
}
TASK [Save to file] ***************************************************************************************************************************************************************************************************************************
changed: [localhost]
TASK [Connect K8s cluster to Shipa] ***********************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "shipa cluster-add scale1 --addr \u001b[0;33mhttps://192.192.56.192\u001b[0m --cacert scale1.crt --pool scale1 --ingress-service-type=LoadBalancer", "delta": "0:00:00.106936", "end": "2020-04-30 22:54:23.213485", "msg": "non-zero return code", "rc": 127, "start": "2020-04-30 22:54:23.106549", "stderr": "Error: 400 Bad Request: either default or a list of pools must be set\n/bin/bash: 33mhttps://192.192.56.192\u001b[0m: No such file or directory", "stderr_lines": ["Error: 400 Bad Request: either default or a list of pools must be set", "/bin/bash: 33mhttps://192.192.56.192\u001b[0m: No such file or directory"], "stdout": "", "stdout_lines": []}
You can disable the fancy terminal output by setting the TERM to dumb
- shell: kubectl cluster-info | awk '/Kubernetes master/ {print $NF}'
environment:
TERM: dumb
register: cluster_info
- set_fact:
cluster_addr: '{{ cluster_info.stdout | trim }}'
then, separately, you don't need to use the file lookup for a value that you have in a jinja2 variable already:
- name: Connect K8s cluster to Shipa
command: shipa cluster-add {{ cluster_name }} --addr {{ cluster_addr }} --cacert {{ cluster_name }}.crt --pool {{ cluster_name }} --ingress-service-type=LoadBalancer
args:
executable: /bin/bash

How to set shell in module user via registered variable?

To disable logins for root I would like to set its shell to the path of nologin, which is determined by a command.
The command module registers the variable properly:
- name: Get nologin path
command: which nologin
register: nologin
- debug:
var: nologin
Debug info:
ok: [192.168.178.25] => {
"nologin": {
"changed": true,
"cmd": [
"which",
"nologin"
],
"delta": "0:00:00.001612",
"end": "2019-08-26 11:23:41.764847",
"failed": false,
"rc": 0,
"start": "2019-08-26 11:23:41.763235",
"stderr": "",
"stderr_lines": [],
"stdout": "/usr/sbin/nologin",
"stdout_lines": [
"/usr/sbin/nologin"
]
}
}
But when I use the user module it takes the registered variable as a string:
- name: Disable root
user:
name: root
shell: nologin.stdout
state: present
Result in /etc/passwd:
$ cat /etc/passwd
root:x:0:0:root:/root:nologin.stdout
Thanks for any help!
It's a variable, to use it you need to put in jinja2 template {{ }} and inside " " as it is required by YAML:
shell: "{{ nologin.stdout }}"
Ref:
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#using-variables-with-jinja2
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#hey-wait-a-yaml-gotcha

How to manipulate this result in Ansible

I have a problem for manipulating data in Ansible.
My main goal is to purge ADRCI log.
In a first time, i'm listing the databases that are launched with users Oracle and Grid with the shell module
- name: Generate List of databases started with user oracle
shell: "ps -ef | grep smon | grep -v grep | grep oracle | awk '{print $NF}' | awk -F '_' '{print $NF}'"
register: list_oracle_bases
Then i want to get the homes to purge with an other shell command :
- name: Get databases homes
register: get_homes
environment:
ORACLE_SID: "{{ item }}"
ORAENV_ASK: "NO"
become: true
become_user: oracle
become_method: su
shell: ". oraenv 1>/dev/null 2>&- && adrci exec='show homes' | grep -v 'ADR Homes:'"
with_items:
- "{{ list_oracle_bases.stdout_lines }}"
And here comes my problem i've got this result for {{ get_homes }}
ok: [host1] => {
"msg": {
"changed": true,
"msg": "All items completed",
"results": [
{
"_ansible_ignore_errors": null,
"_ansible_item_label": "ANSIBLE1",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": true,
"cmd": ". oraenv 1>/dev/null 2>&- && adrci exec='show homes' | grep -v 'ADR Homes:'",
"delta": "0:00:00.028970",
"end": "2019-03-18 11:29:29.708709",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": ". oraenv 1>/dev/null 2>&- && adrci exec='show homes' | grep -v 'ADR Homes:'",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "ANSIBLE1",
"rc": 0,
"start": "2019-03-18 11:29:29.679739",
"stderr": "",
"stderr_lines": [],
"stdout": "diag/rdbms/ansible1/ANSIBLE1",
"stdout_lines": [
"diag/rdbms/ansible1/ANSIBLE1"
]
}
]
}
}
I tried to get get_homes.results.stdout_lines but i've got the following error when i want to display it in a debug statement.
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'stdout_lines'\n\nThe error appears to have been in '/tmp/sylvain/roles/test/tasks/query.yml': line 59, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
What is the correct way to get that stdout_lines ("diag/rdbms/ansible1/ANSIBLE1") in a variable.
Regards
Sylvain
results is an array, because you have created it in a loop (with_items). The correct syntax is:
get_homes.results[0].stdout_lines
for the first and so on for the rest.
In order to get the list of home directories you have to filter the results array by the attribute you need. And in the end you have to flatten the array of arrays. Here is a mcve:
---
- name: filter
hosts: localhost
connection: local
tasks:
- shell: |-
echo ~{{ item }}
with_items:
- man
- lp
- mail
register: get_homes
ignore_errors: true
changed_when: false
- debug: var=get_homes
- set_fact:
homes: >-
{{ get_homes.results | map(attribute='stdout_lines') | list | flatten }}
- debug: var=homes

Difference filter not working in Ansible

I am trying to find which ports are available for my use. The logic goes like this, I am firs finding the used ports and providing a list of ports that I can use, the difference filter should filter out the ones that are available, but somehow it is not working.
Here is the code block:
- name: Gather occupied tcp v4 ports
shell: netstat -nlt| awk '{print $4}'|awk -F':' '{print $2}'
register: used_ports
- debug:
var: used_ports
- name: Difference
vars:
allowed_ports:
- 107
- 823
- 4750
set_fact:
bind_port: "{{ allowed_ports | difference(used_ports) | first }}"
- name: Show bind port
debug:
var: bind_port
Output:
ok: [] => {
"used_ports": {
"changed": true,
"cmd": "netstat -nlt| awk '{print $4}'|awk -F':' '{print $2}'",
"delta": "0:00:00.077467",
"end": "2018-08-12 15:25:04.477710",
"failed": false,
"rc": 0,
"start": "2018-08-12 15:25:04.400243",
"stderr": "",
"stderr_lines": [],
"stdout": ",
"stdout_lines": [
"",
"",
"107",
"202",
"106"
]
} }
TASK [serverbuild : Difference]
********************************************************************* ok: []
TASK [serverbuild : Show bind port]
***************************************************************** ok: [] => {
"bind_port": "107" }
Ideally it should not show 107 as it is already used. What am I doing wrong here?
There are two problems:
You should use used_ports.stdout_lines as an argument to the difference filter,
You should either define allowed_ports to contain strings, or map used_ports.stdout_lines to integers.
So:
- name: Difference
vars:
allowed_ports:
- "107"
- "823"
- "4750"
set_fact:
bind_port: "{{ allowed_ports | difference(used_ports.stdout_lines) | first }}"
or:
- name: Difference
vars:
allowed_ports:
- 107
- 823
- 4750
set_fact:
bind_port: "{{ allowed_ports | difference(used_ports.stdout_lines|map('int')) | first }}"

Resources