I've recently started using YAML and jinja2. I'm having trouble understanding why I need to reference the entire structure of my YAML mapping in the jinja2 template.
I have the following YAML file
---
PROVIDERS:
PROV1:
int: ge-0/1/1
ipv4: 10.0.1.1/30
PROV2:
int: ge-0/1/2
ipv4: 10.0.1.2/30
and this is my jinja2 template
{%- for provider in PROVIDERS %}
{{ provider }}
{{ PROVIDERS[provider].int }} <-- why not provider.int
{{ PROVIDERS[provider].ipv4 }} <-- why not provider.ipv4
{%- endfor %}
Parsing with pyyaml gives me the (expected) output
PROV2
ge-0/1/2
10.0.1.2/30
PROV1
ge-0/1/1
10.0.1.1/30
However why must I use PROVIDERS[provider].int
? provider.int
doesn't work.
Additionally, I was wondering if I could make this a list of mappings instead of a nested mapping:
---
PROVIDERS:
- PROV1:
int: ge-0/1/1
ipv4: 10.0.1.1/30
- PROV2:
int: ge-0/1/2
ipv4: 10.0.1.2/30
I've tried to do so, but the jinja2 template no longer produced the desired output.
There are two things to consider here:
Answering point 1 is easy:
>>> import yaml
>>> from pprint import pprint
>>> p1 = yaml.load("""
... ---
... PROVIDERS:
... PROV1:
... int: ge-0/1/1
... ipv4: 10.0.1.1/30
... PROV2:
... int: ge-0/1/2
... ipv4: 10.0.1.2/30
... """)
>>> pprint(p1)
{'PROVIDERS': {'PROV1': {'int': 'ge-0/1/1', 'ipv4': '10.0.1.1/30'},
'PROV2': {'int': 'ge-0/1/2', 'ipv4': '10.0.1.2/30'}}}
You have a dictionary with a single item whose key is 'PROVIDERS'
, and whose value is a dictionary with the keys 'PROV1'
and 'PROV2'
, each of whose values is a further dictionary. That's a more deeply nested structure than you need (more on which later), but now that we can see your data structure, we can work out what's going on with your template.
This line:
{%- for provider in PROVIDERS %}
iterates over the keys of PROVIDERS
(which, given your output, is obviously the second-level nested dictionary which is the value for the key 'PROVIDERS'
in your top-level dictionary). Since what you're iterating over are the keys, you then need to use those keys to get at the associated values:
{{ PROVIDERS[provider].int }}
{{ PROVIDERS[provider].ipv4 }}
A more straightforward YAML document for your purposes would be this:
---
- id: PROV1
int: ge-0/1/1
ipv4: 10.0.1.1/30
- id: PROV2
int: ge-0/1/2
ipv4: 10.0.1.2/30
Note that we've ditched the redundant single-item mapping, and replaced the second-level mapping of mappings with a list of mappings. Again, we can check that:
>>> p2 = yaml.load("""
... ---
... - id: PROV1
... int: ge-0/1/1
... ipv4: 10.0.1.1/30
... - id: PROV2
... int: ge-0/1/2
... ipv4: 10.0.1.2/30
... """)
>>> pprint(p2)
[{'int': 'ge-0/1/1', 'ipv4': '10.0.1.1/30', 'id': 'PROV1'},
{'int': 'ge-0/1/2', 'ipv4': '10.0.1.2/30', 'id': 'PROV2'}]
Here's how your template could use this data structure:
{%- for provider in PROVIDERS %}
{{ provider.id }}
{{ provider.int }}
{{ provider.ipv4 }}
{%- endfor %}
Obviously you'll need to modify the code which supplies PROVIDERS
to the template, since it's now the top-level list represented by the entire YAML document, rather than a dictionary nested inside it.