Skip to content

Latest commit

 

History

History
1399 lines (1026 loc) · 36 KB

lab.md

File metadata and controls

1399 lines (1026 loc) · 36 KB

Hands On - Useful Python Libraries for Network Engineers

Setup and Preparation

Devnet Sandbox

This lab was written to be run using the DevNet Devbox Sandbox. This sandbox is a basic CentOS 7 workstation with typical development tools and software installed. Specifically used in this lab are Python 3.6 and Vagrant (used to instantiate an IOS XE router for use in the labs.)

If you are doing this lab on your own, you'll need to reserve an instance of this sandbox before beginning. If you are doing this as part of a guided workshop, the instructor will assign you a pod.

Steps to complete preparation

  1. Using either AnyConnect or OpenConnect, establish a VPN to your pod.

  2. SSH to the Devbox at IP 10.10.20.20 using credentials root / cisco123

  3. Install the Python 3.6 development libraries.

    yum install -y python36u-devel
  4. Add the IOS XE 16.9 Vagrant Box to your workstation. Instructions for creating the Box file are available on github at github.com/hpreston/vagrant_net_prog. If you are completing this as part of a guided lab, the instructor will provide details on how to complete this step.

    vagrant box add --name iosxe/16.09.01 serial-csr1000v-universalk9.16.09.01.box
  5. Clone the code samples to the devbox from GitHub and change into the directory.

    git clone https://github.com/hpreston/python_networking
    cd python_networking
  6. Create a Python 3.6 virtual environment and install Python libraries for exercises.

    python3.6 -m venv venv
    source venv/bin/activate 
    pip install -r requirements.txt
  7. Start and baseline the IOS XE Vagrant environment.

    vagrant up 
    
    # After it completes
    python vagrant_device_setup.py

Libraries to Work with Data

Exercises in this section are intended to be executed from an interactive Python interpreter.

iPython has been installed as part of the requirements.txt installation and is one option. You can start an iPython window by simply typing ipython. For each step in the list below, type the specified command (or commands) and then press enter until the iPython prompt goes to the next step, and/or shows the expected output (ie. print or pprint commands).

Other options could be just python or idle.

XML with xmltodict

  1. From the root of the python_networking repository, change into the exercise directory.

    cd data_manipulation/xml
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:
  3. Import the xmltodict library

    import xmltodict
  4. Open the sample xml file and read it into variable

    with open("xml_example.xml") as f:
        xml_example = f.read()
  5. Print the raw XML data

    print(xml_example)
  6. Parse the XML into a Python (Ordered) dictionary

    xml_dict = xmltodict.parse(xml_example)
  7. Pretty Print the Python Dictionary Object

    from pprint import pprint
    pprint(xml_dict)
  8. Save the interface name into a variable using XML nodes as keys

    int_name = xml_dict["interface"]["name"]
  9. Print the interface name

    print(int_name)
  10. Change the IP address of the interface

    xml_dict["interface"]["ipv4"]["address"]["ip"] = "192.168.0.2"
  11. Check that the IP address has been changed in the dictionary

    pprint(xml_dict)
  12. Revert to the XML string version of the dictionary

    print(xmltodict.unparse(xml_dict))
  13. After you've completed exploring, exit the interpreter.

    exit()

JSON with json

  1. From the root of the python_networking repository, change into the exercise directory.

    cd data_manipulation/json
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:
  3. Import the jsontodict library

    import json
  4. Open the sample json file and read it into variable

    with open("json_example.json") as f:
        json_example = f.read()   
  5. Print the raw json data

    print(json_example)
  6. Parse the json into a Python dictionary

    json_dict = json.loads(json_example)
  7. Pretty Print the Python Dictionary Object

    from pprint import pprint
    pprint(json_dict)
  8. Save the interface name into a variable

    int_name = json_dict["interface"]["name"]
  9. Print the interface name

    print(int_name)
  10. Change the IP address of the interface

    json_dict["interface"]["ipv4"]["address"][0]["ip"] = "192.168.0.2"
  11. Check that the IP address has been changed in the dictionary

    pprint(json_dict)
  12. Revert to the json string version of the dictionary

    print(json.dumps(json_dict))
  13. After you've completed exploring, exit the interpreter.

    exit()

