[Day #15 PyATS Series] Building a Reusable Test Template for All Vendors using pyATS for Cisco [Python for Network Engineer]
Table of Contents
Introduction
Welcome to Day 15 of the 101 Days of pyATS (Vendor-Agnostic) journey. As a network engineer stepping into the world of automation, one of the first challenges you’ll face is code duplication. When you manage networks with multiple vendors—Cisco, Arista, Palo Alto, Fortinet—it’s common to write different test scripts for each device type.
This not only wastes time but makes your automation framework hard to maintain and scale.
This is where reusable test templates with pyATS become game-changers.
By the end of this guide, you’ll know how to:
- Build a single test template that works across vendors
- Abstract device-specific commands using Genie parsers
- Validate device health and connectivity with one testscript
- Scale tests to 1000+ devices without rewriting code
If you’re serious about becoming a Python for Network Engineer, understanding reusable templates will set you apart and prepare you for real-world, multi-vendor network automation.
Topology Overview
For this demonstration, imagine a typical enterprise network:

- All devices have management IPs reachable from the pyATS controller.
- Devices support SSH access.
- Test scripts are executed centrally and connect to each device using the testbed.yaml file.
- The goal is to run a single test template to:
- Collect interface status
- Perform ping reachability
- Validate software version
Topology & Communications
- Transport Protocol: SSH (for CLI commands)
- Framework: pyATS with Genie parsers
- Execution: pyATS AEtest testscript
- Devices:
- Cisco IOS-XE (router/switch)
- Arista EOS (switch)
- Palo Alto firewall
- Fortinet firewall
pyATS handles communication via Unicon. With Genie, we can parse outputs into Python dictionaries, making test logic vendor-neutral.
Workflow Script – reusable_test_template.py
from pyats import aetest from genie.testbed import load from genie.utils import Dq class CommonSetup(aetest.CommonSetup): @aetest.subsection def connect_to_devices(self, testbed): self.parent.parameters['testbed'] = load(testbed) for device in self.parent.parameters['testbed'].devices.values(): device.connect(log_stdout=False) self.parent.parameters[device.name] = device class InterfaceHealthTest(aetest.Testcase): @aetest.test def verify_interfaces(self, testbed): for device in testbed.devices.values(): output = device.parse('show interface status') down_interfaces = Dq(output).contains('notconnect').get_values('interface') if down_interfaces: self.failed(f"Device {device.name} has down interfaces: {down_interfaces}") else: self.passed(f"All interfaces are up on {device.name}") class PingReachabilityTest(aetest.Testcase): @aetest.test def ping_test(self, testbed): for device in testbed.devices.values(): result = device.ping('8.8.8.8') if not result: self.failed(f"Ping failed from {device.name}") else: self.passed(f"Ping successful from {device.name}") class SoftwareVersionTest(aetest.Testcase): @aetest.test def check_version(self, testbed): for device in testbed.devices.values(): version_output = device.parse('show version') software_version = version_output.get('version') or version_output.get('os', 'unknown') print(f"{device.name} running software: {software_version}") if '15.' not in software_version and 'EOS' not in software_version: self.failed(f"Unexpected software version on {device.name}") else: self.passed(f"Valid software version on {device.name}") class CommonCleanup(aetest.CommonCleanup): @aetest.subsection def disconnect_all(self, testbed): for device in testbed.devices.values(): device.disconnect()
Explanation by Line
- CommonSetup: Establishes connections to all devices once, reused by all tests.
- InterfaceHealthTest: Uses Genie
parse
to fetch structured interface status, checking fornotconnect
. - PingReachabilityTest: Vendor-neutral ping method.
- SoftwareVersionTest: Parses software version, ensuring compliance with desired releases.
- CommonCleanup: Gracefully disconnects from devices.
- Reusability: This single script can run across all vendors—no need to rewrite for each OS.
testbed.yml Example
devices: cisco_router: os: iosxe type: router connections: cli: protocol: ssh ip: 10.0.0.1 credentials: default: username: admin password: cisco123 arista_switch: os: eos type: switch connections: cli: protocol: ssh ip: 10.0.0.2 credentials: default: username: admin password: arista123 palo_alto_fw: os: panos type: firewall connections: cli: protocol: ssh ip: 10.0.0.3 credentials: default: username: admin password: palo123 fortigate_fw: os: fortinet type: firewall connections: cli: protocol: ssh ip: 10.0.0.4 credentials: default: username: admin password: forti123
Post-validation CLI Screenshots (Expected Output)
After running:
pyats run job reusable_test_template.py --testbed testbed.yml
Sample console output:
Connecting to all devices... All interfaces are up on cisco_router Ping successful from cisco_router Valid software version on cisco_router --- All interfaces are up on arista_switch Ping successful from arista_switch Valid software version on arista_switch --- All interfaces are up on palo_alto_fw Ping successful from palo_alto_fw Valid software version on palo_alto_fw --- All interfaces are up on fortigate_fw Ping successful from fortigate_fw Valid software version on fortigate_fw Test Result: PASSED for all devices
FAQs
Q1: What is the main benefit of using a reusable test template in pyATS?
A reusable template allows you to write a single test script that works across multiple vendors (Cisco, Arista, Palo Alto, Fortinet) without changing the logic for each device. It simplifies maintenance, scales easily to thousands of devices, and enforces standardized testing practices across your network automation framework.
Q2: How does pyATS handle different command outputs from different vendors?
pyATS uses Genie parsers that normalize raw CLI output into structured Python dictionaries. This means you can use the same script to run commands on different operating systems (IOS-XE, EOS, PAN-OS, FortiOS) without manually writing separate parsing logic.
Q3: Can I add or remove tests from the reusable template without affecting other parts of the script?
Yes. The template is modular, built with aetest.Testcase
classes. You can easily add new test cases (e.g., CPU, memory checks) or remove existing ones without breaking other sections. This modular approach makes it highly adaptable for different testing needs.
Q4: How does the test template scale to 1000+ devices?
The reusable template, combined with a properly defined testbed.yml
, allows pyATS to dynamically connect to multiple devices in parallel or sequentially. Since all devices are abstracted in the testbed, the script doesn’t need manual edits to handle large-scale networks.
Q5: What if a device OS is not officially supported by pyATS or Genie parsers?
If Genie doesn’t provide a parser for a certain OS or command, you can:
- Use custom parsers in pyATS
- Use raw CLI output and Python regex for temporary parsing
- Submit contributions to Genie’s open-source parsers
This flexibility ensures you can still integrate unsupported devices into your test template.
Q6: Can we integrate reusable templates with CI/CD pipelines?
Absolutely. Reusable pyATS templates are designed for automation pipelines. You can run them on Jenkins, GitLab CI/CD, or Ansible Tower, triggering them after configuration changes or scheduled intervals for continuous network validation.
Q7: How do we handle vendor-specific features in a generic template?
You can use conditional logic within the script (e.g., if device.os == 'iosxe'
) to handle commands or validations that only exist on a specific vendor. The rest of the script remains reusable for common checks like interface health or reachability.
Q8: Is this reusable template approach suitable for beginners learning Python for Network Engineers?
Yes. In fact, reusable templates reduce coding complexity by avoiding repeated scripts for each vendor. Beginners can focus on learning test structures, parsers, and validations without worrying about device-specific syntax, making it an ideal starting point for Python for Network Engineer learning paths.
YouTube Link
Watch the Complete Python for Network Engineer: Building a Reusable Test Template for All Vendors using pyATS for Cisco [Python for Network Engineer] Lab Demo & Explanation on our channel:
Join Our Training
Mastering pyATS and building scalable, reusable test frameworks is a must-have skill for modern network engineers.
Trainer Sagar Dhawan runs a 3-month instructor-led training program that teaches:
- Python for Network Engineers
- pyATS testing and reusable framework design
- Multi-vendor automation with Genie parsers
- Building CI/CD pipelines for network testing
- Real-world labs with Cisco, Arista, Palo Alto, and Fortinet devices
Turn yourself into a network automation expert – learn by doing, not theory.
Enroll now: https://course.networkjourney.com/python-ansible-api-cisco-devnet-for-network-engineers
Make your scripts vendor-agnostic, scalable, and production-ready with Python for Network Engineer best practices.
Enroll Now & Future‑Proof Your Career
Email: info@networkjourney.com
WhatsApp / Call: +91 97395 21088