How to replace several characters in an Ansible variable's value? - ansible

I have a variable called joomlaversion which I get using json_query. The value of joomlaversion is 4.0.2 but I am trying to swap the dots with dashes so it becomes 4-0-2
How can I substitute dots for dashes in this ansible variable value?
I am using Ansible 2.9.6
Here is what I have tried.
---
- name: Download JSON content
uri:
url: https://api.github.com/repos/joomla/joomla-cms/releases
return_content: yes
register: jsoncontent
- name: Get latest version of Joomla from the tag using contains
set_fact:
joomlaversion: "{{ jsoncontent.json | to_json | from_json |json_query(jmesquery)|json_query(jmesquery2) }}"
vars:
jmesquery: "[? (draft==`false` && prerelease==`false`)]"
jmesquery2: "[?name.contains(#, 'Joomla! 4')].tag_name|[0]"
- name: Replace the dots with dashes in Joomla version
set_fact:
joomlaversion2: "{{ joomlaversion }} | replace('.', '-')"
#joomlaversion2: '{{ joomlaversion | regex_findall("\\."),("\\-") }}'
Rather than changing the dots to dashes it is appending | replace('.','-') on to the variable value, so it becomes "4.0.2 | replace ('.', '-')"
Perhaps I could use filters as is mentioned at https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#manipulating-strings
If I could split it using split(".") then join it again afterwards perhaps?

If I could split it using split(".") then join it again afterwards perhaps?
You could very well do that!
E.g.:
- set_fact:
joomlaversion2: "{{ joomlaversion.split('.') | join('-') }}"
Or, use regex_replace, which will find a pattern and replace it with hyphen.
# since . matches any char, we need to escape it with \
- set_fact:
joomlaversion2: "{{ joomlaversion | regex_replace('\.', '-') }}"

you have to add {{ }} and escape . with \:
- set_fact:
joomlaversion: "4.0.2"
- set_fact:
joomlaversion2: "{{ joomlaversion | regex_replace('\\.', '-') }}"
- debug:
var: joomlaversion2
result:
ok: [localhost] => {
"joomlaversion2": "4-0-2"
}

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 to escape ? in Ansible task?

Is it possible to include a JSON query in the actual task? All of the examples show using an additional var for the query.
Taking the example from Ansible Filters
- name: "Display all ports from cluster1"
debug:
var: item
loop: "{{ domain_definition | json_query(server_name_cluster1_query) }}"
vars:
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port"
Converted to:
- name: "Display all ports from cluster1"
debug:
var: item
loop: "{{ domain_definition | json_query(domain.server[?cluster=='cluster1'].port) }}"
as is it returns:
FAILED! => {"reason": "Syntax Error while loading YAML.\n found unknown escape character '?'
I have tried to add an escape backslash before the question mark, but it still fails with:
"template error while templating string: unexpected char '?'
It's possible to use back-ticks ` . For example
- name: "Display all ports from cluster1"
debug:
var: item
loop: "{{ domain_definition | json_query('domain.server[?cluster==`cluster1`].port') }}"
(not tested)
Ok it appears you can escape double quote the query and it will work!
- name: "Display all ports from cluster1"
debug:
var: item
loop: "{{ domain_definition | json_query(\"domain.server[?cluster=='cluster1'].port\") }}"
To step a little bit aside from escape hell, I like to take advantage of yaml scalar blocks
- name: "Display all ports from cluster1"
vars:
my_query: >-
domain.server[?cluster=='cluster1'].port
debug:
var: item
loop: "{{ domain_definition | json_query(my_query) }}"
You don't have to escape anything this way (works for quotes and backslash as well, nice for regexps)

How to filter a dictionary using regex on value

I need to filter a dictionary in ansible based on regex matching it's values.
mydict:
some_value: /my/path
another_value: 2345
anotherpath: /my/other/path
What I tried is:
set_fact:
newvar: "{{ mydict | dict2items | selectattr('value', 'match', '^/my/.+$') | list }}"
But what i get is:
expected string or buffer
How can I filter all values based on part of their string for using them later in with_items?
The issue you currently have is that the regex match cannot handle integers. This will solve your issue, though it's slightly more verbose:
- set_fact:
file_list: []
- set_fact:
file_list: "{{ file_list + [item.value] }}"
when: item.value|string | match('^/my/.+$')
loop: "{{ mydict | dict2items }}"
If you can ensure that all values will always be strings, then you could use:
- set_fact:
newvar: "{{ mydict | dict2items | selectattr('value', 'match', '^/my/.+$') | map(attribute='value') | list }}"
Thanks #matt-p for clarifying :-) I didn't know this was actually caused by the match function itself. Is this more like an issue I should report to the Ansible community?
BTW I changed your code a little bit to fit the actual Ansible standard (the when condition is using deprecated syntax):
- set_fact:
file_list: []
- set_fact:
file_list: "{{ file_list + [item.value] }}"
when:
- item.value is string
- item.value is match('^/my/.+$')
with_items: "{{ mydict | dict2items }}"

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.

Ansible parses strings as lists if the format is compatible, how to escape?

Using ansible, I need to put a list of hosts in line in a file like so:
["127.0.0.1", "127.0.0.2", "127.0.0.3"]
But whenever I achieve this format, ansible interprets it as a list and the content of the file is this pythonic version:
['127.0.0.1', '127.0.0.2', '127.0.0.3']
Here's my attempts to get it out thus far:
---
- hosts: all
gather_facts: False
tasks:
- set_fact:
myhosts:
- 127.0.0.1
- 127.0.0.2
- 127.0.0.3
# This comes out as a list, I need a string
- set_fact:
var: "[ \"{{ myhosts | join('\", \"')}}\" ]"
- debug: var=var
# This comes out as a string, but I need no underscore on it
- set_fact:
var: "_[ \"{{ myhosts | join('\", \"')}}\" ]"
- debug: var=var
# This also comes out as a list
- set_fact:
var: >
[ "{{ myhosts | join('", "')}}" ]
- debug: var=var
# Also parsed as a list
- set_fact:
var: "{{ myhosts | to_json }}"
- debug: var=var
# ansible-playbook -i "localhost," this_file.yml
There are some filters that prevent Ansible template engine from doing string evaluation.
This list of filters is stored in STRING_TYPE_FILTERS setting.
In Ansible 2.1 it contains: string, to_json, to_nice_json, to_yaml, ppretty, json.
So, you can do this:
- lineinfile: line="{{ myhosts | to_json }}" dest=output.txt
This will add ["127.0.0.1", "127.0.0.2", "127.0.0.3"] line to the file.
And don't believe debug's output when dealing with exact string formatting.
Always use copy: content="{{ string_output_to_test | string }}" dest=test.txt and check file contents to be sure.
debug: var=myvar will always template with evaluation, so your string will always be printed as a list.
debug: msg="{{ myvar | string }}" will print myvar as JSON encoded string.

Resources