YAML with PyYAML

  1. From the root of the python_networking repository, change into the exercise directory.

    cd data_manipulation/yaml
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:
  3. Import the yamltodict library

    import yaml
  4. Open the sample yaml file and read it into variable

    with open("yaml_example.yaml") as f:
        yaml_example = f.read()
  5. Print the raw yaml data

    print(yaml_example)
  6. Parse the yaml into a Python dictionary

    yaml_dict = yaml.load(yaml_example)
  7. Pretty Print the Python Dictionary Object

    from pprint import pprint
    pprint(yaml_dict)
  8. Save the interface name into a variable

    int_name = yaml_dict["interface"]["name"]
  9. Print the interface name

    print(int_name)
  10. Change the IP address of the interface

    yaml_dict["interface"]["ipv4"]["address"][0]["ip"] = "192.168.0.2"
  11. Check that the IP address has been changed in the dictionary

    pprint(yaml_dict)
  12. Revert to the yaml string version of the dictionary

    print(yaml.dump(yaml_dict, default_flow_style=False))
  13. After you've completed exploring, exit the interpreter.

    exit()

CSV with csv

  1. From the root of the python_networking repository, change into the exercise directory.

    cd data_manipulation/csv
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:
  3. Import the csv library

    import csv
  4. Open the sample csv file and print it to screen

    with open("csv_example.csv") as f:
        print(f.read())
  5. Open the sample csv file, and create a csv.reader object

    with open("csv_example.csv") as f:
        csv_python = csv.reader(f)
        # Loop over each row in csv and leverage the data in code
        for row in csv_python:
            print("{device} is in {location} " \
                  "and has IP {ip}.".format(
                      device = row[0],
                      location = row[2],
                      ip = row[1]
                      )
                    )
  6. Create a new tuple for additional router.

    router4 = ("router4", "10.4.0.1", "Chicago")    
  7. Add new router to CSV file.

    with open("csv_example.csv", "a") as f:
        csv_writer = csv.writer(f)
        csv_writer.writerow(router4)        
  8. Re-read and print out the CSV content.

    with open("csv_example.csv") as f:
        print(f.read())
  9. After you've completed exploring, exit the interpreter.

    exit()

YANG with pyang

  1. From the root of the python_networking repository, change into the exercise directory.

    cd data_manipulation/yang
  2. Print the YANG module in a simple text tree

    pyang -f tree ietf-interfaces.yang
  3. Print only part of the tree

    pyang -f tree --tree-path=/interfaces/interface \
      ietf-interfaces.yang
  4. Print an example XML skeleton (NETCONF)

    pyang -f sample-xml-skeleton ietf-interfaces.yang
  5. Create an HTTP/JS view of the YANG Model (no output expected in the CLI)

    pyang -f jstree -o ietf-interfaces.html \
      ietf-interfaces.yang
  6. Optional: Open ietf-interfaces.html in a web browser. Will need to RDP into the Devbox to do this step.

  7. Control the "nested depth" in trees

    pyang -f tree --tree-depth=2 ietf-ip.yang
  8. Display a full module.

    pyang -f tree \
      ietf-ip.yang
  9. Include deviation models in the processing

    pyang -f tree \
      --deviation-module=cisco-xe-ietf-ip-deviation.yang \
      ietf-ip.yang

Libraries to Work with APIs

Exercises in this section are intended to be executed from an interactive Python interpreter.

iPython has been installed as part of the requirements.txt installation and is one option. You can start an iPython window by simply typing ipython.

Other options could be just python or idle.

Each exercise also includes a Python script file that can be executed directly.

rest with requests

  1. From the root of the python_networking repository, change into the exercise directory.

    cd device_apis/rest
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:

