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

L3LS

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