In part two of this series we are going to extend the data files and templates created in part one. We will also be adding a function to the previously created class ConfigureLeafSpine
. Part 1 can be found here.
We are going to be adding the IP and BGP parameters for the spine switches. Then we will generate the configs.
Topology
The first step is to update hosts.yaml
.
hosts.yaml
Spine-1
spine-1:
managementip: 198.51.100.254/24
managementgw: 198.51.100.1
site: ny
role: spine
interfaces:
- number: 1
ip: 172.16.0.1/30
description: P2P Link to Leaf-1
- number: 2
ip: 172.16.0.5/30
description: P2P Link to Leaf-2
loopbacks:
- number: 0
ip: 192.168.0.1/32
routerid: 192.168.0.1
bgpnetworks:
- 192.168.0.1/32
peergroups:
- name: EBGP-TO-LEAF
maxroutes: 12000
remoteas: 65100
neighbors:
- 172.16.0.2
- 172.16.0.6
Spine-2
spine-2:
managementip: 198.51.100.253/24
managementgw: 198.51.100.1
site: ny
role: spine
interfaces:
- number: 1
ip: 172.16.0.9/30
description: P2P Link to Leaf-1
- number: 2
ip: 172.16.0.13/30
description: P2P Link to Leaf-2
loopbacks:
- number: 0
ip: 192.168.0.2/32
routerid: 192.168.0.2
bgpnetworks:
- 192.168.0.2/32
peergroups:
- name: EBGP-TO-LEAF
maxroutes: 12000
remoteas: 65100
neighbors:
- 172.16.0.10
- 172.16.0.14
I wanted to note that peer groups are not a requirement for this design, but I feel it makes it easier to scale programmatically.
Now that we have updated hosts.yaml
we are going to create a file
called spine.yaml
. This file will hold information that is shared by all of the
spine switches.
spine.yaml
---
bgp:
as: 65000
distance: 20 200 200
maxpaths: 4
maxroutes: 64
To build the configuration we need to create one more file. A Jinja2 template spine.j2
.
spine.j2
ip routing
!
lldp run
!
{% 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
no shutdown
!
{% endfor -%}
{% for loopback in host.loopbacks -%}
interface loopback{{ loopback.number }}
ip address {{ loopback.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 }}
{% for neighbor in peergroup.neighbors -%}
neighbor {{ neighbor }} peer-group {{ peergroup.name }}
{% endfor -%}
{% endfor -%}
{% for network in host.bgpnetworks -%}
network {{ network }}
{% endfor -%}
!
end
The last step is to add a new function called generatespineconfig
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,
):
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)
self.baseconfig = baseconfig
self.spineconfig = spineconfig
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)
if __name__ == "__main__":
lsconfig = ConfigureLeafSpine(
'hosts.yaml',
'groups.yaml',
'baseconfig.j2',
'spine.yaml',
'spine.j2'
)
lsconfig.generatebaseconfig()
lsconfig.generatespineconfig()
The generatespineconfig
function creates two new files in the configs folder.
spine-1.config
ip routing
!
lldp run
!
interface Ethernet1
description P2P Link to Leaf-1
logging event link-status
no switchport
ip address 172.16.0.1/30
arp timeout 900
no shutdown
!
interface Ethernet2
description P2P Link to Leaf-2
logging event link-status
no switchport
ip address 172.16.0.5/30
arp timeout 900
no shutdown
!
interface loopback0
ip address 192.168.0.1/32
!
router bgp 65000
bgp log-neighbor-changes
distance bgp 20 200 200
maximum-paths 4 ecmp 64
neighbor EBGP-TO-LEAF peer-group
neighbor EBGP-TO-LEAF remote-as 65100
neighbor EBGP-TO-LEAF maximum-routes 12000
neighbor 172.16.0.2 peer-group EBGP-TO-LEAF
neighbor 172.16.0.6 peer-group EBGP-TO-LEAF
network 192.168.0.1/32
!
end
spine-2.config
ip routing
!
lldp run
!
interface Ethernet1
description P2P Link to Leaf-1
logging event link-status
no switchport
ip address 172.16.0.9/30
arp timeout 900
no shutdown
!
interface Ethernet2
description P2P Link to Leaf-2
logging event link-status
no switchport
ip address 172.16.0.13/30
arp timeout 900
no shutdown
!
interface loopback0
ip address 192.168.0.2/32
!
router bgp 65000
bgp log-neighbor-changes
distance bgp 20 200 200
maximum-paths 4 ecmp 64
neighbor EBGP-TO-LEAF peer-group
neighbor EBGP-TO-LEAF remote-as 65100
neighbor EBGP-TO-LEAF maximum-routes 12000
neighbor 172.16.0.10 peer-group EBGP-TO-LEAF
neighbor 172.16.0.14 peer-group EBGP-TO-LEAF
network 192.168.0.2/32
!
end
In part three we will create the leaf switch configs. All of the code for this part can be found here