I'm trying to execute a playbook recursively until the condition satisfies. But, I couldn't achieve it some-how. Can anyone suggest me the solution.
Ansible-version: 2.2.1.0
Here is my test-plays.
main_play.yml:
---
- hosts: localhost
tasks:
- name: Wait till you get the needed thing in the get call
include: loop.yml
Here is the loop.yml
- name: Wait until migration jobs reach DbcAllJobxxxxx
uri:
url: "http://<url->/jobs"
method: GET
headers:
Content-Type: "application/json"
Accept: "application/json"
Postman-Token: "31d6"
cache-control: "no-cache"
return_content: yes
register: migration_status
ignore_errors: yes
- debug: msg="{{ migration_status }}"
#write mig-status to file
- copy: content="{{ migration_status.content }}" dest=/path/to/dest/migration_status.json
- name: Get the DbcAllJobxxxxx status from py script
shell: python jsonrc.py /path/to/dest/migration_status.json
register: pyout
- debug: msg="{{ pyout.stdout }}"
- include: loop.yml
when: pyout.stdout != '1'
ignore_errors: yes
- debug: msg="{{ pyout.stdout }}"
Requirement : GET json call will return json. The json may vary time-to-time as it returns dynamic status. So, want to pool the json data continuously to know the value of a key - Which is a sign to call other event. So, I need to wait for the key-value pair in that json. [It may loss within time frame.. Need to catch at that point]. To achieve same parsing the json through python script and catching the return of pyscript and checking the value and calling the same play if it doesn't satisfy the condition.
Executing ansible-playbook main_play.yml
Even the pyout.stdout == '1' it's still throwing ERROR! Unexpected Exception: maximum recursion depth exceeded error. Did I miss any ?? Help me in this regard.
BTW, I tried to achieve this with until using json_query. but, parsing become difficult in this part. So, avoided this solution.
From ansible 2.4 onwards there is the include_tasks builtin that does work recursively.
main_play.yml:
---
- hosts: localhost
tasks:
- set_fact:
counter: 1
- include_tasks: loop.yml
loop.yml:
- set_fact:
counter: "{{counter | int + 1 }}"
- debug: msg="{{ counter }}"
- include_tasks: loop.yml
when: counter | int < 5
Result:
PLAY [localhost] ****************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] *****************************************************************************************************************************************************************************
ok: [localhost]
TASK [include_tasks] ***********************************************************************************************************************************
included: loop.yml for localhost
TASK [set_fact] *****************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "2"
}
TASK [include_tasks] ************************************************************************************************************************************************************************
included: loop.yml for localhost
TASK [set_fact] *****************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "3"
}
TASK [include_tasks] ************************************************************************************************************************************************************************
included: loop.yml for localhost
TASK [set_fact] *****************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "4"
}
TASK [include_tasks] ************************************************************************************************************************************************************************
included: loop.yml for localhost
TASK [set_fact] *****************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "5"
}
TASK [include_tasks] ************************************************************************************************************************************************************************
skipping: [localhost]
PLAY RECAP **********************************************************************************************************************************************************************************
localhost : ok=14 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
This code is to loop recursivley until I get the right message or it reaches MAX retries (In case just set very high)
- name: Group of tasks that are tightly coupled
block:
- name: Increment the retry count
set_fact:
retry_count: "{{ 0 if retry_count is undefined else retry_count | int + 1 }}"
- name: get state file {{ retry_count }}
command:
cmd: cat /tmp/test.txt
register: out
- debug:
msg: "Found END message"
when: "'END' in out.stdout_lines"
- fail:
msg: "END not reached"
when: "not 'END' in out.stdout_lines"
rescue:
- fail:
msg: Maximum retries of grouped tasks reached
when: retry_count | int == 15
- name: pause a moment
pause:
seconds: 1
when: "not 'END' in out.stdout_lines"
- debug:
msg: "hasen't finished yet, retry"
when: "not 'END' in out.stdout_lines"
- include_tasks: loop_control.yml
when: "not 'END' in out.stdout_lines"
It's obvious, I think.
"Here is the loop.yml"
...
- include: loop.yml
ERROR! Unexpected Exception: maximum recursion depth exceeded error.
Related
I have a playbook to get all disks letter configured on my server and I need a task to verify if extra var letter is on the list.
For example I need to check if "F" is on the json data below.
Could you please help me on the best best syntax?
Thanks
{
"disks_drives_letter": [
[
"C"
],
[
"D"
],
[
"E"
],
[]
]
}
You can use setup module to get your host information like disks. For more information about the setup module https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html
Example of playbook:
- hosts: localhost
tasks:
gather_facts: false
vars:
my_disk_drives: ['sda', 'sdb']
tasks:
- name: Collect host hardware information
setup:
gather_subset:
- hardware
- name: Output if disk exist
debug:
msg: "{{ item }} exists"
loop: "{{ my_disk_drives }}"
when: item in hostvars[inventory_hostname].ansible_devices.keys() | list
- name: Output if disks does not exist
debug:
msg: "{{ item }} does not exist"
loop: "{{ my_disk_drives }}"
when: not item in hostvars[inventory_hostname].ansible_devices.keys() | list
Output:
TASK [Output if disk exist]
ok: [localhost] => (item=sda) => {
"msg": "sda exists"
}
skipping: [localhost] => (item=sdb)
TASK [Output if disks does not exist]
skipping: [localhost] => (item=sda)
ok: [localhost] => (item=sdb) => {
"msg": "sdb does not exist"
}
Use filters intersect and difference, and declare the lists
my_disks_exist: "{{ ansible_devices.keys()|intersect(my_disks) }}"
my_disks_not_exist: "{{ my_disks|difference(my_disks_exist) }}"
Example of a complete playbook for testing
- hosts: localhost
vars:
my_disks: [sda, sdb, sdc]
my_disks_exist: "{{ ansible_devices.keys()|intersect(my_disks) }}"
my_disks_not_exist: "{{ my_disks|difference(my_disks_exist) }}"
tasks:
- setup:
gather_subset: devices
- debug:
var: ansible_devices.keys()
- debug:
var: my_disks_exist
- debug:
var: my_disks_not_exist
| flatten help me thanks #vladimir-botka
- name: Get all disks letter from the disks infos
set_fact:
disks_drives_letters: "{{ win_disk_facts | json_query(query) | flatten }}"
- name: Check if disk_letter is used on server fail:
msg: "The disk letter already exist on the VM" when: '"{{ drive_letter }}" in "{{ disks_drives_letters}}"'
I've got a GitLab pipeline that runs some Ansible playbooks in a container. At the end, I want to run some tests to ensure that everything is properly configured, e.g.
- name: Get the Frobzy value
raw: "frobzy -x foobar"
register: frobzy_val
changed_when: false
- name: Check that Frobzy is valid
assert:
that:
- frobzy_val.stdout is match "oogah"
fail_msg: "bad frobzy"
ignore_errors: true # see comments
- name: Get the Blobzy value
raw: "blobzy -y barfoo"
register: blobzy_val
changed_when: false
- name: Check that Blobzy is valid
assert:
that:
- blobzy_val.stdout is match "warra"
fail_msg: "bad blobzy"
ignore_errors: true # see comments
This is all nicely configured so that I get a JUnit report that gets displayed on my GitLab pipeline results.
The problem is, let's say both of these tests fail. If I look at the test results from my pipeline, it correctly displays that the tests failed. Yet the pipeline itself is marked as having passed! If I hadn't taken a detailed look at the test results, I would have assumed that everything is tickety-boo, and merged some potentially broken code.
The reason why the pipeline is marked as successful is because of those ignore_errors: true settings. It registers an error, but follows the directive that this should not fail the pipeline. But if I set ignore_errors: false (which is the default behavior), then as soon as the Frobzy test fails, the pipeline will abort and show a failure on the Frobzy test, but since it never ran the Blobzy test, I don't know that Blobzy would have failed, too.
Basically, I want a way to run all my Ansible assertions, even if some of them fail; I want to see all the test results in my pipeline, and I want the pipeline to fail if any tests failed.
How can I do this?
Here's a quick solution that might help.
Have a task to check if any of the tasks failed. Set a failed variable to true in that case.
Fail the playbook at the end if this variable is set to true.
- hosts: localhost
vars:
failed: false
gather_facts: no
tasks:
- name: Get the Frobzy value
raw: "echo frobzy"
register: frobzy_val
changed_when: false
- name: Check that Frobzy is valid
assert:
that:
- frobzy_val.stdout is match "frobzy"
fail_msg: "bad frobzy"
ignore_errors: true # see comments
- name: Get the Blobzy value
raw: "echo blobzy"
register: blobzy_val
changed_when: false
- name: Check that Blobzy is valid
assert:
that:
- blobzy_val.stdout is match "blobzy"
fail_msg: "bad blobzy"
ignore_errors: true # see comments
- name: Set a variable to true if the task fails
set_fact:
failed: true
ignore_errors: true
when: not ("'blobzy' in blobzy_val.stdout" ) or
not ("'frobzy' in frobzy_val.msg")
- name: Fail if failed is true
fail:
msg: "Failed"
when: failed
Run
─ ansible-playbook test.yaml
PLAY [localhost] ********************************************************************************************************************************************************************************
TASK [Get the Frobzy value] *********************************************************************************************************************************************************************
ok: [localhost]
TASK [Check that Frobzy is valid] ***************************************************************************************************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
TASK [Get the Blobzy value] *********************************************************************************************************************************************************************
ok: [localhost]
TASK [Check that Blobzy is valid] ***************************************************************************************************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
TASK [Set a variable to true if the task fails] *************************************************************************************************************************************************
skipping: [localhost]
TASK [Fail if failed is true] *******************************************************************************************************************************************************************
skipping: [localhost]
PLAY RECAP **************************************************************************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Make a single assertion for both conditions and delay it until the end so that it fails after the two tests are done.
Add a debug task to show results for your pipelines.
- name: Get the Frobzy value
raw: "frobzy -x foobar"
register: frobzy_val
changed_when: false
- name: Get the Blobzy value
raw: "blobzy -y barfoo"
register: blobzy_val
changed_when: false
- name: Show results for Frobzy and Blobzy
debug:
msg:
- "Frobzy is {{ 'ok' if frobzy_val.stdout is match 'oogah' else 'bad' }}"
- "Blobzy is {{ 'ok' if lobzy_val.stdout is match 'warra' else 'bad' }}"
- name: Fail if any test is bad
assert:
that:
- frobzy_val.stdout is match "oogah"
- blobzy_val.stdout is match "warra"
fail_msg: "At least one of Frobzy or Blobzy has bad results. See debug above"
``
I've set up a task which queries the github api meta endpoint and returns the following
{
"verifiable_password_authentication": true,
"github_services_sha": "f9e3a6b98d76d9964a6613d581164039b8d54d89",
"hooks": [
"192.30.252.0/22",
"185.199.108.0/22",
"140.82.112.0/20"
],
"git": [
"192.30.252.0/22",
"185.199.108.0/22",
"140.82.112.0/20",
"13.229.188.59/32",
"13.250.177.223/32",
"18.194.104.89/32",
"18.195.85.27/32",
"35.159.8.160/32",
"52.74.223.119/32"
],
"pages": [
"192.30.252.153/32",
"192.30.252.154/32",
"185.199.108.153/32",
"185.199.109.153/32",
"185.199.110.153/32",
"185.199.111.153/32"
],
"importer": [
"54.87.5.173",
"54.166.52.62",
"23.20.92.3"
]
}
What I need to do is get the 3 hook IPs and read them each into their own variable.
I've tried a couple of solutions i've found around but nothing is seeming to work for me.
I've got as far as drilling down into the json so i'm being returned only the 3 IPs, but how do I get them out and into variables individually?
i gave it a shot using j2 syntax in the variable name part, and - TIL - looks like the jinja2 syntax is allowed in that part as well!
please see playbook to process the hooks list variable and assign to variables variable_1, variable_2, variable_3 and so on:
- hosts: localhost
gather_facts: false
vars:
counter: 1
hooks:
- 192.30.252.0/22
- 185.199.108.0/22
- 140.82.112.0/20
tasks:
- name: populate vars
set_fact:
variable_{{counter}}: "{{ item }}"
counter: "{{ counter | int + 1 }}"
with_items:
- "{{ hooks }}"
- name: print vars
debug:
msg: "variable_1: {{variable_1}}, variable_2: {{variable_2}}, variable_3: {{variable_3}}"
and the output:
[root#optima-ansible ILIAS]# ansible-playbook 50257063.yml
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [populate vars] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=192.30.252.0/22)
ok: [localhost] => (item=185.199.108.0/22)
ok: [localhost] => (item=140.82.112.0/20)
TASK [print vars] **********************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "variable_1: 192.30.252.0/22, variable_2: 185.199.108.0/22, variable_3: 140.82.112.0/20"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
[root#optima-ansible ILIAS]#
hope it helps
UPDATE:
something weird i noticed - also TIL - is that if you reverse the lines:
variable_{{counter}}: "{{ item }}"
counter: "{{ counter | int + 1 }}"
to:
counter: "{{ counter | int + 1 }}"
variable_{{counter}}: "{{ item }}"
you still end up with the same variable names, _1 to _3, while i would expect to get _2 to _4.
I guess ansible loops behave differently than expected from other programming languages.
---
- name: Query Github Meta API and get Hook Ips
hosts: local
connection: local
vars:
counter: 1
tasks:
- name: Query API
uri:
url: https://api.github.com/meta
return_content: yes
register: response
- name: Populate Hook Variables
set_fact:
webhook_ip_{{counter}}: "{{ item }}"
counter: "{{ counter | int + 1 }}"
with_items:
- "{{ response['json']['hooks'] }}"
- name: print vars
debug:
msg: "Variable_1: {{ webhook_ip_1 }}, Variable_2: {{ webhook_ip_2 }}, Variable_3: {{ webhook_ip_3 }}"
Works with GitHub Webhook IPs in a loop
- name: get request to github
uri:
url: "https://api.github.com/meta"
method: GET
return_content: yes
status_code: 200
headers:
Content-Type: "application/json"
#X-Auth-Token: "0010101010"
body_format: json
register: json_response
- name: GitHub webhook IPs
debug:
msg: "{{ item }}"
with_items: "{{ (json_response.content | from_json).hooks }}"
Using Ansible "set_fact" module, I need to get repository url from json file using filters like "from_json". I tried in couple ways, and still doesn't get it how is should work.
- name: initial validation
tags: bundle
hosts: localhost
connection: local
tasks:
- name: register bundle version_file
include_vars:
file: '/ansible/playbook/workbench-bundle/bundle.json'
register: bundle
- name: debug registered bundle file
debug:
msg: '{{ bundle }}'
I get json that I wanted:
TASK [debug registered bundle file] ************************************************
ok: [127.0.0.1] => {
"msg": {
"ansible_facts": {
"engine-config": "git#bitbucket.org/engine-config.git",
"engine-monitor": "git#bitbucket.org/engine-monitor.git",
"engine-server": "git#bitbucket.org/engine-server.git",
"engine-worker": "git#bitbucket.org/engine-worker.git"
},
"changed": false
}
}
And then I'm trying to select each value by key name to use this value as URL to "npm install" each package in separate instances.
- name: set_fact some paramater
set_fact:
engine_url: "{{ bundle.('engine-server') | from_json }}"
And then I get error:
fatal: [127.0.0.1]: FAILED! => {"failed": true, "msg": "template error
while templating string: expected name or number. String: {{
bundle.('engine-server') }}"}
I many others ways like this loopkup, and it still fails with others errors. Can someone help to understand, how I can find each parameter and store him as "set_fact"? Thanks
Here is a sample working code to set a variable like in the question (although I don't see much sense in it):
- name: initial validation
tags: bundle
hosts: localhost
connection: local
tasks:
- name: register bundle version_file
include_vars:
file: '/ansible/playbook/workbench-bundle/bundle.json'
name: bundle
- debug:
var: bundle
- debug:
var: bundle['engine-server']
- name: set_fact some paramater
set_fact:
engine_url: "{{ bundle['engine-server'] }}"
The above assumes your input data (which you did not include) is:
{
"engine-config": "git#bitbucket.org/engine-config.git",
"engine-monitor": "git#bitbucket.org/engine-monitor.git",
"engine-server": "git#bitbucket.org/engine-server.git",
"engine-worker": "git#bitbucket.org/engine-worker.git"
}
Hi I have a problem of getting one of the variables extracted from a json output after doing a curl to be parsed and registered back to ansible
Playbook:
- name: debug stdout
debug:
msg: "{{ result.stdout | from_json }}"
register: dataresult
- name: debug fact
debug:
msg: "{{ dataresult.data.start_time_string }}"
output :
TASK [backup_api : debug stdout]
***********************************************
task path: /home/ansible/cm-dha/roles/backup_api/tasks/main.yml:36
ok: [127.0.0.1] => {
"msg": {
"data": [
{
"backup_id": 40362,
"certified": null,
"instance_id": 148,
"start_time": 1506985211,
"start_time_string": "10/03/2017 03:00:11 am"
}
],
"timestamp": 1507022232
}
}
error:
fatal: [127.0.0.1]: FAILED! => {
"failed": true,
"msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'dict object' has no attribute 'data'\n\nThe error appears to have been in '/home/ansible/cm-dha/roles/backup_api/tasks/main.yml': line 48, 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 - name: debug fact\n ^ here\n"
The error is happening when trying to extract the value start_time_string
so how to do it probably as I tried too many things like using with_items, with_dict , simulating the data[] output to debug and even doing a json query but without success
so any help here?
Don't use debug to assign facts, use set_fact instead:
- name: debug stdout
set_fact:
dataresult: "{{ result.stdout | from_json }}"
- name: debug fact
debug:
msg: "{{ dataresult.data[0].start_time_string }}"
Thanks to accepted answer, I've made it as below for my case. Leaving it here, maybe it helps someone.
- name: Connectors
shell: curl -X GET http://myserver:8083/connectors
register: out6
- set_fact:
dataresult: "{{ out6.stdout | from_json }}"
- debug: var=dataresult