How to deal with errors coming from ansible roles - ansible

I have been calling a playbook having multiple roles, each roles signifies a TESTCASE. I need to run the playbook without failing it if one of the role fails during execution. For which I am using ignore_errors: yes. However, this truly ignores error, I need to print at the end the name of the ROLES which are failed ? is it possible ?
- hosts: WEB
gather_facts: no
vars:
roles:
- { role: CHECK_CONNECTION, ignore_errors: yes, tags: always }
- { role: CHECK_CPU, ignore_errors: yes, tags: always }
- { role: CHECK_MEM, ignore_errors: yes, tags: always }
Question: How to execute whole playbook and at the end print the failed roles ?

An option would be to use the rescue section of Blocks
Create include_role.yml
- name: include role
block:
- include_role:
name: "{{ item_my_role }}"
rescue:
- set_fact:
failed_roles: "{{ failed_roles|default([]) + [ item_my_role ] }}"
and loop include_tasks. It is not possible to loop blocks.
vars:
my_roles:
- CHECK_CONNECTION
- CHECK_CPU
- CHECK_MEM
tasks:
- include_tasks: include_role.yml
loop: "{{ my_roles }}"
loop_control:
loop_var: item_my_role
- debug:
var: failed_roles|default([])
Use loop_control loop_var and create unique variable, e.g. item_my_role, avoiding potential conflict inside the included role. If the variable is used inside the included role the following rescue section will add the wrong item to the list.

Related

A method for listing Ansible modules used by playbooks

I'm creating a requirements.yml in an Ansible project, and I want to identify all of the modules that need to be installed from ansible-galaxy that are used by project playbooks. ansible-doc --list --playbook-dir foo seems like the right tool for the job, but it lists all locally available modules, not just the ones which are actually used in the foo directory. ansible-galaxy list doesn't account for any which are needed but not installed.
Is there a way to do this where I don't end up writing a shell script to sed|awk|grep the info I want?
The best approach I've been able to come up with so far is to ansible-playbook --syntax-check each of the playbooks. This will throw errors such as
ERROR! the role 'foo' was not found ...
ERROR! couldn't resolve module/action 'bar'. This often indicates a misspelling, missing collection, or incorrect module path.
but this is not ideal because it exits as soon as any error occurs. I have to fix each one and run the syntax check again.
FWIW, as a concept, the playbook below lists the modules used in a role
- hosts: localhost
vars:
keywords:
- always
- become
- block
- loop
- loop_control
- name
- notify
- register
- tags
- vars
- when
tasks:
- name: The variable my_role_path is mandatory
assert:
that: my_role_path|d('')|length > 0
- name: Find tasks files
find:
path: "{{ my_role_path }}/tasks"
patterns: '*.yml,*.yaml'
recurse: true
register: result
- name: Create list of tasks
set_fact:
lft: "{{ lft|d([]) + lookup('file', item)|from_yaml }}"
loop: "{{ result.files|map(attribute='path')|list }}"
- name: Get list of keys
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft }}"
- name: Get list of keys from block/rescue/always
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"
- name: Display list of modules
debug:
var: lfk|unique|sort|difference(keywords)
For example, analyze the role systemd
shell> ansible-playbook pb.yml -e my_role_path=roles/ansible-role-systemd
...
lfk|unique|sort|difference(keywords):
- command
- file
- include_tasks
- meta
- systemd
- template
Complete the list of the keywords.
Use the tasks below to analyze a playbook
tasks:
- name: The variable my_playbook_path is mandatory
assert:
that: my_playbook_path|d('')|length > 0
- name: Create list of tasks
set_fact:
lft: "{{ _playbook|map(attribute='tasks')|flatten }}"
vars:
_playbook: "{{ lookup('file', my_playbook_path)|from_yaml }}"
- name: Get list of keys
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft }}"
- name: Get list of keys from block/rescue/always
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"
- name: Display list of modules
debug:
var: lfk|unique|sort|difference(keywords)
For example, analyzing the first playbook gives
lfk|unique|sort|difference(keywords):
- assert
- debug
- find
- set_fact

