This part is going to be very similar to part two. The only difference is we are going to generate the leaf configuration this time.
Topology
The first step is to update ‘hosts.yaml`
hosts.yaml
Leaf-1
leaf-1:
managementip: 198.51.100.252/24
managementgw: 198.51.100.1
site: ny
role: leaf
interfaces:
- number: 1
ip: 172.16.0.2/30
description: P2P Link to Spine-1
- number: 2
ip: 172.16.0.10/30
description: P2P Link to Spine-2
loopbacks:
- number: 0
ip: 192.168.0.3/32
routerid: 192.168.0.3
bgpnetworks:
- 192.168.0.3/32
peergroups:
- name: EBGP-TO-SPINE
maxroutes: 12000
remoteas: 65000
neighbors:
- 172.16.0.1
- 172.16.0.9
allowasin: 1
routemaps:
- name: ROUTE-MAP-OUT
direction: out
prefixlists:
- name: PREFIX-LIST-OUT
action: permit
sequence: 10
ip: 192.168.0.3/32
- name: PREFIX-LIST-OUT
action: permit
sequence: 20
ip: 172.16.0.0/16
- name: PREFIX-LIST-OUT
action: permit
sequence: 30
ip: 10.1.100.0/24
vlaninterfaces:
- number: 100
description: Servers1
mtu: 9214
ip: 10.1.100.1/24
arptimeout: 900
Leaf-2
leaf-2:
managementip: 198.51.100.251/24
managementgw: 198.51.100.1
site: ny
role: leaf
interfaces:
- number: 1
ip: 172.16.0.6/30
description: P2P Link to Spine-1
- number: 2
ip: 172.16.0.14/30
description: P2P Link to Spine-2
loopbacks:
- number: 0
ip: 192.168.0.4/32
routerid: 192.168.0.4
bgpnetworks:
- 192.168.0.4/32
peergroups:
- name: EBGP-TO-SPINE
maxroutes: 12000
remoteas: 65000
neighbors:
- 172.16.0.5
- 172.16.0.13
allowasin: 1
routemaps:
- name: ROUTE-MAP-OUT
direction: out
prefixlists:
- name: PREFIX-LIST-OUT
action: permit
sequence: 10
ip: 192.168.0.4/32
- name: PREFIX-LIST-OUT
action: permit
sequence: 20
ip: 172.16.0.0/16
- name: PREFIX-LIST-OUT
action: permit
sequence: 30
ip: 10.2.100.0/24
vlaninterfaces:
- number: 100
description: Servers1
mtu: 9214
ip: 10.2.100.1/24
arptimeout: 900
Next we are going to create a file called leaf.yaml
that will hold the information that is shared by
all of the leaf switches.
leaf.yaml
---
vlans:
- number: 100
description: Servers1
routemaps:
- name: ROUTE-MAP-OUT
filter: permit
sequence: 10
action: matchip
type: prefix-list
actionname: PREFIX-LIST-OUT
accessinterfaces:
- number: 9
switchport: access
vlan: 100
bgp:
as: 65100
distance: 20 200 200
maxpaths: 4
maxroutes: 4
redistribute: connected
Next we will create the configuration template leaf.j2
.
leaf.j2
ip routing
!
lldp run
!
{% for vlan in vlans -%}
vlan {{ vlan.number }}
name {{ vlan.description }}
!
{% endfor -%}
{% for interface in host.interfaces -%}
interface Ethernet{{ interface.number }}
description {{ interface.description }}
logging event link-status
no switchport
ip address {{ interface.ip }}
arp timeout 900
mtu 9214
no shutdown
!
{% endfor -%}
{% for loopback in host.loopbacks -%}
interface loopback{{ loopback.number }}
ip address {{ loopback.ip }}
!
{% endfor -%}
{% for accessinterface in accessinterfaces -%}
interface Ethernet{{ accessinterface.number }}
switchport access vlan {{ accessinterface.vlan }}
no snmp trap link-status
spanning-tree portfast
spanning-tree bpduguard enable
no shutdown
!
{% endfor -%}
{% for vlaninterface in host.vlaninterfaces -%}
interface Vlan{{ vlaninterface.number }}
description {{ vlaninterface.description }}
{% if vlaninterface.mtu -%}
mtu {{ vlaninterface.mtu }}
{% endif -%}
ip address {{ vlaninterface.ip }}
arp timeout 900
no shutdown
!
{% endfor -%}
{% for routemap in routemaps -%}
route-map {{ routemap.name }} {{ routemap.filter }} 10
{% if routemap.action == 'matchip' -%}
match ip address {{ routemap.type }} {{ routemap.actionname }}
{% endif -%}
!
{% endfor -%}
{% for prefixlist in host.prefixlists -%}
ip prefix-list {{ prefixlist.name }} seq {{ prefixlist.sequence }} {{ prefixlist.action }} {{ prefixlist.ip }}
{% endfor -%}
!
router bgp {{ bgp.as }}
bgp log-neighbor-changes
distance bgp {{ bgp.distance }}
maximum-paths {{ bgp.maxpaths }} ecmp {{ bgp.maxroutes }}
{% for peergroup in host.peergroups -%}
neighbor {{ peergroup.name}} peer-group
neighbor {{ peergroup.name }} remote-as {{ peergroup.remoteas }}
neighbor {{ peergroup.name }} maximum-routes {{ peergroup.maxroutes }}
neighbor {{ peergroup.name }} allowas-in {{ peergroup.allowasin }}
{% for routemap in peergroup.routemap -%}
neighbor {{ peergroup.name }} route-map {{ routemap.name }} {{ routemap.direction }}
{% endfor -%}
{% for neighbor in peergroup.neighbors -%}
neighbor {{ neighbor }} peer-group {{ peergroup.name }}
{% endfor -%}
{% endfor -%}
{% for network in host.bgpnetworks -%}
network {{ network }}
{% endfor -%}
redistribute {{ bgp.redistribute }}
!
end
Finally we are going add a new function called generateleafconfig
to the ConfigureLeafSpine
class.
configureleafspine.py
#!/usr/bin/env python3
from jinja2 import Environment, FileSystemLoader
import yaml
class ConfigureLeafSpine():
"""Class to configure and maintain leaf spine switches"""
def __init__(
self,
hosts,
groups,
baseconfig,
spines,
spineconfig,
leafs,
leafconfig
):
with open(hosts) as file1:
self.hosts = yaml.load(file1)
with open(groups) as file2:
self.groups = yaml.load(file2)
with open(spines) as file3:
self.spines = yaml.load(file3)
with open(leafs) as file4:
self.leafs = yaml.load(file4)
self.baseconfig = baseconfig
self.spineconfig = spineconfig
self.leafconfig = leafconfig
self.ENV = Environment(loader=FileSystemLoader('.'))
def generatebaseconfig(self):
"""Generates base configuration files"""
template = self.ENV.get_template(self.baseconfig)
for key, value in self.hosts.items():
config = template.render(
defaults=self.groups['defaults'],
hostname=key,
host=value,
site=self.groups[value['site']]
)
filename = 'configs/{0}-base.config'.format(key)
with open(filename, 'w') as file:
file.writelines(config)
def generatespineconfig(self):
"""Generates the spine configuration"""
template = self.ENV.get_template(self.spineconfig)
for key, value in self.hosts.items():
if value['role'] == 'spine':
config = template.render(
host=value,
bgp=self.spines['bgp']
)
filename = 'configs/{0}.config'.format(key)
with open(filename, 'w') as file:
file.writelines(config)
def generateleafconfig(self):
"""Generates the leaf configuration"""
template = self.ENV.get_template(self.leafconfig)
for key, value in self.hosts.items():
if value['role'] == 'leaf':
config = template.render(
host=value,
vlans=self.leafs['vlans'],
routemaps=self.leafs['routemaps'],
bgp=self.leafs['bgp']
)
filename = 'configs/{0}.config'.format(key)
with open(filename, 'w') as file:
file.writelines(config)
if __name__ == "__main__":
lsconfig = ConfigureLeafSpine(
'hosts.yaml',
'groups.yaml',
'baseconfig.j2',
'spine.yaml',
'spine.j2',
'leaf.yaml',
'leaf.j2'
)
lsconfig.generatebaseconfig()
lsconfig.generatespineconfig()
lsconfig.generateleafconfig()
Running the configureleafspine.py
file will create the configuration files and save them to the configs folder.
leaf-1.config
ip routing
!
lldp run
!
vlan 100
name Servers1
!
interface Ethernet1
description P2P Link to Spine-1
logging event link-status
no switchport
ip address 172.16.0.2/30
arp timeout 900
mtu 9214
no shutdown
!
interface Ethernet2
description P2P Link to Spine-2
logging event link-status
no switchport
ip address 172.16.0.10/30
arp timeout 900
mtu 9214
no shutdown
!
interface loopback0
ip address 192.168.0.3/32
!
interface Ethernet9
switchport access vlan 100
no snmp trap link-status
spanning-tree portfast
spanning-tree bpduguard enable
no shutdown
!
interface Vlan100
description Servers1
mtu 9214
ip address 10.1.100.1/24
arp timeout 900
no shutdown
!
route-map ROUTE-MAP-OUT permit 10
match ip address prefix-list PREFIX-LIST-OUT
!
ip prefix-list PREFIX-LIST-OUT seq 10 permit 192.168.0.3/32
ip prefix-list PREFIX-LIST-OUT seq 20 permit 172.16.0.0/16
ip prefix-list PREFIX-LIST-OUT seq 30 permit 10.1.100.0/24
!
router bgp 65100
bgp log-neighbor-changes
distance bgp 20 200 200
maximum-paths 4 ecmp 4
neighbor EBGP-TO-SPINE peer-group
neighbor EBGP-TO-SPINE remote-as 65000
neighbor EBGP-TO-SPINE maximum-routes 12000
neighbor EBGP-TO-SPINE allowas-in 1
neighbor 172.16.0.1 peer-group EBGP-TO-SPINE
neighbor 172.16.0.9 peer-group EBGP-TO-SPINE
network 192.168.0.3/32
redistribute connected
!
end
leaf-2.config
ip routing
!
lldp run
!
vlan 100
name Servers1
!
interface Ethernet1
description P2P Link to Spine-1
logging event link-status
no switchport
ip address 172.16.0.6/30
arp timeout 900
mtu 9214
no shutdown
!
interface Ethernet2
description P2P Link to Spine-2
logging event link-status
no switchport
ip address 172.16.0.14/30
arp timeout 900
mtu 9214
no shutdown
!
interface loopback0
ip address 192.168.0.4/32
!
interface Ethernet9
switchport access vlan 100
no snmp trap link-status
spanning-tree portfast
spanning-tree bpduguard enable
no shutdown
!
interface Vlan100
description Servers1
mtu 9214
ip address 10.2.100.1/24
arp timeout 900
no shutdown
!
route-map ROUTE-MAP-OUT permit 10
match ip address prefix-list PREFIX-LIST-OUT
!
ip prefix-list PREFIX-LIST-OUT seq 10 permit 192.168.0.4/32
ip prefix-list PREFIX-LIST-OUT seq 20 permit 172.16.0.0/16
ip prefix-list PREFIX-LIST-OUT seq 30 permit 10.2.100.0/24
!
router bgp 65100
bgp log-neighbor-changes
distance bgp 20 200 200
maximum-paths 4 ecmp 4
neighbor EBGP-TO-SPINE peer-group
neighbor EBGP-TO-SPINE remote-as 65000
neighbor EBGP-TO-SPINE maximum-routes 12000
neighbor EBGP-TO-SPINE allowas-in 1
neighbor 172.16.0.5 peer-group EBGP-TO-SPINE
neighbor 172.16.0.13 peer-group EBGP-TO-SPINE
network 192.168.0.4/32
redistribute connected
!
end
In part four we are going to use Napalm to push the configurations to the switches. All of the code for this part can be found here.