Devnet
pyATS
13 Manage Network Configuration State With

Manage Network Configuration State with pyATS

Introduction

Python Automated Test Systems (pyATS) is an automated testing framework where Python3 tests can be run automatically. Initially used exclusively by Cisco Engineering, pyATS has evolved over time, attracting thousands of test developers and accumulating millions of lines of test scripts and libraries.

The pyATS base platform framework is designed for rapid, agile development cycles, emphasizing data-driven and reusable testing. It offers a strong, extensible Python framework that allows developers to transition from simple, linear test cases to more complex, asynchronous test suites.

Scenario

As the person in charge of managing and testing your company's network configuration, you utilize pyATS for automation. Its structured data output and seamless integration with existing CI/CD and reporting tools simplify the process. Upon discovering pyATS, you realize the necessity of familiarizing yourself with its classes and device definition processes.

In this scenario, you'll employ pyATS to test network configuration states using commands like learn, parse, and diff. Additionally, you'll utilize pyATS to push configuration settings to network devices.

Activity Objective

The objective of this activity is to:

  1. Manage configuration state through network and command comparisons.
  2. Perform network state testing using pyATS.
  3. Push configuration settings to devices with pyATS.

Managing Configuration State with Network and Command Comparisons

A crucial aspect of your role involves managing the state of your network and its configurations. You need the capability to establish a baseline snapshot of the network configuration state and compare subsequent snapshots to identify any changes automatically.

pyATS is well-suited for capturing and managing changes in network and configuration states. Utilizing the learn command, you can create a baseline snapshot of a working configuration and differentiate it from later snapshots.

In this task, you'll learn how to use the learn command to create device configuration snapshots. You'll also understand the significance of producing structured data from CLI output using the parse command and utilizing diff to promptly identify changes in configuration state.

Activity Steps

  1. Retrieve the interface details for R1, R2, and R3 using the learn command.
pyats learn interface --testbed-file my_testbed.yaml --output my_snapshot0

The Genie Learn Summary will display for R1, R2, and R3.

Review the contents of the Connected to section and the Learnt feature status and file locations.

$  pyats learn interface --testbed-file my_testbed.yaml --output my_snapshot0
Learning '['interface']' on devices '['R1', 'R2', 'R3']'
100%|…| 1/1 [00:01<00:00,  1.52s/it]
+==============================================================================+
| Genie Learn Summary for device R1                                            |
+==============================================================================+
|  Connected to R1                                                             |
|  -   Log: my_snapshot0/connection_R1.txt                                     |
|------------------------------------------------------------------------------|
|  Learnt feature 'interface'                                                  |
|  -  Ops structure:  my_snapshot0/interface_iosxe_R1_ops.txt                  |
|  -  Device Console: my_snapshot0/interface_iosxe_R1_console.txt              |
|==============================================================================|


+==============================================================================+
| Genie Learn Summary for device R2                                            |
+==============================================================================+
|  Connected to R2                                                             |
|  -   Log: my_snapshot0/connection_R2.txt                                     |
|------------------------------------------------------------------------------|
|  Learnt feature 'interface'                                                  |
|  -  Ops structure:  my_snapshot0/interface_iosxe_R2_ops.txt                  |
|  -  Device Console: my_snapshot0/interface_iosxe_R2_console.txt              |
|==============================================================================|


+==============================================================================+
| Genie Learn Summary for device R3                                            |
+==============================================================================+
|  Connected to R3                                                             |
|  -   Log: my_snapshot0/connection_R3.txt                                     |
|------------------------------------------------------------------------------|
|  Learnt feature 'interface'                                                  |
|  -  Ops structure:  my_snapshot0/interface_iosxe_R3_ops.txt                  |
|  -  Device Console: my_snapshot0/interface_iosxe_R3_console.txt              |
|=============================================================================|

By following these steps, you'll effectively manage the configuration state of your network using pyATS.

Step Through Connection Log and Review Contents

Introduction

In this step, you will review the connection log contents to understand the interactions with the devices.

Activity Steps

  1. Use the cat command to view the contents of the connection log for R1. Open your Terminal and execute the following command:
cat my_snapshot1/connection_R1.txt | more

Connection Log Output

2024-08-03 17:22:57,294: %UNICON-INFO: +++ R1 logfile my_snapshot1/connection_R1.txt +++
2024-08-03 17:22:57,294: %UNICON-INFO: +++ Unicon plugin iosxe (unicon.plugins.iosxe) +++