Ansible - 'when' is not a valid attribute for a Play

I'm trying to figure out how to "remove" the warning message [WARNING]: Could not match supplied host pattern, ignoring: ps_nodes, by fixing the root cause. The root cause for me is that when we do Linux machine creation we will have the ps_nodes hosts empty. So, I was trying to add the block: + when: (os_type|capitalize) == "Windows", to assure that Play to only execute when os_type is a Windows creation.
How can I achieve that? Because, what I'm trying is to use the when condiction, but looks like it's not possible, and I'm not sure what to search anymore.
Code example:
- name: "Start handling of vm specific delete scripts for Windows machines"
block:
hosts: ps_nodes
any_errors_fatal: false
gather_facts: false
vars:
private_ip_1: "{{ hostvars['localhost']['_private_ip_1']|default('') }}"
scripts: "{{ hostvars['localhost']['scripts'] }}"
sh_script_dir: "{{ hostvars['localhost']['sh_script_dir'] }}"
cred_base_hst: "{{ hostvars['localhost']['cred_base_hst'] }}"
cred_base_gst: "{{ hostvars['localhost']['cred_base_gst'] }}"
newline: "\n"
tasks:
- import_tasks: roles/script/tasks/callWindowsScripts.yml
when: action == 'delete'
when: (os_type|capitalize) == "Windows"
Error using 'when' for a Play:
ERROR! 'when' is not a valid attribute for a Play
The error appears to be in '/opt/projectX/playbooks/create_vm.yml': line 265, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
##############################################################################
- name: \"Start handling of vm specific delete scripts for Windows machines\"
^ here
I think the problem is the indentation. Use 'and':
- name: "Start handling of vm specific delete scripts for Windows machines"
block:
hosts: ps_nodes
any_errors_fatal: false
gather_facts: false
vars:
private_ip_1: "{{ hostvars['localhost']['_private_ip_1']|default('') }}"
scripts: "{{ hostvars['localhost']['scripts'] }}"
sh_script_dir: "{{ hostvars['localhost']['sh_script_dir'] }}"
cred_base_hst: "{{ hostvars['localhost']['cred_base_hst'] }}"
cred_base_gst: "{{ hostvars['localhost']['cred_base_gst'] }}"
newline: "\n"
tasks:
- import_tasks: roles/script/tasks/callWindowsScripts.yml
when: action == 'delete' and (os_type|capitalize) == "Windows"
Got it,
What if you use a host that exists, like localhost, check the number of hosts in ps_nodes and delegate_to them?
Something like this:
hosts: localhost
vars:
tasks:
- import_tasks: roles/script/tasks/callWindowsScripts.yml
delegate_to: ps_nodes
when: {{ ps_nodes | length > 0}}
Same issue and fixed by "indent":
- hosts: test
roles:
- role: test
vars:
k: 1
when: "'dbg' in ansible_run_tags"

Invoke multiple roles in Ansible with where condition

