pyATS and Genie: Part 3

Introduction

If you’ve been following along in this series, you’ve seen how we can programmatically interact and gather information about our network using pyATS and Genie. That’s great and all, but wouldn’t it be better if we could begin providing true value to ourselves and our team?

As mentioned in Part 2, pyATS is essentially the foundation to our testing environment. It provides a structure to how our devices and topology are defined. In this part, we are going to dive deeper into pyATS and how we can begin writing testcases and testscripts. pyATS provides a standard automation testing framework (or harness, as they refer to it) called AEtest. AEtest, or Automation Easy Testing, comes with defined building blocks on how to structure your testscripts. Now before we get deeper, you may be asking, what is a testcase or testscript? We create “testscripts” now for running checks during a change window, such as pinging a device before and after a change to ensure connectivity. No, no, no… a testscript can be much more than that. What if we could check for a specific number of ‘established’ BGP peers AND check the routing table for a specific route before and after a change? If the results don’t meet our expectation, we can roll the changes back. Oh yeah, we can also throw in your ping test at the end too.

On top of it all, AEtest is pythonic in nature with an object-oriented design, which allows you to create relationships amongst objects, add more logic to your tests, and even alter whether a test is ran depending on another test’s results. That’s pretty insane! Enough with the intro, let’s dive into building testscripts with AEtest in pyATS.

So what is a Testscript?

AEtest provides a defined structure on how to build your testscripts. This confused me at first, as I thought this structure was a mere suggestion. However, after playing around with it, I quickly realized how important each section is to the overall testscript. Let’s take a look at the different sections of a testscript.

As I’ve said in previous posts, I’m a visual person. I like creating a picture or diagram to understand how something works. The above picture displays each major section of a testscript and why each one is important. Let’s quickly run through each one:

Common Setup

The Common Setup section of an AEtest testscript provides the basic configuration and connectivity to all test devices. For example, you would use this section to connect to each device in your testbed. If you’ve ever worked with a virtual network topology, you’ll know that you have to enable the interfaces when you bring up a virtual device (CSR1000v, Nexus 9Kv, etc.) by performing a ‘no shut’ on each interface. This would be the appropriate section to do that. Just think of this section as the preparation section.

Testcases

The Testcases section is where you write out your individual tests. Tests can include making a configuration change on a device and parsing out a ‘show’ command to confirm the result meets your expectation, or simply running a ping test. The best part about testcases is that you’re in control and can define the specific tests to meet your requirements. pyATS provides the framework, and Genie provides the set of tools to gather and parse the information that’s important to you.

Common Cleanup

The Common Cleanup section is the last section ran in a testscript. This section is focused on cleaning up any configuration changes or other changes you made during testing. The goal is to reset the environment to how it was before testing. You can think of this section as the custodian of each testscript… CLEAN UP ON TESTSCRIPT ONE!

There are many more features and levels to each section, but I’m trying to keep it as simple as possible to avoid overwhelming you. However, I’ll include links to the documentation at the end of this post so that you can read more about each section.

Writing our own Testscript

So now that we’ve established testscripts are a little more than running a few ping tests and manually recording the results in Notepad (I know we all run more than a few ping tests for verification, but I’m trying to keep it lighthearted!), let’s jump into writing our first testscript using AEtest.

In our first testscript, we are going to write a testcase to check the software version of an IOS-XE device and confirm whether it meets our defined standard.

from pyats import aetest
from pprint import pprint
from genie.utils import Dq
import logging
import sys

logger = logging.getLogger(__name__)

class CommonSetup(aetest.CommonSetup):
    
    @aetest.subsection
    def connect_to_devices(self, steps, testbed, host):
        device = testbed.devices[str(host)]
        
        # Add device as testscript param
        self.parent.parameters.update(device = device)
        
        with steps.start(f'Connect to {device.name}'):
            # Connect to the device
            device.connect()

