Guest Post! WTF Are all those Checkboxes? (ACI L3 Outs) – Part 2 of ???

My friend and colleague Mr. Jason Banker recently ran into some good times with the mysteries of the ACI L3 Out Checkbox Madness! He Slack’d me and told me he’d found some clowns blog post about it (yours truly) and that some updates and additional information was needed, so he kindly volunteered some time to help out! Without further ado here is Jason’s Checkbox Madness:


 

As we continue to deploy fabrics we always joke about these damn routing checkboxes shooting us in the foot.  We play with different scenarios in the lab to ensure we understand how these pesky boxes work and what other options we have for future deployments.   The scenario here was to use get different OSPF areas connected to the same border leaf using ACI as the transit.  This scenario brings up some certain challenges and hopefully my testing will help others understand it a little better as well.

Design:

We have two external routers coming into a border leaf on ACI, two L3Outs (required because of multiple areas), one is Area 0 (backbone) and one is Area 1.  Here is the breakdown of routes on each router:

External Router 1 (Area 0):

  • Loopback0: 2.2.2.2/32
  • Loopback1: 4.4.4.4/32
  • Transits: 192.168.0.0/29

External Router 2 (Area 1):

  • Loopback0: 3.3.3.3/32
  • Loopback1: 5.5.5.5/32
  • Transits: 172.16.0.0/29

 

Using ACI as a transit we want routes from Area 0 to be imported into Area 1 and vice versa across the two L3Outs.   We will skip the build of the L3Out portion but I want to focus on those pesky checkboxes again.  Whenever I build an L3Out my network EPG usually looks something like this:

By default, “External Subnets for the External EPG” is checked (this checkbox simply enforces policy on this L3out and contracts are applied to the specific subnet) and I am using 0.0.0.0/0 network as a catch-all.  Moving along with the defaults, I show full adjacency:

 

As well as a full routing table within ACI, receiving the networks above in the fabric as expected:

Note:  Anything received from area 0 is shown as backbone and everything from area 1 is 0.0.0.1.

 

Now if look at the routing table for External Router 1 we see no routes across the fabric being received from External Router 2.

 

Let me check my OSPF Neighbors:

 

So, we have no OSPF routes but we have a neighbor relationship.  Let’s go check External Router 2:

 

So, we are showing some OSPF routes but they are only the loopbacks of the ACI Fabric (Area 0), not what we are necessarily looking for.  ACI blocks transit routes between different L3Outs unless permitted by policy via an OSPF area filter-list (to verify ssh to the border leaf and run “show route-map”).  Let’s go look at the Network EPG checkboxes again and see if we can get routing to occur between OSPF Areas across the fabric.

 

As we showed earlier we are using a catch-all 0.0.0.0/0 with “External Subnets for the External EPG”. If we select the “!” on the upper right portion of the screen we will receive the properties of this screen:

 

Based on what this screen states, “Export Route Control Subnet” – controls the export route direction and “Import Route Control Subnet” – controls the import direction.  This sounds like what we need to get routing to traverse the fabric.  Let’s go ahead and select them for Area 0 but before we can select import there is another configuration we need to apply before we can get import to not be grayed out.  If we go back to the top of the L3Out in the navigation pane we need to select the “Route Control Enforcement:” import checkbox:

 

Now if we go back to the Network EPG we should have both options available to us:

 

Now let’s see if we have any changes within our routing table:

 

We still have no change in the table.  Remember that we are using a catch all 0.0.0.0/0? This would require us to also select the aggregate export and import features on the subnet/network epg we have created for Area 0 and Area 1:

 

Time to verify:

 

This looks great.  Now we will verify External Router 2:

 

If we want to take it a step further we can do network specific routes instead of a catch-all:

 

Router 2:

 

Verify Router 2 is receiving 2.2.2.2/32 from Router 1:

 

Now we can send 3.3.3.3/32 from Router 2 into Router 1:

 

Router 1:

 

