JMESPath filtering on response - ansible

I try to filter to get lldp-remote-system-name when lldp-remote-system-name contains slc1.
But I get the error:
Error in jmespath.search in json_query filter plugin:\n'in ' requires string as left operand, not NoneType
Tasks:
- name: get system information
juniper_junos_rpc:
rpc: get-lldp-neighbors-information
register: response
- name: Get remote system name
set_fact:
lldp_interface: "{{ response.parsed_output | to_json | from_json | json_query(interface) }}"
vars:
interface: '"lldp-neighbors-information"."lldp-neighbor-information"[?contains("lldp-remote-system-name","slc1")]."lldp-remote-system-name"'
- name: Print response
debug:
msg:
- "{{ lldp_interface }}"
Response
{
"lldp-neighbors-information": {
"lldp-neighbor-information": [
{
"lldp-local-parent-interface-name": "ae1",
"lldp-local-port-id": "et-0/0/50",
"lldp-remote-chassis-id": "22:22:22:22:22:22",
"lldp-remote-chassis-id-subtype": "Mac address",
"lldp-remote-port-description": "las1-router-1:et-0/0/50",
"lldp-remote-system-name": "las1-router-1"
},
{
"lldp-local-parent-interface-name": "ae0",
"lldp-local-port-id": "xe-0/0/1",
"lldp-remote-chassis-id": "11:11:11:11:11:11",
"lldp-remote-chassis-id-subtype": "Mac address",
"lldp-remote-port-description": "slc1-router-1-xe-0/0/1",
"lldp-remote-system-name": "slc1-router-1"
}
]
}
}

