How do I find which register attribute to use in Ansible? - ansible

I am testing out the results of the register commands and it yields different attribute for various tasks: failures, msg, stderr, err..etc
- yum:
name: packagenotfound
state: present
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": false,
"failed": true,
"failures": [
"No package packagenotfound available."
],
"msg": "Failed to install some of the specified packages",
"rc": 1,
"results": []
}}
And
- lvg:
pvs: /dev/sddnotfound
vg: vgdata
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": false,
"failed": true,
"msg": "Device /dev/sddnotfound not found."
}
And
- shell: thiscommandwontwork
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": true,
"cmd": "thiscommandwontwork",
"delta": "0:00:00.002560",
"end": "2020-02-05 04:24:35.297556",
"failed": true,
"msg": "non-zero return code",
"rc": 127,
"start": "2020-02-05 04:24:35.294996",
"stderr": "/bin/sh: thiscommandwontwork: command not found",
"stderr_lines": [
"/bin/sh: thiscommandwontwork: command not found"
],
"stdout": "",
"stdout_lines": []
}
And
- lvol:
lv: lvdata
vg: vgroup
size: 2000M
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": false,
"err": " Volume group \"vgroup\" not found\n Cannot process volume group vgroup\n",
"failed": true,
"msg": "Volume group vgroup does not exist.",
"rc": 5
}
Now if I tried to use when: '"xxx" in command_result.err' with yum task for example, it will result in dict_object not found error.
Is there a way to find out which attribute to use without testing?

Testing is definitely the easiest and fastest way to have a look at the content of your registered var in several situation and to take decisions on how to use it in your playbook.
Meanwhile, there are ways to have a global knowledge of what is returned in your registered var from the documenations:
There is a page on Common modules return values
Modules returning specific values usually document them on each relevant doc page. Here is an example for the stat module
You should also be aware that the global register structure is changed when using a loop by addind a top level results list, as explained in registering variables
Knowing what could be in your register does not mean it will be. Your example mentions the (undocumented...) err attribute for the lvol module, which will only be available for an lvol task in error. You can work around such cases by using tests (like my_register is failed) or defaulting values with the default filter.

Related

Ansible search for a substring in stdout output

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"
}

Ansible: How do I execute tasks in ansible playbook in parallel with some time gap

