Ansible search for a substring in stdout output - ansible

Team,
I am trying to match a version and that fails when whole string does not match. so I just want to match first two octects. I tried several combons but no luck.
- name: "Validate k8s version"
shell: "kubectl version --short"
register: k8s_version_live
failed_when: k8s_version_live.stdout_lines is not search("{{ k8s_server_version }}")
#failed_when: "'{{ k8s_server_version }}' not in k8s_version_live.stdout_lines"
ignore_errors: yes
- debug:
var: k8s_version_live.stdout_lines
output:
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: k8s_version_live.stdout_lines is not search("{{
k8s_server_version }}")
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "kubectl version --short", "delta": "0:00:00.418128", "end": "2019-12-05 02:13:15.108997", "failed_when_result": true, "rc": 0, "start": "2019-12-05 02:13:14.690869", "stderr": "", "stderr_lines": [], "stdout": "Client Version: v1.13.3\nServer Version: v1.13.10", "stdout_lines": ["Client Version: v1.13.3", "Server Version: v1.13.10"]}
...ignoring
TASK [team-services-pre-install-checks : debug] *************************************************************************************************************************
Thursday 05 December 2019 02:13:15 +0000 (0:00:00.902) 0:00:01.039 *****
ok: [localhost] => {
"k8s_version_live.stdout_lines": [
"Client Version: v1.13.3",
"Server Version: v1.13.10"
]
}```

As the error says:
conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}.
In a conditional statement, you are already inside a Jinja template context. You can just refer to variables by name:
- name: "Validate k8s version"
shell: "kubectl version --short"
register: k8s_version_live
failed_when: k8s_version_live.stdout_lines is not search(k8s_server_version)
ignore_errors: yes
Although you probably want k8s_version_live.stdout instead of k8s_version_live.stdout_lines.
I would probably write the task as:
- name: "Validate k8s version"
command: "kubectl version --short"
register: k8s_version_live
failed_when: k8s_server_version not in k8s_version_live.stdout
ignore_errors: true

Q: "Match a version ... match first two octets"
A: Use Version Comparison. For example, create the variable k8s_server_version from the registred output
- set_fact:
k8s_server_version: "{{ k8s_version_live.stdout_lines.1.split(':').1[2:] }}"
Compare the first two numbers of the version
- debug:
msg: "{{ k8s_server_version }} match 1.13"
when:
- k8s_server_version is version('1.13', '>=')
- k8s_server_version is version('1.14', '<')
gives
}
localhost | SUCCESS => {
"msg": "1.13.10 match 1.13"
}
Fail when the version does not match
- fail:
msg: "{{ k8s_server_version }} does not match 1.12"
when: not (k8s_server_version is version('1.12', '>=') and
k8s_server_version is version('1.13', '<'))
gives
localhost | FAILED! => {
"changed": false,
"msg": "1.13.10 does not match 1.12"
}

Related

Get exactly data from "stdout" or "stdout_lines" with exact words of output

I have a task
- name: DELEGATED ADMIN ACCOUNTS - check, get and send to the file domain.list
shell: /opt/zimbra/bin/zmprov -l gaaa -v zimbraIsDelegatedAdminAccount
and after this task I got output
changed: [Shrrah] => {
"changed": true,
"cmd": [
"sh",
"/home/information_domain.sh"
],
"delta": "0:00:02.495922",
"end": "2022-03-29 10:25:16.936051",
"invocation": {
"module_args": {
"_raw_params": "sh /home/information_domain.sh",
"_uses_shell": false,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": false
}
},
"msg": "",
"rc": 0,
"start": "2022-03-29 10:25:14.440129",
"stderr": "",
"stderr_lines": [],
"stdout": "# name admin#shrrah.esquimail.com\nzimbraIsDelegatedAdminAccount: FALSE\n\n# name prueba5#prueba5.com\n\n# name prueba7#prueba7.com\nzimbraIsDelegatedAdminAccount: TRUE\n\n# name prueba9#prueba9.com",
"stdout_lines": [
"# name admin#shrrah.esquimail.com",
"zimbraIsDelegatedAdminAccount: FALSE",
"",
"# name prueba5#prueba5.com",
"",
"# name prueba7#prueba7.com",
"zimbraIsDelegatedAdminAccount: TRUE",
"",
"# name prueba9#prueba9.com"
]
}
I need to get data with n# name prueba7#prueba7.com\nzimbraIsDelegatedAdminAccount: TRUE from "stdout" or from "stdout_lines" in format:
prueba7#prueba7.com zimbraIsDelegatedAdminAccount: TRUE
or
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: TRUE
and send it to the file.txt. Number of lines can be different (one o more users with domain).
I have no idea how I can do this, is this possible? If you know could you please help with advice? Thank you!
You may have a look into debug – Print statements during execution, Using Variables and Return Values.
---
- hosts: localhost
become: true
gather_facts: false
vars:
RESULT:
STDOUT_LINES:
- "# name admin#shrrah.esquimail.com"
- "zimbraIsDelegatedAdminAccount: FALSE"
- ""
- "# name prueba5#prueba5.com"
- ""
- "# name prueba7#prueba7.com"
- "zimbraIsDelegatedAdminAccount: TRUE"
- ""
- "# name prueba9#prueba9.com"
tasks:
- name: Show STDOUT_LINES
debug:
msg: "{{ RESULT.STDOUT_LINES }}"
resulting into an output only of
TASK [Show STDOUT_LINES] *****************
ok: [localhost] =>
msg:
- '# name admin#shrrah.esquimail.com'
- 'zimbraIsDelegatedAdminAccount: FALSE'
- ''
- '# name prueba5#prueba5.com'
- ''
- '# name prueba7#prueba7.com'
- 'zimbraIsDelegatedAdminAccount: TRUE'
- ''
- '# name prueba9#prueba9.com'
and if Ansible Callback plugin is configured to YAML instead of JSON.
To get lines containing certain strings only you may Loop over the list based on a Condition
- name: Show lines with TRUE only
debug:
msg: "{{ item }}"
when: "'TRUE' in item"
loop: "{{ RESULT.STDOUT_LINES }}"
resulting into an output of
TASK [Show lines with TRUE only] *******************************
ok: [localhost] => (item=zimbraIsDelegatedAdminAccount: TRUE) =>
msg: 'zimbraIsDelegatedAdminAccount: TRUE'
Further Documenation
Index of all Callback Plugins
If you like to have the line before included, you could use an approach like
- name: Show lines with TRUE and line before
debug:
msg: "{{ RESULT.STDOUT_LINES[ansible_loop.index0 - 1] }}\n{{ item }}"
when: "'TRUE' in item"
loop: "{{ RESULT.STDOUT_LINES }}"
loop_control:
extended: true
label: "{{ ansible_loop.index0 }}"
resulting into an output of
TASK [Show lines with TRUE and line before] *************************************************************************************************************************************
ok: [localhost] => (item=6) =>
msg: |-
# name prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: TRUE
Further Documentation
Extended loop variables
Since you are using the shell module, you could use also an approach like
- name: DELEGATED ADMIN ACCOUNTS - check, get and send to the file domain.list
shell:
cmd: /opt/zimbra/bin/zmprov -l gaaa -v zimbraIsDelegatedAdminAccount | grep -B 1 TRUE
and gather only result lines which are true an the line before.
Further Q&A
grep a file, but show several surrounding lines?
Regarding
... send it to the file.txt
you may have a look into
Ansible - Save registered variable to file
Ansible: Save registered variables to file
...
Create a dictionary
- set_fact:
info: "{{ info|d({})|combine({_key: _val}) }}"
loop: "{{ stdout.split('#')[1:] }}"
vars:
_list: "{{ item.split('\n')|map('trim') }}"
_key: "{{ _list.0.split(' ')|last }}"
_val: "{{ _list[1:]|select()|map('from_yaml')|combine }}"
gives
info:
admin#shrrah.esquimail.com:
zimbraIsDelegatedAdminAccount: false
prueba5#prueba5.com: {}
prueba7#prueba7.com:
zimbraIsDelegatedAdminAccount: true
prueba9#prueba9.com: {}
Then, the template is trivial. Either print all items
- copy:
content: |-
{% for k,v in info.items() %}
{{ k }}
{{ v|to_nice_yaml }}
{% endfor %}
dest: file.txt
gives
shell> cat file.txt
admin#shrrah.esquimail.com
zimbraIsDelegatedAdminAccount: false
prueba5#prueba5.com
{}
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: true
prueba9#prueba9.com
{}
, or explicitly select item(s)
- copy:
content: |-
prueba7#prueba7.com
{{ info['prueba7#prueba7.com']|to_nice_yaml }}
dest: file.txt
gives
shell> cat file.txt
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: true
Note
Additional attributes will be parsed too, e.g.
stdout_lines: [
"# name admin#shrrah.esquimail.com",
"zimbraIsDelegatedAdminAccount: FALSE",
"",
"# name prueba5#prueba5.com",
"",
"# name prueba7#prueba7.com",
"zimbraIsDelegatedAdminAccount: TRUE",
"zimbraIsDelegatedRootAccount: TRUE",
"",
"# name prueba9#prueba9.com"
]
will give
info:
admin#shrrah.esquimail.com:
zimbraIsDelegatedAdminAccount: false
prueba5#prueba5.com: {}
prueba7#prueba7.com:
zimbraIsDelegatedAdminAccount: true
zimbraIsDelegatedRootAccount: true
prueba9#prueba9.com: {}
and consequently
shell> cat file.txt
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: true
zimbraIsDelegatedRootAccount: true

Club multiple variable and get one output in ansible

Club multiple variable and get one output in ansible
I will be using combined variable many other parts of the code and Is there a way to do it.
name:
hosts: dummy
gather_facts: True
tasks:
block:
Capture package-Infra agent version
v1
- name: Get package-Integration agent version ostype1.4
shell: command1
register: NRAV1
when: ansible_distribution == "ostype" and ansible_distribution_version == "v1"
- name: Debug
debug:
msg: "{{ NRAV1 }}"
v2
- name: Get package-Integration agent version ostype2.2
shell: command2
register: NRAV2
when: ansible_distribution == "ostype" and ansible_distribution_version == "v2"
- name: Debug
debug:
msg: "{{ NRAV2 }}"
v3
- name: Get package-Integration agent version ostype2.3
shell: command3
register: NRAV3
when: ansible_distribution == "ostype" and ansible_distribution_version == "v3"
- name: Debug
debug:
msg: "{{ NRAV3 }}"
- name: Result
debug:
msg: "{{ NRAV1.stdout | NRAV2.stdout | NRAV2.stdout }}"
#Output:
TASK [setup] *******************************************************************
ok: [dummy]
TASK [Get package-Integration agent version ostype1.4] *************************
skipping: [dummy]
TASK [Debug] *******************************************************************
ok: [dummy] => {
"msg": {
"changed": false,
"skip_reason": "Conditional check failed",
"skipped": true
}
}
TASK [Get package-Integration agent version ostype2.2] *************************
changed: [dummy]
TASK [Debug] *******************************************************************
ok: [dummy] => {
"msg": {
"changed": true,
"cmd": "rpm -qa --last | grep package-infra | awk '{print $1}' | cut -d'-' -f 3,4 | cut -d'.' -f 1,2,3,4,5",
"delta": "0:00:00.524132",
"end": "2021-03-07 12:06:33.150778",
"rc": 0,
"start": "2021-03-07 12:06:32.626646",
"stderr": "",
"stdout": "1.15.1-1.ostype2.2",
"stdout_lines": [
"1.15.1-1.ostype2.2"
],
"warnings": [
"Consider using yum module rather than running rpm"
]
}
}
TASK [Get package-Integration agent version ostype2.3] *************************
skipping: [dummy]
TASK [Debug] *******************************************************************
ok: [dummy] => {
"msg": {
"changed": false,
"skip_reason": "Conditional check failed",
"skipped": true
}
}
TASK [Result] *******************************************************************
fatal: [dummy]: FAILED! => {"failed": true, "msg": "ERROR! template error while templating string: no filter named 'NRAV3.stdout'"}
PLAY RECAP *********************************************************************
dummy : ok=5 changed=1 unreachable=0 failed=1
#My expected result would be >> NRAV2.stdout >> "1.15.1-1.ostype2.2"
Ansible is unable to parse below line -
msg: "{{ NRAV1.stdout | NRAV2.stdout | NRAV3.stdout }}"
You need to replace it with something like this -
msg: "{{ NRAV1.stdout }} | {{ NRAV2.stdout }} | {{ NRAV3.stdout }}"

Ansible mount module to just check state and not report status

Team,
I am writing a validation task that is supposed to just check if a mount exists or not and report its state from output. so my task is below but it fails and am not sure how to handle it. any hint what adjustments do i need to make?
- name: "Verify LVP Mounts on CPU Nodes for mount_device"
shell: "mount | grep sdd"
register: lvp_mount
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-cpu-node'] }}"
#failed_when: lvp_mount.rc != 0
#ignore_errors: yes
# - debug:
# var: lvp_mount
- name: "Report status of mounts"
fail:
msg: |
Mounts sdd not found
Output of `mount | grep sdd`:
{{ lvp_mount.stdout }}
{{ lvp_mount.stderr }}
when: lvp_mount | failed
changed: [localhost -> ] => (item=hostA)
[WARNING]: Consider using the mount module rather than running 'mount'. If you
need to use command because mount is insufficient you can add 'warn: false' to
this command task or set 'command_warnings=False' in ansible.cfg to get rid of
this message.
failed: [localhost -> hostA.test.net] (item=hostA) => {"ansible_loop_var": "item", "changed": true, "cmd": "mount | grep sdd", "delta": "0:00:00.009284", "end": "2019-11-06 18:22:56.138007", "failed_when_result": true, "item": "hostA", "msg": "non-zero return code", "rc": 1, "start": "2019-11-06 18:22:56.128723", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring
TASK [services-pre-install-checks : Report status of mounts] ************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout'\n\nThe error appears to be in '/home/run_ansible_playbook/k8s/baremetal/roles/services-pre-install-checks/tasks/main.yml': line 265, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: \"Report status of mounts\"\n ^ here\n"}
Your task "Verify LVP Mounts on CPU Nodes for mount_device" is a loop so the register behavior is modified as specified in the documentation.
You can access the various outputs with lvp_mount.results.X.stdout where X is the index.
There is a cleaner way to write your script however. More specifically using:
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-cpu-node'] }}"
is bad practice. You can accomplish your desired outcome at the play level.
For example:
- hosts: kube-cpu-node # allows you to iterate over all hosts in kube-cpu-node group
tasks:
- name: "Verify LVP Mounts on CPU Nodes for mount_device"
shell: "mount | grep sdd"
register: lvp_mount
ignore_errors: yes
# notice there is no loop here
- name: "Report status of mounts"
fail:
msg: |
Mounts sdd not found
Output of `mount | grep sdd`:
{{ lvp_mount.stdout }} # no loop so you can use lvp_mount.stdout
{{ lvp_mount.stderr }} # no loop so you can use lvp_mount.stderr
when: lvp_mount | failed

Test value of all members in hash variable in Ansible playbook

I need to run a task only if one or more files from a pre-defined list of files are missing. I tried the following (and some variants):
touch a
cat test.yml
- hosts: localhost
vars:
filelist:
- a
- b
tasks:
- stat:
path: "{{ item }}"
with_items: "{{ filelist }}"
register: all_files
- debug:
var: all_files
- debug:
msg: "Some file(s) missing"
when: "false in all_files['results'][*]['stat']['exists']"
ansible-playbook test.yml
...
TASK [debug] ********************************************************************
ok: [localhost] => {
"all_files": {
...
"item": "a",
"stat": {
...
"exists": true,
...
"item": "b",
"stat": {
"exists": false
...
TASK [debug] ********************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "The conditional check 'false in all_files['results'][*]['stat']['exists']' failed. The error was: template error while templating string: unexpected '*'. String: {% if false in all_files['results'][*]['stat']['exists'] %} True {% else %} False {% endif %}\n\nThe error appears to have been in 'test.yml': line 16, column 5, 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 syntax to use in the 'when:' clause? Or is this the wrong way altogether?
- hosts: localhost
gather_facts: false
vars:
file_vars:
- {name: file1}
- {name: file2}
tasks:
- name: Checking existing file name
stat:
path: ./{{ item.name }}
with_items: "{{ file_vars }}"
register: check_file_name
- debug:
msg: 'file name {{item.item.name}} not exists'
with_items: "{{ check_file_name.results }}"
when: item.stat.exists == False
- name: Create file
file:
path: "./{{item.item.name}}"
state: touch
with_items: "{{ check_file_name.results }}"
when: item.stat.exists == False

Ansible register result of multiple commands

I was given a task to verify some routing entries for all Linux server and here is how I did it using an Ansible playbook
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
You can see I have to repeat same task for each routing entry and I believe I should be able to avoid this. I tried use with_items loop but got following error message
One or more undefined variables: 'dict object' has no attribute 'stdout'
is there a way to register variable for each command and loop over them one by one ?
Starting in Ansible 1.6.1, the results registered with multiple items are stored in result.results as an array. So you can use result.results[0].stdout and so on.
Testing playbook:
---
- hosts: localhost
gather_facts: no
tasks:
- command: "echo {{item}}"
register: result
with_items: [1, 2]
- debug:
var: result
Result:
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] **************************************************************
TASK: [command echo {{item}}] *************************************************
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
TASK: [debug ] ****************************************************************
ok: [localhost] => {
"var": {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": [
"echo",
"1"
],
"delta": "0:00:00.002502",
"end": "2015-08-07 16:44:08.901313",
"invocation": {
"module_args": "echo 1",
"module_name": "command"
},
"item": 1,
"rc": 0,
"start": "2015-08-07 16:44:08.898811",
"stderr": "",
"stdout": "1",
"stdout_lines": [
"1"
],
"warnings": []
},
{
"changed": true,
"cmd": [
"echo",
"2"
],
"delta": "0:00:00.002516",
"end": "2015-08-07 16:44:09.038458",
"invocation": {
"module_args": "echo 2",
"module_name": "command"
},
"item": 2,
"rc": 0,
"start": "2015-08-07 16:44:09.035942",
"stderr": "",
"stdout": "2",
"stdout_lines": [
"2"
],
"warnings": []
}
]
}
}
}
PLAY RECAP ********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
A slightly different situation, which took a while to figure out. If you want to use the results of multiple items, but for changed_when, then the register variable will not have a var.results! Instead, changed_when, is evaluated for each item, and you can just directly use the register var.
Simple example, which will result in changed: false:
- action: command echo {{item}}
register: out
changed_when: "'z' in out.stdout"
with_items:
- hello
- foo
- bye
Another example:
- name: Create fulltext index for faster text searches.
mysql_db: name={{SO_database}} state=import target=/tmp/fulltext-{{item.tableName}}-{{item.columnName}}.sql
with_items:
- {tableName: Posts, columnName: Title}
- {tableName: Posts, columnName: Body}
- {tableName: Posts, columnName: Tags}
- {tableName: Comments, columnName: Text}
register: createfulltextcmd
changed_when: createindexcmd.msg.find('already exists') == -1
Finally, when you do want to loop through results in other contexts, it does seem a bit tricky to programmatically access the index as that is not exposed. I did find this one example that might be promising:
- name: add hosts to known_hosts
shell: 'ssh-keyscan -H {{item.host}}>> /home/testuser/known_hosts'
with_items:
- { index: 0, host: testhost1.test.dom }
- { index: 1, host: testhost2.test.dom }
- { index: 2, host: 192.168.202.100 }
when: ssh_known_hosts.results[{{item.index}}].rc == 1
Posting because I can't comment yet
Relating to gameweld's answer, since Ansible 2.5 there's another way to accessing the iteration index.
From the docs:
Tracking progress through a loop with index_var
New in version 2.5.
To keep track of where you are in a loop, use the index_var directive
with loop_control. This directive specifies a variable name to contain
the current loop index:
- name: count our fruit
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
This also allows you to gather results from an array and act later to the same array, taking into account the previous results
- name: Ensure directories exist
file:
path: "{{ item }}"
state: directory
loop:
- "mouse"
- "lizard"
register: reg
- name: Do something only if directory is new
debug:
msg: "New dir created with name '{{ item }}'"
loop:
- "mouse"
- "lizard"
loop_control:
index_var: index
when: reg.results[index].changed
Please note that the "mouse lizard" array should be exactly the same
If what you need is to register the output of two commands separately, use different variable names.
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result0
changed_when: false
- debug: msg="{{result0.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result1
changed_when: false
- debug: msg="{{result1.stdout}}"

Resources