Ansible Double Colon in with_items - ansible

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

Related

Ansible to print the variable value having another variable inside it

In a.yml file, I have stored data like below
---
Server:
"Node1" : ["Node1", "Owner1", "ID1"]
"Node2" : ["Node2", "Owner2", "ID2"]
Now, in xyz.yml playbook, I tried to debug a variable as below and I am passing the Node_Name in commandline (ansible-playbook xyz.yml -e "Node_Name=Node1")
---
- name: "Print Variable value"
hosts: all
gather_facts: no
vars:
Node_Name: Node
ID_Name: "{{ Server.{{ Node_Name }}[2] }}"
tasks:
- name: "Print the id"
debug:
msg:
- "The id is {{ ID_Name }}"
But this is failing with error - Template error while templating string :expected name or number
Can someone please help to fix this and let me know how can I get the ID printed as output. Here expected output is ID1
You never nest {{...}} markers.
Recall that to access a nested variable. the syntax Server.Node1 is exactly equivalent to Server["Node1"]. The second syntax allows us to make use of variables (and string interpolation) on the key, so we can write:
Server[Node_Name]
In other words:
- name: "Print Variable value"
hosts: all
gather_facts: no
vars:
Node_Name: Node
ID_Name: "{{ Server[Node_Name][2] }}"
tasks:
- name: "Print the id"
debug:
msg:
- "The id is {{ ID_Name }}"

JMESPath filtering on response

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

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)

The error was: 'dict object' has no attribute 'ansible_facts

I am iterating over yml file and filtering and keeping those microservice metadata in the list which is in the command line argument passed
ansible-playbook -i inventory/inventory sp-deployer.yml --ask-vault-pass --extra-vars '{"microservices_list":[iwan,csrservice]}'
Finally I need these three value from the yml file based on the criteria mentioned above. I have created ansible sp-deployer.yml for this purpose. I have used set_facts for creating dynamic list. First list works fine but the moment I create second one it fails.
name: "ms_service"
port: "830"
streams: "noti,jk-noti"
vars.yml
version: 1
name: user
jobs:
ns1:
ip: "1.1.1.1"
ns_version: "4.2"
f_packs:
- f-pack-1:
name: "pack1"
microservices:
- microservice-1:
name: "ms_service"
port: "830"
streams: "noti,jk-noti"
- microservice-2:
name: "ms_service1"
port: "830"
streams: "noti1,jk-noti1"
- f-pack-2:
name: "pack4"
microservices:
- microservice-1:
name: "ms_service3"
port: "830"
streams: "noti,jk-noti3"
- microservice-2:
name: "ms_service4"
port: "830"
streams: "noti,jk-noti4"
- microservice-3:
name: "ms_service5"
port: "830"
streams: "noti,jk-noti5"
Script:sp-deployer.yml
---
- hosts: localhost
vars_files:
- ./vars.yml
sudo: yes
tasks:
- name: Reading vars.yml file and preparing list of microservice with its metadata
set_fact: foo_item="{{ item.1 }}"
register: result
with_subelements:
- "{{ jobs.ns1.f_packs }}" ###item.0
- microservices ###item.1
- name: make first list
set_fact: foo="{{ result.results | map(attribute='ansible_facts.foo_item') | list }}"
- name: print register
debug: msg="{{ item }}" verbosity=3
with_items:
- "{{ foo }}"
- name: Filtering micro service list with match found from command line input
when: item[0].name == item[1]
set_fact: foo_item1="{{ item.0 }}"
register: result_final
with_nested:
- "{{ foo }}"
- "{{ microservices_list }}"
- name: make a list
set_fact: foo1="{{ result_final.results | map(attribute='ansible_facts.foo_item1') | list }}"
ERROR
TASK [make a list] *************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'dict object' has no attribute 'ansible_facts'\n\nThe error appears to have been in '/home/user/ansible/sp-deployer1.yml': line 40, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: make a list\n ^ here\n"}
to retry, use: --limit #/home/user/ansible/sp-deployer1.retry
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=1
Friendly advice: always check registered variables with debug if you bump on such errors.
The reason for your error is that set_fact don't yield ansible_facts dict if the loop iteration is skipped.
And I see you have when statement in your loop.
To overcome this error, you should select only those loop iterations, that have ansible_facts dict defined:
- name: make a list
set_fact: foo1="{{ result_final.results | selectattr('ansible_facts','defined') | map(attribute='ansible_facts.foo_item1') | list }}"

iteration using with_items and register

Looking for help with a problem I've been struggling with for a few hours. I want to iterate over a list, run a command, register the output for each command and then iterate with debug over each unique registers {{ someregister }}.stdout
For example, the following code will spit out "msg": "1" and "msg": "2"
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: result
with_items: "{{ numbers }}"
- debug: msg={{ item.stdout }}
with_items: "{{ result.results }}"
If however, I try and capture the output of a command in a register variable that is named using with_list, I am having trouble accessing the list or the elements within it. For example, altering the code slightly to:
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: "{{ item.name }}"
with_items: "{{ numbers }}"
- debug: var={{ item.name.stdout }}
with_items: "{{ numbers }}"
Gives me:
TASK [debug]
> ******************************************************************* fatal: [localhost]: FAILED! => {"failed": true, "msg": "'unicode
> object' has no attribute 'stdout'"}
Is it not possible to dynamically name the register the output of a command which can then be called later on in the play? I would like each iteration of the command and its subsequent register name to be accessed uniquely, e.g, given the last example I would expect there to be variables registered called "first" and "second" but there aren't.
Taking away the with_items from the debug stanza, and just explicitly defining the var or message using first.stdout returns "undefined".
Ansible version is 2.0.2.0 on Centos 7_2.
Thanks in advance.
OK so I found a post on stackoverflow that helped me better understand what is going on here and how to access the elements in result.results.
The resultant code I ended up with was:
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: echo_out
with_items: "{{ numbers }}"
- debug: msg="item.item={{item.item.name}}, item.stdout={{item.stdout}}"
with_items: "{{ echo_out.results }}"
Which gave me the desired result:
"msg": "item.item=first, item.stdout=1"
"msg": "item.item=second, item.stdout=2"
I am not sure if I understand the question correctly, but maybe this can help:
- debug: msg="{{ item.stdout }}"
with_items: echo_out.results
Please note that Ansible will print each item and the msg both - so you need to look carefully for a line that looks like "msg": "2".

Resources