### TESTCASE SECTION ###
class FirstTestcase(aetest.Testcase):
    
    @aetest.setup
    def setup(self, device, testbed):
        # Confirm we are connected to the device before running commands
        if device.connected:
            self.passed('Successfully connected to the device')
        else:
            self.failed('Could not connect to the device')
    
    @aetest.test
    def verify_ios_version(self, steps, device):
        with steps.start('Checking the IOS version') as step:
            try:
                self.version = device.parse('show version')
                current_dev_ios = Dq(self.version).get_values('xe_version')
                # Checks to see whether the IOS-XE version meets the set standard version of 17.3.3
                ios_check = Dq(self.version).contains('17.03.03').get_values('xe_version')
                # A populated list is returned if the version matched
                if ios_check:
                    step.passed('Device is running the proper IOS version.')
                else:
                # An empty list is returned if the version didn't match
                    step.failed('Device is running a different IOS version than the defined standard.')
            except Exception as e:
                # If an exception is caught, the testcase fails and prints the error
                self.failed(f'Could not parse data due to the following error: {str(e)}')
                
class CommonCleanup(aetest.CommonCleanup):
    
    @aetest.subsection
    def disconnect_from_devices(self, steps, device):
        with steps.start(f'Disconnecting from {device.name}'):
            # Disconnect from the device
            device.disconnect()

There’s a lot to dissect in this testscript. First, I want to point out that I highlighted the major “container classes” that I summarized earlier. These classes are referred to as “containers” because they contain other test sections. This concept took me awhile to understand, but let’s take a look at this diagram found in the pyATS docs:

                            +--------------+
                            |  TestScript  |
                            +-------+------+
                                    |
       +----------------------------+---------------------------+
       |                            |                           |
+------+------+            +--------+-------+           +-------+-------+
| CommonSetup |            |   Testcases    |           | CommonCleanup |
+------+------+            +--------+-------+           +-------+-------+
       |                            |                           |
+------+------+                     |                    +------+------+
| subsections |          +----------+-----------+        | subsections |
+-------------+          |          |           |        +-------------+
                     +---+---+  +---+---+  +----+----+
                     | setup |  | tests |  | cleanup |
                     +-------+  +-------+  +---------+

You can see that each testscript has a hierarchy to it. The Common Setup and Common Cleanup sections can be broken down into subsections, and Testcases can be broken down even further into setup, test, and cleanup sections. There is even one more level that’s not depicted here called steps. As mentioned previously, AEtest testscripts are pythonic in nature with an object-oriented design, so each of these sections can inherit from one another. For example, the TestScript object is the parent to each of the Common Setup, Testcases, and Common Cleanup sections. Any parameters defined at the TestScript level can be accessed by each of the child sections. There’s ALOT more to how these objects can relate to one another, but I’ll save that for another day. My goal is to provide just enough of an overview of the relationships found in testscripts for you to understand further explanations of our first testscript.

Now that we have a basic understanding of how a testscript is structured and its relationships, let’s start reviewing our first testscript. In our Common Setup, we connect to a specific host that is defined in our testbed. For reference, this testscript is being used in a web app that allows a user to input the hostname and IP address into a form – more to come on that at a later time :). So we have a host, testbed, and steps that are passed in as parameters to this specific section. The device parameter is added to the testscript parameters, so it can be used anywhere in this testscript. After we define the device, I have a subsection for connecting to the device using the device.connect() function – pretty straightforward. Remember, if you are testing with multiple hosts and need to connect to all of them, this would be the section to do that. This section would also be used to apply general configuration across all of testing devices.

