Ansible until loop with regex in conditional statement - regex

I need to check command stdout with a regex pattern in until loop. When I using regex_search to display debug msg all works fine:
- name: Checking if node is ready
shell: "kubectl --kubeconfig {{kubeconf}} get nodes"
register: k_output
- debug:
msg: "{{k_output.stdout | regex_search(node_hostname | string + '\\s+Ready')}}"
The message is
ok: [any_node_hostname] => {
"msg": "any_node_hostname Ready"
}
But if I trying to use that construction inside until conditional statement task fails with syntax error.
- set_fact:
regexp_pattern: "{{node_hostname | string + '\\s+Ready'}}"
- debug:
msg: "{{regexp_pattern}}"
- name: Checking if node is ready
shell: "kubectl --kubeconfig {{kubeconf}} get nodes"
register: k_output
until: "{{k_output.stdout | regex_search(regexp_pattern)}}" != ""
retries: 5
delay: 10
The same behaviour without set_fact when I just copying and pasting full string {{k_output.stdout | regex_search(node_hostname | string + '\\s+Ready')}} to until conditional statement.
So, how can I use regex_search or something that fits this case with until?

you have syntax error at until: statement : you must not quote the vars in expression, like in example here : Retrying a task until a condition is met
until: k_output.stdout | regex_search(regexp_pattern) != ""
I hope this will help

Related

Ansible - How to do integer comparison in a conditional?

I am trying to do a simple integer comparison in a conditional but something is up, and it is not evaluating properly.
Code within playbook:
- name: Check the version of the current sql database on both dispatchers and save that value.
command: /usr/bin/mysql -V
changed_when: false
register: sqlversion
- name: Print to the screen the current sql database version.
debug:
var: "{{ sqlversion.stdout.split()[4] | regex_search('[0-9]+\\.[0-9]+') | replace(\".\",'') }}"
register: w
- name: Show the value of the variable.
debug:
var: w
- name: Test result
command: w
when: ( w | int < 55 )
The output of the command module (ultimately to get the 5.5 part of the 5.5.43 number:
mysql Ver 14.14 Distrib 5.5.43, for Linux (x86_64) using readline 5.2
My actual run in which the Test result task "fails" as in it runs when it should not, because of the evaluation problem:
TASK [Check the version of the current sql database on both dispatchers and save that value.] ***********
ok: [server2]
ok: [server1]
TASK [Print to the screen the current sql database version.] ********************************************
ok: [server1] => {
"55": "55"
}
ok: [server2] => {
"55": "55"
}
TASK [Show the value of the variable.] ******************************************************************
ok: [server1] => {
"w": {
"55": "55",
"changed": false,
"failed": false
}
}
ok: [server2] => {
"w": {
"55": "55",
"changed": false,
"failed": false
}
}
TASK [Test result] **************************************************************************************
changed: [server1]
changed: [server2]
This is not right or expected, clearly the last task shows "changed" in that it ran, and evaluated the condition as true when it shouldn't be. Instead with how I coded the conditional, it should be skipped instead! Additionally, if I take away the " | int" then it always skips no matter what the number(s) are.
What's the issue here? There's got to be a way to make this work.
Simplify the parsing
w: "{{ sqlversion.stdout|split(',')|first|split()|last }}"
gives
w: 5.5.43
Use the test version. See Comparing versions. For example,
- debug:
msg: Version is lower than 5.6
when: w is version('5.6', '<')
Example of a complete playbook for testing
- hosts: localhost
vars:
sqlversion:
stdout: "mysql Ver 14.14 Distrib 5.5.43, for Linux (x86_64) using readline 5.2"
w: "{{ sqlversion.stdout|split(',')|first|split()|last }}"
tasks:
- debug:
var: w
- debug:
msg: Version is lower than 5.6
when: w is version('5.6', '<')
If the filter split is not available use the method twice
w: "{{ (sqlversion.stdout.split(',')|first).split()|last }}"
... but I also am forced on this series of machines to use Ansible 2.9 ...
As mentioned by Vladimir Botka
If the filter split is not available use the method twice. I added an example. The filter split is available since 2.11
the split filter for manipulation strings is available as of Ansible v2.11 but you can just use the Python method .split().
As a workround in Ansible v2.9 or 2.10 one could also implement a Custom Filter Plugin and so a simple Ansible Jinja2 filter to split a string into a list. By doing this a filter like | split() can made be available in older versions.

How can I use regex in when condition

On an Ansible playbook, I'm trying to execute a shell command only if a service exist on the remote server.
I have 3 tasks :
service_facts
execution of shell command if tomcat is installed
display the output of the shell command if tomcat is installed
Here is my code :
- name: Get Infos
hosts: all
gather_facts: yes
become: false
remote_user: [MY_USER]
tasks:
- name: Get the list of services
service_facts:
- name: Get version of Tomcat if installed
become: true
shell: 'java -cp /opt/tomcat/lib/catalina.jar org.apache.catalina.util.ServerInfo | grep "Server version"'
register: tomcat_version
when: "'tomcat.service' in services"
- debug: msg="{{ tomcat_version.stdout_lines }}"
when: "'tomcat.service' in services"
The problem is on certains servers the service name is, for example, tomcat-8.1
How can i use regex in the when condition?
I tried regex(), regex_search(), either I'm doing it wrong or I don't know how to do it.
Have you any idea how to do it?
Thanks in advance!
Count matching items. For example
- service_facts:
- block:
- shell: smartctl --version | head -1
register: smart_version
- debug:
msg: "{{ smart_version.stdout_lines }}"
when: _srvcs|length > 0
vars:
_regex: '.*smart.*'
_srvcs: "{{ services|select('match', _regex) }}"
gives
msg:
- smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-73-generic] (local build)
The next option is to intersect the list of services, e.g.
when: _srvcs|length > 0
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
Debug
Q: "It gives me a failure on the server where my service doesn't exist, cause the playbook still tries to execute the shell. Is it normal?"
A: No. It is not normal. Print debug and find out why the condition evaluates to true, i.e. what service(s) match either the regex or the list. For example
- debug:
msg: |
_srvcs:
{{ _srvcs|to_nice_yaml|indent(2) }}
when: debug|d(false)|bool
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
gives
msg: |-
_srvcs:
- smartmontools.service
To enable the task run the playbook with the option -e debug=true.

