Basic Templating
The following exercises provide a basic introduction to Jinja2 templating.
To verify your solution, you can use an online Jinja sandbox like Jinja101 or the Python CLI tool Nettowel.
Find the input data
In this chapter, the goal of the exercise is to identify the YAML input used to render the configuration snippet with the provided template.
VLAN
!
{%- for vlan in vlans %}
vlan {{ vlan['id'] }}
name {{ vlan['name'] | replace(' ', '_') }}
{%- endfor %}
!
!
vlan 10
name hr
vlan 11
name finance
vlan 21
name printer
vlan 30
name server_external
!
What YAML data was used to render the template?
Solution
System
system {
name-server {
{%- for name_server in dns %}
{{ name_server }};
{%- endfor %}
}
syslog {
{%- for syslog_server in syslog %}
host {{ syslog_server }} {
any any;
daemon info;
}
{%- endfor %}
}
ntp {
{%- for ntp_server in ntp %}
server {{ ntp_server }};
{%- endfor %}
}
}
system {
name-server {
1.1.1.1;
8.8.8.8;
8.8.4.4;
}
syslog {
host 10.0.0.10 {
any any;
daemon info;
}
}
ntp {
server 0.ch.pool.ntp.org;
server 1.ch.pool.ntp.org;
}
}
What YAML data was used to render the template?
Solution
OSPF
!
router ospf {{ ospf.id }}
{%- for net in ospf.network %}
network {{ net.net }} area {{ net.area }}
{%- endfor %}
!
{%- for interface in ospf.interface %}
interface {{ interface.name }}
ip ospf {{ ospf.id }} area {{ interface.area }}
{%- if interface.cost is defined %}
ip ospf {{ ospf.id }} cost {{ interface.cost }}
{%- endif %}
{%- if interface.priority is defined %}
ip ospf priority {{ interface.priority }}
{%- endif %}
!
{%- endfor %}
!
router ospf 1
network 192.168.0.0 0.0.255.255 area 0
network 172.16.10.0 0.0.0.255 area 1
!
interface Lo0
ip ospf 1 area 0
!
interface Ethernet 0
ip ospf 1 area 0
ip ospf 1 cost 10
!
interface Ethernet 3
ip ospf 1 area 0
ip ospf priority 9
!
What YAML data was used to render the template?
Solution
Write the template
In this chapter, the goal of the exercise is to write a Jinja2 template that produces the expected configuration snippet using the provided input data.
VLAN
!
vlan 10
name hr
vlan 11
name finance
vlan 21
name printer
vlan 30
name server_external
!
What template was used to generate the configuration snippet?
Solution
System
---
server:
dns:
- 1.1.1.1
- 8.8.8.8
- 8.8.4.4
ntp:
- 0.ch.pool.ntp.org
- 1.ch.pool.ntp.org
syslog:
- 10.0.0.10
!
ip name-server 1.1.1.1 8.8.8.8 8.8.4.4
!
service timestamps log datetime msec
logging buffered 8192
logging host 10.0.0.10
!
ntp server 0.ch.pool.ntp.org
ntp server 1.ch.pool.ntp.org
!
What template was used to generate the configuration snippet?
Solution
!
ip name-server{% for name_server in server['dns'] %} {{ name_server }}{% endfor %}
!
service timestamps log datetime msec
logging buffered 8192
{%- for syslog_server in server['syslog'] %}
logging host {{ syslog_server }}
{%- endfor %}
!
{%- for ntp_server in server['ntp'] %}
ntp server {{ ntp_server }}
{%- endfor %}
!
Interface
---
interfaces:
- name: ge-0/0/2
description: uplink
ip: 192.168.1.1
mask: 30
- name: Lo0
ip: 10.10.10.10
mask: 32
interfaces {
ge-0/0/2 {
description uplink;
unit 0 {
family inet {
address 192.168.1.1/30;
}
}
}
}
interfaces {
Lo0 {
unit 0 {
family inet {
address 10.10.10.10/32;
}
}
}
}
What template was used to generate the configuration snippet?
Solution
Render the templates
In the following exercises, you will take on the role of Jinja2 and render the templates using the provided input data.
VLAN
!
{%- for id, name in vlans.items() %}
vlan {{ id }}
name {{ name | replace(' ', '_') }}
{%- endfor %}
!
What does the generated configuration snippet look like?
System
!
ip name-server{% for name_server in servers[0] %} {{ name_server }}{% endfor %}
!
service timestamps log datetime msec
logging buffered 8192
{%- for syslog_server in servers[2] %}
logging host {{ syslog_server }}
{%- endfor %}
!
{%- for ntp_server in servers[1] %}
ntp server {{ ntp_server }}
{%- endfor %}
!
---
servers:
- [1.1.1.1, 8.8.8.8, 8.8.4.4]
- [0.ch.pool.ntp.org, 1.ch.pool.ntp.org]
- [10.0.0.10]
What does the generated configuration snippet look like?
Solution
VRF
{%- for vrf in vrfs %}
routing-instances {
{{ vrf.name }} {
instance-type vrf;
route-distinguisher {{ asn }}:{{ vrf.id }}
vrf-target target:{{ asn }}:123456;
protocols {
bgp {
group {{ vrf.name }} {
log-updown;
{%- for neighbor in bgp[vrf.name] %}
neighbor {{ neighbor.ip }} {
description {{ neighbor.name }};
peer-as {{ neighbor.asn }};
}
{%- endfor %}
}
}
}
}
}
{%- endfor %}
---
asn: 65000
vrfs:
- name: CustA
id: 123
- name: CustB
id: 124
bgp:
CustA:
- ip: 10.0.0.1
name: CE01
asn: 65010
- ip: 10.0.0.5
name: CE02
asn: 65010
CustB:
- ip: 192.168.0.1
name: CE01
asn: 65064
- ip: 192.168.0.5
name: CE02
asn: 65064
What does the generated configuration snippet look like?
Solution
routing-instances {
CustA {
instance-type vrf;
route-distinguisher 65000:123
vrf-target target:65000:123456;
protocols {
bgp {
group CustA {
log-updown;
neighbor 10.0.0.1 {
description CE01;
peer-as 65010;
}
neighbor 10.0.0.5 {
description CE02;
peer-as 65010;
}
}
}
}
}
}
routing-instances {
CustB {
instance-type vrf;
route-distinguisher 65000:124
vrf-target target:65000:123456;
protocols {
bgp {
group CustB {
log-updown;
neighbor 192.168.0.1 {
description CE01;
peer-as 65064;
}
neighbor 192.168.0.5 {
description CE02;
peer-as 65064;
}
}
}
}
}
}