Remove duplicate lines from Ansible variable - filter

I need to achieve two things.
Remove duplicate entries from ansible variable called install_loc
Remove any empty blank lines from install_loc.
Below is how install_loc variable is constructed.
- set_fact:
install_loc: "{{ install_loc | default('') + item.split( )[4] | dirname + '\n' }}"
loop: "{{ fdet }}"
Below are the contents of install_loc after writing it to a file.
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
As you can see the variable as newlines as well as duplicate entries.
Desired output should be as below. Note: Order does not matter:
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
I tried
- set_fact:
install_loc: "{{ install_loc | unique }}"
But i get distorted text like below:
set([u'\n', u'/', u'.', u'1', u'0', u'3', u'2', u'C', u'E', u'G', u'F', u'I', u'N', u'a', u'c', u'e', u't', u'i', u'm', u'o', u'o', u'n', u's', u's', u'r', u'u', u't', u'x'])
Can you please suggest ?

The solution:
- set_fact:
install_loc: "{{ install_loc.split('\n') | unique | select | list }}"
Explanation:
install_loc.split('\n') - Split the install_loc by newlines
unique - Remove duplicates
select - Get rid of empty or null values
list - Convert from a Python "generator" to a list
If you want a single string (as the input) replace list by join('\n').

Related

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

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"
}

how to remove 'u' from list output in ansible

I'm trying to do a simple list to show all ports associated with a particular vlan and have this output to a csv.
Code works but can't figure out how can I get around removing the 'u' and the [] associated with the line 'InterfaceID' in the output. I know the array need to be converted to a string just wondering how I can do this without modifying the code too much.
Thank you in advance for any tips.
Code:
- name: parse output
set_fact:
vlan_output: "{{vlan_info.stdout[0] | parse_cli_textfsm(parse_template)}}"
- name: write lines to file
copy:
content: "{{ ['InterfaceID','VlanID','NAME'] | zip([item.INTERFACES,item.VLAN_ID,item.NAME]) | map('join', ', ') | join('\n') }}"
dest: "output.csv"
with_items: "{{vlan_output}}"
- debug:
var: vlan_output
Debug of vlan_output:
"vlan_output": [
{
"INTERFACES": [
"Gi1/0/2",
"Gi1/0/5",
"Gi1/0/7"
],
"NAME": "test",
"STATUS": "active",
"VLAN_ID": "10"
}
]
Excel Output:
InterfaceID, [u'Gi1/0/2', u'Gi1/0/5', u'Gi1/0/7']
VlanID, 10
NAME, test
While you cannot directly do that, once you upgrade version of python used by ansible to 3.x you get rid of these. Only Python 2.x does print unicode strings with u prefix. On 3 all are unicode anyway and the prefix is not printed anymore.
Keep in mind that the u is part of printing the data, is not part of the data itself.
If you try to parse as string an array that was converted to a string you are already doing something wrong.
Since you have a list of Interfaces, you can use the join filter to "join" the list items into a string. From the question its not clear if you'd like to join them with space or comma, but a small change, such as - item.INTERFACES|join(', ') will give (comma separated) - Gi1/0/2, Gi1/0/5, Gi1/0/7.
Example:
- name: write lines to file
copy:
dest: output.csv
content: "{{ ['InterfaceID','VlanID','NAME'] | zip([item.INTERFACES|join(', '),item.VLAN_ID,item.NAME]) | map('join', ', ') | join('\n') }}"
with_items: "{{ vlan_output }}"
Produces:
InterfaceID, Gi1/0/2, Gi1/0/5, Gi1/0/7
VlanID, 10
NAME, test

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"
]

Iterate over unique list created from string split

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.

How can I use jinja2 to join with quotes in Ansible?

I have an ansible list value:
hosts = ["site1", "site2", "site3"]
if I try this:
hosts | join(", ")
I get:
site1, site2, site3
But I want to get:
"site1", "site2", "site3"
Why not simply join it with the quotes?
"{{ hosts | join('", "') }}"
Ansible has a to_json, to_nice_json, or a to_yaml in it's filters:
{{ some_variable | to_json }}
{{ some_variable | to_yaml }}
Useful if you are outputting a JSON/YAML or even (it's a bit cheeky, but JSON mostly works) a python config file (ie Django settings).
For reference: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#filters-for-formatting-data
The previous answer leaves "" if the list is empty. There is another approach that may be more robust, e.g. for assigning the joined list as a string to a different variable (example with single quotes):
{{ hosts | map("regex_replace","(.+)","\'\\1\'") | join(',')}}
From a json file file_name.json array like this
"hosts": [
"site1",
"site2",
"site3"
]
Set_fact from a json file
- name: Set variables from parameters file
set_fact:
vars_from_json: "{{ lookup('file', 'file_name.json') | from_json }}"
Use join with double quote and comma, and wrap all around in double quote
- name: Create host list
set_fact:
host_list: "{{ '\"' + vars_from_json.hosts | join('\"'', ''\"') + '\"' }}"

Resources