After the Common Setup section, we create our first Testcase section. In the Testcase section, we define smaller sections for setup and the actual test section. I decided not to include a cleanup section in the Testcase, as the Common Cleanup section takes care of everything we need for this testscript. In the setup section, we ensure that we’re connected to the device (device.connected() -> returns True/False). Once we confirm we are connected to the device, we issue the show version command to the device and parse the output using Genie. After parsing the output, we check to see whether the device contains a specific IOS-XE version using the Dq library, which we reviewed in Part 2 of this series. If you haven’t already, please check out Part 2 or read the Genie docs to understand the fascinating functionalities of the Dq library – it will save you time and many lines of code! If there’s a match to the specific IOS-XE version we are expecting, a Python list will be returned with the matching string being the first item in the list. For example, if there was a match in our test, the returned value would look something like this: ['17.03.03']. Given that understanding, we check to see whether a populated list is returned. If so, the step passes and the results are rolled up to the Testcase.

The last section is the Common Cleanup section. In this section, we simply disconnect from the device. If any configuration or environmental changes were applied during testing, this would be the section to revert or reset those settings. The goal of this section is to reset the environment to how it looked before testing.

How do I run my Testscript?

So we have a testscript and (for the most part) understand each section of it, but how do we run the actual tests? In the pyATS docs, you’ll find there are two official ways to run an AEtest testscript: Standalone or using Easypy execution. I know, you may be saying, “Greattttttt… now I have to pick and choose a method to run my tests”. Well, pyATS provides some good descriptions and weighs the pros and cons to each method.

Standalone

The Standalone method is the most flexible and easier one to comprehend when first starting out. The docs even suggest this method for “rapid, lightweight script development…”. However, there are some tradeoffs with the ease of use. You are limited to executing a single script, all logging/results are printed to the standard output (stdout), and there is no archiving or official report generation. This truly is your grab n’ go testing method. If you’d like to execute your testscript with this method, you simply need to run your testscript by invoking aetest.main() at the end of your testscript file. Here’s a quick example from the pyATS docs:

# Example
# -------
#
#   enabling standalone execution

import logging
from pyats import aetest

# your testscript sections, testscases & etc
# ...
#

# add the following as the absolute last block in your testscript
if __name__ == '__main__':

    # control the environment
    # eg, change some log levels for debugging
    logging.getLogger(__name__).setLevel(logging.DEBUG)
    logging.getLogger('pyats.aetest').setLevel(logging.DEBUG)

    # aetest.main() api starts the testscript execution.
    # defaults to aetest.main(testable = '__main__')
    aetest.main()

You notice on the last line that all you need to do is invoke aetest.main(), since your testscript and all the testcases are defined in the same file. You can also call on a testscript from another Python file using aetest.main(). Here’s a quick example of how to do that:

aetest.main(testable='./testscripts/first_testscript.py',
         testbed=my_testbed, host=device_hostname)

You can see that you must provide some additional arguments. However, the only necessary argument is the testable argument. The testable argument points to the location of the testscript file. By default, it points to ‘__main__’. All other arguments are keyword arguments that can be later used in the testscript – they are loaded in as testscript-level parameters. The Standalone execution method is great for testing and developing scripts, it may even work for certain use cases, but what if we wanted a something a little more? Easypy execution might be what you’re looking for…

Easypy Execution

Easypy execution is the more “official” way to run your testscripts. You would use this method to perform production-grade testing, such as sanity/regression testing. There are many advantages to using Easypy over Standalone execution. With Easypy, you can aggregate multiple testscripts into a single job file. Easypy will take care of logging configuration, with the option for user customization in each job file. A TaskLog object is used to generate results, reports, and archives. A Reporter object is used for reporting results by generating a YAML results file, along with XML summary files.

There are multiple ways to run Easypy jobs. One of the most common ways is to use the pyats run job CLI commands. This eliminates the need to code anything additional to run your testscript. If you have pyATS installed, you will have access to the pyATS command-line. The other option is to use the run() API. Just like with the Standalone execution, you pass in the location of the testscript file as the first argument and the execution environment takes care of the rest. Here’s another example from the pyATS docs:

# Example
# -------
#
#   pyats job file example, with script arguments

from pyats.easypy import run

def main():

    # providing a couple custom script arguments as **kwargs
    run(testscript='/path/to/your/script.py',
        pyats_is_awesome = True,
        aetest_is_legendary = True)

