pyATS
pyATS (Python Automation Test System) is an end-to-end test automation framework, primarily developed by Cisco, designed for network engineers. It provides a robust infrastructure for defining network topologies, running tests against network devices (multi-vendor compatible), and parsing device outputs into structured data using the integrated Genie library. The current version is 26.3, and it maintains an active release cadence with frequent updates to parsers and APIs. [1, 4, 15, 18]
Common errors
-
ModuleNotFoundError: No module named 'pyats.topology'
cause pyATS or its sub-packages are not installed, or the current Python environment is not the one where pyATS was installed. [21]fixEnsure you are in the correct Python virtual environment (if used) and install pyATS: `pip install 'pyats[full]'`. Verify installation with `pyats version check`. [21] -
ConnectionError: Failed to connect to device 'my_device'
cause This generic error indicates a problem establishing a connection to the network device. Common causes include incorrect IP address/hostname, firewall blocking, incorrect credentials, device not reachable, or device hostname mismatch in the testbed file. [10, 20]fixDouble-check the IP address and credentials in your `testbed.yaml`. Ping the device to verify reachability. Ensure the device name in `testbed.yaml` exactly matches the device's configured hostname. Verify SSH/Telnet access manually. [10] -
AttributeError: 'Device' object has no attribute 'parse'
cause The Genie library, which provides the `parse` method for structured output, might not be fully installed or accessible. This typically happens with a minimal `pyats` installation without `genie`. [14]fixEnsure you have installed pyATS with the `[library]` or `[full]` extra, which includes Genie: `pip install 'pyats[full]'`. [5, 14] -
ERROR: Could not find a version that satisfies the requirement pyats[full] (from versions: none)
cause This usually means pip cannot find the package, often due to network issues (e.g., corporate proxy) or a misspelled package name. [21]fixCheck your internet connection. If behind a corporate proxy, configure pip to use it: `pip install --proxy http://proxy.company.com:8080 'pyats[full]'` or set `HTTP_PROXY`/`HTTPS_PROXY` environment variables. Also, ensure the package name is spelled correctly. [21]
Warnings
- breaking In pyATS version 25.5, file transfer utilities were moved from the `pyats` package to `genielibs`. If you were directly importing these utilities from `pyats`, your code will break.
- gotcha Not using Python virtual environments can lead to dependency conflicts and unexpected behavior, especially with multiple Python projects or different pyATS versions. [5, 14, 21]
- gotcha The device name in your `testbed.yaml` file *must* exactly match the configured hostname of the device you are trying to connect to. Mismatches will prevent successful connections. [10]
- gotcha When installing pyATS with optional dependencies (e.g., `pyats[full]`) on Zsh (common on macOS), the square brackets `[]` might need to be quoted due to shell interpretation. [5, 10, 14]
- gotcha Overcomplicating test scripts prematurely, especially when new to pyATS, can lead to frustration and errors. [24]
Install
-
pip install 'pyats[full]' -
pip install pyats[library] -
pip install pyats.contrib
Imports
- aetest
from pyats import aetest
- loader
from pyats.topology import loader
- load
from genie.testbed import load
- run
from pyats.easypy import run
- ConnectionError
from unicon.core.errors import ConnectionError
Quickstart
import os
import logging
from pyats import aetest
from genie.testbed import load
from unicon.core.errors import ConnectionError
# Set up basic logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Create a dummy testbed file (testbed.yaml) for demonstration
# In a real scenario, this would describe your network devices.
# For a live environment, set environment variables for credentials:
# export PYATS_TESTBED_USERNAME='your_username'
# export PYATS_TESTBED_PASSWORD='your_password'
# Example content for testbed.yaml:
# devices:
# CSR1:
# os: iosxe
# connections:
# cli:
# protocol: ssh
# ip: 10.1.1.1 # Replace with your device IP
# credentials:
# default:
# username: '%ENV{PYATS_TESTBED_USERNAME}'
# password: '%ENV{PYATS_TESTBED_PASSWORD}'
class MyFirstTest(aetest.Testcase):
@aetest.setup
def setup(self, testbed):
# Load the testbed from a YAML file
try:
self_testbed = load(os.path.join(os.path.dirname(__file__), 'testbed.yaml'))
self.parent.parameters.update(testbed=self_testbed)
logger.info(f"Testbed loaded successfully: {list(self_testbed.devices.keys())}")
except Exception as e:
self.failed(f"Failed to load testbed: {e}")
@aetest.test
def connect_and_execute(self, testbed):
if not hasattr(self.parent.parameters, 'testbed'):
self.skipped('Testbed not loaded in setup.')
return
for device_name, device in self.parent.parameters['testbed'].devices.items():
logger.info(f"Attempting to connect to device: {device_name}")
try:
device.connect(init_exec_params={'timeout': 60})
logger.info(f"Successfully connected to {device_name}")
# Example: Execute a 'show version' command
output = device.execute('show version')
logger.info(f"Output from {device_name}:\n{output[:200]}...") # Print first 200 chars
# Example: Parse 'show ip interface brief'
parsed_output = device.parse('show ip interface brief')
logger.info(f"Parsed output from {device_name} (first entry): {list(parsed_output.values())[0] if parsed_output else 'No interfaces found'}")
self.passed(f"Successfully connected, executed, and parsed on {device_name}")
except ConnectionError as e:
self.failed(f"Connection failed for {device_name}: {e}")
except Exception as e:
self.failed(f"An error occurred with {device_name}: {e}")
finally:
if device.is_connected():
device.disconnect()
logger.info(f"Disconnected from {device_name}")
@aetest.cleanup
def cleanup(self):
logger.info("Testcase cleanup completed.")
if __name__ == '__main__':
import sys
# Create a dummy testbed.yaml for local execution if it doesn't exist
testbed_path = os.path.join(os.path.dirname(__file__), 'testbed.yaml')
if not os.path.exists(testbed_path):
with open(testbed_path, 'w') as f:
f.write("""
---
devices:
CSR1:
os: iosxe
connections:
cli:
protocol: ssh
ip: 10.1.1.1 # REPLACE WITH YOUR DEVICE IP OR A MOCK IP FOR TESTING
credentials:
default:
username: '%ENV{PYATS_TESTBED_USERNAME}'
password: '%ENV{PYATS_TESTBED_PASSWORD}'
""")
logger.warning(f"Created a dummy '{testbed_path}'. Please replace 10.1.1.1 with a real device IP and set environment variables PYATS_TESTBED_USERNAME and PYATS_TESTBED_PASSWORD.")
# Set dummy env vars if not present for local execution to avoid crashes
os.environ.setdefault('PYATS_TESTBED_USERNAME', 'admin')
os.environ.setdefault('PYATS_TESTBED_PASSWORD', 'password')
# Run the AETest
aetest.main(argv=sys.argv)