Is there a possibility to invoke roles depends on the when condition OR may be use Ansible handlers?
I have a below playbook which gets the current status of deployment on remote host and if and only required then perform the next steps. Below is the validation.yml from validations role (1st one to invoke) which does the validations -
---
- name: Getting the status of current deployment
stat:
path: "{{ tomcat_symlink_path }}"
register: p
- set_fact:
current_release: "{{ p.stat.lnk_target.split('/')[4] | regex_replace('^Release(.*)$', '\\1') }}"
- debug:
msg: "The currently deployed release is : {{ p.stat.lnk_target.split('/')[4] | regex_replace('^Release(.*)$', '\\1') }}"
- name: Copying Application Configuration files and get the checksum
template:
src: "{{ item }}"
dest: "{{config_location}}/{{ item | basename | regex_replace('.j2$', '') }}"
mode: 0755
with_fileglob:
- /temp/env/*.j2
register: config_var
- block:
- name: "Exit the deployment if no changes required...."
debug:
msg: "Target Release and currently deployed release is same OR no configuration changed required.. so Exiting the Deployment!!!!"
- meta: end_play
when: myvm_release_version == current_release and config_var.changed == false
Now depends on the above 2 variables. I need to invoke roles. For example -
if config_var.changed == true and myvm_release_version == current_release then invoke only roles stoptomcat and starttomcat and exit the deployment because it is just the config change so only restart tomcat is required.
if only config_var.changed == false and myvm_release_version != current_release then continue with the playbook which will execute everything and all the roles
This may be a weird requirement but may be someone expert can throw some light on it.
It's a common requirement
You can include role with when condition as simple as following..
Solution: 1
you can not refer two of more than two task with when condition, only one task is allowed,
simple hack can be include a external playbook In that conditional task.
Solution: 2
Your Ansible code till "register: config_var"
- name: include conditional role
include_role: "{{item}}"
when: config_var.changed == true and myvm_release_version == current_release
with_items:
- "stoptomcat"
- 'starttomcat"
- name: block of code
block:
// conditional ansible tasks
when: config_var.changed == false and myvm_release_version != current_release
I was able to find a solution as below using meta
- block:
- name: "Doing Configuration Changes...."
include_role:
name: '{{ roleinputvar }}'
loop:
- stoptomcat
- starttomcat
loop_control:
loop_var: roleinputvar
- meta: end_play
when: config_var.changed == true and myvm_release_version == current_release
Just posting so it might help others.

how can i loop over a variable that might have single value?

I'm writing a playbook and want to loop a role over a variable that gets its value from the user. however that value might not always be a list of items, it might be a single value and whenever that happens it throws an error.
My Task:
- name: task name
include role:
name: role name
vars:
cluster_name: '{{ item }}'
loop: "{{ list_or_not }}"
loop_control:
loop_var: item
error:
...Invalid data passed to 'loop', it requires a list...
Have you tried the: "| list" filter?
Sorry cannot test at the moment.
You could test if the variable is a string, and if so, transform it into a single-item list. Something like this:
---
- hosts: localhost
gather_facts: false
tasks:
- set_fact:
list_or_not: ["{{ list_or_not }}"]
when: list_or_not is string
- debug:
msg: "{{ item }}"
loop: "{{ list_or_not }}"

Return Variable from Included Ansible Playbook

I have seen how to register variables within tasks in an ansible playbook and then use those variables elsewhere in the same playbook, but can you register a variable in an included playbook and then access those variables back in the original playbook?
Here is what I am trying to accomplish:
This is my main playbook:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
roles:
- some_role
sub-playbook.yml:
---
- hosts: localhost
tasks:
- name: Collect info from Jenkins Job
script: whatever.py --url "{{ job_url }}"
register: jenkins_artifacts
I'd like to be able to access the jenkins_artifacts results back in main_playbook if possible. I know you can access it from other hosts in the same playbook like this: "{{ hostvars['localhost']['jenkins_artifacts'].stdout_lines }}"
Is it the same idea for sharing across playbooks?
I'm confused what this question is about. Just use the variable name jenkins_artifacts:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
debug:
var: jenkins_artifacts
This might seem complicated but I love doing this in my Playbooks:
rc defines the name of the variable which contains the return value
ar gives the arguments to the include tasks
master.yml:
- name: verify_os
include_tasks: "verify_os/main.yml"
vars:
verify_os:
rc: "isos_present"
ar:
image: "{{ os.ar.to_os }}"
verify_os/main.yml:
---
- name: check image on device
ios_command:
commands:
- "sh bootflash: | inc {{ verify_os.ar.image }}"
register: image_check
- name: check if available
shell: "printf '{{ image_check.stdout_lines[0][0] }}\n' | grep {{ verify_os.ar.image }} | wc -l"
register: image_available
delegate_to: localhost
- set_fact: { "{{ verify_os.rc }}": "{{ true if image_available.stdout == '1' else false }}" }
...
I can now use the isos_present variable anywhere in the master.yml to access the returned value.

Resources