Pulling the value of the MAC address and adding the ":" character - regex

I have the following value:
stdout_lines: [
[
"iso.3.6.1.2.1.17.4.3.1.1.0.80.121.102.104.4 \"00 50 79 66 68 04 \""
],
[
"iso.3.6.1.2.1.17.4.3.1.1.0.80.121.102.104.6 \"00 50 79 66 68 06 \""
],
[
"iso.3.6.1.2.1.17.4.3.1.1.0.80.121.102.104.8 \"00 50 79 66 68 08 \""
]
]
I want to get the MAC address values in the following form:
00:50:79:66:68:04
00:50:79:66:68:06
00:50:79:66:68:08
That's what I'm trying to do in my playbook:
- set_fact:
mac: "{{ stdout_lines|first|regex_replace(_regex, _replace)|trim }}"
vars:
_regex: '.*"(.*)"'
_replace: '\1'
- set_fact:
matched: "{{ matched|d([]) + [item[2:]|join(':')] }}"
with_items:
- "{{ mac }}"
It turns out some nonsense. What am I doing wrong?

flatten the data then map regex_replace and trim. For example
- set_fact:
mac: "{{ stdout_lines|
flatten|
map('regex_replace', _regex, _replace)|
map('trim')|
map('split')|
map('join', ':')|
list }}"
vars:
_regex: '.*"(.*)"'
_replace: '\1'
gives
mac:
- 00:50:79:66:68:04
- 00:50:79:66:68:06
- 00:50:79:66:68:08

try this playbook: flatten the list, trap the right part of string with regex_search, trim and replace the space by :
- name: "make this working"
hosts: localhost
vars:
mac:
- - iso.3.6.1.2.1.17.4.3.1.1.0.80.121.102.104.4 "00 50 79 66 68 04 "
- - iso.3.6.1.2.1.17.4.3.1.1.0.80.121.102.104.6 "00 50 79 66 68 06 "
- - iso.3.6.1.2.1.17.4.3.1.1.0.80.121.102.104.8 "00 50 79 66 68 08 "
tasks:
- set_fact:
result: "{{ result | d([]) + [reg] }}"
loop: "{{ mac | flatten }}"
vars:
reg: "{{ item | regex_search('(\\d\\d ){6}') | trim | replace(' ',':')}}"
- debug:
var: result
result:
ok: [localhost] => {
"result": [
"00:50:79:66:68:04",
"00:50:79:66:68:06",
"00:50:79:66:68:08"
]
}

Related

display message when regex does not match (task failed)

