Published on

Pyats - Network framework

Authors
  • avatar
    Name
    Jimmy Lai
    Twitter

The Get Started with pyATS Guide walks you through the features and functionality of the pyATS network test automation system. It is intended for test script developers and network engineers who want to get started with pyATS and the pyATS Library (previously "Genie").

Introduction

pyATS and the pyATS Library together define an ecosystem that streamlines and standardizes how you set up and run automated network tests. pyATS and the pyATS Library provide sanity, feature, solution, system, and scale test automation for any type of device or virtual device. pyATS is currently used with devices such as routers and switches, access points, firewalls, Linux servers, phones, cable CPEs, and many more.

Originally developed for internal Cisco engineers, the pyATS ecosystem is at the core of Cisco’s Test Automation Solution. It’s currently used by Cisco engineering, DevNet engineers, network engineers, and developers.

The pyATS ecosystem empowers your team to create and run consistent, repeatable, iterative, and reusable tests. pyATS provides the test framework, and the pyATS Library offers ready-to-use test components.

Support Platform

For example, if os=iosxe and platform=abc, since abc is not found in the iosxe table, it will fallback to use the generic iosxe plugin. If os=iosxe and platform=cat3k, it will use the specific plugin iosxe/cat3k.

(https://pubhub.devnetcloud.com/media/unicon/docs/user_guide/supported_platforms.html)[Support Platform]

https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/parsers/show%2520cdp%2520neighbors

Shell mode

If you want to use Python, you can use pyats shell to load the testbed API and create your testbed and device objects. Then, tell the system to connect to each device and to learn the specified features. In this example, the system stores the output as a Python dictionary in the variable learnt and displays the output:

(pyats) $ pyats shell --testbed-file mock.yaml
   >>> learnt = {}
   >>> for name, dev in testbed.devices.items():
   ...     dev.connect()
   ...     learnt[name] = {}
   ...     learnt[name]['bgp'] = dev.learn('bgp')
   ...     learnt[name]['ospf'] = dev.learn('ospf')
   ...
(pyats) $ pyats shell --testbed-file mock.yaml
   >>> dev = testbed.devices['uut']
   >>> dev.connect()
   >>> output = dev.learn('all')

Basic example

To launch a job, use pyats. The built-in testbed file handling plugin accepts a --testbed-file argument, which automatically loads and parses the provided testbed file into testbed parameter, and provide it to the testscript. When launched, each testscript called by run() api inside the job runs as a child process, and the contents inside its if name == 'main' block is ignored. Add the --html-logs argument to enable generation of HTML log files - they are easier to read.

bash$ pyats run job ios_job.py --testbed-file ios_testbed.yaml --html-logs

# Example: ios_job.py
# -------------------
#
#   a simple job file for the script above

from pyats.easypy import run

def main():

    # run api launches a testscript as an individual task.
    run('connectivity_check.py')
import logging
from pyats import aetest 

log = logging.getLogger(__name__)

class common_setup(aetest.CommonSetup):
  @aetest.subsection 
  def sample_subsection(self, section):
    log.info("Aetest Common Setup %s - %s " % (section, self.uid)) 

class tc_one(aetest.Testcase):
    @aetest.setup 
    def prepare_testcase(self, section):
        log.info("Preparing the test")
        log.info(section)

    @aetest.test
    def simple_test_1(self):
        log.info("First test")

    @aetest.cleanup
    def clean_testcase(self):
        log.info("testcase cleanup")

RUN JOB

import os
from genie.testbed import load

def main(runtime):

    # ----------------
    # Load the testbed
    # ----------------
    if not runtime.testbed:
        # If no testbed is provided, load the default one.
        # Load default location of Testbed
        testbedfile = os.path.join('testbed.yaml')
        testbed = load(testbedfile)
    else:
        # Use the one provided
        testbed = runtime.testbed

    # Find the location of the script in relation to the job file
    testscript = os.path.join(os.path.dirname(__file__), 'pyats_dmvpn_tests.py')

    # run script
    runtime.tasks.run(testscript=testscript, testbed=testbed)

Secret Strings¶

Secret strings (such as passwords) may be specified in an encoded form suitable for sharing with a wide audience, while ensuring that only authorized users are able to decode these strings into their plaintext form.

A secret string class is provided that behaves like a string but has some special features.

[secrets]
string.representer = pyats.utils.secret_strings.FernetSecretStringRepresenter
string.key = dSvoKX23jKQADn20INt3W3B5ogUQmh6Pq00czddHtgU=

pip install cryptography

pyats secret keygen

> chmod 600 ~/.pyats/pyats.conf

Do a test decode of the encoded password:

> pyats secret decode gAAAAABdsgvwElU9_3RTZsRnd4b1l3Es2gV6Y_DUnUE8C9y3SdZGBc2v0B2m9sKVz80jyeYhlWKMDwtqfwlbg4sQ2Y0a843luOrZyyOuCgZ7bxE5X3Dk_NY=
Decoded string :
MySecretPassword


# Snippet of your testbed.yaml
testbed:
    name: sampleTestbed
    credentials:
        default:
            username: admin
            password: "%ENC{gAAAAABdsgvwElU9_3RTZsRnd4b1l3Es2gV6Y_DUnUE8C9y3SdZGBc2v0B2m9sKVz80jyeYhlWKMDwtqfwlbg4sQ2Y0a843luOrZyyOuCgZ7bxE5X3Dk_NY=}"

Using as a context manager¶

A FileServer can also be used as a context manager if not defined in the testbed. It will only last for the duration of the context block, but can also add itself to the testbed for discovery by various copy APIs during that time.

from genie.libs.filetransferutils import FileServer
with FileServer(protocol='ftp',
                path='/path/to/root/dir',
                testbed=testbed,
                name='mycontextserver') as fs:
    uut.api.copy_to_device(protocol='ftp',
                           server='mycontextserver',
                           remote_path='myimage.bin',
                           local_path='flash:/)

Helpful Device APIs

This topic describes how to use pyATS Library Device api function to perform a multitude of operations on a Device such as adding or removing configuration, verifying configuration states, retrieving current system state or configuration etc.

device.api.shut_interface(interface='GigabitEthernet3') 
routes = device.api.get_routes()

show ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is 10.255.0.1 to network 0.0.0.0

S*    0.0.0.0/0 [254/0] via 10.255.0.1
      10.0.0.0/8 is variably subnetted, 10 subnets, 3 masks
C        10.0.1.0/24 is directly connected, GigabitEthernet2
L        10.0.1.1/32 is directly connected, GigabitEthernet2
C        10.0.2.0/24 is directly connected, GigabitEthernet3
L        10.0.2.1/32 is directly connected, GigabitEthernet3
C        10.1.1.1/32 is directly connected, Loopback0
O        10.2.2.2/32 [110/2] via 10.0.2.2, 6d01h, GigabitEthernet3
                     [110/2] via 10.0.1.2, 00:02:40, GigabitEthernet2
C        10.11.11.11/32 is directly connected, Loopback1