Let me try and explain my need:
As part of the regular deployment of our application, we have a SQL script(which would alter tables, add tables or update, etc) which needs to be run on 3 schemas in a region and 5 schemas in another for example. The application is in AWS and the database is Arora db(RDS)- MySQL. This schema can take anywhere between 30 minutes to 3 hours.
This SQL script needs to be run in parallel and with a delay of 2 minutes between each schema run.
This is what I have achieved till now:
A file having DB details- dbdata.yml
---
conn_details:
- { host: localhost, user: root, password: "Password1!" }
- { host: localhost, user: root, password: "Password1!" }
The playbook:
- hosts: localhost
vars:
script_file: "{{ path }}"
vars_files:
- dbdata.yml
tasks:
- name: shell command to execute script in parallel
shell: |
sleep 30s
"mysql -h {{ item.host }} -u {{ item.user }} -p{{ item.password }} < {{ script_file }} >> /usr/local/testscript.log"
with_items: "{{ conn_details }}"
register: sql_query_output
async: 600
poll: 0
- name: Wait for sql execution to finish
async_status:
jid: "{{ item.ansible_job_id }}"
register: _jobs
until: _jobs.finished
delay: 20 # Check every 20 seconds. Adjust as you like.
retries: 10
with_items: "{{ sql_query_output.results }}"
1st part- executes the script in parallel and this also includes a time gap of 30 seconds before each execution.
2nd part- picks the ansible job id from the registered output and checks if the job is completed or not.
Please note: before including the 30 seconds sleep, this playbook was working fine.
We have following erroneous output upon execution:
ansible-playbook parallel_local.yml --extra-vars "path=RDS_script.sql"
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] **************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
ok: [localhost]
TASK [sample command- ansible-playbook my_sqldeploy.yml --extra-vars "path=/home/NICEONDEMAND/bsahu/RDS_create_user1.sql"] ****************************************************************************************
changed: [localhost] => (item={u'host': u'localhost', u'password': u'Password1!', u'user': u'root'})
changed: [localhost] => (item={u'host': u'localhost', u'password': u'Password1!', u'user': u'root'})
TASK [Wait for creation to finish] ********************************************************************************************************************************************************************************
FAILED - RETRYING: Wait for creation to finish (10 retries left).
FAILED - RETRYING: Wait for creation to finish (9 retries left).
failed: [localhost] (item={'ansible_loop_var': u'item', u'ansible_job_id': u'591787538842.77844', 'item': {u'host': u'localhost', u'password': u'Password1!', u'user': u'root'}, u'started': 1, 'changed': True, 'failed': False, u'finished': 0, u'results_file': u'/root/.ansible_async/591787538842.77844'}) => {"ansible_job_id": "591787538842.77844", "ansible_loop_var": "item", "attempts": 3, "changed": true, "cmd": "sleep 30s\n\"mysql -h localhost -u root -pPassword1! < RDS_script.sql >> /usr/local/testscript.log\"\n", "delta": "0:00:30.073191", "end": "2019-11-28 17:01:57.632285", "finished": 1, "item": {"ansible_job_id": "591787538842.77844", "ansible_loop_var": "item", "changed": true, "failed": false, "finished": 0, "item": {"host": "localhost", "password": "Password1!", "user": "root"}, "results_file": "/root/.ansible_async/591787538842.77844", "started": 1}, "msg": "non-zero return code", "rc": 127, "start": "2019-11-28 17:01:27.559094", "stderr": "/bin/sh: line 1: mysql -h localhost -u root -pPassword1! < RDS_script.sql >> /usr/local/testscript.log: No such file or directory", "stderr_lines": ["/bin/sh: line 1: mysql -h localhost -u root -pPassword1! < RDS_script.sql >> /usr/local/testscript.log: No such file or directory"], "stdout": "", "stdout_lines": []}
failed: [localhost] (item={'ansible_loop_var': u'item', u'ansible_job_id': u'999397686792.77873', 'item': {u'host': u'localhost', u'password': u'Password1!', u'user': u'root'}, u'started': 1, 'changed': True, 'failed': False, u'finished': 0, u'results_file': u'/root/.ansible_async/999397686792.77873'}) => {"ansible_job_id": "999397686792.77873", "ansible_loop_var": "item", "attempts": 1, "changed": true, "cmd": "sleep 30s\n\"mysql -h localhost -u root -pPassword1! < RDS_script.sql >> /usr/local/testscript.log\"\n", "delta": "0:00:30.120136", "end": "2019-11-28 17:01:58.694713", "finished": 1, "item": {"ansible_job_id": "999397686792.77873", "ansible_loop_var": "item", "changed": true, "failed": false, "finished": 0, "item": {"host": "localhost", "password": "Password1!", "user": "root"}, "results_file": "/root/.ansible_async/999397686792.77873", "started": 1}, "msg": "non-zero return code", "rc": 127, "start": "2019-11-28 17:01:28.574577", "stderr": "/bin/sh: line 1: mysql -h localhost -u root -pPassword1! < RDS_script.sql >> /usr/local/testscript.log: No such file or directory", "stderr_lines": ["/bin/sh: line 1: mysql -h localhost -u root -pPassword1! < RDS_script.sql >> /usr/local/testscript.log: No such file or directory"], "stdout": "", "stdout_lines": []}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Any suggestions how to overcome this. Thanks in advance for all the help.
My bad. I had a silly mistake which was creating trouble. I needed to remove "" from the line executing the sql file. Reproducing the correct yaml file below
- hosts: localhost
vars:
script_file: "{{ path }}"
vars_files:
- dbdata.yml
tasks:
- name: sample command- ansible-playbook my_sqldeploy.yml --extra-vars "path=/home/NICEONDEMAND/bsahu/RDS_create_user1.sql"
shell: |
sleep 30s
mysql -h {{ item.host }} -u {{ item.user }} -p{{ item.password }} < {{ script_file }} >> /usr/local/testscript.log
with_items: "{{ conn_details }}"
register: sql_query_output
async: 600
poll: 0
- name: Wait for creation to finish
async_status:
jid: "{{ item.ansible_job_id }}"
register: _jobs
until: _jobs.finished
delay: 20 # Check every 5 seconds. Adjust as you like.
retries: 10
with_items: "{{ sql_query_output.results }}"
Thanks all for the help.

