display message when regex does not match (task failed) - regex

I want to display a message if the task failed. This is that I currently have. I can't seem to store the stdout
- hosts: localhost
vars:
regexp: '(?i)^\s*PASS_MAX_DAYS\s+[0-9]+\s*$'
pass_max_days: "{{ out.stdout_lines|
select('match', regexp)|first|split|last }}"
tasks:
- command: cat /tmp/login.defs
register: out
- debug:
var: pass_max_days
- debug:
msg: Less than 90 days
when: pass_max_days|int < 90
- debug:
msg: Exactly 90 days
when: pass_max_days|int == 90
- debug:
msg: More than 90 days
when: pass_max_days|int > 90
- debug:
msg: Does not match!
when: pass_max_days.rc != 0

Use block & rescue.
The task under rescue will be executed, only if a task in the block fails.
- hosts: localhost
vars:
regexp: '(?i)^\s*PASS_MAX_DAYS\s+[0-9]+\s*$'
pass_max_days: "{{ out.stdout_lines|
select('match', regexp)|first|split|last }}"
tasks:
- block:
- name: cat the file
command: cat /etc/login.defs
register: out
- name: line search
debug:
var: pass_max_days
- name: line check
debug:
msg: "{% if pass_max_days|int < 90 %}Less than 90 days{% elif pass_max_days|int == 90 %}Exactly 90 days{% elif pass_max_days|int > 90 %}More than 90 days{% endif %}"
rescue:
- name: Failure message
debug:
msg: "VARIABLE IS NOT DEFINED!"
Success:
TASK [cat the file] *************************************
changed: [localhost]
TASK [line search] **************************************
ok: [localhost] => {
"pass_max_days": "99999"
}
TASK [line check] ***************************************
ok: [localhost] => {
"msg": "More than 90 days"
}
Failure:
TASK [cat the file] *************************************
TASK [line search] **************************************
ok: [localhost] => {
"pass_max_days": "VARIABLE IS NOT DEFINED!"
}
TASK [line check] ***************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: {{ out.stdout_lines| select('match', regexp)|first|split|last }}: Unable to look up a name or access an attribute in template string ({{ out.stdout_lines| select('match', regexp)|first|split|last }}).\nMake sure your variable name does not contain invalid characters like '-': descriptor 'split' for 'str' objects doesn't apply to a 'AnsibleUndefined' object\n\nThe error appears to be in '/root/ansible-test/max.yml': line 16, column 8, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: line check\n ^ here\n"}
TASK [Failure message] *********************************************************
ok: [localhost] => {
"msg": "VARIABLE IS NOT DEFINED!"
}
If you want the tasks in block not to be executed at failure, use check_mode in lineinfile module to check if the line exists.
Register an output for the task, which will have the attribute changed as true, if the line is found, but it will not make any changes to the file.
- hosts: localhost
gather_facts: no
vars:
regexp: '(?i)^\s*PASS_MAX_DAYS\s+[0-9]+\s*$'
pass_max_days: "{{ out.stdout_lines | select('match', regexp)|first|split|last }}"
tasks:
- name: Check if line exists
lineinfile:
name: /etc/login.defs
regexp: "{{ regexp }}"
line: ""
state: present
check_mode: yes
register: check
- name: Failure message
debug:
msg: "VARIABLE IS NOT DEFINED!"
when: check is not changed
- block:
- name: cat the file
command: cat /etc/login.defs
register: out
- name: line check
debug:
msg: "{% if pass_max_days|int < 90 %}Less than 90 days{% elif pass_max_days|int == 90 %}Exactly 90 days{% elif pass_max_days|int > 90 %}More than 90 days{% endif %}"
when: check is changed

Related

Access yaml list of dictionaries file with ansible

