Iterate over unique list created from string split - ansible

I have a list of domains:
---
domains:
- foo.bar
- baz.bar
I have tasks, where I need to iterate over these domains, extract domain tail, make an unique list of these tails and then create directories named by these tails.
Something like this but AFAIK jinja2 doesn't support list comprehension:
---
- name: Ensure all directories exist
file:
path: "/tmp/sandbox/{{ item }}"
state: directory
with_items: "[domain.split('.')[-1] for domain in domains] | unique"
Is it possible or do I need to create a custom jinja2 filter? Will this work?
---
- name: Ensure all directories exist
file:
path: "/tmp/sandbox/{{ item }}"
state: directory
with_items: "{{ domain_tails | my_custom_filter }}"
Thanks!

You can achieve this with map and regex_search:
- debug: msg="Ensure dir for {{ item }}"
with_items: "{{ domains | map('regex_search','\\w+$') | list | unique }}"
\w+$ match the last word (i.e. domain tail after dot).
Note that the slash is escaped, because it is inside double quotes.

Related

Filter list in Ansible for files ending with j2

I am trying to filter a list but have only limited success.
The list is hardcoded (all_files) and filtering starting with anchor '^' works just fine.
But what I really need is identify all *.j2 files
My Ansible version is 2.9.25
- name: Execute any command on localhost
hosts: localhost
gather_facts: false
tasks:
- set_fact:
all_files: [
"ansible_vars/dev-deploy-vars.yml",
"Precompiled/Application/config.js.j2",
"Precompiled/Application/web.config.j2"
]
- set_fact:
files_starting_with_pattern: "{{ j2_files | select('match', '^Precompiled') | list }}"
files_ending_with_pattern: "{{ j2_files | select('match', 'j2$') | list }}"
Any idea? All I need is a list of jinja2 files (which can be empty)
Thanks!
Your problem is that you're using match instead of search to look for a pattern at the end of the string. From the documentation:
match succeeds if it finds the pattern at the beginning of the string, while search succeeds if it finds the pattern anywhere within string. By default, regex works like search, but regex can be configured to perform other tests as well, by passing the match_type keyword argument. In particular, match_type determines the re method that gets used to perform the search. The full list can be found in the relevant Python documentation here.
So you want:
- name: Execute any command on localhost
hosts: localhost
gather_facts: false
vars:
all_files:
- ansible_vars/dev-deploy-vars.yml
- Precompiled/Application/config.js.j2
- Precompiled/Application/web.config.j2
tasks:
- set_fact:
files_starting_with_pattern: >-
{{ all_files | select("match", "^Precompiled") | list }}
files_ending_with_pattern: >-
{{ all_files | select("search", "j2$") | list }}
- debug:
var: files_starting_with_pattern
- debug:
var: files_ending_with_pattern
You could alternately use match if you modify your search pattern:
- set_fact:
files_starting_with_pattern: >-
{{ all_files | select("match", "^Precompiled") | list }}
files_ending_with_pattern: >-
{{ all_files | select("match", ".*j2$") | list }}

How do I check a list of directories from a file, but some entries have multiple directories separated by a comma

I have a text file that looks like this:
hints_directory: /opt/cassandra/hints
data_file_directory: /opt/cassandra/data,/opt/cassandra/data2
commitlog_directory: /opt/cassandra/commitlog
cdc_raw_directory: /opt/cassandra/cdc_raw
saved_caches_directory: /opt/cassandra/saved_caches
tmp_directory: /opt/cassandra/tmp
jna_directory: /opt/cassandra/jna
dump_directory: /opt/cassandra/dump
I want to read that file and check for the existence of each directory using ansible. The tricky part comes on the second line, where the line contains 2 directory paths. How do I get ansible to read each line of the file as in the example below and check for the existence of each directory.
Here is what I have thus far, but it stumbles on the data_file_directory due to having multiple path values for the single line. I've tried to split the line entry, but that doesn't solve the issue.
Thoughts?
- name: Ensure the directories exist
file:
path: "{{item.split(':')[1].split(' ')[1].split(',')}}"
state: directory
with_lines: cat <<pathtofile>>/directories.txt
Read the variables from the file into a dictionary and convert the strings to lists, e.g.
- include_vars:
file: directories.txt
name: dirs_str
- set_fact:
dirs_list: "{{ dict(_keys|zip(_vals)) }}"
vars:
_keys: "{{ dirs_str.keys()|list }}"
_vals: "{{ dirs_str.values()|map('split', ',')|list }}"
gives
dirs_list:
cdc_raw_directory:
- /opt/cassandra/cdc_raw
commitlog_directory:
- /opt/cassandra/commitlog
data_file_directory:
- /opt/cassandra/data
- /opt/cassandra/data2
dump_directory:
- /opt/cassandra/dump
hints_directory:
- /opt/cassandra/hints
jna_directory:
- /opt/cassandra/jna
saved_caches_directory:
- /opt/cassandra/saved_caches
tmp_directory:
- /opt/cassandra/tmp
Select and flatten the values, e.g.
- set_fact:
allDirectories: "{{ dirs_list.values()|flatten }}"
gives
allDirectories:
- /opt/cassandra/hints
- /opt/cassandra/data
- /opt/cassandra/data2
- /opt/cassandra/commitlog
- /opt/cassandra/cdc_raw
- /opt/cassandra/saved_caches
- /opt/cassandra/tmp
- /opt/cassandra/jna
- /opt/cassandra/dump
, or use the expression directly in the loop
- name: Ensure the directories exist
file:
path: "{{ item }}"
state: directory
loop: "{{ dirs_list.values()|flatten }}"
For anyone else that may have a similar exercise, I have figured out how to do this:
- set_fact:
allDirectories: []
- set_fact:
allDirectories: "{{ item.split(':')[1].split(' ')[1].split(',') + allDirectories}}"
with_lines: cat <<pathtofile>>/directories.txt
- name: Ensure the directories exist
file:
path: "{{item}}"
state: directory
with_items: "{{allDirectories}}"