As I stated earlier these checkboxes are updating route-maps and prefix-lists within ACI.  Prior to us selecting the import/export feature our route-maps had a deny all so no routes would traverse areas.  Upon selecting these checkboxes we can see the change:

 

Instead of giving you route-map blah I will try and breakdown the map for you just focusing on export route-map. Prior to import/export our route-map looked like this:

route-map exp-ctx-2392064-deny-external-tag, deny, sequence 1

  Match clauses:

    tag: 4294967295

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, deny, sequence 9998

  Match clauses:

    ospf-area: backbone

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, deny, sequence 9999

  Match clauses:

    ospf-area: 0.0.0.1

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, permit, sequence 10000

  Match clauses:

  Set clauses:

route-map exp-ctx-2392064-set-external-tag, permit, sequence 2

  Match clauses:

  Set clauses:

    tag 4294967295

route-map imp-ctx-bgp-st-interleak-2392064, deny, sequence 1

  Match clauses:

    tag: 4294967295

  Set clauses:

route-map imp-ctx-bgp-st-interleak-2392064, permit, sequence 10000

  Match clauses:

  Set clauses:

 

You can see that we had “deny” for backbone and area 0.0.0.1 preventing us from using the fabric as a transit.  After we selected import/export features our route-map is updated as such (just focusing on the export route-map):

route-map exp-ctx-2392064-deny-external-tag, deny, sequence 1

  Match clauses:

    tag: 4294967295

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, permit, sequence 9801

  Match clauses:

    ip address prefix-lists: IPv4-ospf-rt2392064--0-dst-rtpfx

    ipv6 address prefix-lists: IPv6-deny-all

    ospf-area: backbone

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, permit, sequence 9802

  Match clauses:

    ip address prefix-lists: IPv4-ospf-rt2392064--1-dst-rtpfx

    ipv6 address prefix-lists: IPv6-deny-all

    ospf-area: 0.0.0.1

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, deny, sequence 9998

  Match clauses:

    ospf-area: backbone

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, deny, sequence 9999

  Match clauses:

    ospf-area: 0.0.0.1

  Set clauses:

route-map exp-ctx-2392064-deny-external-tag, permit, sequence 10000

  Match clauses:

  Set clauses:

route-map exp-ctx-2392064-set-external-tag, permit, sequence 2

  Match clauses:

  Set clauses:

    tag 4294967295

route-map imp-ctx-bgp-st-interleak-2392064, deny, sequence 1

  Match clauses:

    tag: 4294967295

  Set clauses:

route-map imp-ctx-bgp-st-interleak-2392064, permit, sequence 10000

  Match clauses:

  Set clauses:

Now our route-map has been updated with prefix-lists to allow our traffic across areas, we will look at the prefix-list itself:

 

Leaf-103# show ip prefix-list IPv4-ospf-rt2392064--0-dst-rtpfx

ip prefix-list IPv4-ospf-rt2392064--0-dst-rtpfx: 1 entries

   seq 1 permit 0.0.0.0/0 le 32

 

The 0.0.0.0/0 catch-all has been added and our routes can traverse the fabric.  I suggest you also peak at the import route-maps and see what is happening under the hood there as well.

 

 

 

 

Advertisements

ACI Power Deployment Tool (acipdt)

I’ve been spending a lot of my free time working on a little side project with Python. Rather than waxing on about it, I’ll just post a very quick and dirty video, and the readme.md that will accompany my tool.

You can find the video here. Note that the issue (which worked out as a decent demo) in the video where there were a bunch of 400 errors has been fixed — you can blame silly Python sets not being ordered!

You can find the acitool library here.

## Synopsis

ACIPDT – or ACI Power Deployment Tool – is a Python library that is intended to be used for network engineers deploying an ACI fabric. ACIPDT is very much early alpha, later releases should add additional features/functionality as well as optimization and improved error handling.

## Overview