Retrieve Network Configuration Details with RESTCONF with restconf_example1.py

  1. Import libraries

    import requests, urllib3
    import sys
  2. Add parent directory to path to allow importing common vars

    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
  3. Disable Self-Signed Cert warning for demo

    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
  4. Setup base variable for request

    restconf_headers = {"Accept": "application/yang-data+json"}
    restconf_base = "https://{ip}:{port}/restconf/data"
    interface_url = restconf_base + "/ietf-interfaces:interfaces/interface={int_name}"
  5. Create URL GigE2 Config

    url = interface_url.format(ip = device["address"],
                               port = device["restconf_port"],
                               int_name = "GigabitEthernet2"
                              )
  6. Check the complete URL you just composed

    print(url)
  7. Send RESTCONF request to core1 for GigE2 Config

    r = requests.get(url,
            headers = restconf_headers,
            auth=(device["username"], device["password"]),
            verify=False)
  8. Print returned data

    print(r.text)
  9. If REST call was successful, report interesting details.

    if r.status_code == 200:
        # Process JSON data into Python Dictionary and use
        interface = r.json()["ietf-interfaces:interface"]
        print("The interface {name} has ip address {ip}/{mask}".format(
                name = interface["name"],
                ip = interface["ietf-ip:ipv4"]["address"][0]["ip"],
                mask = interface["ietf-ip:ipv4"]["address"][0]["netmask"],
                )
            )
    else:
        print("No interface {} found.".format("GigabitEthernet2"))

Modify Network Configuration Details with RESTCONF with restconf_example2.py

  1. Continuing from previous exercise. If starting from new interpreter, execute these steps.

    import requests, urllib3, sys
    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    restconf_headers = {"Accept": "application/yang-data+json"}
    restconf_base = "https://{ip}:{port}/restconf/data"
    interface_url = restconf_base + "/ietf-interfaces:interfaces/interface={int_name}"
  2. Add additional Content-Type header.

    restconf_headers["Content-Type"] = "application/yang-data+json"
  3. Create dictionary with details on a new loopback interface.

    loopback = {"name": "Loopback101",
                "description": "Demo interface by RESTCONF",
                "ip": "192.168.101.1",
                "netmask": "255.255.255.0"}
  4. Setup data body to create new loopback interface

    data = {
        "ietf-interfaces:interface": {
            "name": loopback["name"],
            "description": loopback["description"],
            "type": "iana-if-type:softwareLoopback",
            "enabled": True,
            "ietf-ip:ipv4": {
                "address": [
                    {
                        "ip": loopback["ip"],
                        "netmask": loopback["netmask"]
                    }
                ]
            }
        }
    }
  5. Create URL

    url = interface_url.format(ip = device["address"],
                               port = device["restconf_port"],
                               int_name = loopback["name"]
                              )
  6. Check the complete URL you just composed

    print(url)
  7. Send RESTCONF request to device

    r = requests.put(url,
            headers = restconf_headers,
            auth=(device["username"], device["password"]),
            json = data,
            verify=False)
  8. Check Status Code (expected 201)

    print("Request Status Code: {}".format(r.status_code))
  9. Query for details on the new interface you just created

    # Create URL and send RESTCONF request to core1 for GigE2 Config
    url = interface_url.format(ip = device["address"],
                               port = device["restconf_port"],
                               int_name = "Loopback101"
                              )
    r = requests.get(url,
            headers = restconf_headers,
            auth=(device["username"], device["password"]),
            verify=False)
    
    # Print returned data
    print(r.text)

Delete Network Configuration Details with RESTCONF with restconf_example3.py

  1. Continuing from previous exercise. If starting from new interpreter, execute these steps.

    import requests, urllib3, sys
    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    restconf_headers = {"Accept": "application/yang-data+json"}
    restconf_base = "https://{ip}:{port}/restconf/data"
    interface_url = restconf_base + "/ietf-interfaces:interfaces/interface={int_name}"
    url = interface_url.format(ip = device["address"],
                               port = device["restconf_port"],
                               int_name = "Loopback101"
                              )
  2. Send DELETE request to remove the Loopback.

    r = requests.delete(url,
            headers = restconf_headers,
            auth=(device["username"], device["password"]),
            verify=False)
  3. Check Status Code (expected 204)

    print("Request Status Code: {}".format(r.status_code))
  4. Query for details on the new interface (no output expected, as you just deleted it)

    r = requests.get(url,
            headers = restconf_headers,
            auth=(device["username"], device["password"]),
            verify=False)
  5. Check status code (expected 404)

    print(r.status_code)

NETCONF with ncclient

  1. From the root of the python_networking repository, change into the exercise directory.

    cd device_apis/netconf
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:

