Ansible- How to include /import playbook based on when condition result - ansible

I have folder in which i have many sub folder and those sub folder will have yaml file, by using the below code , I have got all yaml file and register those file,
tasks:
- name: Find .yml files
find:
paths: /temp/
patterns: '*.yml,*.yaml'
recurse: yes
register: yaml_path_files
One of the yaml file content is below,
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
---
apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
namespace: default
data:
log_level: INFO
---
apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
namespace: default
data:
log_level: INFO
i want to check if the yaml contains kind is ConfigMap and that yaml file needs to be passed to role/another playbook as parmeter,i tried with below code, it is not working,
- debug:
var: item.path
when: lookup('file',item.path ) | from_yaml_all | list | selectattr('kind', '==', 'ConfigMap') | list | length > 0
with_items: "{{ yaml_path_files.files }}"
i am facing the below error,
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'lookup('file',item.path ) | from_yaml | list | selectattr('kind', '==', 'ConfigMap') | list | length > 0' failed. The error was: error while evaluating conditional (lookup('file',{{ filename }} ) | from_yaml | list | selectattr('kind', '==', 'ConfigMap') | list | length > 0): 'dict object' has no attribute 'kind'\n\nThe error appears to be in '/cygdrive/c/senthil/Ansible/Ansibleusecase4/EKSartifactvalidationV2.yml': line 14, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n # with_items: "{{ yaml_path_files.files }}"\n - debug:\n ^ here\n"}
kindly help me to achive it in ansible

Preliminary note: Your question is missing a complete playbook to be 100% sure of the problem. Edit your question with more info, possibly with an MCVE, if this is not your exact issue.
Your find tasks looks for files on your remote target. The file lookup, as all other lookups (see notes in the lookup plugins documentation), reads files locally on your ansible controller. In other words, you're looking for a file which does not exists.
You have to fetch or slurp the files from the remote target to get their content.
Here is a quickly written untested sample to put you on track. Please note on the last task that item.item.path is not a typo. item.item references the preceding item loop when registering the new var yaml_files_content (i.e. each yaml_path_files.files[*] from the preceding task, debug the full item to see for yourself)
- hosts: all
tasks:
- name: Find .yml files
find:
paths: /temp/
patterns: '*.yml,*.yaml'
recurse: yes
register: yaml_path_files
- name: Slurp file contents from remote
slurp:
src: "{{ item.path }}"
loop: "{{ yaml_path_files.files }}"
register: yaml_files_contents
- name: Show path for files containing ConfigMaps
debug:
var: item.item.path
loop: "{{ yaml_files_contents.results }}"
when: item.content | b64decode | from_yaml_all | list | selectattr('kind', '==', 'ConfigMap') | list | length > 0

Related

How to register and capture only the output that meets a failed condition criteria in ansible playbook?