How to solve AnsibleUndefinedVariable in the serial mode

I am setting up a new play in my Ansible playbook that does some operations on a group of machines.
These operations require to play in serial mode 1 by 1.
My job is working well for the 1st machine but for the others one it seems that the variables are not valued.
- name: "My play"
hosts: my_hosts_group
tags: [test]
serial: 1
become: yes
become_user: root
tasks:
- shell: echo {{ MYVARIBLE.MYSOFTWARE.souche_path }} > /home/USER/test.txt
register: result
- debug: var=result
Here is my datamodel :
MYVARIBLE:
MYSOFTWARE:
souche_path: "/mybinpath/mybin"
For the 1st machine it's working well and I am getting this message :
ok: [host1] => {
"changed": false,
"result": {
"changed": true,
"cmd": "echo my_variable_value > /home/USER/test.txt",
"delta": "0:00:00.005541",
"end": "2019-04-26 16:27:09.415017",
"failed": false,
"rc": 0,
"start": "2019-04-26 16:27:09.409476",
"stderr": "",
"stderr_lines": [],
"stdout": "",
"stdout_lines": []
}
}
But fort he others one I got this one :
fatal: [host2]: FAILED! => {}
MSG:
The task includes an option with an undefined variable. The error was: 'MYVARIABLE' is undefined
The error appears to have been in '/my playbook/install.yml': line 85, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
tasks:
- shell: echo {{ MYVARIBLE }} > /home/USER/test.txt
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
exception type: <class 'ansible.errors.AnsibleUndefinedVariable'>
exception: 'MYVARIBLE' is undefined
I have played it in parallel mode and it's working well. So I am sure that the problem is the variable are only valued for the 1st loop.
Am I missing something ?

Extract exact value from stdout_lines from Ansible debug

I got my playbook from which I get value what I need through debug method, However I am not able to get exact value of child of my debug var
Below is my playbook
- hosts: db
tasks:
- name: Checking if For Page Life Expectancy.
win_command: sqlcmd -q "SELECT [object_name],[counter_name],[cntr_value] FROM sys.dm_os_performance_counters WHERE [object_name] LIKE '%Manager%'AND [counter_name] = 'Page life expectancy'"
register: win_command_result
- debug:
var: win_command_result.stdout_lines.object_name
And We get output like this
TASK [debug]
ok: [db1.local] => {
"win_command_result": {
"changed": true,
"cmd": "sqlcmd -q \"SELECT [object_name],[counter_name],
[cntr_value] FROM sys.dm_os_performance_counters WHERE [object_name]
LIKE '%Manager%'AND [counter_name] = 'Page life expectancy'\"",
"delta": "0:00:01.099974",
"end": "2018-09-11 05:08:36.022907",
"failed": false,
"rc": 0,
"start": "2018-09-11 05:08:34.922933",
"stderr": "",
"stderr_lines": [],
"stdout": "object_name
counter_name
cntr_value \r\n---------------------------
----------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- --------------------\r\nSQLServer:Buffer Manager
Page life expectancy
238579\r\n\r\n(1 rows affected)\r\n",
"stdout_lines": [
"object_name
counter_name
cntr_value ",
"---------------------------------------------------------
----------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- --------------------",
"SQLServer:Buffer Manager
Page life expectancy
238579",
"",
"(1 rows affected)"
]
}
}
We only need value of cntr_value which is 238579
If i set as I though that cntr_value is child of stdout_lines
- debug:
var: win_command_result.stdout_lines.cntr_value
It says
ok: [db1.local] => {
"win_command_result.stdout_lines.cntr_value": "VARIABLE IS NOT DEFINED!"
}
How do I extract exact value of cntr_value
Your output final variable is:
myvar.win_command_result.stdout
So if you want to extract the number, do a regex:
- name: Debug
debug:
msg: "{{ myvar.win_command_result.stdout | regex_search('\\d{6}')}}"
Another option:
- name: Fact
set_fact:
mylist: "{{ myvar.win_command_result.stdout_lines | list }}"
- name: Debug
debug:
msg: "{{ item | regex_search('\\d+') }}"
with_items: "{{ mylist[2] }}"

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