Retrieve Network Configuration Details with NETCONF with netconf_example1.py

  1. Import libraries

    from ncclient import manager
    from xml.dom import minidom
    import xmltodict
    import sys
  2. Add parent directory to path to allow importing common vars

    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
  3. Create filter template for an interface

    interface_filter = """
    <filter>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
          <name>{int_name}</name>
        </interface>
      </interfaces>
    </filter>
    """
  4. Open NETCONF connection to device

    • Note: Normally you'd use a with block to open connection to device. This avoids needing to manually m.close_session() at the end of a script, but for interactive use, this format is chosen.
    m = manager.connect(host = device["address"],
                         port = device["netconf_port"],
                         username = device["username"],
                         password = device["password"],
                         hostkey_verify = False)
  5. Verify NETCONF connection is active (expected output true)

    m.connected
  6. Create desired NETCONF filter for a particular interface

    filter = interface_filter.format(int_name = "GigabitEthernet2")
  7. Execute a NETCONF using the filter

    r = m.get_config("running", filter)
  8. Pretty print raw xml to screen

    xml_doc = minidom.parseString(r.xml)
    print(xml_doc.toprettyxml(indent = "  "))
  9. Process the XML data into Python Dictionary and use

    interface = xmltodict.parse(r.xml)
  10. Pretty Print the full Python (Ordered) Dictionary.

    from pprint import pprint
    pprint(interface)
  11. If RPC returned data, print out the interesting pieces.

    if not interface["rpc-reply"]["data"] is None:
        # Create Python variable for interface details
        interface = interface["rpc-reply"]["data"]["interfaces"]["interface"]
    
        print("The interface {name} has ip address {ip}/{mask}".format(
                name = interface["name"]["#text"],
                ip = interface["ipv4"]["address"]["ip"],
                mask = interface["ipv4"]["address"]["netmask"],
                )
            )
    else:
        print("No interface {} found".format("GigabitEthernet2"))

Modify Network Configuration Details with NETCONF with netconf_example2.py

  1. Continuing from previous exercise. If starting from new interpreter, execute these steps.

    from ncclient import manager
    from xml.dom import minidom
    import xmltodict
    import sys
    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
    interface_filter = """
    <filter>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
          <name>{int_name}</name>
        </interface>
      </interfaces>
    </filter>
    """
    m = manager.connect(host = device["address"],
                         port = device["netconf_port"],
                         username = device["username"],
                         password = device["password"],
                         hostkey_verify = False)
  2. Verify NETCONF connection is active

    m.connected
  3. Create Python dictionary with new Loopback Details

    loopback = {"int_name": "Loopback102",
                "description": "Demo interface by NETCONF",
                "ip": "192.168.102.1",
                "netmask": "255.255.255.0"}
  4. Create NETCONF template for an interface

    config_data = """
    <config>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
          <interface>
            <name>{int_name}</name>
            <description>{description}</description>
            <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">
              ianaift:softwareLoopback
            </type>
            <enabled>true</enabled>
            <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
              <address>
                <ip>{ip}</ip>
                <netmask>{netmask}</netmask>
              </address>
            </ipv4>
          </interface>
      </interfaces>
    </config>
    """
  5. Create desired NETCONF config payload

    config = config_data.format(**loopback)
  6. Send operation

    r = m.edit_config(target = "running", config = config)
  7. Print OK status (expected output true)

    print("NETCONF RPC OK: {}".format(r.ok))
  8. Create a new NETCONF to check on new loopback interface

    filter = interface_filter.format(int_name = "Loopback102")
  9. Execute a NETCONF using this filter

    r = m.get_config("running", filter)
  10. Pretty print the raw XML to screen

    xml_doc = minidom.parseString(r.xml)
    print(xml_doc.toprettyxml(indent = "  "))

