I have some output from a text file on a Windows system (using winrm) that I am parsing. I am trying to make the playbook fail if it does not see the word 'test' more than once.
This is what I have that works just for continuing the playbook if it sees the word 'test' at least one time:
- name: "Windows Output | Parse file"
win_shell: |
(Get-Content C:\TEST\output.txt)
register: 'testing_parse'
- name: "File output | verification"
assert:
that:
- "'test' in testing_parse.stdout"
fail_msg: "Exiting now."
success_msg: "Proceeding with task."
I thought that the following would work and it did not:
- name: "File output | verification"
assert:
that:
- "'test' in testing_parse.stdout >= '2'"
fail_msg: "Exiting now."
success_msg: "Proceeding with task."
How can I fix my assert
task so that it meets my requirement?
- name: "File output | verification"
vars:
watch_regex: '(?:^|\W)(test)(?:$|\W)'
assert:
that:
- testing_parse.stdout | regex_findall(watch_regex) | length > 1
fail_msg: "Exiting now."
success_msg: "Proceeding with task."
in
test is not appropriatein
returns a booleanLet's first see what we get back from the in
test in several situations
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
-e "my_test='a test'"
localhost | SUCCESS => {
"msg": true
}
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
-e "my_test='a test and an other test'"
localhost | SUCCESS => {
"msg": true
}
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
-e "my_test='no word we look for'"
localhost | SUCCESS => {
"msg": false
}
As you can see, it will always return a boolean depending on the presence or not of the needle in the haystack.
in
does not find wordsNote also that in
is not very good at finding words (since you mentioned that) as demonstrated below:
# It will match a substring in a middle of an other word
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
-e my_test="blahtesttoto"
localhost | SUCCESS => {
"msg": true
}
# It will not match a word inside an element of a list...
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
-e '{"my_test":["this is", "a test"]}'
localhost | SUCCESS => {
"msg": false
}
# ... but only an exact match of an element of a list
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
-e '{"my_test":["a", "test"]}'
localhost | SUCCESS => {
"msg": true
}
To end with, let's look at your try when you wrote the expression:
'test' in testing_parse.stdout >= '2'
This means:
testing_parse.stdout
is lexically superior or equal to the string '2'
.'test'
is found inside the preceding boolean result.As you can now guess with the explanation, there is absolutely no chance this will ever return true
.
regex_findall
to the rescueA way to look for a specific word is to use a regex. The following will look for the test
word i.e. the "test"
string preceded and followed by any non-word character (end of line, beginning of line, white space, tab, punctuation....).
(?:^|\W)(test)(?:$|\W)
If you are not familiar with regular expressions, see this answer for a specific explanation and https://www.regextutorial.org/ for a general resource (I'm not affiliated, you can find others using your favorite search engine).
The regex_find_all
filter can return all matches of a regex against a string into a list
$ ansible localhost -m debug \
-a "msg={{ my_test | regex_findall('(?:^|\\W)(test)(?:$|\\W)') }}" \
-e "my_test='test'"
localhost | SUCCESS => {
"msg": [
"test"
]
}
$ ansible localhost -m debug \
-a "msg={{ my_test | regex_findall('(?:^|\\W)(test)(?:$|\\W)') }}" \
-e "my_test='test and an other test but not blahtesttoto yet test'"
localhost | SUCCESS => {
"msg": [
"test",
"test",
"test"
]
}
$ ansible localhost -m debug \
-a "msg={{ my_test | regex_findall('(?:^|\\W)(test)(?:$|\\W)') }}" \
-e "my_test='notest'"
localhost | SUCCESS => {
"msg": []
}
Once we have that, we only need to count the number of elements in the returned list whith the length
filter.
$ ansible localhost -m debug \
-a "msg={{ my_test | regex_findall('(?:^|\\W)(test)(?:$|\\W)') | length }}" \
-e "my_test='test and an other test but not blahtesttoto yet test'"
localhost | SUCCESS => {
"msg": "3"
}
And finally we can fix your assert
task according to your requirement:
- name: "File output | verification"
vars:
watch_regex: '(?:^|\W)(test)(?:$|\W)'
assert:
that:
- testing_parse.stdout | regex_findall(watch_regex) | length > 1
fail_msg: "Exiting now."
success_msg: "Proceeding with task."