I want to display a message if the task failed. This is that I currently have. I can't seem to store the stdout
- hosts: localhost
vars:
regexp: '(?i)^\s*PASS_MAX_DAYS\s+[0-9]+\s*$'
pass_max_days: "{{ out.stdout_lines|
select('match', regexp)|first|split|last }}"
tasks:
- command: cat /tmp/login.defs
register: out
- debug:
var: pass_max_days
- debug:
msg: Less than 90 days
when: pass_max_days|int < 90
- debug:
msg: Exactly 90 days
when: pass_max_days|int == 90
- debug:
msg: More than 90 days
when: pass_max_days|int > 90
- debug:
msg: Does not match!
when: pass_max_days.rc != 0
Use block & rescue.
The task under rescue will be executed, only if a task in the block fails.
- hosts: localhost
vars:
regexp: '(?i)^\s*PASS_MAX_DAYS\s+[0-9]+\s*$'
pass_max_days: "{{ out.stdout_lines|
select('match', regexp)|first|split|last }}"
tasks:
- block:
- name: cat the file
command: cat /etc/login.defs
register: out
- name: line search
debug:
var: pass_max_days
- name: line check
debug:
msg: "{% if pass_max_days|int < 90 %}Less than 90 days{% elif pass_max_days|int == 90 %}Exactly 90 days{% elif pass_max_days|int > 90 %}More than 90 days{% endif %}"
rescue:
- name: Failure message
debug:
msg: "VARIABLE IS NOT DEFINED!"
Success:
TASK [cat the file] *************************************
changed: [localhost]
TASK [line search] **************************************
ok: [localhost] => {
"pass_max_days": "99999"
}
TASK [line check] ***************************************
ok: [localhost] => {
"msg": "More than 90 days"
}
Failure:
TASK [cat the file] *************************************
TASK [line search] **************************************
ok: [localhost] => {
"pass_max_days": "VARIABLE IS NOT DEFINED!"
}
TASK [line check] ***************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: {{ out.stdout_lines| select('match', regexp)|first|split|last }}: Unable to look up a name or access an attribute in template string ({{ out.stdout_lines| select('match', regexp)|first|split|last }}).\nMake sure your variable name does not contain invalid characters like '-': descriptor 'split' for 'str' objects doesn't apply to a 'AnsibleUndefined' object\n\nThe error appears to be in '/root/ansible-test/max.yml': line 16, column 8, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: line check\n ^ here\n"}
TASK [Failure message] *********************************************************
ok: [localhost] => {
"msg": "VARIABLE IS NOT DEFINED!"
}
If you want the tasks in block not to be executed at failure, use check_mode in lineinfile module to check if the line exists.
Register an output for the task, which will have the attribute changed as true, if the line is found, but it will not make any changes to the file.
- hosts: localhost
gather_facts: no
vars:
regexp: '(?i)^\s*PASS_MAX_DAYS\s+[0-9]+\s*$'
pass_max_days: "{{ out.stdout_lines | select('match', regexp)|first|split|last }}"
tasks:
- name: Check if line exists
lineinfile:
name: /etc/login.defs
regexp: "{{ regexp }}"
line: ""
state: present
check_mode: yes
register: check
- name: Failure message
debug:
msg: "VARIABLE IS NOT DEFINED!"
when: check is not changed
- block:
- name: cat the file
command: cat /etc/login.defs
register: out
- name: line check
debug:
msg: "{% if pass_max_days|int < 90 %}Less than 90 days{% elif pass_max_days|int == 90 %}Exactly 90 days{% elif pass_max_days|int > 90 %}More than 90 days{% endif %}"
when: check is changed

ansible - list with item index as hash

I'm trying to add list element index as hash, preferably without using with_indexed_items because result is to be filtered later.
I'd like to convert this:
vmware_disks:
- datastore: "datastores0"
size_gb: 24
- datastore: "datastores0"
size_gb: 397
storage_policy: "vSAN standard cluster RAID-1"
to something like this:
vmware_disks:
- datastore: "datastores0"
size_gb: 24
nr: 0
- datastore: "datastores0"
size_gb: 397
storage_policy: "vSAN standard cluster RAID-1"
nr: 1
So far I was able to:
using "{{ vmware_disks | zip(range(0,2)) }}"
[
{
"datastore": "datastores0",
"size_gb": 24
},
0
],
[
{
"datastore": "datastores0",
"size_gb": 397,
"storage_policy": "vSAN standard cluster RAID-1"
},
1
]
"{{ vmware_disks | map('combine', {'nr':0}) }}"
[
{
"datastore": "datastores0",
"nr": 0,
"size_gb": 24
},
{
"datastore": "datastores0",
"nr": 0,
"size_gb": 397,
"storage_policy": "vSAN standard cluster RAID-1"
}
]
Please help me to combine both to get desired result.
You can use a set_fact to create another dictionary and add the index in:
- set_fact:
indexed_vmware_disks: >-
{{
indexed_vmware_disks | default([])
+ [ item | combine( { 'nr': index } ) ]
}}
loop: "{{ vmware_disks }}"
loop_control:
index_var: index
Then if you really need the dictionary to have the exact name it had before, you can just reassign it in another set_fact.
So an example playbook:
- hosts: all
gather_facts: no
vars:
vmware_disks:
- datastore: "datastores0"
size_gb: 24
- datastore: "datastores0"
size_gb: 397
storage_policy: "vSAN standard cluster RAID-1"
tasks:
- set_fact:
indexed_vmware_disks: >-
{{
indexed_vmware_disks | default([])
+ [ item | combine( { 'nr': index } ) ]
}}
loop: "{{ vmware_disks }}"
loop_control:
index_var: index
- set_fact:
vmware_disks: "{{ indexed_vmware_disks }}"
- debug:
var: vmware_disks
Would yield the recap:
PLAY [all] *******************************************************************************************************
TASK [set_fact] **************************************************************************************************
ok: [localhost] => (item={'datastore': 'datastores0', 'size_gb': 24})
ok: [localhost] => (item={'datastore': 'datastores0', 'size_gb': 397, 'storage_policy': 'vSAN standard cluster RAID-1'})
TASK [set_fact] **************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************
ok: [localhost] =>
vmware_disks:
- datastore: datastores0
nr: 0
size_gb: 24
- datastore: datastores0
nr: 1
size_gb: 397
storage_policy: vSAN standard cluster RAID-1
PLAY RECAP *******************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible merge list with dict