Delete Network Configuration Details with NETCONF with netconf_example3.py

  1. Continuing from previous exercise. If starting from new interpreter, execute these steps.

    from ncclient import manager
    from xml.dom import minidom
    import xmltodict
    import sys
    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
    interface_filter = """
    <filter>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
          <name>{int_name}</name>
        </interface>
      </interfaces>
    </filter>
    """
    loopback = {"int_name": "Loopback102",
                "description": "Demo interface by NETCONF",
                "ip": "192.168.102.1",
                "netmask": "255.255.255.0"}
    m = manager.connect(host = device["address"],
                         port = device["netconf_port"],
                         username = device["username"],
                         password = device["password"],
                         hostkey_verify = False)
  2. Verify NETCONF connection is active

    m.connected
  3. Create new config template to delete an interface

    config_data = """
    <config>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
          <interface operation="delete">
            <name>{int_name}</name>
          </interface>
      </interfaces>
    </config>
    """
  4. Create desired NETCONF config payload and execute to delete the interface

    config = config_data.format(**loopback)
    r = m.edit_config(target = "running", config = config)
  5. Print OK status (expected output true)

    print("NETCONF RPC OK: {}".format(r.ok))
  6. Create a new NETCONF to check on new loopback interface

    filter = interface_filter.format(int_name = "Loopback102")
  7. Execute a NETCONF using this filter

    r = m.get_config("running", filter)
  8. Pretty print the raw XML to screen (expected output will not include the loopback interface, as you just deleted it)

    xml_doc = minidom.parseString(r.xml)
    print(xml_doc.toprettyxml(indent = "  "))

End the NETCONF Connection

  1. Send a RPC request to disconnect the connection.

    m.close_session()
    m.connected
  2. End the Python interpreter.

    exit()

CLI with netmiko

  1. From the root of the python_networking repository, change into the exercise directory.

    cd device_apis/cli
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:

Retrieve Network Configuration Details with CLI with netmiko_example1.py

  1. Import libraries

    from netmiko import ConnectHandler
    import re
    import sys
  2. Add parent directory to path to allow importing common vars

    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
  3. Set device_type for netmiko

    device["device_type"] = "cisco_ios"
  4. Create a CLI command template

    show_interface_config_temp = "show running-config interface {}"
  5. Open CLI connection to device.

    • Note: Normally you'd use a with block to open connection to device. This avoids needing to manually m.close_session() at the end of a script, but for interactive use, this format is chosen.
    ch = ConnectHandler(ip = device["address"],
                        port = device["ssh_port"],
                        username = device["username"],
                        password = device["password"],
                        device_type = device["device_type"])
  6. Create desired CLI command

    command = show_interface_config_temp.format("GigabitEthernet2")
  7. Verify the command has been created correctly

    print(command)
  8. Send command to device

    interface = ch.send_command(command)
  9. Print the raw command output to the screen

    print(interface)
  10. Create regular expression searches to parse the output for desired interface details

    name = re.search(r'interface (.*)', interface).group(1)
    description = re.search(r'description (.*)', interface).group(1)
  11. Pull out the ip and mask for the interface

    ip_info = re.search(r'ip address (.*) (.*)', interface)
    ip = ip_info.group(1)
    netmask = ip_info.group(2)
  12. Print the desired info to the screen

    print("The interface {name} has ip address {ip}/{mask}".format(
            name = name,
            ip = ip,
            mask = netmask,
            )
        )

Modify Network Configuration Details with CLI with netmiko_example2.py

  1. Continuing from previous exercise. If starting from new interpreter, execute these steps.

    from netmiko import ConnectHandler
    import re, sys
    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
    device["device_type"] = "cisco_ios"
    show_interface_config_temp = "show running-config interface {}"
    ch = ConnectHandler(ip = device["address"],
                        port = device["ssh_port"],
                        username = device["username"],
                        password = device["password"],
                        device_type = device["device_type"])
  2. Create Python dictionary with new Loopback Details

    loopback = {"int_name": "Loopback103",
                "description": "Demo interface by CLI and netmiko",
                "ip": "192.168.103.1",
                "netmask": "255.255.255.0"}
  3. Create a CLI configuration

    interface_config = [
        "interface {}".format(loopback["int_name"]),
        "description {}".format(loopback["description"]),
        "ip address {} {}".format(loopback["ip"], loopback["netmask"]),
        "no shut"
    ]
  4. Send configuration to device

    output = ch.send_config_set(interface_config)
  5. Print the raw command output to the screen

    print("The following configuration was sent: ")
    print(output)
  6. Create a CLI command to retrieve the new configuration.

    command = show_interface_config_temp.format("Loopback103")
    interface = ch.send_command(command)
    print(interface)