In JMESPath, double quotes are not string delimiters, they serve a specific purpose: they delimit an identifier that have special characters:
An identifier can also be quoted. This is necessary when an identifier has characters not specified in the unquoted-string grammar rule. In this situation, an identifier is specified with a double quote, followed by any number of unescaped-char or escaped-char characters, followed by a double quote.
Source: https://jmespath.org/specification.html#identifiers
If you want to have a raw string littoral, use back ticks instead: `.
So, your JMESPath query should be — split on multiple lines to make it more readable:
interface: >-
"lldp-neighbors-information"
."lldp-neighbor-information"[?
contains("lldp-remote-system-name",`slc1`)
]
."lldp-remote-system-name"
Given the task:
- debug:
msg: "{{ json | json_query(interface) }}"
vars:
interface: >-
"lldp-neighbors-information"
."lldp-neighbor-information"[?
contains("lldp-remote-system-name",`slc1`)
]
."lldp-remote-system-name"
json:
lldp-neighbors-information:
lldp-neighbor-information:
- lldp-local-parent-interface-name: ae1
lldp-local-port-id: et-0/0/50
lldp-remote-chassis-id: 22:22:22:22:22:22
lldp-remote-chassis-id-subtype: Mac address
lldp-remote-port-description: las1-router-1:et-0/0/50
lldp-remote-system-name: las1-router-1
- lldp-local-parent-interface-name: ae0
lldp-local-port-id: xe-0/0/1
lldp-remote-chassis-id: 11:11:11:11:11:11
lldp-remote-chassis-id-subtype: Mac address
lldp-remote-port-description: slc1-router-1-xe-0/0/1
lldp-remote-system-name: slc1-router-1
This yields:
ok: [localhost] =>
msg:
- slc1-router-1

no need to use jmepath:
- name: testplaybook jinja2
hosts: localhost
gather_facts: no
vars:
response:
lldp-neighbors-information:
lldp-neighbor-information:
- lldp-local-parent-interface-name: ae1
lldp-local-port-id: et-0/0/50
lldp-remote-chassis-id: 22:22:22:22:22:22
lldp-remote-chassis-id-subtype: Mac address
lldp-remote-port-description: las1-router-1:et-0/0/50
lldp-remote-system-name: las1-router-1
- lldp-local-parent-interface-name: ae0
lldp-local-port-id: xe-0/0/1
lldp-remote-chassis-id: 11:11:11:11:11:11
lldp-remote-chassis-id-subtype: Mac address
lldp-remote-port-description: slc1-router-1-xe-0/0/1
lldp-remote-system-name: slc1-router-1
tasks:
- name: Disp
debug:
msg: "{{ item['lldp-remote-system-name'] }}"
loop: "{{ response['lldp-neighbors-information']['lldp-neighbor-information'] }}"
loop_control:
label: "interface name: {{ item['lldp-local-parent-interface-name'] }}"
when: "'slc1' in item['lldp-remote-system-name']"
result:
skipping: [localhost] => (item=interface name: ae1)
ok: [localhost] => (item=interface name: ae0) => {
"msg": "slc1-router-1"
}

Related

Ansible regex replace in variable to cisco interface names

I'm currently working with a script to create interface descriptions based on CDP neighbor info, but it's placing the full names e.g., GigabitEthernet1/1/1, HundredGigabitEthernet1/1/1.
My regex is weak, but I would like to do a regex replace to keep only the first 3 chars of the interface name.
I think a pattern like (dredGigatbitEthernet|abitEthernet|ntyGigabitEthernet|etc) should work, but not sure how to put that into the playbook line below to modify the port value
nxos_config:
lines:
- description {{ item.value[0].port }} ON {{ item.value[0].host }}
E.g, I am looking for GigabitEthernet1/1/1 to end up as Gig1/1/1
Here is an example of input data:
{
"FastEthernet1/1/1": [{
"host": "hostname",
"port": "Port 1"
}]
}
Final play to make it correct using ansible net neighbors as the source
Thank you - I updated my play, adjusted for ansible net neighbors
- name: Set Interface description based on CDP/LLDP discovery
hosts: all
gather_facts: yes
connection: network_cli
tasks:
- debug:
msg: "{{ ansible_facts.net_neighbors }}"
- debug:
msg: >-
description
{{
item[0].port
| regex_search('(.{3}).*([0-9]+\/[0-9]+\/[0-9]+)', '\1', '\2')
| join
}}
ON {{ item.value[0].host }}"
loop: "{{ ansible_facts.net_neighbors | dict2items }}"
loop_control:
label: "{{ item.key }}"
Thanks for the input!
Given that you want the three first characters along with the last 3 digits separated by a slash, then the regex (.{3}).*([0-9]+\/[0-9]+\/[0-9]+) should give you two capturing groups containing those two requirements.
In Ansible, you can use regex_search to extract those groups, then join them back, with the help on the join Jinja filter.
Given the playbook:
- hosts: localhost
gather_facts: no
tasks:
- debug:
msg: >-
description
{{
item.key
| regex_search('(.{3}).*([0-9]+\/[0-9]+\/[0-9]+)', '\1', '\2')
| join
}}
ON {{ item.value[0].host }}"
loop: "{{ interfaces | dict2items }}"
loop_control:
label: "{{ item.key }}"
vars:
interfaces:
GigabitEthernet1/1/1:
- port: Port 1
host: example.org
HundredGigabitEthernet1/1/1:
- port: Port 2
host: example.com
This yields:
TASK [debug] ***************************************************************
ok: [localhost] => (item=eth0) =>
msg: description Gig1/1/1 ON example.org"
ok: [localhost] => (item=eth1) =>
msg: description Hun1/1/1 ON example.com"

Jinja2 expression for split , replace and join

In Ansible playbooks, We have the variable 'dns_name: xyz.abc.pqr.*.com' where as we have one template file called es_config.yml there the value of cname should be (cname: .abc.pqr..com)
How can we write jinja2 expression for this ?
dns_name: xyz.abc.com (Or) xyz.abc.pqr.***.com
cname: *.abc.com (Or) .abc.pqr.**.com (We have to use variable of dns_name)
Playbook
- hosts: elastic-search-servers
gather_facts: true
vars:
es_admin_hostname: test.develop123.com
tasks:
- name: split string
set_fact:
cname: "{{ es_admin_hostname.split('.') }} | first | replace('*')"
- name: debug
debug:
msg: "{{ cname[1:] }} is dns name"
Required Output
*.develop123.com
just change your split by regex_replaces:
- name: split string
set_fact:
cname: "{{ es_admin_hostname| regex_replace('^[^\\.]+', '*') }}"
- name: debug
debug:
msg: "{{ cname }} is dns name"
result:
ok: [localhost] => {
"msg": "*.develop123.com is dns name"
}
'^[^\\.]+' means trap all chars from beginning of string until i meet a dot and replace them by * (the \\ is needed because . is special char)

Issue using a variable inside api_filter with netbox.netbox.nb_lookup

I have a variable that is set from a Tower survey and I am using it to retrieve an associated IP address in netbox. I am not able to get it to match when I use square brackets and when I use {{ or ' or " everything is matched and my whole IPAM database is returned.
vars:
location: "{{ LOCATION }}"
c_description: "{{CIRCUIT_DESC}}"
prefix_length: "{{PREFIX}}"
tasks:
- name: "Print IP"
debug:
msg: "{{ query('netbox.netbox.nb_lookup', 'ip-addresses', api_filter= 'description=
[c_description]', api_endpoint='http://netbox', token='', validate_certs='False') }}"
Here is my output:
TASK [Print IP] ****************************************************************
ok: [localhost] => {
"msg": []
}
Solved in another forum using a tilda:
- name: "Print IP"
debug:
msg: "{{ query('netbox.netbox.nb_lookup', 'ip-addresses', api_filter='description=' ~ c_description,
api_endpoint='http://netbox.', token='', validate_certs='False') }}"

Ansible Double Colon in with_items

The first key of my host_var has a :. Like so,
---
openconfig-vlan:vlans:
vlan:
- vlan-id: '1001'
config:
vlan-id: 1001
name: test22
status: ACTIVE
However, I cannot seem to find a way to escape it so I can loop over the list within vlan.
Playbook
---
- name: Configure Devices via Native
hosts: ios
gather_facts: no
tasks:
- name: Create VLAN
ios_vlan:
vlan_id: "{{ item.config.vlan-id }}"
name: "{{ item.config.name }}"
state: present
with_items: "{{ openconfig-vlan:vlans['vlan'] }}"
Error
TASK [Create VLAN] ********************************************************************************************************************************************************************
fatal: [ios1]: FAILED! => {"msg": "template error while templating string: expected token 'end of print statement', got ':'. String: {{ openconfig-vlan:vlans['vlan'] }}"}
Any ideas? Thanks,
Q: "The first key of my host_var has a :. Like so,"
openconfig-vlan:vlans:
A: There are variables in in host_var no keys. Quoting from Creating valid variable names:
"Variable names should be letters, numbers, and underscores. Variables should always start with a letter."
There is only one idea available. Fix the syntax.
FWIW. For example, include the erroneous host_vars and put it into a valid variable. The play below
- hosts: localhost
tasks:
- include_vars:
file: vars-1-data.yml
name: test_var
- debug:
var: test_var['openconfig-vlan:vlans']
with the data
$ cat vars-1-data.yml
openconfig-vlan:vlans:
vlan:
- vlan-id: '1001'
config:
vlan-id: 1001
name: test22
status: ACTIVE
works as expected
"test_var['openconfig-vlan:vlans']": {
"vlan": [
{
"config": {
"name": "test22",
"status": "ACTIVE",
"vlan-id": 1001
},
"vlan-id": "1001"
}
]
}

Processing embedded lists inside dict variables in Ansible

I got the example code in Ansible:
- name: testing
hosts: localhost
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: port={{item.key}} value={{item.value}}
with_dict:
- "{{svc}}"
And this outputs to:
ok: [127.0.0.1] => (item=None) => {
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
}
ok: [127.0.0.1] => (item=None) => {
"msg": "port=https value=[u'tcp/443']"
}
What I would like to achieve is, when there is more than one element in list of values it would split like this:
- name=https, prot=tcp, port=443
- name=app_svc, prot=tcp, port=5543
- name=app_svc, prot=udp, port=5543
- name=app_svc, prot=tcp, port=3100
with_dict stanza only displays me a whole list and I couldn't find a way do do it differently. Is it possible to do it like that without reorganizing the var section? Thanks in advance for input.
To see the syntax errors run
# ansible-lint <YOUR-PLAYBOOK>
Correct syntax should be
- hosts: localhost
gather_facts: no
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: "port={{ item.key }} value={{ item.value }}"
with_dict: "{{ svc }}"
gives
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
"msg": "port=https value=[u'tcp/443']"
Change the loop
- name: print
debug:
msg: "name={{ item.0.key }},
prot={{ item.1.split('/')[0] }},
port={{ item.1.split('/')[1] }}"
loop: "{{ lookup('subelements', svc|dict2items, 'value') }}"
to get the format
"msg": "name=app_svc, prot=tcp, port=5543"
"msg": "name=app_svc, prot=udp, port=5543"
"msg": "name=app_svc, prot=tcp, port=3100"
"msg": "name=https, prot=tcp, port=443"
dict2items is available since version 2.6 . Without "dict2items" transform the data first. See below (not tested).
https:
- {key: 'https', value: ['tcp/443']}
- {key: 'app_svc', value: ['tcp/5543', 'udp/5543', 'tcp/3100']}

Resources