# if this job file was run with the following command:
#   pyats run job example_job.py --testbed-file /path/to/my/testbed.yaml
#
# and the script had one testcase that prints out the script's parameters,
# the output of the script ought to be:
#
#   starting test execution for testscript 'a.py'
#   +------------------------------------------------------------------------------+
#   |                          Starting testcase Testcase                          |
#   +------------------------------------------------------------------------------+
#   +------------------------------------------------------------------------------+
#   |                            Starting section test                             |
#   +------------------------------------------------------------------------------+
#   Parameters = {'testbed': <Testbed object at 0xf742f74c>,
#                 'pyats_is_awesome': True,
#                 'aetest_is_legendary': True}
#   The result of section test is => PASSED
#   The result of testcase Testcase is => PASSED

As you can see, the run() API is called and the first argument, testscript, is the file path to the testscript we want to execute. After Easypy execution, the runtime logs and archives will be stored in a local directory. You can review each of these file manually or use the pyats logs view CLI command to view them in a nice HTML format on a local web server.

Now that I’ve reviewed each method to run a testscript, it’s up to you decide, based on requirements, which method is best for you.

Running the Testscript

To run my example testscript, I chose to use the Standalone execution and capture the results in the standard output (stdout). I used the ‘IOS XE on CSR Recommended Code Always On‘ sandbox hosted by Cisco DevNet as my test device. Here’s what the results look like for Standalone execution of the testscript:

2021-06-22T11:04:06: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:06: %AETEST-INFO: |                            Starting common setup                             |
2021-06-22T11:04:06: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:06: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:06: %AETEST-INFO: |                    Starting subsection connect_to_devices                    |
2021-06-22T11:04:06: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:07: %AETEST-INFO: +..............................................................................+
2021-06-22T11:04:07: %AETEST-INFO: :                    Starting STEP 1: Connect to csr1000v-1                    :
2021-06-22T11:04:07: %AETEST-INFO: +..............................................................................+

2021-06-22 11:04:07,061: %UNICON-INFO: +++ csr1000v-1 logfile /tmp/csr1000v-1-cli-20210622T110407061.log +++

2021-06-22 11:04:07,061: %UNICON-INFO: +++ Unicon plugin iosxe +++


2021-06-22 11:04:10,775: %UNICON-INFO: +++ connection to spawn: ssh -l developer 64.103.37.51 -p 8181, id: 140725016193680 +++

2021-06-22 11:04:10,775: %UNICON-INFO: connection to csr1000v-1
Password: 

Welcome to the DevNet Sandbox for CSR1000v and IOS XE

The following programmability features are already enabled:
  - NETCONF
  - RESTCONF

Thanks for stopping by.



csr1000v-1#

2021-06-22 11:04:11,234: %UNICON-INFO: +++ initializing handle +++

2021-06-22 11:04:11,297: %UNICON-INFO: +++ csr1000v-1 with alias 'cli': executing command 'term length 0' +++
term length 0
csr1000v-1#

2021-06-22 11:04:11,659: %UNICON-INFO: +++ csr1000v-1 with alias 'cli': executing command 'term width 0' +++
term width 0
csr1000v-1#

2021-06-22 11:04:12,059: %UNICON-INFO: +++ csr1000v-1 with alias 'cli': executing command 'show version' +++
show version
Cisco IOS XE Software, Version 16.09.03
Cisco IOS Software [Fuji], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.9.3, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2019 by Cisco Systems, Inc.
Compiled Wed 20-Mar-19 07:56 by mcpre


Cisco IOS-XE software, Copyright (c) 2005-2019 by cisco Systems, Inc.
All rights reserved.  Certain components of Cisco IOS-XE software are
licensed under the GNU General Public License ("GPL") Version 2.0.  The
software code licensed under GPL Version 2.0 is free software that comes
with ABSOLUTELY NO WARRANTY.  You can redistribute and/or modify such
GPL code under the terms of GPL Version 2.0.  For more details, see the
documentation or "License Notice" file accompanying the IOS-XE software,
or the applicable URL provided on the flyer accompanying the IOS-XE
software.