I have below playbook in which I search for all vars starting with static_routes__ and then merge them.
---
- hosts: localhost
gather_facts: no
vars:
static_routes__host:
management:
- address: '0.0.0.0/0'
next_hop: '192.168.0.1'
static_routes__lab:
management:
- address: '1.1.1.1/32'
next_hop: '192.168.0.1'
static_routes__test:
test:
- address: '8.8.8.8/32'
next_hop: '192.168.2.1'
tasks:
- set_fact:
static_routes: "{{ static_routes | default({}) | combine(lookup('vars', item, default={}), recursive=True, list_merge='append') }}"
loop: "{{ query('varnames', 'static_routes__') }}"
- name: Output static_routes
debug:
var: static_routes
The above will result in:
TASK [Output static_routes] ***************************************************************************************************************************************************************
ok: [localhost] => {
"static_routes": {
"management": [
{
"address": "0.0.0.0/0",
"next_hop": "192.168.0.1"
},
{
"address": "1.1.1.1/32",
"next_hop": "192.168.0.1"
}
],
"test": [
{
"address": "8.8.8.8/32",
"next_hop": "192.168.2.1"
}
]
}
}
However the merge_list_ is only available in Ansible version > 2.9, which is currently not available to me due to company policies. I'm looking for a way to replicate above output in Ansible version =< 2.9.
With the below task I'm able to sort of reproduce it but it only allows one list item.
- set_fact:
static_routes: "{{ static_routes | default({}) | combine({vrf: route | default([]) }) }}"
loop: "{{ query('varnames', 'static_routes__') }}"
vars:
vrf: "{{ lookup('dict', lookup('vars', item)).key }}"
route: "{{ lookup('dict', lookup('vars', item)).value }}"
subnet: "{{ lookup('dict', lookup('vars', item)).value.0.address }}"
next_hop: "{{ lookup('dict', lookup('vars', item)).value.0.next_hop }}"
- name: Output static_routes
debug:
var: static_routes
Found the solution:
- set_fact:
static_routes_list: "{{ static_routes_list | default({}) | combine({item: lookup('vars', item)}) }}"
loop: "{{ query('varnames', 'static_routes__') }}"
- set_fact:
static_routes: "{{ static_routes|
default({})|
combine({item.0: item.1|json_query('[].value')|flatten | unique})
}}"
loop: "{{ static_routes_list|
dict2items|
json_query('[*].value')|
map('dict2items')|list|flatten|
groupby('key')
}}"
This one seems simpler
- set_fact:
_list: "{{ _list|default([]) + [lookup('vars', item)] }}"
loop: "{{ query('varnames', 'static_routes__') }}"
- set_fact:
static_routes: "{{ static_routes|default({})|
combine({item.0: item.1|json_query('[].value')|flatten}) }}"
loop: "{{ _list|map('dict2items')|map('first')|groupby('key') }}"
gives
static_routes:
management:
- address: 0.0.0.0/0
next_hop: 192.168.0.1
- address: 1.1.1.1/32
next_hop: 192.168.0.1
test:
- address: 8.8.8.8/32
next_hop: 192.168.2.1