Delete Network Configuration Details with CLI with netmiko_example3.py

  1. Continuing from previous exercise. If starting from new interpreter, execute these steps.

    from netmiko import ConnectHandler
    import re, sys
    sys.path.append("..") 
    from device_info import vagrant_iosxe as device
    device["device_type"] = "cisco_ios"
    show_interface_config_temp = "show running-config interface {}"
    ch = ConnectHandler(ip = device["address"],
                        port = device["ssh_port"],
                        username = device["username"],
                        password = device["password"],
                        device_type = device["device_type"])
  2. Create a new CLI configuration to delete the interface.

    interface_config = [
        "no interface {}".format(loopback["int_name"])
    ]
  3. Send configuration to device

    output = ch.send_config_set(interface_config)
  4. Print the raw command output to the screen

    print("The following configuration was sent: ")
    print(output)
  5. Create a CLI command to verify configuration removed.

    command = show_interface_config_temp.format("Loopback103")
    interface = ch.send_command(command)
    print(interface)

    Note: attempting to view the configuration of a non-existing interface will generate a CLI error. This output is expected, and one of the reasons APIs like NETCONF or RESTCONF are better suited to programmatic interactions.

End the CLI connection to the device

  1. Disconnect from the device.

    ch.disconnect()
  2. End the Python interpreter.

    exit()

Other Cool Python Stuff

Introduction to pyATS

pyATS is a network testing tool developed by Cisco and made available for free, with significant elements of the underlying code open source.

pyATS offers network developers the ability to profile the network state of hardware, interfaces, protocols, etc... before, during and after changes, to ensure the network is operating as designed, and identify problems before the dreaded phone call. To enable this level of robust testing, pyATS offers a standard way to communicate with network elements and standardize the data returned into native Python objects. This core functionality opens up a lot of flexibility on how pyATS can be used by network developers.

In the following exercises, you will get a brief introduction to pyATS to connect and learn about device details.

Connect and Interact with a Device

  1. From the root of the python_networking repository, change into the exercise directory.

    cd network_testing/pyats
  2. Start an interactive Python interpreter. Example below:

    # ipython
    
    Python 3.6.5 (default, Apr 10 2018, 17:08:37)
    Type 'copyright', 'credits' or 'license' for more information
    IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]:
  3. Import in pyATS libraries and tools

    from genie.conf import Genie
    from ats.topology import loader
    from genie.abstract import Lookup
    from genie.libs import ops # noqa
  4. Read and process the testbed (inventory) file

    genie_testbed = Genie.init("./default_testbed.yaml")
  5. Create a pyATS device object from testbed

    vagrant_iosxe1 = genie_testbed.devices["vagrant-iosxe1"]
  6. Connect to the device

    vagrant_iosxe1.connect()
    • pyATS establishes a connection to the device
  7. Create an abstract device to standardize Python API and code for platform

    vagrant_iosxe1_abstract = Lookup.from_device(vagrant_iosxe1)
  8. Using the abstract device, learn about the Interfaces on the end device

    vagrant_iosxe1_interfaces = vagrant_iosxe1_abstract.ops.interface.interface.Interface(vagrant_iosxe1)
    vagrant_iosxe1_interfaces.learn()
  9. Print out the interface details that were learned

    vagrant_iosxe1_interfaces.info
  10. Display a single interface from the device

    vagrant_iosxe1_interfaces.info["GigabitEthernet1"]
  11. Print the mac address for the interface

    vagrant_iosxe1_interfaces.info["GigabitEthernet1"]["mac_address"]
  12. Notice that there was no parsing of command line output needed to access this data

  13. Execute a command on the device and print the output

    print(vagrant_iosxe1.execute("show version"))
  14. Or store the output into a variable

    version = vagrant_iosxe1.execute("show version")
  15. Send a configuration command to the device

    vagrant_iosxe1.configure("ntp server 10.10.10.10")
  16. Create a configuration command list and send to the device

    config_loopback = [
                        "interface Loopback201", 
                        "description Configured by pyATS", 
                        "ip address 172.16.201.1 255.255.255.0", 
                        "no shut"
                      ]
    vagrant_iosxe1.configure(config_loopback)
  17. Re-learn the interfaces

    vagrant_iosxe1_interfaces = vagrant_iosxe1_abstract.ops.interface.interface.Interface(vagrant_iosxe1)
    vagrant_iosxe1_interfaces.learn()
  18. Get details about new interface

    vagrant_iosxe1_interfaces.info["Loopback201"]
  19. Disconnect from the devices

    vagrant_iosxe1.disconnect()