Been a few months since my last post as I’ve had a crazy busy summer/autumn and it will probably take a while longer until I get going again. Cisco has introduced their new revamped certifications track which looks hard but very interesting, I’ll probably give the DevOps-track a go next year. But for now I’ve recently renewed my CCNP R&S for another 3 years at least.
So what have I been up to the last few months?
I became a dad!
We bought a house!
And everything else feels pretty minor in comparison.. I’ve still been doing some coding though as I’ve attempted to build a smart home (project ongoing..) via Home Assistant and IoT.
Lots of hair pulling to get everything working but it’s slowly coming together now with more and more automation’s running to make everyday life a little simpler. I have a project open over at GitHub that’s updated now and then when I find some time to spare (happens very rarely now for some reason..). 🙂
Something as unusual as a non-networking post, but it was a fun project so thought I could do a short post about it. We’re soon moving a bit further out of town to a house so i’ve been eyeing an eletric bike for a while, but in the end decided to build my own as the bikes for sale here in Sweden is pretty bland – and tinkering with stuff is fun… 🙂
The bike I used is a 2016 Nishiki Pro Air which has a BSA 68mm bottom-bracket thread which is required to mount Bafangs middrive motors which I went with. For wider bottom brackets there are a few alternatives going up to 120mm, but BSA threading is required however. Electricbike.com has a great guide on this, very import to get this right or you will be stuck with a motor that doesn’t fit.
I ordered a Bafang BBSHD 1000W (~1500W Max), Jumbo Shark Battery 52v 14.8Ah HG2 (~44A Continous/55A Max discharge) and a 46t Lekkie Blingring. This is ooof cooourse limited to 250W & 25km/h however to keep it road legal here in Sweden. 🙂 But i’ve heard rumors that you can put a throttle on it and it will do ~60km/h even uphill with current gearing and some light pedaling, so it pretty much turns in to a rocket.
The installation was a breeze after getting the old bottom bracket out, hardest part was probably routing all the extra cables and making it look nice. I easily doubled the weight of the bike however.. The motor must weigh in around ~6kg + battery ~3-4kg, it’s very very heavy so let’s hope I never run out of juice and have to pedal back home uphill… 🙂
All finished up! Been using it for a month now and it’s crazy fun, the performance you get is so far beyond the ridiculously expensive pre built bikes – which still usually have cheap components and limited to 25km/h & 250/350w anyway.
The BBSHD has a pretty weird pedal assist however due to missing a torque sensor, so it will basically work less the harder you pedal, totally opposite of other electric bikes which I didn’t really like.
Not that I have, but a way to fix this is installing a throttle instead and do some reprogramming of the controller unit. There’s cheap cables to buy on ebay or you can build one yourself, the software is free however. I really recommend reading this page to learn more.
Some settings you could use is for example:
PAS 0 – I=100%, S= 0% PAS 1 – I= 50%, S= 72% PAS 2 – I= 71%, S= 72% PAS 3 – I=100%, S=100%
PAS designated assist level = 0 Throttle designated assist level = By Display’s Command Set throttle to “current” and start current to 1% for super smooth power delivery.
This will give three power levels for the throttle, 0 = off, 3 = max fun and turn off the annoying PAS. 🙂 I ordered my kit from Paul over at www.em3ev.com based in China which has excellent reviews and I highly recommend them myself, they build their batteries on order and can’t be compared to the stuff you get from aliexpress etc. Price is higher though but you get what you pay for!
Let’s continue playing around some more with NSO. In the last posts we’ve added our netsim-devices to NSO and managed to push out some simple config & tried rollbacks. Instead of specifying each single router everytime we want to manage them we can instead use device-groups for easier management.
Let’s start with creating two groups for our Core- & Edge-routers:
We can then use the group-name when we want to check if router are in sync f.ex.:
admin@ncs# devices device-group Core check-sync
sync-result {
device r0
result in-sync
}
sync-result {
device r1
result in-sync
}
We can also have groups in a group:
devices device-group all
device-group [ Core Edge ]
!
Let’s also take a look at creating a template that we can later call on to configure something generic on our device(s). By using “{$VARIABLENAME}” NSO gives us the ability to input our own variables when calling the specific template. Here’s an example of a simple VRF-template:
When we want to configure a new VRF we than only have to name our NEW_VRF-template and input the variables for CUSTOMER & VRF. NSO’s autocomplete/tab feature is excellent here and keeps track of available variables we can use.
admin@ncs(config)# devices device-group Edge apply-template template-name NEW_VRF variable { name CUSTOMER value 'GEKAS' } variable { name VRF value '22222' }
apply-template-result {
device r2
result ok
}
apply-template-result {
device r3
result ok
}
admin@ncs(config)# commit
Commit complete.
admin@ncs(config)#
admin@ncs(config)# devices device-group all apply-template template-name NEW_VRF variable { name CUSTOMER value 'MGMT' } variable { name VRF value '999' }
apply-template-result {
device r0
result ok
}
apply-template-result {
device r1
result ok
}
apply-template-result {
device r2
result ok
}
apply-template-result {
device r3
result ok
}
If we login and check our routers we can see that all routers now have the MGMT-vrf & Edge-routers (R2 & R3) also have a customer vrf GEKAS.
$ ncs-netsim cli-i r1
admin connected from 192.168.15.186 using ssh on labb-nso
r1> en
r1# show running-config vrf
vrf definition 999
description MGMT
rd 3301:999
address-family ipv4
route-target export 3301:999
route-target import 3301:999
exit-address-family
!
!
$ ncs-netsim cli-i r2
admin connected from 192.168.15.186 using ssh on labb-nso
r2> en
r2# show running-config vrf
vrf definition 22222
description GEKAS
rd 3301:22222
address-family ipv4
route-target export 3301:22222
route-target import 3301:22222
exit-address-family
!
!
vrf definition 999
description MGMT
rd 3301:999
address-family ipv4
route-target export 3301:999
route-target import 3301:999
exit-address-family
!
!
Let’s see what happens when we do a manual change and R2 gets out of sync.
r2# conf
Enter configuration commands, one per line. End with CNTL/Z.
r2(config)# vrf definition 999
r2(config-vrf)# description CustomerA
r2(config-vrf)# exit
admin@ncs# devices device-group all check-sync
sync-result {
device r0
result in-sync
}
sync-result {
device r1
result in-sync
}
sync-result {
device r2
result out-of-sync
info got: 77727c766ca3b799f52c13a9cb76daa3 expected: 1d88fb85cf89d8f417701fac26e6df3d
}
sync-result {
device r3
result in-sync
}
admin@ncs# *** ALARM out-of-sync: got: 77727c766ca3b799f52c13a9cb76daa3 expected: 1d88fb85cf89d8f417701fac26e6df3d
That hash-value isn’t helping us very much, but we can easily check what the actual difference is with compare-config:
There’s also a pretty cool option to use policy-rules to f.ex warn the user before he/she tries to removes an important vrf. It was pretty tricky however how to figure out how it should be written so I had a lot of trial and error here, but ultimately this is what I ended up with:
admin@ncs(config)# policy rule MGMT foreach /devices/device expr config/ios:vrf/definition[name='999'] warning-message "Device {name} must have a Management-VRF"
A really good command to easier see what path we should check in our expr-string was by using the “| display xpath”:
When a user tries to remove the vrf 999 for management they should now get an error:
admin@ncs(config)# no devices device r2 config ios:vrf definition 999
admin@ncs(config)# show configuration
devices device r2
config
no ios:vrf definition 999
!
!
admin@ncs(config)# commit
The following warnings were generated:
Device r2 must have a Management-VRF
Proceed? [yes,no] no
Aborted: by user
I’ve only scratched the surface yet but i’m really starting to dig NSO. 🙂
As we now have NSO up and running on our local VM let’s try and do some basic stuff like actually push out some configuration changes to our network. 🙂
Cisco are nice enough to provide us with a tool (netsim) to simulate devices that we can use NSO on. These are not to be compared with virtual routers using CSRv etc however.
They use practically 0 memory and is more or less just a config-template that we can “login” to and view/change the current config in a CiscoIOS or Juniper CLI-view. There’s no actual topology we can build though and we can’t send any traffic between these devices.
Anyway, let’s start with creating 4 routers running IOS with the names r* in a new directory somewhere suitable.
joco02 at labb-nso in ~/nso-labs/ios
$ source $HOME/ncs-4.7/ncsrc
$ ncs-netsim create-network $NCS_DIR/packages/neds/cisco-ios 4 r
DEVICE r0 CREATED
DEVICE r1 CREATED
DEVICE r2 CREATED
DEVICE r3 CREATED
We then “boot them up” by using ncs-netsim start.
joco02 at labb-nso in ~/nso-labs/ios
$ ncs-netsim start
DEVICE r0 OK STARTED
DEVICE r1 OK STARTED
DEVICE r2 OK STARTED
DEVICE r3 OK STARTED
If we later want to check if all devices are still up and running we can use the command:
joco02 at labb-nso in ~/nso-labs/ios
$ ncs-netsim is-alive
DEVICE r0 OK
DEVICE r1 OK
DEVICE r2 OK
DEVICE r3 OK
We can login to these “routers” and a rather nifty function in netsim is that we can also choose how the CLI should be displayed (juniper or cisco-style) by adding the -i flag. Cisco is also nice enough to provide a base config for us to play around with when using CiscoIOS netsim-devices.
There’s also options to create IOS XR, NX, Juniper, Dell ftos & a10 acos devices – but for at least IOS XR it seems there’s no default config included to play with so in this example i’ll just use the IOS for now.
We now have our devices, so let’s set up NSO to use our new netsim-routers it can find in our current folder.
$ ncs-setup --netsim-dir ./netsim --dest .
Using netsim dir ./netsim
If we check our current directory we can see that we got a whole bunch of new files suddenly:
joco02 at labb-nso in ~/nso-labs/ios
$ la
total 52
drwxrwxr-x 8 joco02 joco02 4096 May 4 18:41 ./
drwxrwxr-x 3 joco02 joco02 4096 May 4 18:19 ../
drwxrwxr-x 2 joco02 joco02 4096 May 4 18:41 logs/
drwxrwxr-x 2 joco02 joco02 4096 May 4 18:41 ncs-cdb/
-rw-rw-r-- 1 joco02 joco02 9357 May 4 18:41 ncs.conf
drwxrwxr-x 3 joco02 joco02 4096 May 4 18:41 netsim/
drwxrwxr-x 2 joco02 joco02 4096 May 4 18:41 packages/
-rw-rw-r-- 1 joco02 joco02 608 May 4 18:41 README.ncs
-rw-rw-r-- 1 joco02 joco02 1128 May 4 18:41 README.netsim
drwxrwxr-x 4 joco02 joco02 4096 May 4 18:41 scripts/
drwxrwxr-x 2 joco02 joco02 4096 May 4 18:41 state/
And finally we’re ready to start NSO.
joco02 at labb-nso in ~/nso-labs/ios
$ ncs
joco02 at labb-nso in ~/nso-labs/ios
$ ncs --status | grep status
status: started
When loggin in to the NSO CLI we can chose between using Cisco IOSXR or Juniper style CLI: $ ncs_cli -C -u admin <- XR $ ncs_cli -J -u admin <- Juniper
I feel way more at home in the XR so that’s what ill be using.
joco02 at labb-nso in ~/nso-labs/ios
$ ncs_cli -C -u admin
admin connected from 192.168.15.188 using ssh on labb-nso
admin@ncs#
At this point the only thing NSO knows about our devices are how to connect to them (address/port/authentication) and what type of device it is (Cisco IOS). We can check NSO’s current view of the configuration of each device with:
admin@ncs# show running-config devices device r0
devices device r0
address 127.0.0.1
port 10022
ssh host-key ssh-rsa
key-data "..long key.."
!
authgroup default
device-type cli ned-id cisco-ios
state admin-state unlocked
config
no ios:service pad
no ios:ip domain-lookup
no ios:ip http secure-server
ios:ip source-route
A nice feature is that we can also use tab-completion to see which devices we have in our database:
admin@ncs# show running-config devices device
Possible completions:
r0 r1 r2 r3 | <cr>
But before we go any further we have to make sure NSO is up to sync with the actual configuration of our devices, so let’s start with checking that it can actually connect to our devices:
admin@ncs# devices connect
connect-result {
device r0
result true
info (admin) Connected to r0 - 127.0.0.1:10022
}
connect-result {
device r1
result true
info (admin) Connected to r1 - 127.0.0.1:10023
}
connect-result {
device r2
result true
info (admin) Connected to r2 - 127.0.0.1:10024
}
connect-result {
device r3
result true
info (admin) Connected to r3 - 127.0.0.1:10025
}
And finally lets sync data from the devices, NSO will save this data to its configuration database (CDB) and respond with true if it was successful.
admin@ncs# devices sync-from
sync-result {
device r0
result true
}
sync-result {
device r1
result true
}
sync-result {
device r2
result true
}
sync-result {
device r3
result true
}
We can now check NSOs view of R0 config again.
admin@ncs# show running-config devices device r0
devices device r0
address 127.0.0.1
port 10022
ssh host-key ssh-rsa
key-data "..long key.."
!
authgroup default
device-type cli ned-id cisco-ios
state admin-state unlocked
config
no ios:service pad
ios:ip vrf my-forward
bgp next-hop Loopback 1
!
ios:ip community-list 1 permit
ios:ip community-list 2 deny
ios:ip community-list standard s permit
no ios:ip domain-lookup
no ios:ip http server
no ios:ip http secure-server
ios:ip routing
ios:ip source-route
ios:interface FastEthernet1/0
exit
ios:interface Loopback0
exit
ios:class-map match-all a
!
ios:class-map match-all cmap1
match mpls experimental topmost 1
match packet length max 255
match packet length min 2
match qos-group 1
!
ios:policy-map a
!
ios:policy-map map1
class c1
drop
estimate bandwidth delay-one-in 500 milliseconds 100
priority percent 33
!
!
no ios:spanning-tree optimize bpdu transmission
ios:mpls ip propagate-ttl
ios:router bgp 64512
aggregate-address 10.10.10.1 255.255.255.251
neighbor 1.2.3.4 remote-as 1
neighbor 1.2.3.4 ebgp-multihop 3
neighbor 2.3.4.5 remote-as 1
neighbor 2.3.4.5 activate
neighbor 2.3.4.5 capability orf prefix-list both
neighbor 2.3.4.5 weight 300
!
!
!
It’s also possible to filter the output and just check the routing specific config etc:
So how does NSO actually handle configuration changes we want to do in our network? In the ideal world all configuration changes would be performed only via NSO by either the CLI, WebGui, REST etc.
When committing a change NSO will compare the new config with its CDB and calculate the difference. It will then send the needed changes required over to the specific NED that in turn will translate this over to CLI-specific commands to make the change.
Only if all the changes succeeds on the actual device(s) it will be committed to the CDB, if it fails on any device NSO will roll back the changes on all devices, this even works on devices which doesn’t have native rollback like the Cisco IOS.
Here we have a problem though, if any changes are done outside of NSO it will not be known to the CDB and NSO will be out of sync and unable to do these steps correctly. We would then first have to sync the changes to the CDB or overwrite the current device config with what’s stored in NSOs CDB.
Any way, let’s try and make a configuration change! First we specify which device or range of devices we want to change, in this case r0, r1 & r2 and then the actual change, in this case lets add redistribution of static routes.
In this state no changes has actually been made and is local only. NSO will calculate the difference to its CDB, we can view what the actual changes are going to be with show configuration:
But before we do that let’s verify that one of our devices actually have redistribution configured now.
$ ncs-netsim cli-i r1
admin connected from 192.168.15.188 using ssh on labb-nso
r1> en
r1# show running-config router
router bgp 64512
aggregate-address 10.10.10.1 255.255.255.251
neighbor 1.2.3.4 remote-as 1
neighbor 1.2.3.4 ebgp-multihop 3
neighbor 2.3.4.5 remote-as 1
neighbor 2.3.4.5 activate
neighbor 2.3.4.5 capability orf prefix-list both
neighbor 2.3.4.5 weight 300
redistribute static
Cool! Let’s do the rollback and see what happens.
admin@ncs(config)# rollback configuration ?
Possible completions:
10001 2019-05-04 18:44:00 by system via system
10002 2019-05-04 18:44:03 by system via system
10003 2019-05-04 18:56:56 by admin via cli
10004 2019-05-04 18:56:56 by admin via cli
10005 2019-05-04 18:56:56 by admin via cli
10006 2019-05-04 18:56:56 by admin via cli
10007 2019-05-04 19:32:13 by admin via cli
<cr> latest
admin@ncs(config)# rollback configuration 10007
admin@ncs(config)# show configuration
devices device r0
config
ios:router bgp 64512
no redistribute static
!
!
!
devices device r1
config
ios:router bgp 64512
no redistribute static
!
!
!
devices device r2
config
ios:router bgp 64512
no redistribute static
!
!
!
admin@ncs(config)# commit
Commit complete.
Checking back in R1 we can see that we no longer have any static redist configured.
All gone! I think that will be all for tonight, must say i’m impressed with how easy it was to get started! And that not only is NSO free to use for us nerds to play around with, they also provide tools like Netsim and great documentation etc for everyone.
I’ve been wanting to test Cisco’s NSO for a long time now and finally had some spare time to try and get it up and running on a local VM. It’s free for non-production use now so go ahead and grab it over at https://developer.cisco.com/site/nso/ (available for both MacOS & Linux).
I installed Ubuntu 18.04 LTS on a fresh vmbox (I didn’t manage to get it working on a raspberry), the only other pre-requirements NSO has is:
Java JDK-7.0 or higher
Ant
python2 or python3
python-paramiko
To make life simpler I also installed python3-pip
$ sudo apt-get install python3-pip $ sudo apt-get install default-jdk $ sudo apt-get install ant $ pip3 install paramiko $ java -version openjdk version "11.0.2" 2019-01-15 OpenJDK Runtime Environment (build 11.0.2+9-Ubuntu-3ubuntu118.04.3) OpenJDK 64-Bit Server VM (build 11.0.2+9-Ubuntu-3ubuntu118.04.3, mixed mode, sharing)
I then downloaded the file nso-4.7.linux.x86_64.signed.bin from Cisco, installation was actually much more simpler than I had imagined.
joco02 at labb-nso in ~/Downloads
$ ls
nso-4.7.linux.x86_64.signed.bin
$ sh nso-4.7.linux.x86_64.signed.bin --skip-verification
Unpacking...
joco02 at labb-nso in ~/Downloads
$ ls
cisco_x509_verify_release.py nso-4.7.linux.x86_64.installer.bin nso-4.7.linux.x86_64.installer.bin.signature nso-4.7.linux.x86_64.signed.bin README.signature tailf.cer
joco02 at labb-nso in ~/Downloads
$ sh nso-4.7.linux.x86_64.installer.bin $HOME/ncs-4.7 --local-install
INFO Using temporary directory /tmp/ncs_installer.41189 to stage NCS installation bundle
INFO Unpacked ncs-4.7 in /home/joco02/ncs-4.7
INFO Found and unpacked corresponding DOCUMENTATION_PACKAGE
INFO Found and unpacked corresponding EXAMPLE_PACKAGE
INFO Generating default SSH hostkey (this may take some time)
INFO SSH hostkey generated
INFO Environment set-up generated in /home/joco02/ncs-4.7/ncsrc
INFO NCS installation script finished
INFO Found and unpacked corresponding NETSIM_PACKAGE
INFO NCS installation complete
When then have to source our new folder to enable built-in variables and then setup our environment.
joco02 at labb-nso in ~/Downloads
$ source $HOME/ncs-4.7/ncsrc
$ ncs-setup --dest $HOME/ncs-run
That should be everything, we can now start NSO:
$ ncs
To check status we can use:
joco02 at labb-nso in ~/ncs-run
$ ncs --status | grep status
status: started
joco02 at labb-nso in ~/ncs-run
$ ncs --version
4.7
We should now also be able to reach NSO’s GUI/Webpage for administration at http://serverip:8080/login.html
Login credentials are admin / admin
If your like me and allergic to GUIs we can also connect to the CLI instead.
joco02 at labb-nso in ~/ncs-4.7
$ ncs_cli -u admin
admin connected from 192.168.15.188 using ssh on labb-nso
admin@ncs> ?
Possible completions:
clear - Clear parameter
compare - Compare running configuration to another configuration or a file
configure - Manipulate software configuration information
describe - Display transparent command information
exit - Exit the management session
file - Perform file operations
help - Provide help information
id - Show user id information
monitor - Real-time debugging
ping - Ping a host
ping6 - Ping an ipv6 host
quit - Exit the management session
request - Make system-level requests
script - Script actions
set - Set CLI properties
set-path - Set relative show path
show - Show information about the system
source - File to source
switch - Change CLI style
templatize - Find patterns in subtree.
top - Exit to top level and optionally run command
traceroute - Trace the route to a remote host
up - Exit one level of configuration
We should now be able to do some labs on NSO! 🙂 Cisco luckily provides a few “Network Elements Drivers” for their netsim-devices for lab purposes, if you want to manage your own devices (real/virtual) you will have to buy a license for it as I understand it.
joco02 at labb-nso in ~/ncs-4.7/packages/neds
$ ls
a10-acos cisco-ios cisco-iosxr cisco-nx dell-ftos juniper-junos
The last few examples of using netmiko hasn’t really been very scalable as the devices was provided as a list directly in the code. In this example I thought we could try out and use YAML for an inventory-file instead.
My very small network could look like this (but preferably you would use getpass() instead of writing it directly in your script):
We then import “yaml” in our python-script, read the file and we will have a very nice dictionary to work with! If we wanted to we could divide our topology in different sites and decide for ourselves on which devices we want to run the script instead of all like I do here.
yaml_ouput.py
import yaml
def read_yaml(path="inventory.yml"):
with open(path) as f:
yaml_content = yaml.safe_load(f.read())
return yaml_content
def main():
parsed_yaml = read_yaml()
print(parsed_yaml)
if __name__ == "__main__":
main()
So let’s rework our previous script so we only use your yaml-file as input to netmiko. It’s a little tricky at first if your as me, not super confident with using dictionaries in python.
We create an empty dict for every device and insert the login credentials, device_type and ip-address from the parsed_yaml dict which we then send over to netmiko with the standard “ConnectHandler(**device)”.
from netmiko import ConnectHandler
from datetime import datetime
from copy import deepcopy
import yaml
SH_COMMANDS = [
"show ip route | beg Gate", "show ip interface brief | excl admin"
]
def read_yaml(path="inventory.yml"):
with open(path) as f:
yaml_content = yaml.safe_load(f.read())
return yaml_content
def get_connection_parameters(parsed_yaml):
parsed_yaml = deepcopy(parsed_yaml)
login_credentials = parsed_yaml["all"]["vars"]
for site_dict in parsed_yaml["all"]["sites"]:
for host in site_dict["hosts"]:
host_dict = {}
host_dict.update(login_credentials)
host_dict.update(host)
yield host_dict
def show_commands(devices, commands):
for device in devices:
start_time = datetime.now()
hostname = device.pop("hostname")
connection = ConnectHandler(**device)
device_result = ["{0} {1} {0}".format("=" * 15, hostname)]
for command in commands:
command_result = connection.send_command(command)
device_result.append("{0} {1} {0}".format("=" * 10, command))
device_result.append(command_result)
device_result_string = "\n\n".join(device_result)
connection.disconnect()
device_result_string += "\nElapsed time: " + str(datetime.now() - start_time)
yield device_result_string
def main():
parsed_yaml = read_yaml()
print(parsed_yaml)
connection_parameters = get_connection_parameters(parsed_yaml, site_name=SITE_NAME)
for device_result in show_commands(connection_parameters, SH_COMMANDS):
print(device_result)
if __name__ == "__main__":
main()
Output:
$ python login.py
=============== R1 ===============
========== show ip route | beg Gate ==========
Gateway of last resort is 192.168.15.254 to network 0.0.0.0
S* 0.0.0.0/0 [1/0] via 192.168.15.254
192.168.15.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.15.0/24 is directly connected, Ethernet0/0
L 192.168.15.10/32 is directly connected, Ethernet0/0
========== show ip interface brief | excl admin ==========
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.15.10 YES manual up up
Elapsed time: 0:00:05.841709
=============== R2 ===============
========== show ip route | beg Gate ==========
Gateway of last resort is 192.168.15.254 to network 0.0.0.0
S* 0.0.0.0/0 [1/0] via 192.168.15.254
192.168.15.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.15.0/24 is directly connected, Ethernet0/0
L 192.168.15.11/32 is directly connected, Ethernet0/0
========== show ip interface brief | excl admin ==========
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.15.11 YES manual up up
Elapsed time: 0:00:05.805187
=============== R3 ===============
========== show ip route | beg Gate ==========
Gateway of last resort is 192.168.15.254 to network 0.0.0.0
S* 0.0.0.0/0 [1/0] via 192.168.15.254
192.168.15.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.15.0/24 is directly connected, Ethernet0/0
L 192.168.15.12/32 is directly connected, Ethernet0/0
========== show ip interface brief | excl admin ==========
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.15.12 YES manual up up
Elapsed time: 0:00:05.808079
A follow-up on the latest post about Netmiko where we logged in to our devices and did some show commands, let’s continue and do a very common thing in production networks with many devices – pushing out new config from a template.
I’ll be using our latest login_simple.py from last post as a base and just modify it slightly. First we create our config-file that we want in all our devices., let’s say we want to rollout a new route-map in our network.
config_template.txt
ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0
!
route-map DEFAULT-INJECT permit 10
match ip address prefix-list DEFAULT-ROUTE
set extcommunity rt 300:301
!
route-map DEFAULT-INJECT permit 20
set extcommunity rt 300:300
In netmiko we can then use “send_config_from_file” when we’ve logged in to our device(s), super simple! Notice that we don’t have to manage the context in our router (enable/conf t etc), netmiko does it for us.
conf_from_file.py
from netmiko import ConnectHandler
from datetime import datetime
from getpass import getpass
DEVICES = ["192.168.15.10", "192.168.15.11", "192.168.15.12"]
USERNAME = "joco02"
PASSWORD = getpass()
FILE = 'config_template.txt'
def config_commands(devices, file):
for device in devices:
start_time = datetime.now()
connection = ConnectHandler(
device_type="cisco_ios", host=device,
username=USERNAME, password=PASSWORD)
hostname = connection.find_prompt()
device_result = ["{0} {1} {0}".format("=" * 20, hostname)]
command_result = connection.send_config_from_file(FILE)
device_result.append(command_result)
device_result_string = "\n\n".join(device_result)
connection.disconnect()
device_result_string += "\nElapsed time: " + str(datetime.now() - start_time)
yield device_result_string
def main():
results = config_commands(DEVICES, FILE)
for result in results:
print(result)
if __name__ == "__main__":
main()
Results:
$ python conf_from_file.py
Password:
==================== R1# ====================
config term
Enter configuration commands, one per line. End with CNTL/Z.
R1(config)#ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0
R1(config)#!
R1(config)#route-map DEFAULT-INJECT permit 10
R1(config-route-map)# match ip address prefix-list DEFAULT-ROUTE
R1(config-route-map)# set extcommunity rt 300:301
R1(config-route-map)#!
R1(config-route-map)#route-map DEFAULT-INJECT permit 20
R1(config-route-map)# set extcommunity rt 300:300
R1(config-route-map)#end
R1#
Elapsed time: 0:00:08.875642
==================== R2# ====================
config term
Enter configuration commands, one per line. End with CNTL/Z.
R2(config)#ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0
R2(config)#!
R2(config)#route-map DEFAULT-INJECT permit 10
R2(config-route-map)# match ip address prefix-list DEFAULT-ROUTE
R2(config-route-map)# set extcommunity rt 300:301
R2(config-route-map)#!
R2(config-route-map)#route-map DEFAULT-INJECT permit 20
R2(config-route-map)# set extcommunity rt 300:300
R2(config-route-map)#end
R2#
Elapsed time: 0:00:08.799664
==================== R3# ====================
config term
Enter configuration commands, one per line. End with CNTL/Z.
R3(config)#ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0
R3(config)#!
R3(config)#route-map DEFAULT-INJECT permit 10
R3(config-route-map)# match ip address prefix-list DEFAULT-ROUTE
R3(config-route-map)# set extcommunity rt 300:301
R3(config-route-map)#!
R3(config-route-map)#route-map DEFAULT-INJECT permit 20
R3(config-route-map)# set extcommunity rt 300:300
R3(config-route-map)#end
R3#
Elapsed time: 0:00:09.067742
We can now easily just change the config in our template and run the script again when it’s time for another rollout.
I’ve been playing around with Netmiko this weekend as I would like to try and rewrite all my old bash- & expect-scripts at work to python instead (if I ever will find the time)… 🙂
Just starting out I felt like it was incredibly slow however and was pretty disappointed when for ex. pushing out config from a file to multiple devices compared to expect. Perhaps I did something wrong but it also looks like there’s several ways to speed things up so I thought I should give it another go.
I’m using simple topology with three routers and some basic config allowing ssh-login:
R1 192.168.15.10
R2 192.168.15.11
R3 192.168.15.12
login_supersimple.py
We start by importing netmiko’s ConnectHandler, specify some “show commands” we’d like to run and lastly we need to specify what type of device we’re going to connect to and login credentials.
One very simple way to do this is using a dictionary for each device:
from netmiko import ConnectHandler
from getpass import getpass
R1 = {
"host": "192.168.15.10",
"username": "joco02",
"password": getpass(),
"device_type": "cisco_ios",
}
We then use the dictionary when calling our ConnectHandler to provide all necessary info for logging in and setting the correct device type.
We can also skip using the dictionary and specify connection parameters manually instead. Here i’m sending multiple commands to multiple devices using only lists. In a later post I thought we could try and use a YAML-file for our devices and connection parameters.
from netmiko import ConnectHandler from datetime import datetime from getpass import getpass
SH_COMMANDS = [ "show ip route", "show ip interface brief" ]
results = show_commands(DEVICES, SH_COMMANDS) for result in results: print(result)
if __name__ == "__main__": main()
Results:
$ python login_simple.py
Password:
==================== R1# ====================
==================== show ip route ====================
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
a - application route
+ - replicated route, % - next hop override
Gateway of last resort is 192.168.15.254 to network 0.0.0.0
S* 0.0.0.0/0 [1/0] via 192.168.15.254
10.0.0.0/8 is variably subnetted, 16 subnets, 2 masks
C 10.10.0.0/24 is directly connected, Loopback0
L 10.10.0.1/32 is directly connected, Loopback0
C 10.10.1.0/24 is directly connected, Loopback1
L 10.10.1.1/32 is directly connected, Loopback1
C 10.10.2.0/24 is directly connected, Loopback2
L 10.10.2.1/32 is directly connected, Loopback2
C 10.10.3.0/24 is directly connected, Loopback3
L 10.10.3.1/32 is directly connected, Loopback3
O 10.11.0.0/24 [110/11] via 192.168.15.11, 23:58:37, Ethernet0/0
O 10.11.1.0/24 [110/11] via 192.168.15.11, 23:58:27, Ethernet0/0
O 10.11.2.0/24 [110/11] via 192.168.15.11, 23:58:27, Ethernet0/0
O 10.11.3.0/24 [110/11] via 192.168.15.11, 23:58:27, Ethernet0/0
O 10.12.0.0/24 [110/11] via 192.168.15.12, 23:58:15, Ethernet0/0
O 10.12.1.0/24 [110/11] via 192.168.15.12, 23:58:05, Ethernet0/0
O 10.12.2.0/24 [110/11] via 192.168.15.12, 23:58:05, Ethernet0/0
O 10.12.3.0/24 [110/11] via 192.168.15.12, 23:58:05, Ethernet0/0
192.168.15.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.15.0/24 is directly connected, Ethernet0/0
L 192.168.15.10/32 is directly connected, Ethernet0/0
==================== show ip interface brief ====================
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.15.10 YES manual up up
Ethernet0/1 unassigned YES NVRAM administratively down down
Ethernet0/2 unassigned YES NVRAM administratively down down
Ethernet0/3 unassigned YES NVRAM administratively down down
Ethernet1/0 unassigned YES NVRAM administratively down down
Ethernet1/1 unassigned YES NVRAM administratively down down
Ethernet1/2 unassigned YES NVRAM administratively down down
Ethernet1/3 unassigned YES NVRAM administratively down down
Serial2/0 unassigned YES NVRAM administratively down down
Serial2/1 unassigned YES NVRAM administratively down down
Serial2/2 unassigned YES NVRAM administratively down down
Serial2/3 unassigned YES NVRAM administratively down down
Serial3/0 unassigned YES NVRAM administratively down down
Serial3/1 unassigned YES NVRAM administratively down down
Serial3/2 unassigned YES NVRAM administratively down down
Serial3/3 unassigned YES NVRAM administratively down down
Loopback0 10.10.0.1 YES manual up up
Loopback1 10.10.1.1 YES manual up up
Loopback2 10.10.2.1 YES manual up up
Loopback3 10.10.3.1 YES manual up up
Elapsed time: 0:00:06.128156
==================== R2# ====================
==================== show ip route ====================
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
a - application route
+ - replicated route, % - next hop override
Gateway of last resort is 192.168.15.254 to network 0.0.0.0
S* 0.0.0.0/0 [1/0] via 192.168.15.254
10.0.0.0/8 is variably subnetted, 16 subnets, 2 masks
O 10.10.0.0/24 [110/11] via 192.168.15.10, 1d00h, Ethernet0/0
O 10.10.1.0/24 [110/11] via 192.168.15.10, 23:58:53, Ethernet0/0
O 10.10.2.0/24 [110/11] via 192.168.15.10, 23:58:53, Ethernet0/0
O 10.10.3.0/24 [110/11] via 192.168.15.10, 23:58:43, Ethernet0/0
C 10.11.0.0/24 is directly connected, Loopback0
L 10.11.0.1/32 is directly connected, Loopback0
C 10.11.1.0/24 is directly connected, Loopback1
L 10.11.1.1/32 is directly connected, Loopback1
C 10.11.2.0/24 is directly connected, Loopback2
L 10.11.2.1/32 is directly connected, Loopback2
C 10.11.3.0/24 is directly connected, Loopback3
L 10.11.3.1/32 is directly connected, Loopback3
O 10.12.0.0/24 [110/11] via 192.168.15.12, 23:58:21, Ethernet0/0
O 10.12.1.0/24 [110/11] via 192.168.15.12, 23:58:11, Ethernet0/0
O 10.12.2.0/24 [110/11] via 192.168.15.12, 23:58:11, Ethernet0/0
O 10.12.3.0/24 [110/11] via 192.168.15.12, 23:58:11, Ethernet0/0
192.168.15.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.15.0/24 is directly connected, Ethernet0/0
L 192.168.15.11/32 is directly connected, Ethernet0/0
==================== show ip interface brief ====================
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.15.11 YES manual up up
Ethernet0/1 unassigned YES NVRAM administratively down down
Ethernet0/2 unassigned YES NVRAM administratively down down
Ethernet0/3 unassigned YES NVRAM administratively down down
Ethernet1/0 unassigned YES NVRAM administratively down down
Ethernet1/1 unassigned YES NVRAM administratively down down
Ethernet1/2 unassigned YES NVRAM administratively down down
Ethernet1/3 unassigned YES NVRAM administratively down down
Serial2/0 unassigned YES NVRAM administratively down down
Serial2/1 unassigned YES NVRAM administratively down down
Serial2/2 unassigned YES NVRAM administratively down down
Serial2/3 unassigned YES NVRAM administratively down down
Serial3/0 unassigned YES NVRAM administratively down down
Serial3/1 unassigned YES NVRAM administratively down down
Serial3/2 unassigned YES NVRAM administratively down down
Serial3/3 unassigned YES NVRAM administratively down down
Loopback0 10.11.0.1 YES manual up up
Loopback1 10.11.1.1 YES manual up up
Loopback2 10.11.2.1 YES manual up up
Loopback3 10.11.3.1 YES manual up up
Elapsed time: 0:00:06.205272
==================== R3# ====================
==================== show ip route ====================
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
a - application route
+ - replicated route, % - next hop override
Gateway of last resort is 192.168.15.254 to network 0.0.0.0
S* 0.0.0.0/0 [1/0] via 192.168.15.254
10.0.0.0/8 is variably subnetted, 16 subnets, 2 masks
O 10.10.0.0/24 [110/11] via 192.168.15.10, 1d00h, Ethernet0/0
O 10.10.1.0/24 [110/11] via 192.168.15.10, 23:58:59, Ethernet0/0
O 10.10.2.0/24 [110/11] via 192.168.15.10, 23:58:49, Ethernet0/0
O 10.10.3.0/24 [110/11] via 192.168.15.10, 23:58:49, Ethernet0/0
O 10.11.0.0/24 [110/11] via 192.168.15.11, 23:58:49, Ethernet0/0
O 10.11.1.0/24 [110/11] via 192.168.15.11, 23:58:39, Ethernet0/0
O 10.11.2.0/24 [110/11] via 192.168.15.11, 23:58:39, Ethernet0/0
O 10.11.3.0/24 [110/11] via 192.168.15.11, 23:58:39, Ethernet0/0
C 10.12.0.0/24 is directly connected, Loopback0
L 10.12.0.1/32 is directly connected, Loopback0
C 10.12.1.0/24 is directly connected, Loopback1
L 10.12.1.1/32 is directly connected, Loopback1
C 10.12.2.0/24 is directly connected, Loopback2
L 10.12.2.1/32 is directly connected, Loopback2
C 10.12.3.0/24 is directly connected, Loopback3
L 10.12.3.1/32 is directly connected, Loopback3
192.168.15.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.15.0/24 is directly connected, Ethernet0/0
L 192.168.15.12/32 is directly connected, Ethernet0/0
==================== show ip interface brief ====================
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.15.12 YES manual up up
Ethernet0/1 unassigned YES NVRAM administratively down down
Ethernet0/2 unassigned YES NVRAM administratively down down
Ethernet0/3 unassigned YES NVRAM administratively down down
Ethernet1/0 unassigned YES NVRAM administratively down down
Ethernet1/1 unassigned YES NVRAM administratively down down
Ethernet1/2 unassigned YES NVRAM administratively down down
Ethernet1/3 unassigned YES NVRAM administratively down down
Serial2/0 unassigned YES NVRAM administratively down down
Serial2/1 unassigned YES NVRAM administratively down down
Serial2/2 unassigned YES NVRAM administratively down down
Serial2/3 unassigned YES NVRAM administratively down down
Serial3/0 unassigned YES NVRAM administratively down down
Serial3/1 unassigned YES NVRAM administratively down down
Serial3/2 unassigned YES NVRAM administratively down down
Serial3/3 unassigned YES NVRAM administratively down down
Loopback0 10.12.0.1 YES manual up up
Loopback1 10.12.1.1 YES manual up up
Loopback2 10.12.2.1 YES manual up up
Loopback3 10.12.3.1 YES manual up up
Elapsed time: 0:00:05.982471
Around 6 seconds per device isn’t that bad when we’re not even using threading or asyncio to speed things up. There is probably other parameters that can be tuned inside netmiko as well, i’ve seen delay-values that defaults to 1 second for example but we’ll save that for a later post.
I’ve been using GNS3 when playing around with smaller topologies instead of booting up my R610 which is more or less like working next to a jet plane, but had a few issues getting it up and running on a new Win10-installation so figured I should document my steps for future reference.
Install everything as normal, follow the official GNS3-guide if there’s any questions. I started with VMware & VIX, imported the GNS3 VM, adjusted memory to at least 4GB RAM and added an extra bridge adapter so we can reach our topology from other devices on our network.
After installing GNS3 I had some strange issues that the program was unable to connect to the VM for some reason. The error-code I got was:
Error while saving settings: GNS3VM: Error while executing VMware command: vmrun has returned an error: Unable to connect to host.
Error: The specified version was not found
I found some old post recommending to downgrade the version of VMware station to 12.xx but a better fix I found was instead to edit the following file (depending on where you installed it):
# latest un-versioned
ws 19 vmdb e.x.p Workstation-14.0.0
player 19 vmdb e.x.p Workstation-14.0.0
To:
# latest un-versioned
ws 19 vmdb e.x.p Workstation-14.0.0
player 19 vmdb 15.0.4 Workstation-14.0.0
Restart WMware & GNS3 and you should be good to go. Import the IOU-appliances as normal, if you put the required .bin-files in the same folder it should find them automatically. Last step is to enter your license or GNS3 will refuse to start them, if you’re unsure how to get hold of this contact Cisco or do some googling.
Haven’t done much labbing during my vacation and instead focused on reading/doing flashcards now and then, guess it’s important to unplug as well but at the same time I don’t want to lose all momentum… 🙂 Think i’ve managed to find a pretty good balance but the downside is that I haven’t had any interesting labs to posts about. I have however stumbled upon some pretty cool communities and other stuff that I thought I could share:
RouterGods – https://www.meetup.com/routergods
A large community full of really talented & friendly people, comes with a large chat room full of CCIE/NP/NA study groups, lab-sessions, work help and much more. Highly recommended!
ACM – https://www.acm.org/
“The world’s largest educational and scientific computing society”. One of the major benefits of becoming a member for $99 is that it also includes a membership to Safari Books (which originally costs $399!). There’s no secret that to become a CCIE you’ll have to read, a LOT, and Safari has pretty much everything you could ever ask for.
Dmitry Figols Network Programmability Lab on Youtube
Dmitry has an excellent youtube-series i’ve been trying to catch up on where he shows how to build automation tools in Python using things like netmiko, YAML, Ansible, asyncio, NAPALM, NSO and much much more.
Anki Flashcards – https://apps.ankiweb.net/
Wish I would have found this earlier instead of using Cram.com as my main resource for making flashcards during my earlier study sessions. Anki is free and really, really good. I’d recommend you always write your own flashcards to improve memorization but I also found an excellent resource written by Jedaiah Casey over at neckercube.com (3,500 high quality flashcards for the CCIE R&S 5.1). I’m sure there’s some benefit alternating your own flashcards with these to make sure you just haven’t missed some major points.