pyATS/Genie - Cisco 테스트 자동화

17 조회 2025-11-17 오픈소스 도구
GitHub 문서

pyATS/Genie - 네트워크 테스트 자동화

개요

pyATS(Python Automated Test Systems)는 Cisco가 개발한 네트워크 테스트 자동화 프레임워크이며, Genie는 그 위에서 동작하는 라이브러리입니다.

주요 특징

1. 네트워크 상태 스냅샷

  • 변경 전후 상태 비교
  • 자동 Diff 생성
  • 회귀 테스트

2. 파서 라이브러리

  • 2000+ show 명령어 파서
  • 구조화된 데이터 반환
  • 멀티 벤더 지원

3. 테스트 프레임워크

  • 체계적인 테스트 케이스
  • 상세한 로깅
  • HTML 리포트 생성

설치

pip install pyats[full]
pip install genie

Testbed 파일 (장비 정의)

# testbed.yaml
testbed:
  name: Lab_Network

devices:
  router1:
    os: iosxe
    type: router
    connections:
      cli:
        protocol: ssh
        ip: 192.168.1.1
        port: 22
    credentials:
      default:
        username: admin
        password: cisco

  router2:
    os: iosxe
    type: router
    connections:
      cli:
        protocol: ssh
        ip: 192.168.1.2
    credentials:
      default:
        username: admin
        password: cisco

  switch1:
    os: nxos
    type: switch
    connections:
      cli:
        protocol: ssh
        ip: 192.168.1.10
    credentials:
      default:
        username: admin
        password: cisco

기본 사용법

1. 장비 연결

from genie.testbed import load

# Testbed 로드
testbed = load('testbed.yaml')

# 장비 연결
device = testbed.devices['router1']
device.connect()

# 명령 실행
output = device.execute('show version')
print(output)

device.disconnect()

2. 파서 사용

from genie.testbed import load

testbed = load('testbed.yaml')
device = testbed.devices['router1']
device.connect()

# 파싱된 데이터
parsed = device.parse('show ip interface brief')

# 구조화된 데이터
for interface, data in parsed['interface'].items():
    print(f"{interface}: {data['ip_address']} - {data['status']}")

Learn 기능 (상태 스냅샷)

변경 전 상태 저장

from genie.testbed import load

testbed = load('testbed.yaml')
device = testbed.devices['router1']
device.connect()

# 모든 정보 학습
device.learn('all', output_dir='./before')

# 특정 feature만 학습
device.learn('interface', output_dir='./before')
device.learn('routing', output_dir='./before')
device.learn('bgp', output_dir='./before')

변경 작업 수행

# 설정 변경
config = """
interface GigabitEthernet0/1
 description Changed by pyATS
 no shutdown
"""

device.configure(config)

변경 후 상태 비교

# 변경 후 상태 학습
device.learn('interface', output_dir='./after')

# Diff 비교
from genie.utils.diff import Diff

diff = Diff('./before/interface_iosxe_router1_ops.txt',
            './after/interface_iosxe_router1_ops.txt')

diff.findDiff()
print(diff)

테스트 자동화

테스트 스크립트

# test_network.py
from pyats import aetest
from genie.testbed import load

class CommonSetup(aetest.CommonSetup):
    @aetest.subsection
    def connect_to_devices(self, testbed):
        """모든 장비 연결"""
        for device in testbed.devices.values():
            device.connect()

class VerifyInterfaces(aetest.Testcase):
    @aetest.setup
    def setup(self, testbed):
        """테스트 준비"""
        self.device = testbed.devices['router1']

    @aetest.test
    def check_interface_status(self):
        """인터페이스 상태 확인"""
        parsed = self.device.parse('show ip interface brief')

        for intf, data in parsed['interface'].items():
            if data['status'] == 'administratively down':
                self.passed(f"{intf} is admin down (expected)")
            elif data['status'] == 'up':
                self.passed(f"{intf} is up")
            else:
                self.failed(f"{intf} is {data['status']}")

    @aetest.test
    def check_bgp_neighbors(self):
        """BGP 이웃 상태 확인"""
        parsed = self.device.parse('show bgp summary')

        for neighbor, data in parsed['bgp']['instance']['default']['vrf']['default']['neighbor'].items():
            if data['address_family']['ipv4 unicast']['state'] == 'Established':
                self.passed(f"BGP neighbor {neighbor} is Established")
            else:
                self.failed(f"BGP neighbor {neighbor} is not Established")

