The repository comes with the WHO data file from 06 April 2020 (WHO-COVID-19-global-data.csv). The simplest run of the script will use this WHO data file.
To download the latest file go to the Who Overview Map and download the Map Data from the link on the lower right hand side.
This CSV file will need clean up. Remove spaces from column titles. Some rows have spaces in the country names and so spaces have shifted columns (Belize and Palestine). You will need to combine the name and shift the data back to the correct columns. Welcome to the world of data.
John Hopkins University (JHU) Center for Systems Science and Engineering (CSSE) Data
The John Hopikns Unversity CSSE data is widely used in the media and either drives or is incorporated into many other data sets. More importantly for our purposes, this wonderful institution of higher learning makes the raw data available on a public repository (GitHub).
I’ve cloned the repository so that it sits as a subdirectory in my pandas_for_pandemic_data folder and I refresh it every day.
# Clones the pands_for_pandemic_data Repository
git clone https://github.com/cldeluna/pandas_for_pandemic_data.git
# Change into the pands_for_pandemic_data Repository
# Clones the John Hopkins University CSSE Data
git clone https://github.com/CSSEGISandData/COVID-19.git
# Refresh the JHU Data
Feel free to put it elsewhere in your directory structure. The script sets the default path in the arguments section at the bottom. You can either update the default path directly or use the -d option when you execute the script to redirect script to look there for the daily files.
New York Times Data
The New York Times has also shared their data. This repository only contains data for the US. They share two flavors:
US State Level data
US County Level data
They do a good job of keeping the data set very clean. Its all numeric and so far I’ve not seen any missing data which is rare for any data set.
== Number of MISSING values in each column: date 0 state 0 fips 0 cases 0 deaths 0 dtype: int64
I took the same approach with this repository as I did for the JHU data. I’ve cloned the repository so that it sits as a subdirectory in my pandas_for_pandemic_data folder and I refresh it every day.
In general, the script will take in a CSV data file, turn it into a Pandas Data Frame, and execute a set of commands against the data. The main section manages which options to execute and sends the relevant data frame to a function that prints out the various analysis statements for the data frame. In general, this is what will be shown for each data frame.
Describe the data (pandas method showing interesting statistical facts about the data)
Show the shape of the data frame (number of rows and columns)
Show the first and last 5 lines of data
List the column headings
Show the data type of each column
Look for the total number of missing values in each column
Sum the columns (only makes sense for columns holding numeric data)
The various options let you control which data set you want to investigate and filter. The output is sent to your screen. These are just some of the actions available to you with Pandas. Once you have the data in a Pandas data frame you can query the data frame for the data that is meaningful to you.
Cheatsheet for todays_totals.py script
python todays_totals.py -h
Display all the options available (Help)
WHO Data Without any options, the script will load the local WHO data file from 6 April into a Pandas Data Frame and run some commands to investigate the data. Reminder: If you download a fresh WHO CSV file please note the updates I list above so that you can cleanly import the CSV into a Data Frame
python todays_totals.py -c “MX”
WHO Data Filtered for a Specific Country Note: use the 2 letter country code as an argument with the -c option
python todays_totals.py -t
John Hopkins University CSSE Data The -t option will look for todays daily log file in the JHU CSSE (remember to clone the repository)
python todays_totals.py -t -c “Mexico”
John Hopkins University CSSE Data The -t -c “country or region” option will let you filter for a country
python todays_totals.py -t -s “California”
John Hopkins University CSSE Data The -t -s “state” option filters the JHU data set for a state or province
python todays_totals.py -t -f 06037
John Hopkins University CSSE Data The -t -f FIPS option filters the JHU data set for a FIPS county code. Note: FIPS code 06037 is for Los Angeles County
python todays_totals.py -n
New York Times Data US Totals Only for the full NY Times data set with the -n option (remember to clone the repository)
New York Times Data This data set has both “state” and “fips” but fips represents FIPS State Code so in this example 6 is the FIPS state code. This should get you exactly the same data as ** python todays_totals.py -n -p “California”**
Script CLI Cheat Sheet
The todays_totals.py script will give you an idea of how to load pandemic data into a Pandas data frame and interrogate the data. Executing it with the -h option will give you help on the options.
(pandas) Claudias-iMac:pandas_for_pandemic_data claudia$ python todays_totals.py -h
usage: todays_totals.py [-h] [-d DAILY_REPORTS_FOLDER] [-c COUNTRY_REGION]
[-p PROVINCE_STATE] [-s SPECIFIC_DAY] [-f FIPS] [-w]
-h, --help show this help message and exit
-d DAILY_REPORTS_FOLDER, --daily_reports_folder DAILY_REPORTS_FOLDER
Set path to CSSE Dailty Report folder
csse_covid_19_daily_reports. Default is ./COVID-19/css
-c COUNTRY_REGION, --country_region COUNTRY_REGION
Filer on 2 letter Country Region. Example: "US"
-p PROVINCE_STATE, --province_state PROVINCE_STATE
Filer on Province State. Example: "California"
-s SPECIFIC_DAY, --specific_day SPECIFIC_DAY
File for specific day. Example: 04-01-2020
-f FIPS, --fips FIPS FIPS County Code Example: 06037 (Los Angeles County)
-w, --who_data_file Analyze the WHO data file provided
-t, --today_csse Analyze todays file in the CSSE repo
-n, --new_york_times Analyze the New York Times Data
Usage: 'python todays_totals'
Running the script without any parameters yields some information on the WHO data set from 6 April which is part of the repository. This information includes:
A description of the data frame including some statistical data on the numeric values.
The rows and
Example output for WHO Data:
(pandas) Claudias-iMac:pandas_for_pandemic_data claudia$ python todays_totals.py
==================== DATA FRAME CHECK ====================
==================== WHO Data Frame from WHO-COVID-19-global-data.csv ====================
== Describe the Data Frame:
Deaths CumulativeDeaths Confirmed CumulativeConfirmed
count 6786.000000 6786.000000 6786.000000 6786.000000
mean 9.971412 104.946360 178.487179 2313.689213
std 73.268455 761.569677 1183.935476 12918.006118
min 0.000000 0.000000 0.000000 1.000000
25% 0.000000 0.000000 0.000000 4.000000
50% 0.000000 0.000000 2.000000 26.000000
75% 0.000000 3.000000 25.000000 235.000000
max 2003.000000 15889.000000 33510.000000 307318.000000
== Shape of the Data Frame:
== SAMPLE (first and last 5 rows):
day Country CountryName Region Deaths CumulativeDeaths Confirmed CumulativeConfirmed
0 2/25/20 AF Afghanistan EMRO 0 0 1 1
1 2/26/20 AF Afghanistan EMRO 0 0 0 1
2 2/27/20 AF Afghanistan EMRO 0 0 0 1
3 2/28/20 AF Afghanistan EMRO 0 0 0 1
4 2/29/20 AF Afghanistan EMRO 0 0 0 1
day Country CountryName Region Deaths CumulativeDeaths Confirmed CumulativeConfirmed
6781 4/2/20 ZW Zimbabwe AFRO 0 1 0 8
6782 4/3/20 ZW Zimbabwe AFRO 0 1 0 8
6783 4/4/20 ZW Zimbabwe AFRO 0 1 1 9
6784 4/5/20 ZW Zimbabwe AFRO 0 1 0 9
6785 4/6/20 ZW Zimbabwe AFRO 0 1 0 9
== Column Headings of the data set:
['day' 'Country' 'CountryName' 'Region' 'Deaths' 'CumulativeDeaths'
== Column default data type:
== Number of MISSING values in each column:
== Sum just the numeric columns in the Data Frame:
(pandas) Claudias-iMac:pandas_for_pandemic_data claudia$
An example of using the fuzzywuzzy Python module to match data sets with similar but not exact data – fuzzy matches!
I was recently given a list of locations that I had to analyze.
For the analysis, I needed data that was not in the original list (lets call that the source list). Luckily I had a larger data set (lets call that the detail list) which did have all the additional details I needed.
The source list with locations was a subset of the detail list. Even better, both lists had an “Address” column so I figured it would be a simple matter of looking up each location in my source list in the larger detail list and picking out the additional data that I needed.
…or so I thought…
The Same Column but NOT the Same Data
As I drilled down into the actual data, what I saw immediately derailed my original plan to use the “Address” column in both data sets as the key to match. For that to work, the keys would need to be the same or nearly so but I saw drastically different data. I might have been able to account for spaces and capitalization differences but never something like this:
Value From Source List Address
Value From Detailed List Address
Fort Irwin Military Base
93 Goldstone Rd (Ft. Irwin)
As ever, Google to the rescue and enter the fuzzywuzzy python module.
Let’s take a closer look at the data.
I’ve used a list of NASA’s Deep Space Network Complexes to simulate the data and issues I had. My actual source list had hundreds of locations and the details list had thousands.
Numbers correspond to the image below.
This is the original source list containing the list of locations to be analyzed.
I happened to have the additional detail file which had all the additional information I needed for the locations listed in the original source list.
At first look it seemed a perfect match as both files had an “Address” column I could use as a “key” to get the additional information for the locations in my original source list. However, on closer inspection you can see that the addresses for the same location were quite different. Even the “City” column had differences.
You can also see that the source list was missing some State data.
Finally, for each location in my source list I needed ZIP, complete state data, 2 Letter Country code, Directions, and the URL for each location.
What I needed was all of the data in #5 extracted from the much larger detail file and combined with the locations from my original list.
6 & 7 For those that like to flip to the end, the script we will discuss here provided the last file combining the source list of locations with the detail data for each location from the larger detail file.
The main body of the script is very simple thanks to Pandas (another wonderful python module). It’s really only 8 lines of code and 4 steps.
Load the data sets from the Excel files into Pandas Data Frames
# Create a Data Frame from the Source Excel File
df_src = df_from_excel(arguments.source_file)
# Create a Data Frame from the Additional Details Excel File
df_det = df_from_excel(arguments.detail_file)
Add a new column to the source data set for “Full_Address” which contains the address information from the detailed file. This allows us to use the Pandas merge function to merge the two data sets in one line in exactly the way we want. This one statement calls a function that performs the fuzzy lookups
# Add a new column "Full_Address" to the Source data frame which has the "Address" information form the additional details data frame
# This new column will be used in the Pandas merge
df_src['Full_Address'] = df_src['Address'].apply(get_series_match, args=[df_det['Address']])
Merge the two Data Frames into one data set with all the information needed
I will be the first to admit this is in no way elegant or “pythonic” but I’m not a developer. I’m a network engineer. It works!
In this one statement, thanks to the power of Pandas, we can iterate over the values in the Address column of the source data frame df_src[‘Address’], sending each to the get_series_match function to search for a match in the detail data frame Address column.
To the left of the equal sign, we tell the df_src data frame that we want to add a new column, “Full_Address”. We build the contents of that new column by passing the “Address” value from the source data frame, df_src[‘Address’], and using Pandas “apply” method to build the new value of each row using the get_series_match function (and we pass to the function the column “Address” data from the details data frame, df_det[‘Address’]).
In line 38 we see the function defined with the two values it expected to be passed to it when called. The variable row_val has the “cell” value from the source data frame “Address” column.
Line 44 sets an empty variable to hold the match value (the address from the detail data frame).
The args variable is defined as a list and it has the column values from the detail data frame “Address” column so that we can iterate through that list looking for the specific row_val. Specifically the row_val passed to the function will be compared against this list of addresses from the detail data frame, df_det[‘Address’] :
Address 0 93 Goldstone Rd (Ft. Irwin) 1 421 Discovery Drive, Paddy's River District 2 Ctra. M-531 Robledo de Chavela a Colmenar del ... 3 4800 Oak Grove Drive
Line 48 starts the for loop which will iterate over each value in the df_det[‘Address’] data which we have stored as a python list in the variable args looking for the closest match to row_val.
Line 51 attempts an exact match. That is, if the src “Address” value matches the detail “Address” value (case insensitive), then we set match_value to the address from the detail data frame and we break out of the loop. We have found what we were looking for.
Lines 58 – 60 are executed if we don’t find an exact match. In reality, to make this shorter I would remove the exact match test (lines 51 – 56) but I left it in as an example of what I tried to do initially.
These 3 variables hold a ratio or “score” of how closely the two address values matched given different algorithms available in the fuzzywuzzy module. Each algorithm has a sweet spot. I try all three and see what gets me closest to what I need. In this case, with this data, token_sort_ration worked best (gave me the consistently higher score).
Value From Source List Address
Value From Detailed List Address
Token Sort Ratio
Fort Irwin Military Base
93 Goldstone Rd (Ft. Irwin)
Fort Irwin Military Base
421 Discovery Drive, Paddy’s River District
Fort Irwin Military Base
Ctra. M-531 Robledo de Chavela a Colmenar del Arroyo, Km 7.1
Fort Irwin Military Base
4800 Oak Grove Drive
The get_series_match function returns match_value = “93 Goldstone Rd (Ft. Irwin)” in this example.
Script output with print enabled (stdout_details= True):
>>>>>>>>>>>>> Comparing row value Fort Irwin Military Base with additional detail series value 93 Goldstone Rd (Ft. Irwin)
Fuzzy Ratio: 35
Fuzzy Partial Ratio: 38
Fuzzy Token Sort Ratio: 50 of type <class 'int'>
Fuzzy Match Found for row:
Fort Irwin Military Base
with series value:
93 Goldstone Rd (Ft. Irwin)
with Fuzzy Token Sort Ratio of 50!
>>>>>>>>>>>>> Comparing row value Fort Irwin Military Base with additional detail series value 421 Discovery Drive, Paddy's River District
Fuzzy Ratio: 32
Fuzzy Partial Ratio: 38
Fuzzy Token Sort Ratio: 33 of type <class 'int'>
>>>>>>>>>>>>> Comparing row value Fort Irwin Military Base with additional detail series value Ctra. M-531 Robledo de Chavela a Colmenar del Arroyo, Km 7.1
Fuzzy Ratio: 24
Fuzzy Partial Ratio: 33
Fuzzy Token Sort Ratio: 24 of type <class 'int'>
>>>>>>>>>>>>> Comparing row value Fort Irwin Military Base with additional detail series value 4800 Oak Grove Drive
Fuzzy Ratio: 27
Fuzzy Partial Ratio: 25
Fuzzy Token Sort Ratio: 27 of type <class 'int'>
match_value is 93 Goldstone Rd (Ft. Irwin)
Line 66 performs the critical test. I knew from looking at all the ration values that token_sort_ration was what I wanted to use in my logic test and by looking at the ratios or scores I knew that a score of 50 or better got me a good match. The rest is just like the exact match test. I set the match_value and break out of the loop.
Line 78 returns the match_value back to the calling statement and inserted that value into the new column in the appropriate row. Note that in line 74 if match_value never gets set in the for loop (the search), that means a match could not be made and so it gets set to “No match found”.
it is important to note that the question is not “should I learn any?” but rather “how much should I learn?”. The new Cisco DevNet Certifications help us answer that question. Let me share my journey to that conclusion.
In early February I decided to take the DevNet Associates Exam. I scheduled it for the first slot available once it went live on February 24th, 2020.
I had a study plan
Vacation & study
Back for a week & mostly study
Day before test nothing but study
The study plan execution was somewhat different
Study during vacation was contingent on downloading some content before I got to my destination where there would be no Internet access. For various reasons, that download didn’t happen and neither did any studying.
Some unforeseen events took place the week I came back so no studying except for listening to my CBT Nuggets course while I was driving from San Francisco to Los Angeles.
The day before the test I was still in Los Angeles and the only studying was more listening to my CBT Nuggets course at 1.6x speed as I headed North. I was too tired to make it all the way home so I had to go through San Jose traffic to make my 9:30AM test (listening to my CBT Nuggets course at 2x speed).
Somehow, despite all of that, I managed to pass the DevNet Associates exam and be one of the first 500 people to pass a DevNet exam. Let me explain why.
My true study plan
Working with network automation since 2014
The CBT Nuggets course
First, let me give kudos to the CBT Nuggets team. They had one of the few courses available for the Cisco Certified DevNet Associate 200-901 DEVASC exam (that I could find anyway). I recommend this course even if you are not studying for the exam. It was a terrific refresher, learned some new things, and filled in some gaps I didn’t realize I had!
I came away from the experience with two conclusions.
The test content accurately reflected useful skills one obtains by doing network automation
The test and study material are really focused on “teaching you to fish” (from the saying “if you give a hungry man a fish, you feed him for a day, but if you teach him how to fish, you feed him for a lifetime“)
Skills I had going into the test from real-world experience and training
The only reason I passed was experience in networking and in network automation. Specifically, experience and familiarity with:
Serialization protocols (YAML, JSON, XML)
A network engineers’ knowledge of Python
APIs (REST in particular)
how to interact with them and how to read their documentation
RESTCONF, NETCONF, and YANG
Ansible and how to read Ansible module documentation
A basic knowledge of Linux and Bash
These are all skills I picked up at some level over the last 5 years in the course of automating tasks and workflows for my projects.
Given everything that transpired, I fully expected to fail. Without any of the formal training and material I planned to study, I figured I would get killed in the “Answer the Cisco Way” category alone. Even though I’ve worked with all of the topics listed above in one form or fashion I was sure there would be some special Cisco spin that I would get wrong.
I was pleasantly surprised and wrong. Cisco did a very good job with this test. It’s not a test of the “Cisco Way” or a test of your ability to memorize but a genuine attempt at testing your skill in this area. If you are familiar with the topics on the exam blueprint, know how to use documentation, and have hands on experience with the topics you will be successful.
If you think about it, the only reason I passed was because I have experience actually automating and the topics were ones highly relevant to day to day network automation. The CBT Nuggets course refreshed the stuff I knew and filled in some gaps for technologies I had not worked with directly BUT I could figure out how to work with them because I knew how to use the documentation and I had experience with APIs in general.
The exam looks to test your real world skills and knowledge. It tests your ability to solve a problem with a set of tools and information. These skills will serve you well regardless of vendor.
Use the exam blueprint
I don’t advise my study path but do advise gettingstarted on a path that will get you some of these basic skills. You are going to need them.
I believe every network engineer has been (or should be) asking (struggling with) the following question:
How much of this automation stuff should I know to be successful as a network engineer in this new era of network programmability and automation?
I think this DevNet Associates exam helps to quantify that answer. With this base skill set you will know if you want to move further into programmability & automation (i.e create automation) or if you only want to consume and execute automation. These are the basic skills you will need to do that.
I figure if you are reading this you’ve got this one covered!
* What is a “network engineers’ knowledge of Python”, you ask? Here is how I answer that question: Basic python (language, object types, program flow control, using modules) and some familiarity with the common modules used for network programmability.
I tend to assess automation tools in four different contexts which is, in fact, a very general networking and automation workflow:
How easy is it to find out about the network, document its configuration (the configuration of a device itself) and state (show commands “snapshotting” its state)?
How easy is it to generate configurations for a device, given a template?
How easy is it to apply a set of configuration commands to one or more devices based on criteria?
Verification & Testing
How easy is it to audit and validate configurations and script results?
Yes, unglamorous though this may be it is a vital function for any network engineer and I would say doubly so with automation.
We looked at Step 1, discovery, in the introduction to Nornir. This is step 2 in that workflow as I become more familiar with Nornir.
In my initial notes about Nornir I mentioned a few areas where Nornir really seemed to shine. Since then, I’ve had occasion to truly appreciate its native python roots. I recently worked with a Client where I was not able to install Ansible in their environment but they had no issues with Python. Nornir saves the day!
In keeping with my “assessment methodology” (trust me that sounds far more rigorous than it is) my first use of Nornir (then called Brigade) involved using Napalm get_facts against a couple of network devices and then decomposing the returned data (figuring out what it is and how to get to the data). In this way, I was easily able to discover key facts about all the network devices in my inventory and return them as structured data.
Why do we talk about “structured data” so often? Its our way of saying you don’t have to parse the ever changing stream of data you get back from some network devices. Perhaps it is more accurate to say that someone has already parsed the data for us and is returning it in a nice data structure that we can easily manipulate (a list, a dictionary, or more commonly a combination of both). For todays task we are going to parse the unstructured data we get back from each device ourselves so we can truly appreciate all the heavy lifting tools like Napalm do for us.
It drove me crazy that the first thing everyone always taught in Ansible was how to generate configs because that is not what I found powerful about it. For quite a while all anyone (in the networking community at least) ever learned to do with Ansible was generate configs! So I was pleased that most of the early Nornir examples started with what I call “discovery”. However, now it is time to look at configuration creation with Nornir.
Let’s get started.
I have a simple task I want to accomplish. I need to evaluate all of my switches and remove any vlans that are not in use. I also want to make sure I have a set of standard vlans configured on each switch:
10 for Data
100 for Voice
300 for Digital Signage
666 for User Static IP Devices
We will take this one step at a time.
First we will query our devices for the data that we need to help us decide what vlans to keep and what vlans to remove.
We will then take that data and generate a customized configuration “snippet” or set of commands that we can later apply to each device to achieve our desired result.
Each switch will only have vlans that are in use and a standard set of vlans supporting voice, data, digital signage, and user devices with static IPs. As an added wrinkle, I have some devices that are only accessible via Telnet (you would be surprised at how often I find this to be the case.)
I’m not doing too much here with idempotency or “declarative” networking but I find that I understand things a bit better if I look at it from the current “classical” perspective and then look at how these tools can help leapfrog me into a much more efficient way of doing things.
Nornir, and python of course, provide our ready made inventory, connection, and task environment. With Nornir in place I can now perform tasks on any subset of my inventory. We won’t cover filtering here but know it is an available feature.
Napalm easily accessible to us via Nornir provides the connectivity method (napalm_cli) that allows us to query, real time, our devices and obtain the data we need to achieve our “vlan cleanup & standardization” task.
TextFMS and NetworkToCode parsing templates allow us to extract our data as structured data so we can easily manipulate it and apply logic. We need this because napalm does not yet make available a “get vlans” getter and so we have to do this ourselves.
Note that for some devices we might be able to use the interface getter for this data but I think there is great value in knowing how to do this ourselves should the need arise. We can’t have Mr. Barroso and team do all of our work for us!
Jinja2 also available to us via the Nornir framework will allow us to generate the customized configuration snippets.
Let see where they come into play in our task breakdown
Query devices for data
Environment set up via Nornir
Connectivity method used via a standard Nornir task using Napalm CLI
Analyze the data retrieved from each device and determine a vlan “disposition” (keeping or removing)
Taking the data provided by the Napalm CLI (and the “show vlan” command we sent) we were able to quickly parse it via TextFSM and an existing TextFSM Template from the NetworkToCode TextFSM Template Repository
With our data now in a python data structure we were able to apply our “business rules logic” to get an understanding of the changes required in each device.
== Parsing vlan output for device arctic-as01 using TextFSM and NetworkToCode template.
VLAN_ID NAME STATUS TOTAL_INT_IN_VLAN ACTION
1 default active 7 Keeping this vlan
10 Management_Vlan active 1 Keeping this vlan
20 Web_Tier active 0 This vlan will be removed!
30 App_Tier active 0 This vlan will be removed!
40 DB_Tier active 0 This vlan will be removed!
Generate a customized set of configuration commands to achieve our desired vlan state
Using the built in Nornir task that allows us to generate configurations based on Jinja2, we used the logic above to generate specific configuration commands for each device that, when applied, will achieve our desired state.
Here is the resulting configuration snippet for this particular device.
! For device arctic-as01
no vlan 20
no vlan 30
no vlan 40
As you can see, Nornir is bringing together the tools and functions that we use for day to day network automation tasks under one native Python wrapper. Where we need some customization or a tool does not exist it is a simple matter of using Python to bridge that gap!
Lets add a little more functionality to our original repository and try to gain a better understanding of Nornir.
nornir (formerly brigade) – A new network automation framework
Before getting started, let me say that I’m big fan of Ansible. It is one of my go-to automation frameworks. Having said that, there have been use cases where I’ve run into some of the limitations of Ansible and to be fair some of those limitations may have been my own.
By limitations, I don’t mean I could not do what I wanted but rather to do what I wanted got a bit more complex than perhaps it should have been. When I go back in 6 months, I’ll have no idea how I got it to work. These use cases often involve more complex logic than what Ansible handles with its “simpler” constructs and Domain Specific Language (DSL) .
So I was very intrigued to hear that the following automation heavyweights have been working on a new automation framework, Nornir:
David Barroso (think NAPALM – the python library not the sticky flammable stuff)
Kirk Byers (think Netmiko, Netmiko tools, and teacher extraordinaire – I can say that with full confidence as I’m pretty sure I’ve taken every one of his courses – some twice!)
As an engineer one of my favorite question is “What problem are we trying to solve?” and here is my answer to that question when applied to Nornir.
Simpler, more complex logic
An oxymoron? Certainly. Read on and I will try to explain.
By using pure Python, this framework solves the complex logic frustrations you may ultimately encounter with Ansible. If you can do it with python or have done it with python you are good to go. Thats not to say you can’t take your python script and turn it into an Ansible module but Nornir may save you that step.
Domain specific languages can be both a blessing and a curse. They can allow you the illusion that you are not programing and so facilitate getting you started if “programming” is not your cup of tea. They can help you get productive very quickly but eventually you may hit a tipping point where the cost of doing what you need to do with the tools and features in the DSL is too high in terms of complexity and supportability. Nornir “simplifies” that complex logic by allowing you access to all the tools you have in native python. As a side, and not insignificant, benefit you might actually remember what your code does when you get back to it in 6 months.
Native on all platforms
Many companies only provide Windows laptops and so I’ve always tried to be very mindful of that when developing solutions.
Scenario: I’ve got all of these Ansible play books that we can use to standardize network discovery, build configurations, apply configurations but most of my colleagues have Windows laptops and while I was able to develop and run these on my Mac where I can easily run an Ansible control server now we need to get an Ansible control server on a bunch of Windows laptops (this is not natively supported by Ansible).
I spent a Sunday afternoon dabbling in Nornir and it was well worth the time. It took me about 45 minutes to get things set up on my Windows system and run the example Patrick Ogenstad included in his post. While Nornir is said to support Python 2.7 (but recommends Python 3.6) I did have installation issues even with the latest pip installed. That was a significant part of the 45 minutes. Once I set up a Python3 virtual environment it worked flawlessly. You can see my work in this GitHub repository.
This is an exciting new framework with a great deal of promise which we can add to our automation arsenal!
Over the next few weeks (or months ) I’ll continue to familiarize myself with Nornir and report back.
This was originally published May 7, 2018 on Linkedin but has been updated to support the latest Nornir and scripts have been renamed so that there is no confusion between brigade and nornir. But let me say that this renaming is tied with the renaming of Cisco’s Spark platform to Webex Teams as the worst ever, or at least the last decade.
My original title for this article was going to be *Decomposing Pandas* as a follow on to *Decomposing Data Structures* but I was advised against that name. Go figure.
One of the things I love most about Python is that its always waiting for me to get just a little bit better so it can show me a slightly smarter way to do something. Pandas is the latest such example.
Pandas is a powerful data science Python library that excels at manipulating multidimensional data.
Why is this even remotely interesting to me as a network engineer?
Well, thats what Excel does, right?
I spend more time than I care to admit processing data in Excel. I find that Excel is always the lowest common denominator. I understand why and often I’m a culprit myself but eventually one grows weary of all the data being in a spreadsheet and having to manipulate it. I’m working on the former and Pandas is helping on the latter.
Google around enough for help on processing spreadsheets and you will come across references to the Pandas Python module.
If you are anything like me, you go through some or all of these stages:
You dismiss it as irrelevant to what you are trying to do
You dismiss it because its seems to be about big data, analytics, and scientific analysis of data (not your thing right?)
As you continue to struggle with what got you here in the first place (there has got to be a better way to deal with this spreadsheet data) you reconsider. So you try to do some processing in Pandas and pull a mental muscle…and what the heck is this NaN thing that keeps making my program crash? Basically, you find yourself way way out of your comfort zone (well..I did)!
You determine that your limited Python skills are not up to something quite this complex…after all, you know just enough Python to do the automation stuff you need to do and you are not a data scientist.
Finally, in a fit of desperation as you see all the Excel files you have to process, you decide that a python module is not going to get the better of you and you give it another go!
So here I am, on the other side of that brain sprain, and better for it, as is usually the case.
What is possible with Pandas…
Once you get the hang of it, manipulating spreadsheet-like data sets becomes so much simpler with Pandas. In fact, thats true for any data set, not just ones from spreadsheets. In fact, in the examples below, the data set comes from parsing show commands with TextFSM.
Knowing how to work with Pandas, even in a limited fashion as is the case with me, is going to be a handy skill to have for any Network Engineer who is (or is trying to become) conversant in programmability & automation.
My goal here is not to teach you Pandas as there is quite alot of excellent material out there to do that. I’ve highlighted the content which helped me the most in the “Study Guide” section at the end.
My goal is to share what I’ve been able to do with it as a Network Engineer, what I found most useful as I tried to wrap my head around it, and my own REPL work.
Lets look at something simple. I need to get the ARP table from a device and “interrogate” the data.
In this example, I have a text file with the output of the “show ip arp” command which I’ve parsed with TextFSM.
Here is the raw data returned from the TextFSM parsing script:
Note: don’t read anything into the variable name strained. The function I use to parse the data is called textfsm_strainer because I “strain” the data through TextFSM to get structured data out of it so I put the resulting parsed data from that function into a variable called “strained”.
Here is that data in a Pandas Data Frame:
# strained is the parsed data from my TextFSM function and the first command below
# loads that parsed data into a Pandas Data Frame called "df"
In : df = pd.DataFrame(strained, columns=strainer.header)
In : df
PROTOCOL ADDRESS AGE MAC TYPE INTERFACE
0 Internet 10.1.10.1 5 28c6.8ee1.659b ARPA Vlan1
1 Internet 10.1.10.11 4 6400.6a64.f5ca ARPA Vlan1
2 Internet 10.1.10.10 172 0018.7149.5160 ARPA Vlan1
3 Internet 10.1.10.21 0 a860.b603.421c ARPA Vlan1
4 Internet 10.1.10.37 18 a4c3.f047.4528 ARPA Vlan1
5 Internet 10.10.101.1 - 0018.b9b5.93c2 ARPA Vlan101
6 Internet 10.10.100.1 - 0018.b9b5.93c1 ARPA Vlan100
7 Internet 10.1.10.102 - 0018.b9b5.93c0 ARPA Vlan1
8 Internet 184.108.40.206 4 28c6.8ee1.6599 ARPA Vlan1
9 Internet 10.1.10.170 0 000c.294f.a20b ARPA Vlan1
10 Internet 10.1.10.181 0 000c.298c.d663 ARPA Vlan1
I now have a spreadsheet like data structure with columns and rows that I can query and manipulate.
My first question:
What are all the IPs in Vlan1?
Before Pandas, I would initialize an empty list to hold the one or more IPs and then I would iterate through the data structure (strained in this example) and where the interface “column” value (which in this list of lists in the strained variable is at index 5) was equal to ‘Vlan1’ I appended that IP to the list. The IP is in index 1 in each item the strained list.
# Using Python Only
print("\n\tUsing Python only..")
vlan1ips = 
for line in strained:
if line == 'Vlan1':
The resulting output would look something like this:
For those more conversant with Python, you could say that list comprehension is just as efficient.
# Using list comprehension print("Using Python List Comprehension...") lc_vlan1ips = [line for line in strained if line == 'Vlan1' ]
Using List Comprehension: ['10.1.10.1', '10.1.10.11', '10.1.10.10', '10.1.10.21', '10.1.10.37', '10.1.10.102', '220.127.116.11', '10.1.10.170', '10.1.10.181']
So yes..list comprehension gets us down to one line but I find it a bit obscure to read and a week later I will have no idea what is in line or line.
I could turn the data into a list of dictionaries so that rather than using the positional indexes in a list I could turn line into line[‘IP_ADDRESS’] and line into line[‘INTERFACE’] which would make reading the list comprehension and the basic python easier but now we’ve added lines to the script.
Finally, Yes its one line but I’m still iterating over the data.
Pandas is set up to do all the iteration for me and lets me refer to data by name or by position “out of the box” and without any extra steps.
Lets decompose the one line of code:
If you think of this expression as a filter sandwich, the df[‘ADDRESS’] and .values are the bread and the middle .loc[df[‘INTERFACE’]] == ‘Vlan1’] part that filters is the main ingredient.
Without the middle part you would have a Pandas Series or list of all the IPs in the ARP table. Basically you get the entire contents of the ‘ADDRESS” column in the data frame without any filtering.
When you “qualify” df[‘ADDRESS’] with .loc[df[‘INTERFACE’]] == ‘Vlan1’] you filter the ADDRESS column in the data frame for just those records where INTERFACE is ‘Vlan1’ and you only return the IP values by using the .values method.
Now, this will return a numpy.ndarray which might be great for some subsequent statistical analysis but as network engineers our needs are simple.
I’m using iPython in the examples below as you can see from the “In” and “Out” line prefixes.
In : pandas_vlan1ips = df['ADDRESS'].loc[df['INTERFACE'] == 'Vlan1'].values
In : type(pandas_vlan1ips)Out: numpy.ndarray
I would like my list back as an actual python list and thats no problem for Pandas.
In : pandas_vlan1ips = df['ADDRESS'].loc[df['INTERFACE'] == 'Vlan1'].to_list()
I’d like to add a column of data to our Data Frame with the Vendor OUI for each MAC address.
In the one line below, I’ve added a column to the data frame titled ‘OUI’ and populated its value by performing a lookup on each MAC and using the result from the get_oui_macvendors function.
df['OUI'] = df['MAC'].map(get_oui_macvendors)
The left side of the equation references a column in the data Fram which does not exist so it will be added.
The right side takes the existing MAC column in the data frame and takes each MAC address and runs it through the get_oui_macvendors function to get the Vendor OUI and “maps” that result into the new OUI “cell” for that MACs row in the data frame.
Now we have an updated Data Frame with a new OUI column giving the vendor code for each Mac.
In : df
PROTOCOL ADDRESS AGE MAC TYPE INTERFACE OUI
0 Internet 10.1.10.1 5 28c6.8ee1.659b ARPA Vlan1 NETGEAR
1 Internet 10.1.10.11 4 6400.6a64.f5ca ARPA Vlan1 Dell Inc.
2 Internet 10.1.10.10 172 0018.7149.5160 ARPA Vlan1 Hewlett Packard
3 Internet 10.1.10.21 0 a860.b603.421c ARPA Vlan1 Apple, Inc.
4 Internet 10.1.10.37 18 a4c3.f047.4528 ARPA Vlan1 Intel Corporate
5 Internet 10.10.101.1 - 0018.b9b5.93c2 ARPA Vlan101 Cisco Systems, Inc
6 Internet 10.10.100.1 - 0018.b9b5.93c1 ARPA Vlan100 Cisco Systems, Inc
7 Internet 10.1.10.102 - 0018.b9b5.93c0 ARPA Vlan1 Cisco Systems, Inc
8 Internet 18.104.22.168 4 28c6.8ee1.6599 ARPA Vlan1 NETGEAR
9 Internet 10.1.10.170 0 000c.294f.a20b ARPA Vlan1 VMware, Inc.
10 Internet 10.1.10.181 0 000c.298c.d663 ARPA Vlan1 VMware, Inc.
Lets interrogate our data set further.
I want a unique list of all the INTERFACE values.
In : df['INTERFACE'].unique()
Out: array(['Vlan1', 'Vlan101', 'Vlan100'], dtype=object)
How about “Give me a total count of each of the unique INTERFACE values?”
Lets take it down a level and get unique totals based on INTERFACE and vendor OUI.
In : df.groupby(['INTERFACE','OUI']).size()
Vlan1 Apple, Inc. 1
Cisco Systems, Inc 1
Dell Inc. 1
Hewlett Packard 1
Intel Corporate 1
VMware, Inc. 2
Vlan100 Cisco Systems, Inc 1
Vlan101 Cisco Systems, Inc 1
I could do this all day long!
I’ve just scratched the surface of what Pandas can do and I hope some of the examples I’ve shown above illustrate why investing in learning how to use data frames could be very beneficial. Filtering, getting unique values with counts, even Pivot Tables are possible with Pandas.
Don’t be discouraged by its seeming complexity like I was.
Don’t discount it because it does not seem to be applicable to what you are trying to do as a Network Engineer, like I did. I hope I’ve shown how very wrong I was and that it is very applicable.
In fact, this small example and some of the other content in this repository comes from an actual use case.
I’m involved in several large refresh projects and our workflow is what you would expect.
Snapshot the environment before you change out the equipment
Perform some basic reachability tests
Replace the equipment (switches in this case)
Perform basic reachability tests again
Compare PRE and POST state and confirm that all the devices you had just before you started are back on the network.
Troubleshoot as needed
As you can see if you delve into this repository, its heavy on APR and MAC data manipulation so that we can automate most of the workflow I’ve described above. Could I have done it without Pandas? Yes. Could I have done it as quickly and efficiently with code that I will have some shot of understanding in a month without Pandas? No.
I hope I’ve either put Pandas on your radar as a possible tool to use in the future or actually gotten you curious enough to take the next steps.
I really hope that the latter is the case and I encourage you to just dive in.
The “Study Guide” links below have some very good and clear content to get you started. Of all the content out there, these resources were the most helpful for me.
Let me also say that it took a focused effort to get the point where I was doing useful work with Pandas and I’ve only just scratched the surface. I was worth every minute! What I have described here and in this repository are the things that were useful for me as a Network Engineer.
Once you’ve gone through the Study Guide links and any others that you have found, you can return to this repository to see examples. In particular, this repository contains a Python script called arp_interrogate.py.
It goes through loading the ARP data from the “show ip arp” command, parsing it, and creating a Pandas Data Frame.
It then goes through a variety of questions (some of which you have seen above) to show how the Data Frame can be “interrogated” to get to information that might prove useful.
There are comments throughout which are reminders for me and which may be useful to you.
The script is designed to run with data in the repository by default but you can pass it your own “show ip arp” output with the -o option.
Using the -i option will drop you into iPython with all of the data still in memory for you to use. This will allow you to interrogate the data in the Data Frame yourself..
If you would like to use it make sure you clone or download the repository and set up the expected environment.
Options for the arp_interrogate.py script:
(pandas) Claudias-iMac:pandas_neteng claudia$ python arp_interrogate.py -h usage: arp_interrogate.py [-h] [-t TEMPLATE_FILE] [-o OUTPUT_FILE] [-v] [-f FILENAME] [-s] [-i] [-c] Script Description optional arguments: -h, --help show this help message and exit -t TEMPLATE_FILE, --template_file TEMPLATE_FILE TextFSM Template File -o OUTPUT_FILE, --output_file OUTPUT_FILE Full path to file with show command show ip arp output -v, --verbose Enable all of the extra print statements used to investigate the results -f FILENAME, --filename FILENAME Resulting device data parsed output file name suffix -s, --save Save Parsed output in TXT, JSON, YAML, and CSV Formats -i, --interactive Drop into iPython -c, --comparison Show Comparison Usage: ' python arp_interrogate.py Will run with default data in the repository' (pandas) Claudias-iMac:pandas_neteng claudia$
A Quick Introduction to the “Pandas” Python Library
THE SOFTWARE in the mentioned repository IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
I’m often asked variants of “What language would you recommend when getting started with Network Automation and how would you get started?
As with most things in IT, the answer requires context.
In general, right now I would still pick Python. Go is getting popular but still can’t compete with the huge body for work and modules and examples available for Python.
Before getting to some suggestions on how I would start learning Python if I had it to do over, let me mention that there are other ways to get started with automation that also involve learning “new syntax” but may seem simpler (at first). Ansible and other automation frameworks use Domain Specific Language (DSL) which may be simpler to learn if you are doing very basic things or things that have examples in the “Global Data Store” more commonly known as Google & the Internet!
So if you are new to programming you may want to start with Ansible…lets you do lots of basic automation with more of an abstracted language. All of the resources I mention below also have Ansible training. Try to get one that is recent and geared towards networking as many Ansible courses are geared towards servers and while useful, you will wind up spending more time ramping up.
All roads lead to Pythontoday
Having said that, once you start trying to do more complex things you may run into limitations or issues where some Python knowledge would be useful. Ansible is written in Python. Also, if you are new to linux, then while you may have saved some time with Ansible, you will need to invest some time figuring out how to get around in Linux as the Ansible control host runs on Linux and its variants but there are many other reasons why being conversant with Linux as a network engineer is important.
If you are serious about “upgrading” your skill set as a network engineer make sure you are somewhat comfortable with Linux basics and, while Ansible can get you going with some simple immediate tasks you may need to do for work, get started with Python as soon as you can.
One more point. In the interest of getting you productive as quickly as possible, I have built a series of Docker images that provide an Ansible control server as well as a python programing environment with many of the modules network engineers tend to use.
I would start with Kurt Byers free Learning Python course.
Note: I have no affiliation with him or his company but he speaks to Network Engineers better than anyone I’ve seen and it is an on-line course of recorded lessons (with a lab), one lesson a week, so from a time perspective is very easy to “consume” his course while still having an opportunity to interact and ask questions. The course with the community forum is well worth the additional cost.You will make new acquaintances who are interested in what you are doing and you sometimes run into old friends.
Your next step can take you along various paths. All of these are paid courses and can be done on demand or in a class. There are many options here but I’m listing the options with which I have first hand experience.
I’m also a BIG fan of Udemy and they have some very good courses as well at very reasonable price points. Always listen to the free examples and always check the dates before you purchase. Make sure you find out if the Python course is using Python3 as there are many courses there which use Python 2 and if you are just staring, please start with Python3.!
These courses are at a nominal cost, self-paced but, again, make sure you get a current one that is Python 3.
A quick start guide for using the purpose built Docker images for Ansible and Python
Built for Network Engineers by a Network Engineer
Over the last few years I’ve built up a repository of Docker images to help me learn Ansible. If you are new to Ansible you may not know that while Ansible can control all manner of devices (Windows, Linux, Network, Virtual or Bare Metal, Cloud, etc.) the Ansible control server itself only runs on Linux. There are ways around that now (Win10 Subsystem for Linux and Cygwin) but they are not supported. If you are trying to get started, you first have to stand up a control server. Depending on your familiarity with Linux and Virtualization technology you can spend quite a bit of time going down different avenues just to get to the point where you can run a playbook.
Docker was the answer for me.
These Ansible containers have been built over the years to provide a quick alternative to running an Ansible Control server on a local virtualization VM (VirtualBox or VMware). A container is also handy if you need an Ansible Control server to use via a VPN connection. You will see that many of the test playbooks are playbooks designed to perform discovery on a set of devices (aka run lots of show commands and save the output) and so a common practice is to VPN in (or connect directly) to a client network and quickly perform this discovery task.
I’ve kept the various images with different Ansible versions so I can test playbooks on specific versions. In many cases I work with clients who use a specific version of Ansible and so its handy to be able to test locally on the version they use.
This “Quick Start” cheat sheet is intended to get you up and running quickly with the various Ansible containers. Each offers an Ansible control server and a python environment.
The following Docker images are available on Docker Hub
To run any Docker container (aka Docker image) you will need to install Docker Desktop on your Mac or Windows system (see note below about Docker Desktop for Windows and consider Docker Toolbox if you run other virtualization software) or Docker Engine on your linux host. You can use the free Docker Community Edition.
The instructions below focus on Mac and Windows operating systems as those tend to be the most prevalent among Network Engineers (at least the ones I know!).
Installing Docker Desktop on Windows
WARNING: The Docker Desktop application is only supported on Windows 10 Pro (or better) 64-bit and requires Hyper-V and the Containers Window features to be enabled.
This means that other Virtualization software that does not support HyperV will not work work (i.e. VMware Workstation and VirtualBox) while you have Hyper-V enabled and Docker Desktop won’t work when you have have Hyper-V disabled (but VirtualBox and VMware will).
If you have existing Virtualization software installed and which you use, Docker Toolbox for Windows is still available.
Before starting make sure that Docker is installed and running on your Mac.
2. Open a terminal window and use the docker run -it command to start the container on your Ma
Full command to start an interactive session
docker run -it cldeluna/disco-immigrant
The first time you execute this command, the docker image will download and then put you into an interacive bash shell in the container.
-i, --interactive Keep STDIN open even if not attached
`-t, --tty Allocate a pseudo-TTY``
This will basically take over your terminal window so if you need to do something else on your system open up a new or different terminal window. Check the command cheat sheet for alternatives like using the -dit option to run in the background and the docker exec command to “attach” to the running container.
If you have not downloaded the image using the docker pull <image> command the docker run command will know and pull it down for your. Once the download is complete and the container is running you will notice that the prompt in your terminal window has changed.
It will look something like “root@c421cab61f1f:/ansible_local“.
Claudias-iMac:disco-immigrant claudia$ docker run -it cldeluna/disco-immigrant
3. Start looking around
Check the version of ansible on the container. In the example below we are using the disco-immigrant image which comes with ansible 2.9.1.
Several repositories are cloned into the container to get you started quickly. Check out the Ansible playbook repositories and change directory into one to see the example playbooks and try one!…you can find details in the “Run one of the ready built Playbooks!” At this point, once you are in a working docker CLI the process is basically the same across all operating systems.
Always do a “git pull” in any of the cloned repositories to make sure you are running the latest scripts and playbooks.
Run your first playbook! You don’t need to bring up any device as many of the playbooks use the DevNet AlwaysOn Sandbox devices.
If you cd or change directory into the cisco_ios directory you can get started with some basic Playbooks.
Claudias-iMac:disco-immigrant claudia$ docker run -it cldeluna/disco-immigrant root@c421cab61f1f:/ansible_local# root@c421cab61f1f:/ansible_local# ls ansible2_4_base cisco_aci cisco_ios root@c421cab61f1f:/ansible_local# cd cisco_ios root@c421cab61f1f:/ansible_local/cisco_ios# ls ansible.cfg ios_all_vrf_arp.yml ios_show_cmdlist_zip.yml logs filter_plugins ios_facts_lab.yml ios_show_lab.yml nxos_facts_lab.yml group_vars ios_facts_report.yml ios_show_lab25.yml nxos_show_cmdlist.yml hosts ios_show_cmdlist.yml ios_show_lab_brief.yml templates root@c421cab61f1f:/ansible_local/cisco_ios#
Intel(R) Core(TM)i7-6700K CPU @ 4.00GHz, 4008 Mhz, 4Core(s)…
Summary of Steps
Make sure Docker Desktop is installed and running
Open a terminal window or “default” VirtualBox VM console
Look around the ready built repositories which are cloned in the container to get you started quickly.
Docker Toolbox is quirky, no question about it. The desktop shortcuts often don’t work for me but going directly to the VirtualBox VM typically does. Open up VirtualBox and make sure the Docker Toolbox VM is running (it is actually called “default”!)
For me, what generally works is opening up the default VM console directly. Double-click on 1 in the image below to start the default container and open up the container VM console.
Start looking around! Check the version of ansible on the container. In the example below we are using the disco-immigrant image which comes with ansible 2.9.1. Check out the Ansible playbook repositories and change directory into one to see the example playbooks and try one!…you can find details in the “Run one of the ready built Playbooks!” At this point, once you are in a working docker CLI the process is basically the same across all operating systems.
Always do a “git pull” in any of the cloned repositories to make sure you are running the latest scripts and playbooks.
Run your first playbook! You don’t need to bring up any device as most use the DevNet AlwaysOn Sandbox devices.
You will see that the playbook saves different types of output into different text files.
cd to the logs directory and review the results.
root@c421cab61f1f:/ansible_local/cisco_ios# cd logs root@c421cab61f1f:/ansible_local/cisco_ios/logs# tree . |-- ios-xe-mgmt.cisco.com-config.txt |-- ios-xe-mgmt.cisco.com-raw-output.txt |-- ios-xe-mgmt.cisco.com-readable-show-output.txt `-- output_directory 0 directories, 4 files
4 At this point, you can start making these playbooks your own.
Update the hosts file and create your own group of devices. Update the show commands. Start your own Playbook now that you have an Ansible Control server you are ready to go!
Since this is a container, it will leverage your systems network connection so if you VPN into your lab for example, you can use Control Server on your system.
Tip: Mount a Shared Folder
All the work you do will be “captive” in your container unless you start your container with an option to share a folder between your desktop and the container.
That may not be a problem. I try to develop locally and then “git pull” updates inside the container and test but there are time when I want to try new things within the container and without this mounting option can’t get to the new files you created. You often have to copy them out and worst case you forget and destroy your container and lose work. So if the git push/pull model is not to your liking then the -v option is for you!
Note that this is more difficult to do using Docker Toolbox as there are several levels of abstraction.
But Cisco’s Application Centric Infrastructure (ACI) is a different animal altogether.
Cisco offers a simulator (hardware and VM) (check out the DevNet Always On APIC to see it in action) which is terrific for automation testing and configuration training but there is no data plane so testing routing, vpcs, trunks, and contracts is a non starter. For that you need the hardware…and so began my search for an ACI Lab for rent.
The compilation below is still a work in progress but I thought I would share my findings, so far, over the last 6 months.
First let’s set the context.
I was looking to rent a physical ACI lab (not a simulator) with the following requirements:
Gen2 or better hardware (to test ACL logging among other things) and ACI 4.0 or later
Accessible (Configurable) Layer 3 device or devices (to test L3 Outs)
Accessible (Configurable) Layer 2 device or devices (to test L2)
VMM integration (vCenter and ESXi Compute)
Pre configured test VMs
My own custom test VMs
A means of running Ansible and/or Python within the lab environment and cloning repositories
Going in, I didn’t have a timeframe in mind. I would take a 4 hour block of time or a week and that is still the case. I also realized that it was unlikely anything for rent would meet all of my requirements but that was the baseline I would use to assess the various offerings.
A lower cost option for just a few hours is handy for quick tests. Having a lab for a week is very nice and gives you the latitude to test without the time pressure.
Out of 13 possible options, I’ve tried 4. Two were very good and I would use them again and 2 were the opposite.
While the INE lab is a bit difficult to schedule (you have to plan ahead which isn’t compatible with my “immediate gratification” approach to things) it’s a terrific lab with configuration access to the L2/L3 devices which gives you good flexibility.
NterOne also offers a terrific lab and I was able to schedule it within a week of when I called. The lab is superbly documented and designed so as to make it very easy to understand. I got two pods/tenants which gave me some good flexibility in terms of exporting and testing contracts etc. The L2 and L3 devices are read only and pre-configured so those are a little limiting.
So far, no one is running Gen2+ equipment.
Almost all of the designs I have seen have single link L2/L3s so its difficult to test vpcs (and you generally need access to the other device unless its been preconfigured for you).
All the labs were running ACI 4.x
Global Knowledge has some interesting offerings and I was very excited initially but even getting the simplest answer was impossible. Like many larger companies, if you try to deviate from the menu it does not go well. I moved on.
INE, NterOne, and Firefly all spent the time understanding my requirements and offering solutions. Sadly Firefly was way out of my price range.
On a final note, I would avoid My CCIE Rack and CCIE Rack Rentals which may actually use the same lab. Documentation is terrible and I’ve tried 3 or 4 times and have gotten in about 50% of the time. The first time, I didn’t realize I needed to rent both of their DC labs (one has ACI and the other gives you access to the L2/L3 devices). The last time I rented a lab (both labs) they just simply cancelled my labs and never responded to emails either before or after. If you have money you would like to dispose of, send it here (Coral Restoration Foundation Curaçao) or some other worthy cause. A much better use of those funds I’d have to say.
If anyone has has good experience with an ACI Lab rental that I’ve not included here I would love to hear about it!
Kudos to INE and NterOne for great customer service and flexibility!
1. The INE Staff was open to allowing me to put some of my own repos and tools into the environment but when I scheduled the lab that became problematic. INE was very honorable and let me have the lab in the non-customized way for the week without charge since they were not able to honor my customization request at that time!!
2. The Student Jump box can be customized which was very nice (I had access to my GitHub repos) and Python was available although it was Python 2.7.
3. Cost is not unreasonable but there is a minimum of
4. students so unless you have 3 like minded friends it becomes very expensive. 4 I’ve always been a big fan of Global Knowledge but my interactions with them were not positive. I could not get even the most basic question answered (for example, did they have a money back guarantee or 30 day return policy since I was never able to get my more specific questions answered? I figured if I had 30 days to see if the lab met my requirements then I could test it out and find out for myself.)
5. Great customer service but the pricing was a non starter. $$$+ per day and it would have bene limited to business hours
6. When I first reached out with questions about their ACI lab they said it would not be available until late October (I assumed this year). When I reached out in November, they didn’t even answer the question so clearly this is still al work in progress.
Modern enterprise networking is going to require a level of structure and consistency that the majority of its networking community may find unfamiliar and perhaps uncomfortable. As a community, we’ve never had to present our designs and configuration data in any kind of globally consistent or even industry standard format.
I’m fascinated by all things relating to network automation but the one thing that eluded me was the discussion around data models (YANG, OpenConfig).
Early on, the little I researched around YANG led me to conclude that it was interesting, perhaps something more relevant to the service provider community, and a bit academic. In short, not something directly relevant to what I was doing.
Here is how I figured out that nothing could be further from the truth and why I think this is an area that needs even more focus.
You can also cut to the chase by going to the companion repository Data_Model_Design on GitHub where you can see a “proof of concept” taking a modified Cisco data model containing a handful of components and developing the high level diagram for those components and a sample Markdown design Document.
The Current Landscape
Since its earliest days as a discipline, networking (at least in the enterprise) has generally allowed quite a bit of freedom in the design process and its resulting documentation. That is one of the things I love about it and I’m certain I’m not alone in that feeling. A little island of creativity in an ocean of the technical.
For every design, I put together my own diagrams, my own documentation, and my own way to represent the configuration or just the actual configuration. Organizations tried to put some structure around that with a Word, Visio, or configuration text template, but often even that was mostly just for the purposes of branding and identification of the material. How many of us have been given a Word template with the appropriate logos on the title page and if you were lucky a few headings? Many organizations certainly went further requiring a specific format and structure so that there was consistency within the organization but move on to a different organization and everything was different.
The resulting design documentation sets were many and varied and locally significant.
In effect, the result was unstructured data. Unstructured or even semi structured data as text or standard output from a device or system is well know but this is unstructured data on a broader scale.
Design and Configuration Data
Over the last few years I’ve observed a pattern that I’m just now able to articulate. This pattern speaks to the problem of unstructured design and configuration data. The first thing I realized is that, as usual, I’m late to the party. Certainly the IETF has been working on the structured configuration data problem for almost 20 years and longer if you include SNMP! The Service Provider community is also working hard in this area.
For the purposes of this discussion let me summarize with a very specific example.
We have a text template that we need to customize with specific configuration values for a specific device:
!EXAMPLE SVI Template <configuration item to be replaced with actual value> interface Vlan< Vlan ID > description < SVI description > ipv6 address < IP Address >/< IP MASK> ipv6 nd prefix < Prefix >/< Prefix MASK > 0 0 no-autoconfig ipv6 nd managed-config-flag ipv6 dhcp relay destination < DHCP6 Relay IP >
If we are lucky we have this:
More often than not we have this:
The problem is a little broader but I think this very specific example illustrates the bigger issue. Today there is no one standard way to represent our network design and configuration data. A diagram (in Visio typically) is perhaps the de-facto standard but its not very automation friendly. I’ve had design and configuration data handed to me in Word, PowerPoint, Excel (and their open source equivalents), Text, Visio, and the PDF versions of all of those.
Let me be clear. I am not advocating one standard way to document an entire network design set…yet. I’m suggesting that automation will drive a standard way to represent configuration data and that should drive the resulting design documentation set in whatever form the humans need it. That configuration data set or data model should drive not just the actual configuration of the devices but the documentation of the design. Ultimately, we can expect to describe our entire design in a standard system data model but that is for a future discussion.
Structured design and configuration data
In order to leverage automation we need the configuration data presented in a standard format. I’m not talking configuration templates but rather the actual data that feeds those templates (as shown above) and generates a specific configuration and state for a specific device.
Traditionally, when developing a design you were usually left to your own devices as to how to go about doing that. In the end, you likely had to come up with a way to document the design for review and approval of some sort but that documentation was static (hand entered) and varied in format. Certainly not something that could be easily ingested by any type of automation. So over the last few years, I’ve developed certain structured ways to represent what I will call the “configuration payload”…all the things you need to build a specific working configuration for a device and to define the state it should be in.
All of this data should be in a format that could be consumed by automation to, at the very least, generate specific device configurations and, ideally, to push those configurations to devices, QA, and ultimately to document.
My experience over the last few years tells me we have some work ahead of us to achieve that goal.
The problem – Unstructured design and configuration data is the norm today
As a consultant you tend to work with lots of different network engineers and client engineering teams. I started focusing on automation over 4 years ago and during that time I’ve seen my share of different types of configuration payload data. I’m constantly saying, if you can give me this data in this specific format, look what can be done with it!
My first memorable example of this problem was over 2 years ago. The client at the time had a very particular format that they wanted followed to document their wireless design and the deliverable had to be in Visio. I put together a standard format in Excel for representing access point data (name, model, and other attributes). This structured data set in Excel (converted to CSV) would then allow you to feed that data into a diagram that had a floor plan. You still had to move the boxes with the data around to where the APs were placed but it saved quite a lot of typing (and time) and reduced errors. I demonstrated the new workflow but the team felt that it would be simpler for them to stick to the old manual process. I was disappointed to be sure but it was a bit of a passion project to see how much of that process I could automate. We had already standardized on how to represent the Access Point configuration data for the automated system that configured the APs so it was a simple matter of using that data for the documentation.
The issue was more acute on the LAN side of the house. On the LAN side the structured documentation format (also in Excel) was not an option. It fed all the subsequent stages of the process including ordering hardware, configurations (this was the configuration payload!), staging, QA, and the final documentation deliverable.
When fellow network engineers were presented with the format we needed to use, lets just say, the reception lacked warmth. I used Excel specifically because I thought it would be less intimidating and nearly everyone has some familiarity with Excel. These seasoned, well credentialed network engineers, many who were CCIEs, struggled. I struggled right along with them…they could not grasp why we had to do things this way and I struggled to understand why it was such an issue. It is what we all do as part of network design…just the format was a little different, a little more structured (in my mind anyway).
I figured I had made the form too complicated and so I simplified it. The struggle continued. I developed a JSON template as an alternative. I think that made it worse. The feedback had a consistent theme. “I don’t usually do it that way.” “I’ve never done it this way before.” “This is confusing.” “This is complicated.”
Lets be clear, at the end of the day we were filling in hostname, timezone information, vlans, default gateway, SVI IP/Mask, uplink Interface configuration, and allowed vlans for the uplinks. These were extremely capable network engineers. I wasn’t asking them to do anything they couldn’t do half asleep. I was only requiring a certain format for the data!
During these struggles I started working with a young engineer who had expressed an interest in helping out with the automation aspects of the project. He grasped the structured documentation format (aka the Excel spreadsheet) in very little time! So much so, that he took on the task of training the seasoned network engineers. So it wasn’t the format or it wasn’t just the format if a young new hire with very little network experience could not just understand it, but master it enough to teach it to others.
With that, the pieces fell into place for me. What I was struggling against was years of tradition and learned behavior. Years of a tradition where the configuration payload format was arbitrary and irrelevant. All you needed was your Visio diagram and your notes and you were good to go.
Unstructured configuration payload data in a variety of formats (often static and binary) is of little use in this brave new world of automation and I started connecting the dots. YANG to model data, Vendor Yang data models…OK…I get it now. These are ways to define the configuration payload for a device in a structured way that is easily consumed by “units of automation” and the device itself.
This does not solve the broader issue of unlearning years of behavior but it does allow for the learning of a process that has one standard method of data representation. So if that transition can be made I can get out of the data modeling business (Excel) and there is now a standard way to represent the data and a single language we can use to talk about it. That is, of course, the ideal. I suspect I’m not out of the data modeling business just yet but I’m certainly a step closer to being out of it and, most importantly, understanding the real issue.
The diagram above shows an evolution of the network design process. The initial design activities won’t change much. We are always going to need to:
Understand the business needs, requirements, and constraints
Analyze the current state
Develop solutions perhaps incorporating new technology or design options using different technologies
Model the new design
Present & Review the new design
In this next evolution, we may use many of the old tools, some in new ways. We will certainly need new tools many of which I don’t believe exist yet. As we document requirements in a repository and configuration payload in a data model, those artifacts can now drive:
An automated packaging effort to generate the design information in human readable formats that each organization wants see
Here the Design Document, Presentation, and Diagram are an output of the configuration payload captured in the data model (I’ve deliberately not used Word, PowerPoint, and Visio…)
The actual configuration of the network via an automation framework since by definition our data models can be consumed by automation.
All of it in a repository under real revision control (not filenames with the date or a version identifier tacked on)
As with any major technology shift, paradigm change, call it what you will, the transition will likely result in three general communities.
Those who eagerly adopt, evangelize, and lead the way
Those who accept and adapt to fit within the new model
Those who will not adapt
I’m sorry to say I’ve been wholly focused on the first community until now. It is this second “adapt community”, which will arguably be the largest of the three, that needs attention. These will be the network engineers who understand the benefits of automation and are willing to adapt but at least initally will likely not be the ones evangelizing or contributing directly to the automation effort. They will be the very capable users and consumers of it.
We need to better target skills development for them as the current landscape can be overwhelming.
Its also important to note that the tooling for this is woefully inadequate right now and is likely impeding adoption.
The solution may elude us for a while and may change over time. For me at least the next steps are clear.
I need to do a better job of targeting the broader network community who isn’t necessarily exited (yet) about all the automation but is willing to adapt.
I will start discussing and incorporating data models into the conversations and documentation products with my new clients and projects.
I will start showcasing the benefits of this approach in every step of the design process and how it can help improve the overall product.
If you want to see how some of these parts can start to fit together please visit my Data_Model_Design repository on GitHub where you can see a “proof of concept” taking a modified Cisco data model containing a handful of components and developing the high level diagram for those components and a sample Markdown design Document which I then saved to PDF for “human consumption”.
Don’t miss these resources
Always a fan of DevNet, the DevNet team once again does not disappoint.
The OpenConfig group bears watching as they are working on vendor agnostic real world models using the YANG language. This is very much a Service Provider focused initiative whose efforts may be prove very useful in the Enterprise space.