Netmiko part 3 – YAML

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):

inventory.yml

all:
  vars:
    username: joco02
    password: cisco
  sites:
    - name: gns3
      hosts:
        - hostname: R1
          host: 192.168.15.10
          device_type: cisco_ios
        - hostname: R2
          host: 192.168.15.11
          device_type: cisco_ios
        - hostname: R3
          host: 192.168.15.12
          device_type: cisco_ios

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()

Output:

$ python login.py
{'all': {'vars': {'username': 'joco02', 'password': 'cisco'}, 'sites': [{'name': 'gns3', 'hosts': [{'hostname': 'R1', 'host': '192.168.15.10', 'device_type': 'cisco_ios'}, {'hostname': 'R2', 'host': '192.168.15.11', 'device_type': 'cisco_ios'}, {'hostname': 'R3', 'host': '192.168.15.12', 'device_type': 'cisco_ios'}]}]}}

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

Netmiko part 2

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.

Netmiko

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.

connection = ConnectHandler(**R1)
print(connection.find_prompt())
connection.disconnect()

Results:

$ python login_supersimple.py
Password:
R1#

login_simple.py

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"
]

DEVICES = ["192.168.15.10", "192.168.15.11", "192.168.15.12"]
USERNAME = "joco02"
PASSWORD = getpass()

def show_commands(devices, commands):
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)]

for command in commands:
command_result = connection.send_command(command)
device_result.append("{0} {1} {0}".format("=" * 20, 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():

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.

Last week of vacation

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.