class CommonCleanup(aetest.CommonCleanup):
    @aetest.subsection
    def disconnect_from_devices(self, testbed):
        """모든 장비 연결 해제"""
        for device in testbed.devices.values():
            device.disconnect()

if __name__ == '__main__':
    import sys
    from pyats import topology

    # Testbed 로드
    testbed = topology.loader.load('testbed.yaml')

    # 테스트 실행
    aetest.main(testbed=testbed)

테스트 실행

python test_network.py

# 결과 확인
cat TaskLog.html  # HTML 리포트

네트워크 변경 검증 워크플로우

from genie.testbed import load
from genie.utils.diff import Diff
import logging

def network_change_workflow(testbed_file, config_commands):
    """네트워크 변경 검증 워크플로우"""

    # 1. Testbed 로드
    testbed = load(testbed_file)
    device = testbed.devices['router1']
    device.connect()

    # 2. 변경 전 상태 저장
    print("📸 변경 전 상태 수집 중...")
    device.learn('all', output_dir='./snapshots/before')

    # 3. 설정 변경
    print("⚙️  설정 변경 중...")
    device.configure(config_commands)

    # 4. 변경 후 상태 저장
    print("📸 변경 후 상태 수집 중...")
    device.learn('all', output_dir='./snapshots/after')

    # 5. Diff 비교
    print("🔍 변경사항 비교 중...")
    diff = Diff('./snapshots/before', './snapshots/after')
    diff.findDiff()

    # 6. 결과 출력
    if diff:
        print("
⚠️  변경사항 발견:")
        print(diff)
    else:
        print("
✅ 변경사항 없음")

    # 7. 연결 종료
    device.disconnect()

# 사용 예
config = """
interface GigabitEthernet0/1
 description Updated by pyATS
"""

network_change_workflow('testbed.yaml', config)

CLI Tool (Genie CLI)

# 장비 정보 학습
genie learn interface --testbed-file testbed.yaml --output before

# 설정 변경 후
genie learn interface --testbed-file testbed.yaml --output after

# Diff 비교
genie diff before after

# 특정 명령어 파싱
genie parse "show ip interface brief"   --testbed-file testbed.yaml   --devices router1

# Robot Framework 통합
robot --testbed testbed.yaml test_network.robot

지원 플랫폼

Cisco

  • IOS, IOS-XE
  • NX-OS
  • IOS-XR
  • ASA

기타

  • Juniper Junos
  • Arista EOS
  • Linux

실전 예제

펌웨어 업그레이드 검증

def verify_upgrade(device, expected_version):
    """펌웨어 업그레이드 후 검증"""

    # 버전 확인
    parsed = device.parse('show version')
    actual_version = parsed['version']['version']

    assert actual_version == expected_version,         f"Version mismatch: {actual_version} != {expected_version}"

    # 인터페이스 상태 확인
    interfaces_before = device.learn('interface')
    # 업그레이드 수행
    # ...
    interfaces_after = device.learn('interface')

    # Diff
    diff = Diff(interfaces_before, interfaces_after)
    assert not diff.findDiff(), "Interface state changed unexpectedly"

장점

✓ Cisco 공식 테스트 프레임워크 ✓ 2000+ 파서 내장 ✓ 강력한 Diff 기능 ✓ 자동화된 회귀 테스트 ✓ 상세한 로깅 및 리포팅

단점

✗ Cisco 장비에 최적화 ✗ 학습 곡선 높음 ✗ 복잡한 API ✗ 대용량 스냅샷 관리 어려움

링크

  • 공식 사이트: https://developer.cisco.com/pyats/
  • GitHub: https://github.com/CiscoTestAutomation/pyats
  • 문서: https://pubhub.devnetcloud.com/media/pyats/docs/