ROM: IOS-XE ROMMON

csr1000v-1 uptime is 2 minutes
Uptime for this control processor is 3 minutes
System returned to ROM by reload
System image file is "bootflash:packages.conf"
Last reload reason: reload



This product contains cryptographic features and is subject to United
States and local country laws governing import, export, transfer and
use. Delivery of Cisco cryptographic products does not imply
third-party authority to import, export, distribute or use encryption.
Importers, exporters, distributors and users are responsible for
compliance with U.S. and local country laws. By using this product you
agree to comply with applicable laws and regulations. If you are unable
to comply with U.S. and local laws, return this product immediately.

A summary of U.S. laws governing Cisco cryptographic products may be found at:
http://www.cisco.com/wwl/export/crypto/tool/stqrg.html

If you require further assistance please contact us by sending email to
export@cisco.com.

License Level: ax
License Type: Default. No valid license found.
Next reload license Level: ax


Smart Licensing Status: Smart Licensing is DISABLED

cisco CSR1000V (VXE) processor (revision VXE) with 2392579K/3075K bytes of memory.
Processor board ID 9UB7M1TZDUS
3 Gigabit Ethernet interfaces
32768K bytes of non-volatile configuration memory.
8113280K bytes of physical memory.
7774207K bytes of virtual hard disk at bootflash:.
0K bytes of WebUI ODM Files at webui:.

Configuration register is 0x2102

csr1000v-1#

2021-06-22 11:04:13,457: %UNICON-INFO: +++ csr1000v-1 with alias 'cli': configure +++
config term
Enter configuration commands, one per line.  End with CNTL/Z.
csr1000v-1(config)#no logging console
csr1000v-1(config)#line console 0
csr1000v-1(config-line)#exec-timeout 0
csr1000v-1(config-line)#end
csr1000v-1#
2021-06-22T11:04:15: %AETEST-INFO: The result of STEP 1: Connect to csr1000v-1 is => PASSED
2021-06-22T11:04:15: %AETEST-INFO: +----------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: |                       STEPS Report                       |
2021-06-22T11:04:15: %AETEST-INFO: +----------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: STEP 1 - Connect to csr1000v-1                    Passed    
2021-06-22T11:04:15: %AETEST-INFO: ------------------------------------------------------------
2021-06-22T11:04:15: %AETEST-INFO: The result of subsection connect_to_devices is => PASSED
2021-06-22T11:04:15: %AETEST-INFO: The result of common setup is => PASSED
2021-06-22T11:04:15: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: |                       Starting testcase FirstTestcase                        |
2021-06-22T11:04:15: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: |                            Starting section setup                            |
2021-06-22T11:04:15: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: Passed reason: Successfully connected to the device
2021-06-22T11:04:15: %AETEST-INFO: The result of section setup is => PASSED
2021-06-22T11:04:15: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: |                     Starting section verify_ios_version                      |
2021-06-22T11:04:15: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:15: %AETEST-INFO: +..............................................................................+
2021-06-22T11:04:15: %AETEST-INFO: :                  Starting STEP 1: Checking the IOS version                   :
2021-06-22T11:04:15: %AETEST-INFO: +..............................................................................+

2021-06-22 11:04:16,465: %UNICON-INFO: +++ csr1000v-1 with alias 'cli': executing command 'show version' +++
show version
Cisco IOS XE Software, Version 16.09.03
Cisco IOS Software [Fuji], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.9.3, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2019 by Cisco Systems, Inc.
Compiled Wed 20-Mar-19 07:56 by mcpre


