- Published on
Pyats - Network framework
- Authors
- Name
- Jimmy Lai
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").
- Support Platform
- Shell mode
- Basic example
- RUN JOB
- Secret Strings¶
- Using as a context manager¶
- Helpful Device APIs
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