I am trying to set up a playbook as follows where I need to send an email notification when my task fails. The alert with email notification as such works as expected, however I am unable to capture the output just for the failed conditions and currently the way I am registering gives me all results including the variables (file paths in this example) where the condition does not fail.
---
- hosts: test-box
tasks:
- name: alert on failure
block:
- name: generate a list of desired file paths
find:
paths: /base/sdir
recurse: yes
file_type: file
patterns: "abrn.*.dat"
use_regex: yes
register: file_paths
- name: check if file stopped updating
vars:
msg: |
"{{ item }}"
"{{ ansible_date_time.epoch }}"
"{{ item.stat.mtime|int }}"
"{{ ( (ansible_date_time.epoch|int - item.stat.mtime|int) / 60 ) | int }} min"
with_items: "{{ ts.results }}"
fail:
msg: |
"{{ msg.split('\n') }}"
register: failed_items ### -> HOW TO REGISTER ONLY THE FILE PATHS (RESULTS) WHERE THIS FAIL CONDITION IS MET??
when: ( (ansible_date_time.epoch|int - item.stat.mtime|int) / 60 ) | int > 2
rescue:
- name: email notification
mail:
host: localhost
port: 25
from: A
to: B
subject: TASK FAILED
body: |
Failed subdirs: {{ failed_items }} ## This gives me all results including those where the failed condition is not met
delegate_to: localhost
...
In the body of the email, I want to capture only the file paths where the mtime condition is met but currently I get all file paths.
Any suggestions on how I can filter to capture the output only for matching condition?
Thanks.
You should use set_fact, this way you will assign a list variable only if your when condition is true.
---
- hosts: localhost
become: false
vars:
path: "{{ '%s/bin' | format(lookup('env', 'HOME')) }}"
time: "{{ 2 * 60 }}" # 2 mins
tasks:
- name: Find files in a defined path
find:
paths: "{{ path }}"
register: _result
- name: Add files not modified for longer time than defined to list
set_fact:
stale_files: "{{ stale_files | default([]) + [item.path] }}"
loop: "{{ _result.files }}"
when: ((ansible_date_time.epoch | float) - item.mtime) > (time | float)
- name: Show stale files
debug:
msg: "{{ stale_files }}"
Well you could use also another approach, ie. make loop with filtering (for me selectattr filter did not work with lt test, thus json_query filter should work, see Can't compare attribute to a number. Error: "not supported between instances of 'AnsibleUnsafeText' and 'int'").

In Ansible ,can we loop through list of files and match content in file using lineinfile module

I have to find all config.xml on a server and produce the list on a given server.
Once we registered the files list, i have to check the content in each file on the list using Ansible
I tried to derive the paths for all config.xml
register them and print the list
Added the registered variable into lineinfile path
##Derive Config.xml path
- name: Find the location of xml file
shell: find {{ wlx_mount_point }} -maxdepth 1 -name {{xml_file}} | rev | cut -d '/' -f3- | rev
register: wlx_domain_home
ignore_errors: yes
- debug:
msg: "{{ wlx_domain_home.stdout_lines|list }}"
- name: check domain home directory exists
stat:
path: "{{ wlx_domain_home |list }}"
ignore_errors: true
- debug:
msg: "{{ wlx_domain_home.stdout_lines|list }}"
- name: "Ensure Logging Settings in config.xml"
lineinfile:
line: "{{ item.line }}"
regexp: "{{ item.regexp }}"
path: "{{ wlx_domain_home.stdout_lines|list }}/config/config.xml"
state: present
backrefs: yes
register: config.xml.Logging
with_fileglob: "{{ wlx_domain_home.stdout_lines|list }}/config/config.xml"
with_items:
- line: "<logger-severity>Info</logger-severity>"
regexp: "^logger-severity.*"
Expected results are , it has to look for lines in each file and loop through the list. ` Its printing the list and not able to find the content
"_ansible_ignore_errors": true, "msg": "Destination
[u'/appl/cmpas9/user_projects/pte-ipscasws',
u'/appl/bbb/user_projects/qa-ucxservices_bkp',
u'/appl/app/user_projects/weiss_apps12',
u'appl/view/user_projects/weiss_apps12_oldbkp',
u'appl/voc/user_projects/qa-voc']/config/config.xml does not exist !"
}
This is how i fixed the issue. Now it gives output
- name: find all weblogic domain paths
shell: find /tech/appl -type f -name config.xml
register: wlx_domain_config
- debug:
var: wlx_domain_config.stdout_lines
name: "Ensure allow-unencrypted-null-cipher Encryption Settings in config.xml"
shell: grep -i "" {{ item }}
with_items: "{{ wlx_domain_config.stdout_lines }}"
register: allowunencrypted
debug:
var: allowunencrypted.stdout_lines

Playbook where item.stat.exist not working

I have created playbook which will run on a remote host and check whether the files exist or not. I want to extract the only files which are not present on the remote host. But my playbook giving all paths whether they are present or not.
Playbook:-
- name: Playbook for files not present on remote hosts
hosts: source
gather_facts: false
vars:
Filepath: /opt/webapps/obiee/oracle_common/inventory/ContentsXML/comps.xml
tasks:
- name: Getting files location path
shell: grep -i "COMP NAME" {{ Filepath }} |sed 's/^.*INST_LOC="//'|cut -f1 -d'"' | sed '/^$/d;s/[[:blank:]]//g' // extract files from comps.xml
register: get_element_attribute
- name: check path present or not
stat:
path: "{{ item }}"
with_items:
- "{{ get_element_attribute.stdout_lines }}"
register: path_output
- name: path exists or not
set_fact:
path_item: "{{ item }}" # here i am getting the output as expected that's files not present on remote host
with_items: "{{ path_output.results }}"
register: final_output
when: item.stat.exists == False
- debug:
var: final_output # giving both output i.e. files present and absent
- name: Create a fact list
set_fact:
paths: "{{ final_output.results | map(attribute='item.item') | list }}" # i have add this condition " item.stat.exists == False' inside this stmt
- name: Print Fact
debug:
var: paths
The issue resolved by using below command:
- name: Create a fact list
set_fact:
paths: "{{ final_output.results | selectattr('item.stat.exists', 'equalto', false) | map(attribute='item.item') | list }}"
register: config_facts
The following query should get all the file names which don't exsist on the remote host and store them in the fact 'paths':
- name: Create a fact list
set_fact:
paths: "{{ final_output | json_query(query)}}"
vars:
query: "results[?(#._ansible_item_label.stat.exists==`false`)]._ansible_item_label.item"

Load Nested Files With host_group_vars Plugin From Custom Locations

I am trying to create ansible role to generate kubernetes deployment with ansible. I want all customization to be stored in var files and be loaded automatically during group_vars and host_vars loading. I end up with following nested structure of files:
group_vars/my_group.yml:
applications:
application_foo: "{{ lookup('template', inventory_dir + '/applications/application_foo.yml') | from_yaml }}"
applications/application_foo.yml:
deployments:
application_foo:
deployment:
{{ lookup('template', inventory_dir + '/deployment_vars/deployment_foo.yml') | to_nice_yaml | indent(6)}}
replicas: 1
deployment_vars/deployment_foo.yml:
containers:
conteiner_bar:
container:
{{ lookup('template',inventory_dir + '/container_vars/container_bar.yml') | to_nice_yaml | indent(6) }}
container_baz:
container:
{{ lookup('template',inventory_dir + '/container_vars/container_baz.yml') | to_nice_yaml | indent(6) }}
container_vars/container_bar.yml:
container_vars/container_baz.yml:
image_url: "https://example.com/image_bar"
cpu_requests: "1"
memory_requests: "512Mi"
cpu_limit: "2"
memory_limit: "1024Mi"
readinessProbe:
exec:
command:
- "sh"
- "-c"
- "true"
playbooks/test-playbook.yml:
- hosts: my_group
gather_facts: False
tasks:
- debug:
msg: "{{ applications['application_foo']['deployments']['application_foo']['deployment']['containers']['container_bar']['cpu_requests'] }}"
delegate_to: localhost
When I run playbook it complains that deployment var is of str type and therefore can't have children objects:
fatal: [localhost]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'str object' has no attribute 'containers'\n\nThe error appears to have been in '/tmp/so_question/playbooks/test-playbook.yml': line 4, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - debug:\n ^ here\n"
}
I want layers to be reusable for cases where container or deployment are required by another application so don't want to put everything in one file.
Any suggestions how to fix this issue or how to achieve same nested loading in other way?
UPD: when I cut string in playbook to end on deployment (like this msg: "{{ applications['application_foo']['deployments']['application_foo']['deployment'] }}") it gives following line which after newline substitution does not look like valid yaml:
containers:
conteiner_bar:
container:
\"image_url: \\\"https://example.com/image_bar\\\"\
cpu_requests: \\\"1\\\"\
memory_requests:\\
\\ \\\"512Mi\\\"\
cpu_limit: \\\"2\\\"\
memory_limit: \\\"1024Mi\\\"\
readinessProbe:\
exec:\
\\
\\ command:\
- \\\"sh\\\"\
- \\\"-c\\\"\
- \\\"true\\\"\
\"
container_baz:
container:
\"image_url: \\\"https://example.com/image_bar\\\"\
cpu_requests: \\\"1\\\"\
memory_requests:\\
\\ \\\"512Mi\\\"\
cpu_limit: \\\"2\\\"\
memory_limit: \\\"1024Mi\\\"\
readinessProbe:\
exec:\
\\
\\ command:\
- \\\"sh\\\"\
- \\\"-c\\\"\
- \\\"true\\\"\
\"
I wondering why indent filter does not indents all lines. Am I using it correctly?
Ended up with following workaround: Moved all files containing defaults to group_vars/all to make them loaded automatically and used vars lookup for nesting:
$ find group_vars/
group_vars/
group_vars/all
group_vars/all/10_container_bar.yml
group_vars/all/20_deployment_foo.yml
group_vars/all/30_application_foo.yml
group_vars/all/10_container_baz.yml
group_vars/my_group.yml
group_vars/my_group.yml:
applications:
application_foo: "{{ lookup('vars', 'defaults_application_application_foo') }}"
group_vars/all/30_application_foo.yml:
defaults_application_application_foo:
deployments:
application_foo:
deployment: "{{ lookup('vars', 'defaults_deployment_deployment_foo') }}"
replicas: 1
And so on...
Still not clear why template thing didn't work. Leaving this question open.

Adding dynamic value into jinja2 expression

I have an ansible playbook that looks like this:
---
- hosts: localhost
vars:
filename: "me-0.0.1"
tasks:
- name: get filenames
find:
paths: /home/vagrant/test
patterns: 'me\-[\d]\.[\d]\.[\d]\.jar'
use_regex: yes
register: fn
- name: remove old files
file:
path: "{{ item }}"
state: absent
with_items:
"{{ (fn.files | sort(attribute='ctime')) | map(attribute='path') | reject('search', 'me-0.0.1') | list }}"
the object here is to get the value stored in the filename variable into the expression in with items replacing the hardcoded me-0.0.1 but I am not sure how to go about that.
So my question here is how do I substitute an ansible variable into this expression so that the filter is dynamic.
To answer my own question the answer is this:
{{ (fn.files | sort(attribute='ctime')) | map(attribute='path') | reject('search', (filename)) | list }}"
Meaning you drop the literal quotes and include the externally registered variable in brackets, I hope this helps others also.

Resources