Cisco IOS-XE software, Copyright (c) 2005-2019 by cisco Systems, Inc.
All rights reserved.  Certain components of Cisco IOS-XE software are
licensed under the GNU General Public License ("GPL") Version 2.0.  The
software code licensed under GPL Version 2.0 is free software that comes
with ABSOLUTELY NO WARRANTY.  You can redistribute and/or modify such
GPL code under the terms of GPL Version 2.0.  For more details, see the
documentation or "License Notice" file accompanying the IOS-XE software,
or the applicable URL provided on the flyer accompanying the IOS-XE
software.


ROM: IOS-XE ROMMON

csr1000v-1 uptime is 2 minutes
Uptime for this control processor is 3 minutes
System returned to ROM by reload
System image file is "bootflash:packages.conf"
Last reload reason: reload



This product contains cryptographic features and is subject to United
States and local country laws governing import, export, transfer and
use. Delivery of Cisco cryptographic products does not imply
third-party authority to import, export, distribute or use encryption.
Importers, exporters, distributors and users are responsible for
compliance with U.S. and local country laws. By using this product you
agree to comply with applicable laws and regulations. If you are unable
to comply with U.S. and local laws, return this product immediately.

A summary of U.S. laws governing Cisco cryptographic products may be found at:
http://www.cisco.com/wwl/export/crypto/tool/stqrg.html

If you require further assistance please contact us by sending email to
export@cisco.com.

License Level: ax
License Type: Default. No valid license found.
Next reload license Level: ax


Smart Licensing Status: Smart Licensing is DISABLED

cisco CSR1000V (VXE) processor (revision VXE) with 2392579K/3075K bytes of memory.
Processor board ID 9UB7M1TZDUS
3 Gigabit Ethernet interfaces
32768K bytes of non-volatile configuration memory.
8113280K bytes of physical memory.
7774207K bytes of virtual hard disk at bootflash:.
0K bytes of WebUI ODM Files at webui:.

Configuration register is 0x2102