How $ works in Ansible list

I'm new in ansible so I've been learning about how it works. And while I was doing so, I'm stumped in the RegEx because it doesn't work for some reason. Here's the code i'm working on.
---
- hosts: localhost
#get the targeted file
vars:
contents: "{{lookup ('file', '/home/sample/test.txt')}}"
tasks:
- name: Replacing strings
include_role:
name: ansible-replace-string
And then this the code for the role
- name: Removing the suffix .ear and ear
debug:
msg: "{{contents | map('regex_replace', '\\.ear', '') | list}}"
msg: "{{contents | map('regex_replace', 'ear$', '') | list}}"
I've read that $ is used if you want to capture particular string at the end. So I'm trying to capture .ear and ear. While .ear works fine, if I try and put the ear$ it breaks my code. If I remove the first msg, ear$ only works if there's no dot before it. For example:
byedear -> byed
web.statements.dear -> web.statements.dear
I'm not sure as to why it doesn't work for the second example. If needed further content here's a snippet of the test.txt i'm trying to play around with.
web.appstatementear
web.statements.ear
web.payments.ear
TmStatearments
hellodear
byedear
If anyone has ideas I'd be happy to hear them!

ansible playbook : Error to connect to a windows host (winrm) using aws secret manager

i try to connect my ansible host to a windows server using winrm
my ansible version :
ansible 2.10.8
config file = None
configured module search path = ['/home/ec2-user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/ec2-user/.local/lib/python3.6/site-packages/ansible
executable location = /home/ec2-user/.local/bin/ansible
python version = 3.6.8 (default, Dec 5 2019, 15:45:45) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
it work when the password is in vars
- hosts: win
vars:
ansible_user: "ansible"
ansible_password: "Itismypassword"
and it is not working with this configuration :
- hosts: win
vars:
ansible_user: "ansible"
ansible_password: "{{ lookup('amazon.aws.aws_secret', 'ansible_password', bypath=true) | regex_search ('ansible_password\\\":\\\"(.*)\\\"','\\1')}}"
i retrieve the password ( i used a regex to get only the password, i'm not sure that is the correct way to do that)
when i want to use it i get this error:
fatal: [win_server]: FAILED! => {
"msg": "Invalid type for configuration option plugin_type: connection plugin: winrm setting: remote_password : Invalid type provided for \"string\": ['Itismypassword']"
}
thanks for your help !
The error message is pretty clear: list[str] != str; while I don't have an AWS Secret Manager to test against, thankfully the error message makes knowing the fix pretty obvious: grab the | first of the resulting list
ansible_password: "{{
lookup('amazon.aws.aws_secret', 'ansible_password', bypath=true)
| regex_search ('ansible_password\\\":\\\"(.*)\\\"','\\1')
| first }}"
As for your aside, the answer is for sure not to use regex to extract those values; based on the regex you're using, I'm guessing the output of that aws_secret lookup is in fact JSON, and thus the correct behavior is to | from_json to turn it back into a dict (or list[dict]), then extract the key you want
something like this, but without knowing the actual shape this is likely not 100% correct:
ansible_password: >-
{{ lookup('amazon.aws.aws_secret', 'ansible_password', bypath=true)
| from_json | map(attribute='ansible_password') | first }}
thanks to mdaniel i progress in my understanding of ansible and aws secret manager: it's working with regex but i try to figure out how to do it in a proper way...
when i get secret from aws secret manager with this commands :
debug: msg="{{ lookup('amazon.aws.aws_secret', 'ansible_secret', bypath=true) | dict2items }} "
i have this answer :
"msg": [
{
"key": "ansible_secret",
"value": "{\"password\":\"itismypassword\",\"user\":\"ansible_user\"}"
}
]
i don't know how to get "itismypassword" from value field >< a jquery maybe ?

