I'm using
ansible 2.9.27
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /bin/ansible
python version = 2.7.5 (default, May 27 2022, 11:27:32) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
Pursuant to my question here: Ansible set_fact list using jinja , I am trying to create a list which is derived from a list, but with an element removed: if it contains "default" at the beginning of the list element string, remove it. But why doesn't the regex work properly? The sticking point is the beginning of string operator.
Here is my list:
[ u'default via 10.13.99.1 dev bond0.99',
u'10.13.101.0/23 dev p1p1.101',
u'128.1.66.227 via 10.13.201.1 dev p1p1.201' ]
It's called found_routes. When I try to filter out the first entry, the default route, I attempt:
- name: Populate appended route list
set_fact:
found_added_routes: "{{ found_routes | select('search', ' via ') | reject('regex', '^default') | list }}"
That doesn't work. But this does:
- name: Populate appended route list
set_fact:
found_added_routes: "{{ found_routes | select('search', ' via ') | reject('regex', '^..default') | list }}"
...what are those two invisible characters that my regex is finding?
Here is how I create output:
- name: Debug found_routes
debug:
msg: "Found route list: {{ item }}"
loop: "{{ found_routes }}"
- name: Debug found_added_routes
debug:
msg: "Found: {{ item }}"
loop: "{{ found_added_routes }}"
And here's the output from the successful set_fact:
TASK [networks : Debug found_route_list] **********************************************************************************************************************
ok: [server01.example.com] => (item=default via 10.13.99.1 dev bond0.99) => {
"msg": "Found route list: default via 10.13.99.1 dev bond0.99"
}
ok: [server01.example.com] => (item=10.13.101.0/24 dev p1p1.101) => {
"msg": "Found route list: 10.13.101.0/24 dev p1p1.101"
}
ok: [server01.example.com] => (item=128.1.66.227 via 10.13.101.1 dev p1p1.101) => {
"msg": "Found route list: 128.1.66.227 via 10.13.101.1 dev p1p1.101"
}
TASK [networks : Debug found_added_routes] **********************************************************************************************************************
ok: [server01.example.com] => (item=10.13.101.0/24 dev p1p1.101) => {
"msg": "Found: 10.13.101.0/24 dev p1p1.101"
}
ok: [server01.example.com] => (item=128.1.66.227 via 10.13.101.1 dev p1p1.101) => {
"msg": "Found: 128.1.66.227 via 10.13.101.1 dev p1p1.101"
}
But as you can see, in order to get rid of that first "default" route, I needed to use ^..default instead of ^default and I'm at a loss to understand why.
OK, because of the jinja2 magic that I did in my original question, which was to do this:
- name: Populate appended route list
set_fact:
found_added_routes: |-
{%- set blah = [] %}
{%- for line in found_route_list %}
{%- set _ = blah.append(line) %}
{%- endfor %}
{{ blah }}
...the output was a string that had elements literally like this: u'default via 10.13.99.1 dev bond0.99'
So the string begins not with default... but rather with a literal 'u' and tick mark, sa: u'default...
The first two characters of the string are u and '. Not d and e as I was expecting.
My mistake!
Related
Here is the output that I'm trying to parse:
hostname#show bgp vrf vrfname summary | i 1.1
BGP Route Distinguisher: 1.1.1.1:0
BGP router identifier 1.1.1.1, local AS number 2222
1.1.1.3 0 64512 349608 316062 896772 0 0 2w4d 1
I have the following regex that succesfully matches just the last line. Now I need to split that line and view the last index. In this case it is "1", but I will want to fail if that value is "0".
- name: debug test
debug:
msg: "{{show_bgp_sessions.data | regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*')}}"
I tried adding a split in a couple different formats at the end of the "msg" line so that I can grab the last index to compare it in the failed_when statement:
msg: "{{show_bgp_sessions.data | regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*') | split(' ')}}"
But I'm getting the following error msg:
"template error while templating string: no filter named 'split'. String:
I've also tried to use a few different forms of "ends_with" to verify the last index in the string as I've used that a lot in my python experience, but I can't get it to work in ansible.
I can't create a new task to parse the data and perform the split seperately because I need to run this verification through a loop.
When you select the line, reverse the string, and split the first item. For example
msg: "{{ (my_line|reverse).split()|first }}"
Possibly the regex provided by #Thefourthbird is a better solution.
But for your issue at hand, this is caused by the fact that there is indeed no filter split in Jinja, see the list there: https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-builtin-filters.
The reason why there is no such a filter is simple: split() is a function of the Python String, and since Jinja is Python, you can just use it as is.
Also mind that, since regex_findall is meant for multiple matches, you'll have to select the first element of the list, for example, with the filter first.
So your message ends up being:
msg: >-
{{
(
show_bgp_sessions.data
| regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*')
| first
).split()
}}
Given the playbook:
- hosts: all
gather_facts: no
vars:
show_bgp_sessions:
data: |
hostname#show bgp vrf vrfname summary | i 1.1
BGP Route Distinguisher: 1.1.1.1:0
BGP router identifier 1.1.1.1, local AS number 2222
1.1.1.3 0 64512 349608 316062 896772 0 0 2w4d 1
tasks:
- debug:
msg: >-
{{
(
show_bgp_sessions.data
| regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*')
| first
).split()
}}
Gives the recap:
TASK [debug] ***************************************************************
ok: [localhost] => {
"msg": [
"1.1.1.3",
"0",
"64512",
"349608",
"316062",
"896772",
"0",
"0",
"2w4d",
"1"
]
}
I have a variable dir_lst_raw in an ansible playbook whose value is a list as shown below:
"dir_lst_raw": [
"/path1/dir1/user",
"/path2/dir2/admin",
"/path3/dir3/user.ansible_backup_2020-03-16",
"/path1/dir1/config.ansible_backup_2020-03-16",
"/path2/dir2/dir3/somefile"
]
I need to remove all the lines containing .ansible_backup_ and save to another variable as a list.
I've googled for regex and tried to not match the pattern with the select filter as below:
- set_fact:
dir_lst: "{{ dir_lst_flt_r | select('match','(^.ansible_backup_)+') | list }}"
but the new variable dir_lst turned out as an empty list. I am expecting dir_lst as below:
"dir_lst_raw": [
"/path1/dir1/user",
"/path2/dir2/admin",
"/path2/dir2/dir3/somefile"
]
Could somebody please suggest how can I get it done?
Q: "Remove all the lines containing .ansible_backup_"
A: The task below does the job
- set_fact:
dir_lst: "{{ dir_lst_raw|reject('match', my_regex)|list }}"
vars:
my_regex: '^(.*)\.ansible_backup_(.*)$'
- debug:
var: dir_lst
gives
dir_lst:
- /path1/dir1/user
- /path2/dir2/admin
- /path2/dir2/dir3/somefile
i got a problem how to consume the following command output in Ansible
basically im trying to get the list of Active Directory OUs and loop over that list to search for specific name. My script works well when multiple OUs exists but i have an issue when only single OU exists. Explained below
tasks:
- name: PS - Pull System OUs from AD
win_command: powershell -
args:
stdin: "Get-ADOrganizationalUnit -LDAPFilter '(name=*)' -SearchScope 1 -SearchBase 'OU=SYSTEMS,DC=domain,DC=int' -Server domain.int | select-object name | ConvertTo-json"
become: yes
register: ou_reg_out
- name: Select Systems OU
block:
- name: Set list of standardized OUs as facts
set_fact:
ou_reg_list: "{{ ou_reg_out.stdout }}"
- name: Set System OU for system
set_fact:
ou_name: "OU={{item.name}}"
loop: "{{ ou_reg_list }}"
when: (item.name|upper) == (srv_type|upper)
when: ou_reg_out.stdout|length != 0
basically i need to be able to loop over the ou_reg_out.stdout.
It works when command returns multiple OUs as ou_reg_out.stdout returns list:
ou_reg_out.stdout:
- { name: OU1 }
- { name: OU2 }
issue is when only single OU exists , command doesnt return the list
ou_reg_out.stdout:
{ name: OU1 }
Any idea how to workaround this problem ?
Test the type of the variable and branch the code.
json_query filter helps to select the items from the list. Then ternary helps to conditionally select the value. The value of the first item that matches the condition is used. Defaults to 'NOTFOUND'.
For example the play bellow for both versions of ou_reg_list
- hosts: localhost
vars:
ou_reg_list:
- { name: OU1 }
- { name: OU2 }
# ou_reg_list:
# { name: OU1 }
srv_type: 'ou1'
tasks:
- set_fact:
ou_name: "OU={{ (ou_reg_list.name == srv_type|upper)|
ternary( ou_reg_list.name, 'NOTFOUND') }}"
when: ou_reg_list is mapping
- block:
- set_fact:
ou_names: "{{ ou_reg_list|json_query(query) }}"
vars:
query: "[?name=='{{ srv_type|upper }}'].name"
- set_fact:
ou_name: "OU={{ (ou_names|length > 0)|
ternary( ou_names.0, 'NOTFOUND') }}"
when: ou_reg_list is not mapping
- debug:
var: ou_name
gives
"ou_name": "OU=OU1"
I'm not even quite sure how to describe what I'm trying to do!
So I'll just jump straight in.
I have a hosts file :
[jvbservers]
jvb0 ansible_host=serverA
jvb1 ansible_host=serverB
etc...
And I want a list of the servers in the jvbservers group. What I've come up with so far feels a bit OTT.
set_fact:
jvb_names: "{{ jvb_names|default([]) + [ hostvars[groups.jvbservers[item|int]].ansible_host ] }}"
with_sequence: start=0 end="{{groups.jvbservers|length-1}}"
Gives me a result like I want :
ok: [localhost] => {
"jvb_names": [
"serverA",
"serverB"
]
}
Is this sensible? It feels like there should be a neater way to do something like :
hostvars[groups.jvbservers[*]].ansible_host
(Where "*" would be a pattern matching all valid options. Obvs that particular syntax doesn't exist!)
You may want to check extract filter:
{{ groups['jvbservers'] | map('extract', hostvars, 'ansible_host') | list }}
Have you tried this?:
- name: show all the hosts matching jvbservers
debug:
msg: "{{ item }}"
with_inventory_hostnames:
- all: jvbservers
https://docs.ansible.com/ansible/devel/plugins/lookup/inventory_hostnames.html
I have a variable in my playbook that has a number of values separated by commas. At this point I am not sure if the variable is a string or a list. I believe from the output below the "[]" indicate a list.
Variable populated
-set_fact:
snap_master_01: "{{ ec2_snapshot_facts.snapshots |
selectattr(tags.HostName, equalto, ICINGA2_MASTER_1.tag_value) |
sort(attribute=start_time) | reverse | map(attribute=snapshot_id) | list }}"
- name: Print snapshot ID's
debug:
msg:
- "{{ snap_master_01 }}"`
Gives the following output:
`TASK [Print snapshot ID's] ******************************************************************************** ***********************
task path: /home/r_ansible/playbooks/backup_aws.yml:252
ok: [172.16.1.58] => {
"changed": false,
"msg": [
[
"snap-04c88ef6XXXXXXXXX",
"snap-0bd5785fXXXXXXXXX",
"snap-045e0f4bXXXXXXXXX",
"snap-055fda51XXXXXXXXX",
"snap-03759206XXXXXXXXX"
]
]
}`
I would like to delete the last 3 values. What is the best way of achieving this in Ansible?
To manipulate lists in Ansible, you can use Python slices.
In your case snap_master_01[:-3] will give you all but last three elements.