csr1000v-1#
2021-06-22T11:04:19: %AETEST-ERROR: [31mFailed reason: Device is running a different IOS version than the defined standard.[0m[39m
2021-06-22T11:04:19: %AETEST-INFO: The result of STEP 1: Checking the IOS version is => FAILED
2021-06-22T11:04:19: %AETEST-INFO: +----------------------------------------------------------+
2021-06-22T11:04:19: %AETEST-INFO: |                       STEPS Report                       |
2021-06-22T11:04:19: %AETEST-INFO: +----------------------------------------------------------+
2021-06-22T11:04:19: %AETEST-INFO: STEP 1 - Checking the IOS version                 Failed    
2021-06-22T11:04:19: %AETEST-INFO: ------------------------------------------------------------
2021-06-22T11:04:19: %AETEST-INFO: The result of section verify_ios_version is => FAILED
2021-06-22T11:04:19: %AETEST-INFO: The result of testcase FirstTestcase is => FAILED
2021-06-22T11:04:19: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:19: %AETEST-INFO: |                           Starting common cleanup                            |
2021-06-22T11:04:19: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:19: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:19: %AETEST-INFO: |                 Starting subsection disconnect_from_devices                  |
2021-06-22T11:04:19: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:19: %AETEST-INFO: +..............................................................................+
2021-06-22T11:04:19: %AETEST-INFO: :                Starting STEP 1: Disconnecting from csr1000v-1                :
2021-06-22T11:04:19: %AETEST-INFO: +..............................................................................+
2021-06-22T11:04:30: %AETEST-INFO: The result of STEP 1: Disconnecting from csr1000v-1 is => PASSED
2021-06-22T11:04:30: %AETEST-INFO: +----------------------------------------------------------+
2021-06-22T11:04:30: %AETEST-INFO: |                       STEPS Report                       |
2021-06-22T11:04:30: %AETEST-INFO: +----------------------------------------------------------+
2021-06-22T11:04:30: %AETEST-INFO: STEP 1 - Disconnecting from csr1000v-1            Passed    
2021-06-22T11:04:30: %AETEST-INFO: ------------------------------------------------------------
2021-06-22T11:04:30: %AETEST-INFO: The result of subsection disconnect_from_devices is => PASSED
2021-06-22T11:04:30: %AETEST-INFO: The result of common cleanup is => PASSED
2021-06-22T11:04:30: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:30: %AETEST-INFO: |                               Detailed Results                               |
2021-06-22T11:04:30: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:30: %AETEST-INFO:  SECTIONS/TESTCASES                                                      RESULT   
2021-06-22T11:04:30: %AETEST-INFO: --------------------------------------------------------------------------------
2021-06-22T11:04:30: %AETEST-INFO: .
2021-06-22T11:04:30: %AETEST-INFO: |-- common_setup                                                          PASSED
2021-06-22T11:04:30: %AETEST-INFO: |   `-- connect_to_devices                                                PASSED
2021-06-22T11:04:30: %AETEST-INFO: |       `-- Step 1: Connect to csr1000v-1                                 PASSED
2021-06-22T11:04:30: %AETEST-INFO: |-- FirstTestcase                                                         FAILED
2021-06-22T11:04:30: %AETEST-INFO: |   |-- setup                                                             PASSED
2021-06-22T11:04:30: %AETEST-INFO: |   `-- verify_ios_version                                                FAILED
2021-06-22T11:04:30: %AETEST-INFO: |       `-- Step 1: Checking the IOS version                              FAILED
2021-06-22T11:04:30: %AETEST-INFO: `-- common_cleanup                                                        PASSED
2021-06-22T11:04:30: %AETEST-INFO:     `-- disconnect_from_devices                                           PASSED
2021-06-22T11:04:30: %AETEST-INFO:         `-- Step 1: Disconnecting from csr1000v-1                         PASSED
2021-06-22T11:04:30: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:30: %AETEST-INFO: |                                   Summary                                    |
2021-06-22T11:04:30: %AETEST-INFO: +------------------------------------------------------------------------------+
2021-06-22T11:04:30: %AETEST-INFO:  Number of ABORTED                                                            0 
2021-06-22T11:04:30: %AETEST-INFO:  Number of BLOCKED                                                            0 
2021-06-22T11:04:30: %AETEST-INFO:  Number of ERRORED                                                            0 
2021-06-22T11:04:30: %AETEST-INFO:  Number of FAILED                                                             1 
2021-06-22T11:04:30: %AETEST-INFO:  Number of PASSED                                                             2 
2021-06-22T11:04:30: %AETEST-INFO:  Number of PASSX                                                              0 
2021-06-22T11:04:30: %AETEST-INFO:  Number of SKIPPED                                                            0 
2021-06-22T11:04:30: %AETEST-INFO:  Total Number                                                                 3 
2021-06-22T11:04:30: %AETEST-INFO:  Success Rate                                                             66.7% 
2021-06-22T11:04:30: %AETEST-INFO: --------------------------------------------------------------------------------

You can see that the CSR is not running my set standard version of IOS-XE code (17.3.3), which is why you see the ‘Failed’ result for Step 1 under the verify_ios_version test. Instead, the CSR is running IOS-XE 16.9.3. Our testscript worked!

Conclusion

There was a lot to go over in this blog post, and I can tell you that I just scratched the surface. AEtest is a phenomenal testing framework and provides a structured, but flexible, way to organize and execute testscripts. I hope you were able to learn something from this post and have the time to review the pyATS docs themselves to learn more. In the final part to this series (Part 4), we are going to go over some hidden gem libraries I found in pyATS and Genie that could be useful.

As always, if you have any questions or would like to have a discussion about any topics in this blog post, feel free to hit me up on Twitter (@devnetdan). Thanks again for reading and I’ll catch you in Part 4!

References

pyATS AEtest docs: AEtest – Test Infrastructure ā€” pyATS Documentation
pyATS – Container classes: Object Model ā€” pyATS Documentation
Easypy Runtime Environment docs: Easypy – Runtime Environment ā€” pyATS Documentation

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s