ansible identifying values in a list

The below code is attempting to find all the keys associated with the value HWIC-8A. I have tried a few different variations & I can't get the key to print, without doing something really long winded. As i'll be repeating this code with different values, i don't want to search each key/value pair individually within that list.
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
- name: Apply HWIC-8A Build
debug:
msg: "{{ item.key }}"
with_items: "{{ MODULES }}"
when: "{{ item.value }} == HWIC-8A"
Maybe that's something for you:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
tasks:
- debug: var=MODULES
- debug: msg="{{ MODULES | dict2items }}"
- debug: msg="{{ MODULES | dict2items | selectattr('value','match','HWIC-8A') | map(attribute='key')| list }}"
Then if you would like to have multiple matches, you could solve it with an MATCH list:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
Slot_1_SubSlot_3: HWIC-8C
Slot_1_SubSlot_2: HWIC-8C
MATCH:
- HWIC-8A
- HWIC-8C
tasks:
- debug:
msg: "{{ MODULES | dict2items | selectattr('value','match',item) | map(attribute='key')| list }}"
with_items: "{{ MATCH }}"
Output:
TASK [debug] ***********************************************************************************************************************************************************************************
Thursday 27 August 2020 15:08:10 +0200 (0:00:00.042) 0:00:02.037 *******
ok: [localhost] => (item=HWIC-8A) => {
"msg": [
"Slot_0_SubSlot_0",
"Slot_0_SubSlot_3"
]
}
ok: [localhost] => (item=HWIC-8C) => {
"msg": [
"Slot_1_SubSlot_3",
"Slot_1_SubSlot_2"
]
}
I would use use jinja templates to do it. Something like this:
- name: Apply HWIC-8A Build
debug:
msg: '{% for m in MODULES %}{% if MODULES[m] == "HWIC-8A" %}{{ m }} {% endif %}{% endfor %}'
Which will give you this:
ok: [localhost] => {
"msg": "Slot_0_SubSlot_0 Slot_0_SubSlot_3 "
}
There is probably a fancy way using filters as well.

ansible join list

I run into a problem joining a list in ansible. Please look at the following playbook excerpt:
- name: determine how much time we have left
set_fact:
time_left1: "{{ cmd_output.stdout | regex_search(time_left_regex1, '\\1', '\\2') }}"
time_left2: "{{ cmd_output.stdout | regex_findall(time_left_regex2, '\\1') }}"
vars:
time_left_regex1: 'Remaining Time: ([0-9]+ Minutes) and ([0-9]+ Seconds)'
time_left_regex2: 'Remaining Time: (?:([0-9]+ Minutes) and )?([0-9]+ Seconds)'
- debug:
msg: "{{ time_left1 }}"
- debug:
msg: "{{ time_left1 | join(' ') }}"
- debug:
msg: "{{ time_left2 }}"
- debug:
msg: "{{ time_left2 | join(' ') }}"
When I run this playbook I get:
ok: [localhost] => {
"msg": [
"11 Minutes",
"48 Seconds"
] }
ok: [localhost] => {
"msg": "11 Minutes 48 Seconds" }
ok: [localhost] => {
"msg": [
[
"11 Minutes",
"48 Seconds"
]
] }
ok: [localhost] => {
"msg": [
"11 Minutes",
"48 Seconds"
] }
To me it seems that the regex_search returned I list that I can join to a simple string, and that regex_findall creates a nested list. Is this correct? And if so, how can convert the output to a string similar to the way regex_search works?
PS: for more information about the contents of cmd_output please take a look at my previous question
flatten the nested list. For example
- debug:
msg: "{{ time_left2|flatten|join(' ') }}"