Ansible regex_replace filter does not work

I try to use Ansible regex_replace to filter the sub-string "application_1514971620021_4505" from a status message.
In the shell the message looks like this:
I run this code in Ansible:
---
- hosts: [npif]
remote_user: root
tasks:
- block:
- name: Admin submit check
command: chdir=/usr/spring-xd-1.3.1.RELEASE-yarn/ bin/xd-yarn submitted
register: admininfo
- debug: msg="{{ admininfo.stdout }}"
- debug: msg="{{ admininfo.stdout | regex_replace('^.*(application\_\d.*\_\d*)\s.*', '\\1') }}"
become: yes
become_user: ingestdev
debug: msg="{{ admininfo.stdout }}" returns the status message in the different format than in the shell:
ok: [npif] => {
"msg": " APPLICATION ID USER NAME QUEUE TYPE STARTTIME FINISHTIME STATE FINALSTATUS ORIGINAL TRACKING URL\n ------------------------------ --------- --------- -------- ---- -------------- ---------- ------- ----------- ------------------------\n application_1514971620021_4505 ingestdev spring-xd batch_cb XD 1/3/18 2:49 PM N/A RUNNING UNDEFINED http://x.x.x.x:9394"
}
When I run the second debug with regex_replace, I get the identical output to the first debug output - no regex_replace filter has been applied. The regex filter is correct - I've tested it externally. Basically the Ansible code is working too - I have tested with line below and got "test" as expected.
- debug: msg="{{ 'test.home.com' | regex_replace('^([^.]*).*', '\\1') }}"
Do you have an idea, what is wrong with my approach?
Your first problem is that .* doesn't appear to match newlines. Consider this:
- debug:
msg: "{{ admininfo.stdout | regex_replace('.*application', 'foo') }}"
This will replace application with foo, but will leave the header lines intact. Since the ^ anchors your regular expression to the beginning of the text (not the beginning of a line) your expression will never match.
You can take advantage of the fact that ansible has already provided you with individual lines in the stdout_lines key of your registered output. In this case, you would use something like:
- debug:
msg: >
{{ admininfo.stdout_lines[2] | regex_replace('^.*(application_\d.*_\d*)\s.*', '\1') }}
Note here that I've made a few changes in how things are quoted and escaped. In particular, I'm using the folded literal operator > in place of double quotes, and you neede to use \1 instead of \\1 for your replacement string.
This gives me:
ok: [localhost] => {
"msg": "application_1514971620021_4505\n"
}