The “SDN” (hate the term, but it applies) movement has brought a great deal of discussion to the idea of how a network engineer deploys networking equipment. Historically text files, or perhaps macros in Excel have been used as templates for new deployments, and good old-fashioned copy/paste has been the actual deployment vehicle. Among other things, SDN is attempting to change this. With SDN we (networking folk) have been given APIs! However, most network engineers, myself included, have no idea what to do with said APIs.

Cisco ACI, as with the other networky “SDN products,” in the market have provided some nifty tools in order to begin the journey into this API driven next-generation network world, but the bar to entry in any meaningful way is still rather high. In example, ACI provides an API inspector, which displays the XML or JSON payloads that are configuring the ACI fabric, however the payload on its own of course doesn’t do much for a network guy – where am I supposed to paste that into? What became clear to me is that Postman was the obvious answer. Postman is a great tool for getting started with an API, and I have used it extensively with ACI, even to the point of deploying an entire fabric in 2 minutes with a handful of Postman collections. However…

Postman left much to be desired. I’m fairly certain that the way in which I was using it was never really the intended use case. In order to keep the collections to a reasonable size, which in turn kept spreadsheets relatively organized (spreadsheets contained the data to insert as a variable in the payloads), but then I had nine collections to run, which meant nine spreadsheets. On top of all of that, there was very little feedback in terms of even simple success/fail per post — and even if you captured that output, there would be things that would fail no matter what due to the way the spreadsheet was piping in variables (perhaps more on that later, maybe its just how I was using it).

The result of this frustration is the ACIPDT. My intention is to re-create the functionality that I have used Postman for in Python. In doing so, the goal is to have a single spreadsheet (source of data, could be anything but a spreadsheet is easy), to run a single script, and to have valuable feedback about the success or failure of each and every POST. ACIPDT itself is not a script that will configure your ACI fabric, but is instead a library that contains ReST calls that will configure the most common deployment scenarios. In addition to the library itself, I have created a very simple script to ingest a spreadsheet that contains the actual configuration data and pass the data to the appropriate method in the library.

Key features:
– Have a library that is de-coupled from any deployment type of script.
– This was a goal after early attempts became very intertwined and I was unable to cleanly separate the simple ReST call/payload from the rest of the script.
– Run one script that references one source of data.
– This is more relevant to the run script than it is to the library, but it was taken into consideration when creating the library. A single spreadsheet (with multiple tabs) is used to house all data, is parsed in the run script, then data is passed as kwargs to the appropriate methods for deployment.
– Have discreet configuration items.
– Ensure that an Interface Profile on an L3 Out can be modified without deleting/re-creating the parent object. While this library/script is intended for deployments where this is likely not a big deal, it was at any rate a design goal.
– Capture the status of every single call.
– Each method returns a status code. The run script simply enters this data into the Excel spreadsheet at the appropriate line so you know which POSTs failed and which ones succeeded. This is a simplistic status check, but is leaps better than I was getting with Postman.

## Resources

I believe the code to be relatively straight-forward (and I am very not good at Python), and have provided comments ahead of every method in the library to document what is acceptable to pass to the method. As this is really just a pet project on the weekends, that’s probably about it from a resources perspective. Feel free to tweet at me (@carl_niger) if you run into any issues or have a burning desire for a feature add.

## Code Example

# Import acidpt from the acitool library
from acitool import acipdt
# Initialize the FabLogin class with the APIC IP, username, and password
fablogin = acipdt.FabLogin(apic, user, pword)
# Login to the fabric and capture the challenge cookie
cookies = fablogin.login()
# Initialize the Fabric Pod Policy class with the APIC IP and the returned cookies from the login class
podpol = acipdt.FabPodPol(apic, cookies)
# Configure an NTP server of 1.1.1.1
status = podpol.ntp('1.1.1.1', 'created,modified')

## Getting Started

To get started, unzip the zip file and place the entire contents in a directory in your Python path. For me (using OSX), I placed the folder in /usr/local/lib/python2.7/. You can then import as outlined in the code example above.

## Disclaimer

This is NOT fancy code. It is probably not even “good” code. It does work (for me at least!) though.