So I am trying take values from file, let's call it "test.yaml"
file looks like this (sorry for long output, but it is the shortest cut containing all patterns and structure):
---
results:
- failed: false
item: XXX.XX.XX.XX
invocation:
module_args:
validate_certs: false
vm_type: vm
show_tag: false
username: DOMAIN\domain-user
proxy_host:
proxy_port:
show_attribute: false
password: VALUE_SPECIFIED_IN_NO_LOG_PARAMETER
port: XXX
folder:
hostname: XXX.XX.XX.XX
changed: false
virtual_machines:
- ip_address: XXX.XX.XX.XX
mac_address:
- XX:XX:XX:aa:XX:XX
uuid: XXXX-XX-XX-XXXX-XXXXX
guest_fullname: Red Hat Enterprise Linux X (XX-bit)
moid: vm-XXX
folder: "/DOMAIN-INTERXION/vm"
cluster:
attributes: {}
power_state: poweredOn
esxi_hostname: esx.hostname
tags: []
guest_name: VMnameXX
vm_network:
XX:XX:XX:aa:XX:XX:
ipv6:
- XX::XXX:XX:XXXX
ipv4:
- XXX.XX.XX.XX
I would like, for example to have something like:
results.invocation.virtual_machines.ip_address
results.invocation.module_args.user_name
I tried all kind of stuff but it doesn't work :)
last attempt is this:
---
- name: demo how register works
hosts: localhost
tasks:
- name: Include all .json and .jsn files in vars/all and all nested directories (2.3)
include_vars:
file: test.yml
name: vm
- name: debug
debug:
msg: "{{ item.0.item }}"
with_subelements:
- "{{ vm.results }}"
- virtual_machines
register: subelement
following your structure and after fixing some errors:
results.invocation.virtual_machines.ip_address is results[0].virtual_machines[0].ip_address
and
results.invocation.module_args.user_name is results[0].invocation.module_args.username
(results and virtual_machines are arrays, write results[0] or results.0 is same)
so a sample of playbook doing job:
- name: vartest
hosts: localhost
tasks:
- name: Include all .json and .jsn files in vars/all and all nested directories (2.3)
include_vars:
file: test.yml
name: vm
- name: ip
set_fact:
ipadress: "{{ vm.results[0].virtual_machines[0].ip_address }}"
- name: username
set_fact:
username: "{{ vm.results[0].invocation.module_args.username }}"
- name: display
debug:
msg: "ip: {{ ipadress }} and username: {{ username }}"
result:
ok: [localhost] =>
msg: 'ip: XXX.XX.XX.XX and username: DOMAIN\domain-user'

Parsing a list of strings in ansible

I have this list:
ok: [localhost] => {
"ansible_facts": {
"servs": [
"Is active: Yes\nHost Name: northrend\n Ip Address: 192.168.1.12",
"Is active: Yes\nHost Name: pandaria \n IP Address: 192.168.1.30"
]
},
I've been trying to parse this list to extract the value of "Host name" but I just keep on getting the following error:
fatal: [localhost]: FAILED! => {
"msg": "Unexpected templating type error occurred on ({{ servs | regex_search('Host Name: (.)+\n'),multiline=True) }}): expected string or buffer"
}
This is what I tried:
- set_fact:
output: {{ servs | regex_search('Host Name: (.)+\n'), multiline=True) }}
How do you properly parse the contents of this list?
The list servs is nearly valid YAML. Only the indentation of the attribute 'Ip Address' is wrong. One space shifted to the right. If you manage to get rid of this space, e.g.
servs:
- "Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12"
- "Is active: Yes\nHost Name: pandaria \nIP Address: 192.168.1.30"
the situation would be trivial. Simply convert the list of strings to a list of dictionaries, e.g.
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|from_yaml] }}"
loop: "{{ servs }}"
gives
servs_list:
- Host Name: northrend
Ip Address: 192.168.1.12
Is active: true
- Host Name: pandaria
IP Address: 192.168.1.30
Is active: true
If you can't fix the indentation parse the data on your own. The task below gives the same result
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|
regex_replace(regex, replace)|
from_yaml] }}"
loop: "{{ servs }}"
vars:
regex: '\n '
replace: '\n'
Get the output
- set_fact:
output: "{{ servs_list|map(attribute='Host Name')|list }}"
gives
output:
- northrend
- Pandaria
You have multiple errors here:
regex_search works on strings, not lists. So you need to apply it to each list element.
Your regex creates one group for each character in your hostname. The group should be (.+).
You are returning the whole match (including Host Name:) and not just the name itself. You need to specify which matching group to return.
I guess the list is from the output of some script or in some file. It would be a lot better to have it as json or yaml and then use from_json or from_yaml.
If you want to keep it like that, you need to do this:
- hosts: all
vars:
servs:
- |
Is active: Yes
Host Name: northrend
Ip Address: 192.168.1.12
- |
Is active: Yes
Host Name: pandaria
IP Address: 192.168.1.30
tasks:
- debug:
msg: "{{ servs }}"
- set_fact:
output: "{{ servs | map('regex_search', 'Host Name: (.+)\n', '\\1') | list }}"
- debug:
msg: "{{ output }}"
This is the output:
TASK [debug] ************
ok: [local] => {
"msg": [
"Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12\n",
"Is active: Yes\nHost Name: pandaria\nIP Address: 192.168.1.30\n"
]
}
TASK [set_fact] ***************
ok: [local]
TASK [debug] ************
ok: [local] => {
"msg": [
[
"northrend"
],
[
"pandaria"
]
]
}

ansible identifying values in a list