2024-08-03 17:22:57,748: %UNICON-INFO: +++ connection to spawn: ssh -l cisco 172.21.1.21 -p 22, id: 140119532091232 +++

2024-08-03 17:22:57,749: %UNICON-INFO: connection to R1

R1#
2024-08-03 17:22:57,838: %UNICON-INFO: +++ initializing handle +++

2024-08-03 17:22:57,904: %UNICON-INFO: +++ R1 with via 'cli': executing command 'term length 0' +++
term length 0
R1#

2024-08-03 17:22:58,100: %UNICON-INFO: +++ R1 with via 'cli': executing command 'term width 0' +++
term width 0
R1#
2024-08-03 17:22:58,294: %UNICON-INFO: +++ R1 with via 'cli': executing command 'show version' +++
show version
Cisco IOS XE Software, Version 17.03.04a
. . .

Review Ops Structure Output for 'interface' Feature

Introduction

Now, let's review the Ops structure output for the Learnt feature 'interface' section.

Activity Steps

  1. Use the cat command to view the my_snapshot1/interface_iosxe_R1_ops.txt output. Execute the following command in your Terminal:
cat my_snapshot1/interface_iosxe_R1_ops.txt | more

Ops Structure Output

}, 
    "GigabitEthernet3": {
. . . 
},
      "auto_negotiate": true,
      "bandwidth": 1000000,
      "counters": {
        "in_broadcast_pkts": 0,
        "in_crc_errors": 0,
        "in_errors": 0,
        "in_mac_pause_frames": 0,
        "in_multicast_pkts": 0,
        "in_octets": 12563408,
        "in_pkts": 80466,
        "last_clear": "never",
        "out_broadcast_pkts": 0,
        "out_errors": 0,
        "out_mac_pause_frames": 0,
        "out_multicast_pkts": 0,
        "out_octets": 11526148,
        "out_pkts": 75672,
        "rate": {
          "in_rate": 0, 
. . . 

Review Console Output for Learnt Feature

Introduction

Finally, let's understand how the console output from the learnt feature differs from the structured data in the Ops view.

Activity Steps

  1. Use the cat command to display the contents of the file:
cat my_snapshot1/interface_iosxe_R1_console.txt | more 

Console Output

+++ R1 with via 'cli': executing command 'show vrf' +++
show vrf
  Name                             Default RD            Protocols   Interfaces
  vlan12                           <not set>             ipv4        Gi2
  vlan13                           <not set>             ipv4        Gi3
  vlan23                           <not set>             ipv4        
R1#
+++ R1 with via 'cli': executing command 'show interfaces' +++
show interfaces
GigabitEthernet1 is up, line protocol is up 
  Hardware is CSR vNIC, address is 000c.2992.caa8 (bia 000c.2992.caa8)
  Description: MGMT - 172.21.1.20/24
  Internet address is 172.21.1.21/24
  MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 
     reliability 255/255, txload 1/255, rxload 1/255
  Encapsulation ARPA, loopback not set
  Keepalive set (10 sec)
  Full Duplex, 1000Mbps, link type is auto, media type is Virtual
  output flow-control is unsupported, input flow-control is unsupported
  ARP type: ARPA, ARP Timeout 04:00:00
  Last input 00:00:00, output 00:00:00, output hang never
  Last clearing of "show interface" counters never
  Input queue: 1/375/0/0 (size/max/drops/flushes); Total output drops: 0
  Queueing strategy: fifo
. . .

By reviewing these outputs, you gain insight into the interactions with the devices and understand the structured data provided by pyATS, essential for integrations with other systems and workflows.

Using pyATS Parse Command for CLI Output

Introduction

In this task, you'll utilize the pyATS parse command to convert CLI command output into structured data, either in JSON format or a dictionary.

Activity Steps

  1. Use the parse command to parse the output of the show interfaces CLI command for devices R1 and R2 specified in your my_testbed.yaml file. Save the output to the my_parsed_output/ directory.
pyats parse "show interfaces" --testbed-file my_testbed.yaml --devices R1 R2 --output my_parsed_output/

Parsed Output Summary

+==============================================================================+
| Genie Parse Summary for R1                                                   |
+==============================================================================+
|  Connected to R1                                                             |
|  -  Log: my_parsed_output//connection_R1.txt                                 |
|------------------------------------------------------------------------------|
|  Parsed command 'show interfaces'                                            |
|  -  Parsed structure: my_parsed_output//R1_show-interfaces_parsed.txt        |
|  -  Device Console:   my_parsed_output//R1_show-interfaces_console.txt       |
|------------------------------------------------------------------------------|
+==============================================================================+
| Genie Parse Summary for R2                                                   |
+==============================================================================+
|  Connected to R2                                                             |
|  -  Log: my_parsed_output//connection_R2.txt                                 |
|------------------------------------------------------------------------------|
|  Parsed command 'show interfaces'                                            |
|  -  Parsed structure: my_parsed_output//R2_show-interfaces_parsed.txt        |
|  -  Device Console:   my_parsed_output//R2_show-interfaces_console.txt       |
|------------------------------------------------------------------------------|
  1. Verify the parsed output to ensure it contains the required details and structure.

Use the cat command to view the contents of the my_parsed_output//R1_show-interfaces_parsed.txt output.

cat my_parsed_output//R1_show-interfaces_parsed.txt | more

Parsed Output Example

{
  "GigabitEthernet1": {
    "arp_timeout": "04:00:00",
    "arp_type": "arpa",
    "auto_negotiate": true,
    "bandwidth": 1000000,
    "counters": {
      "in_broadcast_pkts": 0,
      "in_crc_errors": 0,
      "in_errors": 0,
      "in_frame": 0,
      "in_giants": 0,
      "in_ignored": 0,
      "in_mac_pause_frames": 0,
      "in_multicast_pkts": 0,
      "in_no_buffer": 0,
      "in_octets": 241812350,
      "in_overrun": 0,
      "in_pkts": 415010,
      "in_runts": 0,
      "in_throttles": 0,
      "in_watchdog": 0,
      "last_clear": "never",
      "out_babble": 0,
      "out_broadcast_pkts": 0,
      "out_buffer_failure": 0,
      "out_buffers_swapped": 0,
      "out_collision": 0,
      "out_deferred": 0,
. . .
  1. Compare the parsed output with unstructured data by using the parse command with the --raw argument to output unstructured data, saving this data to the my_unparsed_output/ directory.
pyats parse "show interfaces" --testbed-file my_testbed.yaml --devices R1 R2 --raw --output my_unparsed_output/

Unparsed Output Summary

+==============================================================================+
| Genie Execute Summary for R1                                                 |
+==============================================================================+
|  Connected to R1                                                             |
|  -  Log: my_unparsed_output//connection_R1.txt                               |
|------------------------------------------------------------------------------|
|  Executed command 'show interfaces'                                          |
|  -  Device Console:   my_unparsed_output//R1_show-interfaces_console.txt     |
|------------------------------------------------------------------------------|
+==============================================================================+
| Genie Execute Summary for R2                                                 |
+==============================================================================+
|  Connected to R2                                                             |
|  -  Log: my_unparsed_output//connection_R2.txt                               |
|------------------------------------------------------------------------------|
|  Executed command 'show interfaces'                                          |
|  -  Device Console:   my_unparsed_output//R2_show-interfaces_console.txt     |
|------------------------------------------------------------------------------|
  1. Review the format of the unstructured data.

Use the cat command to view the contents of the my_unparsed_output//R1_show-interfaces_console.txt output.

cat my_unparsed_output//R1_show-interfaces_console.txt | more

Unparsed Output Example

+++ R1 with via 'cli': executing command 'show interfaces' +++
show interfaces
GigabitEthernet1 is up, line protocol is up 
  Hardware is CSR vNIC, address is 000c.2992.caa8 (bia 000c.2992.caa8)
  Description: MGMT - 172.21.1.20/24
  Internet address is 172.21.1.21/24
  MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 
     reliability 255/255, txload 1/255, rxload 1/255
  Encapsulation ARPA, loopback not set
  Keepalive set (10 sec)
  Full Duplex, 1000Mbps, link type is auto, media type is Virtual
  output flow-control is unsupported, input flow-control is unsupported
  ARP type: ARPA, ARP Timeout 04:00:00
  Last input 00:00:00, output 00:00:00, output hang never
  Last clearing of "show interface" counters never
  Input queue: 1/375/0/0 (size/max/drops/flushes); Total output drops: 0
  Queueing strategy: fifo
. . .

This example showcases the versatility of the parse command in pyATS, converting CLI output into structured data, which is crucial for effective automation and analysis of network configurations.

Verifying Baseline Snapshots and Comparing Configuration States

Introduction

Before making changes to network configurations, it's crucial to verify existing baselines and compare them with subsequent snapshots. In this task, you'll utilize pyATS to verify and compare baseline snapshots of network configurations.

Activity Steps

  1. Verify the existing baseline, or “Day 1,” snapshot of the working network configuration for your devices.

Use the ls command to view the contents of the my_snapshot_day1 directory.

ls my_snapshot_day1

Output:

connection_R1.txt  interface_iosxe_R1_console.txt  interface_iosxe_R2_ops.txt
connection_R2.txt  interface_iosxe_R1_ops.txt      interface_iosxe_R3_console.txt
connection_R3.txt  interface_iosxe_R2_console.txt  interface_iosxe_R3_ops.txt
  1. Take a second, or “Day 2,” baseline snapshot of questionable network configuration for your devices.

Use the learn command to take a second baseline snapshot of your network configuration.

pyats learn interface --testbed-file my_testbed.yaml --output my_snapshot_day2

Day 2 Snapshot Summary:

+==============================================================================+
| Genie Learn Summary for device R1                                            |
+==============================================================================+
|  Connected to R1                                                             |
|  -   Log: my_snapshot_day2/connection_R1.txt                                 |
|------------------------------------------------------------------------------|
|  Learnt feature 'interface'                                                  |
|  -  Ops structure:  my_snapshot_day2/interface_iosxe_R1_ops.txt              |
|  -  Device Console: my_snapshot_day2/interface_iosxe_R1_console.txt          |
|==============================================================================| 
. . .
  1. Compare the Day 1 and Day 2 snapshots.

Use the diff command to compare the two snapshots.

pyats diff my_snapshot_day1 my_snapshot_day2 --output diff_snapshot

Diff Summary:

+==============================================================================+
| Genie Diff Summary between directories my_snapshot_day1/ and                 |
| my_snapshot_day2/                                                            |
+==============================================================================+
|  File: interface_iosxe_R3_ops.txt                                            |
|   - Identical                                                                |
|------------------------------------------------------------------------------|
|  File: interface_iosxe_R2_ops.txt                                            |
|   - Diff can be found at diff_snapshot/diff_interface_iosxe_R2_ops.txt       |
|------------------------------------------------------------------------------|
|  File: interface_iosxe_R1_ops.txt                                            |
|   - Identical                                                                |
|------------------------------------------------------------------------------|
  1. Review the differences identified in the output.

Use the cat command to view the contents of the diff_snapshot/diff_interface_iosxe_R2_ops.txt output.

cat diff_snapshot/diff_interface_iosxe_R2_ops.txt | more

Difference Output:

--- my_snapshot_day1/interface_iosxe_R2_ops.txt
+++ my_snapshot_day2/interface_iosxe_R2_ops.txt
 info:
  GigabitEthernet1:
   accounting:
-   ipv6:
-    chars_in: 280
-    chars_out: 0
-    pkts_in: 4
-    pkts_out: 0
  GigabitEthernet2:
-  auto_negotiate: True
+  auto_negotiate: False
-  bandwidth: 1000000
+  bandwidth: 10
-  port_speed: 1000mbps
+  port_speed: 10000mbps
+  vrf: vlan12
  GigabitEthernet3:
+  vrf: vlan23

Conclusion

By utilizing pyATS's learn and diff commands, you efficiently verified and compared baseline snapshots of network configurations. Identifying configuration changes between Day 1 and Day 2 snapshots ensures effective network configuration state management.

Test Network State with pyATS and Easypy

Introduction

In this task, you'll be running a job in Easypy using an example job file (half_duplex_job.py) that references a test script (half_duplex.py). The purpose of this script is to verify that all interfaces are operating at full duplex. You'll review the output of the job using the HTML log viewer.

Activity Steps

  1. Begin by locating the half_duplex_job.py and half_duplex.py files in the pyats directory.
ls h*py

Result

half_duplex_job.py  half_duplex.py
  1. Open the half_duplex.py file in VSCode and review its sections and contents.

Common Setup Section:

###################################################################
#                  COMMON SETUP SECTION                           #
###################################################################
 
class CommonSetup(aetest.CommonSetup):
    @aetest.subsection
    def load_testbed(self, testbed):
        # Convert pyATS testbed to Genie Testbed
        logger.info(
            "Converting pyATS testbed to Genie Testbed to support pyATS Library features"
        )
        testbed = load(testbed)
        self.parent.parameters.update(testbed=testbed)
 
    @aetest.subsection
    def connect(self, testbed):
        """
        establishes connection to all your testbed devices.
        """
        # make sure testbed is provided
        assert testbed, "Testbed is not provided!"
 
        # connect to all testbed devices
        #   By default ANY error in the CommonSetup will fail the entire test run
        #   Here we catch common exceptions if a device is unavailable to allow test to continue
        try:
            testbed.connect()
        except (TimeoutError, StateMachineError, ConnectionError):
            logger.error("Unable to connect to all devices")

Testcases Section:

################################################################### # TESTCASES SECTION # ################################################################### class interface_duplex(aetest.Testcase): @aetest.setup def setup(self, testbed): """Learn and save the interface details from the testbed devices.""" self.learnt_interfaces = {} for device_name, device in testbed.devices.items(): # Only attempt to learn details on supported network operation systems if device.os in ("ios", "iosxe", "iosxr", "nxos"): logger.info(f"{device_name} connected status: {device.connected}") logger.info(f"Learning Interfaces for {device_name}") self.learnt_interfaces[device_name] = device.learn("interface").info @aetest.test def test(self, steps): # Loop over every device with learnt interfaces for device_name, interfaces in self.learnt_interfaces.items(): with steps.start( f"Looking for half-duplex Interfaces on {device_name}", continue_=True ) as device_step:
 
continued-
 
                # Loop over every interface that was learnt
                for interface_name, interface in interfaces.items():
                    with device_step.start(
                        f"Checking Interface {interface_name}", continue_=True
                    ) as interface_step:
 
                        # Verify that this interface has "duplex_mode" (Virtual interfaces Lack duplex)
                        if "duplex_mode" in interface.keys():
                            if interface['duplex_mode'] == 'half':
                                interface_step.failed(
                                    f'Device {device_name} Interface {interface_name} is in half-duplex mode')
else:
                         # If the interface has no duplex, mark as skipped
                            interface_step.skipped(
                                f"Device {device_name} Interface {interface_name} has no duplex"
                            )
  1. Open the half_duplex_job.py file and review its contents.

Result

from pyats.easypy import run
from half_duplex import interface_duplex
 
# main entry point
def main(runtime):
    # run the testscript
    run(testscript=interface_duplex)

Explanation:

  • The run function from pyats.easypy is imported to execute the test script.
  • The main function serves as the main entry point for the job file.
  • The test script (half_duplex.py) is imported, and its interface_duplex testcase is executed.
  1. In this setup, multiple test scripts can be executed in the same environment as part of a pyATS job. Job files are fundamental for Easypy, allowing the execution of multiple test scripts as tasks in the same environment, sharing testbeds, and collectively archiving logs and results. It's an excellent method for grouping or combining similar test scripts to produce useful result summaries.

You want to use the best method for regression and sanity testing, so you run the job file in the Easypy runtime environment, where it will produce logs and archives.

$ pyats run job half_duplex_job.py --testbed-file my_testbed.yaml

This command runs the half_duplex_job.py in the Easypy runtime environment using the testbed defined in my_testbed.yaml. Upon completion, you should see output indicating that the job has finished:

. . . 
2022-08-03T22:04:07: %EASYPY-INFO: Done!

To view the logs, you can use the following command:

$ pyats logs view

This command will launch the pyATS log viewer. From there, you can select the link on the left side of the screen for the job that you just ran (LOGxxxxxxxx item that matches the half_duplex_job).

Once you've selected the appropriate log, you can verify the pass-fail status of your testcase by selecting the Overview tab. This tab displays the overall status and the high-level details for the job and the testcase.

You can further review the status of the three testcase sections (common_setup, testcase, and common_cleanup). By expanding the sections, you can see the output of each subsection, such as load_testbed and connect.

To understand how the testcase section is set up and the tests it performs, expand and examine the interface_duplex section. Here, you can review the output of all the steps in the testcase section, including the setup and test subsections.

Additionally, you can navigate to the Files tab in the log viewer and select the half_duplex_job.report file. This file contains the archive location, Task Result Summary, and Task Result Details, providing insights into the artifacts created from running the job.

By completing these steps, you have successfully utilized the Easypy runtime environment, reviewed the components of the job and the testcase, and examined the log viewer archive to illustrate its utility in testing and verifying the network state.