Mapping a list of Debian packages in a specific order of 2nd list with Ansible

I'm trying to map a list of files to a list of filenames. The goal is to install Debian files in specific order (base off the list of names). I can retrieve the list of files with a shell command and register them to a list. The goal is to generate a list of filenames in the order of my predefined name list. Then install them in that order.
ms2Num.stdout_lines is the list of files from the shell command:
# use List -1 to find the file names for the deb files.| grep
- name: Find the needed deb files
shell: "ls -1 {{ DestDir | join }}/ms2install/ms2install/ | grep {{ ms2Num.stdout_lines[0] | join }}"
register: ProviderDebList
This task generates a list ProviderDebList.stdout_lines.
Here is the list of files:
"stdout_lines": [
"ms2-apache_1.6.1.8~20160324_amd64.deb",
"ms2-ctps_1.6.1.8~20160324_amd64.deb",
"ms2-desert_1.6.1.8~20160324_amd64.deb",
"ms2-provider_1.6.1.8~20160324_amd64.deb",
"ms2-w3gui_1.6.1.8+1~20160324_amd64.deb"
]
Mapping task
- name: Display files in order from MS2-list
debug:
msg: "File name: {{ ms2Num.stdout_lines | regex_search( item | string ) | string }}"
loop: "{{ MS2Packages }}"
Running the mapping task I get:
But I get an error:
fatal: [10.0.2.25]: FAILED! => {
"msg": "Unexpected templating type error occurred on (File name: {{ ms2Num.stdout_lines | regex_search( item | string ) | string }}): expected string or buffer"
My knowledge of the Ansible's filters fairly basic so these errors are still a pain to parse. I know that I'm missing something, but what?
Goal:
The goal is to generate a list of filenames in the order of the MS2Packages.
I want to take my name list and map the filenames order it.
Here is the list to base the installation order to:
MS2Packages:
- ms2-desert
- ms2-ctps
- ms2-apache
- ms2-w3gui
- ms2-provider
]
The resulting list should be:
"stdout_lines": [
"ms2-desert_1.6.1.8~20160324_amd64.deb",
"ms2-ctps_1.6.1.8~20160324_amd64.deb",
"ms2-apache_1.6.1.8~20160324_amd64.deb",
"ms2-w3gui_1.6.1.8+1~20160324_amd64.deb"
"ms2-provider_1.6.1.8~20160324_amd64.deb",
]
Some of the later files use the earlier ones as dependencies so I need to install them in a specific order.
Working task: (Solved)
# print the files names in order of the deb list
- name: Create the list files in order from MS2-list
set_fact:
OrderProviderList: "{{ OrderProviderList | default([]) + ProviderDebList.stdout_lines | map('regex_search', '.*' + order + '.*') | select('string') | list }}"
loop: "{{ MS2Packages }}"
loop_control:
loop_var: order
I can now loop through this list and install the needed packages.
Your idea to apply regex_search filter on your list of packages names (ProviderDebList) in a loop of "order list" (MS2Packages) is actually a good one. What you miss is that you have to apply the regex_search filter with map (which will apply the filter on each item of your list).
So here is the working solution:
- name: List sorting
hosts: localhost
gather_facts: no
vars:
MS2Packages:
- ms2-desert
- ms2-ctps
- ms2-apache
- ms2-w3gui
- ms2-provider
ProviderDebList:
- ms2-apache_1.6.1.8~20160324_amd64.deb
- ms2-ctps_1.6.1.8~20160324_amd64.deb
- ms2-desert_1.6.1.8~20160324_amd64.deb
- ms2-provider_1.6.1.8~20160324_amd64.deb
- ms2-w3gui_1.6.1.8+1~20160324_amd64.deb
tasks:
- name: Print package in the right order
debug:
msg: " - {{ ProviderDebList | map('regex_search', '.*'+order+'.*') | select('string') | list }}"
loop: "{{ MS2Packages }}"
loop_control:
loop_var: order
The tasks below
- set_fact:
my_pkg: "{{ my_pkg|default([]) +
ms2Num.stdout_lines|
select('search', item)|
list }}"
loop: "{{ MS2Packages }}"
- debug:
var: my_pkg
give
"my_pkg": [
"ms2-desert_1.6.1.8~20160324_amd64.deb",
"ms2-ctps_1.6.1.8~20160324_amd64.deb",
"ms2-apache_1.6.1.8~20160324_amd64.deb",
"ms2-w3gui_1.6.1.8+1~20160324_amd64.deb",
"ms2-provider_1.6.1.8~20160324_amd64.deb"
]

How to encapsulate ansible filters?

I have an ansible variable that contains a list of win_uri responses (created by loop).
I want to create a dictionary where each single response body (json) contains a value (title) that I want to use as a key and another one as a value (id).
Right now I am lost.
My current implementation ignores the json - which obviously does not work:
- name: populate folder dictionary
set_fact:
app_folders: "{{ app_folders | default({}) | combine({item.jsonContent.title : item.id}) }}"
with_items: "{{ response.results }}"
I know, that it is possible to read JSON into a variable with the from_json - but I do not know how to combine it with the above code.
If I got your question right, try:
- name: populate folder dictionary
set_fact:
app_folders: "{{ app_folders | default({}) | combine({(item.jsonContent|from_json).title : item.id}) }}"
with_items: "{{ response.results }}"

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