The below code is attempting to find all the keys associated with the value HWIC-8A. I have tried a few different variations & I can't get the key to print, without doing something really long winded. As i'll be repeating this code with different values, i don't want to search each key/value pair individually within that list.
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
- name: Apply HWIC-8A Build
debug:
msg: "{{ item.key }}"
with_items: "{{ MODULES }}"
when: "{{ item.value }} == HWIC-8A"
Maybe that's something for you:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
tasks:
- debug: var=MODULES
- debug: msg="{{ MODULES | dict2items }}"
- debug: msg="{{ MODULES | dict2items | selectattr('value','match','HWIC-8A') | map(attribute='key')| list }}"
Then if you would like to have multiple matches, you could solve it with an MATCH list:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
Slot_1_SubSlot_3: HWIC-8C
Slot_1_SubSlot_2: HWIC-8C
MATCH:
- HWIC-8A
- HWIC-8C
tasks:
- debug:
msg: "{{ MODULES | dict2items | selectattr('value','match',item) | map(attribute='key')| list }}"
with_items: "{{ MATCH }}"
Output:
TASK [debug] ***********************************************************************************************************************************************************************************
Thursday 27 August 2020 15:08:10 +0200 (0:00:00.042) 0:00:02.037 *******
ok: [localhost] => (item=HWIC-8A) => {
"msg": [
"Slot_0_SubSlot_0",
"Slot_0_SubSlot_3"
]
}
ok: [localhost] => (item=HWIC-8C) => {
"msg": [
"Slot_1_SubSlot_3",
"Slot_1_SubSlot_2"
]
}
I would use use jinja templates to do it. Something like this:
- name: Apply HWIC-8A Build
debug:
msg: '{% for m in MODULES %}{% if MODULES[m] == "HWIC-8A" %}{{ m }} {% endif %}{% endfor %}'
Which will give you this:
ok: [localhost] => {
"msg": "Slot_0_SubSlot_0 Slot_0_SubSlot_3 "
}
There is probably a fancy way using filters as well.

Access element of list as dict

I'm trying to access the elements of a list, but whenever I try to access it's elements they are treated as text (see the debug output).
How can I loop over the elements of content[0]?
ansible version: 2.5.1
playbook.yml:
- hosts: localhost
gather_facts: no
vars:
content: []
tasks:
- name: Call the Consul API to get all registered services
uri:
url: "https://{{API_URL}}/v1/health/service/{{item}}?Status=critical"
headers:
Authorization: "Bearer {{TOKEN}}"
method: GET
return_content: yes
status_code: 200
body_format: json
force: yes
register: r
with_items:
- service1
- service2
- set_fact:
content: "{{ lookup('flattened', r | json_query('results[*].content')) }}"
- debug:
msg: "{{ content[0][0] | type_debug }}"
- debug:
msg: "{{ item | type_debug }}"
with_items: "{{ content[0] }}"
debug output:
TASK [debug]
ok: [127.0.0.1] => {
"msg": "dict"
}
TASK [debug]
ok: [127.0.0.1] => (item=None) => {
"msg": "AnsibleUnsafeText"
}
I'm hoping this question and answer will help you. I think the approach you are taking is the problem.

Ansible - Check if string exists in file with regex

I need to validate that the PermitRootLogin parameter is equal to "no", for example:
PermitRootLogin no
But sometimes between these words there is more than one space. For this reason I use a regex, but apparently I do it wrong. This is the line that seems to be bad:
when: check_config.stdout.find('PermitRootLogin\s+no') != -1
Any idea how to fix this?
- hosts: redhat
tasks:
- name: check file
shell: cat /etc/ssh/sshd_config
register: check_config
- name: compare string
when: check_config.stdout.find('PermitRootLogin\s+no') != -1
debug: msg="this server is ok"
Q: "Validate that the PermitRootLogin parameter is equal to no."
A: Put the below declaration into the vars
match_lines: "{{ check_config.stdout_lines|
map('regex_search', '^\\s*PermitRootLogin\\s+no$')|
select }}"
and test the length of the list
- debug:
msg: this server is OK
when: match_lines|length > 0
Example of a complete playbook for testing
- hosts: localhost
vars:
match_lines: "{{ check_config.stdout_lines|
map('regex_search', '^\\s*PermitRootLogin\\s+no$')|
select }}"
tasks:
- command: cat /etc/ssh/sshd_config
register: check_config
- debug:
var: match_lines
- debug:
msg: This server is OK
when: match_lines|length > 0
gives, for example (abridged)
TASK [debug] *******************************************
ok: [localhost] =>
match_lines:
- PermitRootLogin no
TASK [debug] *******************************************
ok: [localhost] =>
msg: This server is OK
Given the inventory below set hosts -hosts: rehat
shell> cat hosts
[redhat]
test_11
test_12
test_13
The playbook gives, for example (abridged)
TASK [debug] *******************************************
ok: [test_11] =>
match_lines:
- PermitRootLogin no
ok: [test_12] =>
match_lines: []
ok: [test_13] =>
match_lines: []
TASK [debug] *******************************************
skipping: [test_12]
ok: [test_11] =>
msg: This server is OK
skipping: [test_13]
You can use lookup to simplify the task if the play is running at the localhost only. For example, the playbook below gives the same result
- hosts: localhost
tasks:
- debug:
msg: This server is OK
when: match_lines|length > 0
vars:
match_lines: "{{ lookup('file', '/etc/ssh/sshd_config').splitlines()|
map('regex_search', '^\\s*PermitRootLogin\\s+no$')|
select }}"
If you want to put/replace the line in the config use lineinfile. For example,
- lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin(.*)$'
line: 'PermitRootLogin no'