Building BGP Route Reflector Configuration with Ansible/Jinja2

One of our subscribers sent me this email when trying to use ideas from Ansible for Networking Engineers webinar to build BGP route reflector configuration:

I’m currently discovering Ansible/Jinja2 and trying to create BGP route reflector configuration from Jinja2 template using Ansible playbook. As part of group_vars YAML file, I wish to list all route reflector clients IP address. When I have 50+ neighbors, the YAML file gets quite unreadable and it’s hard to see data model anymore.

Whenever you hit a roadblock like this one, you should start with the bigger picture and maybe redefine the problem.

In this particular case, your problem shouldn’t be “I want to configure BGP neighbors on my route reflector” but “I want to configure IBGP sessions in my network” or even “want to configure BGP routing in my network”. With this scope in mind:

  • Create an inventory file listing all BGP routers in your network (you’ll need it anyway), preferably in YAML format.
  • Define loopback IP address for every router. You could define them in the inventory file, in host_vars if you need per-host variables for some other reason, or in an external file that you’ll include with include_vars.
  • Group RR clients. You could use Ansible groups, or list them somewhere in a YAML file, or add a variable like rr_group to every RR client host.
Ansible calls variables facts just to confuse people with a bit of programming background.

Whatever you do, make sure you don’t have the same information (like router loopback IP) stored in multiple places. You want to have a single source of truth for every bit of information, or as Elisa Jasinska explained in her presentation in Building Network Automation Solutions online course: “duplicate data makes rockets explode

Let’s assume that after you’re done your data model looks like this:

  • BGP Route Reflector clients are in rr_clients Ansible group;
  • Each router has its loopback IP defined in loopback_ip variable;
  • There’s a global variable bgp_as defined in all.yml group variable file.

Furthermore, to simplify the example, assume you have a single route reflector cluster in your autonomous system.

Using that data model, it’s easy to build the route reflector BGP configuration with a Jinja2 template:

  • Iterate over all hosts in specified group
  • For every host fetch the loopback IP to create the neighbor statement.

Here’s the relevant part of a Jinja2 template:

{% for bgp_node in groups['rr_clients'] %}
neighbor {{ hostvars[bgp_node].loopback_ip }} remote-as {{ bgp_as }}
{% endfor %}

You can use the same trick on the RR client side to create a list of IBGP sessions to all route reflectors in your network, and on the reflectors to create a full mesh of IBGP sessions between reflectors.

More examples

3 comments:

  1. Wow, I feel like I was on this same journey this week, glad I popped in :) I was trying to do the same thing this week and trying to do it without having any specific attribute called route reflector or rr-client (not sure why, just my feeling at the time). I used spine and leaf groups and spine and leaf roles.

    Since the spines treat all the leafs as RRCs, I figured I could just use roles and when the spine role is run, I take advantage of hostvars and the fact the leafs are grouped already. So I loop through each device and as long as its in the leafs group its the route-reflector-client option. It works but I am not even sure how to gauge the "IAC-ness" of this solution. Thoughts?

    $ cat roles/bgp-spine/tasks/main.yaml 
    - name: Configure BGP Neighbors with EVPN AF
      nxos_bgp_neighbor_af:
        asn: "65000"
        neighbor: "{{ item.value.routerid }}"
        afi: l2vpn
        safi: evpn
        send_community: both
        route_reflector_client: yes
        state: present
      loop: "{{ hostvars|dict2items }}"
      when: item.key in item.value.groups.leafs
    
  2. Nothing wrong with your solution. Instead of modeling the BGP sessions with a data model (like I did), you decided to make them part of the business logic (every leaf is a route reflector of every spine), and of course that's as IaC-y as it gets... it's just that your data model is at a higher layer of abstraction than mine.

    As for the implementation: unless they radically improved Ansible network modules (fat chance) calling nxos_bgp_neighbor_af might get slow for a large number of leaf switches, as it executes show running every time you're calling the module.

  3. Ah, good point on the "show run" every time. That is slow. Thanks Ivan!

Add comment
Sidebar