overgrowth / test_batfish.py
Graham Paasch
feat: Add Batfish static analysis (Todo #3 - Stage 6b)
264a642
Raw
History Blame
8.14 kB
#!/usr/bin/env python3
"""
Test Batfish integration and digital twin simulation
"""
import logging
from agent.batfish_client import BatfishClient, BatfishAnalysis
from agent.pipeline_engine import OvergrowthPipeline, NetworkIntent
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def test_batfish_mock_mode():
"""Test Batfish client in mock mode"""
print("\n=== Test 1: Batfish Mock Mode ===")
client = BatfishClient(use_batfish=True)
# Should be in mock mode without Batfish service
print(f"Mock mode: {client.mock_mode}")
# Test config analysis
configs = {
"router1": """
hostname router1
!
vlan 10
name Management
!
vlan 20
name Users
!
interface Vlan10
ip address 10.0.10.1 255.255.255.0
!
interface GigabitEthernet0/1
switchport access vlan 10
!
router ospf 1
network 10.0.0.0 0.255.255.255 area 0
!
""",
"router2": """
hostname router2
!
vlan 10
name Management
!
interface Vlan10
ip address 10.0.10.2 255.255.255.0
!
interface GigabitEthernet0/1
switchport access vlan 30
!
router ospf 1
network 10.0.0.0 0.255.255.255 area 0
!
"""
}
analysis = client.analyze_configs(configs)
print(f"\nAnalysis Results:")
print(f" All passed: {analysis.all_passed}")
print(f" Undefined references: {len(analysis.undefined_references)}")
print(f" Routing loops: {len(analysis.routing_loops)}")
print(f" Forwarding errors: {len(analysis.forwarding_errors)}")
if analysis.undefined_references:
print("\nUndefined References:")
for ref in analysis.undefined_references:
print(f" - {ref['device']}: {ref['type']} {ref['name']}")
recommendations = client.generate_config_recommendations(analysis)
print(f"\nRecommendations:")
for rec in recommendations:
print(f" - {rec}")
print("\n✓ Mock mode test complete")
def test_acl_validation():
"""Test ACL behavior validation"""
print("\n=== Test 2: ACL Validation ===")
client = BatfishClient(use_batfish=True)
# Test traffic permissibility
permitted = client.validate_acl_behavior(
src="10.0.10.0/24",
dst="10.0.20.0/24",
protocol="TCP",
dst_port=443
)
print(f"Traffic 10.0.10.0/24 -> 10.0.20.0/24:443/TCP: {'PERMIT' if permitted else 'DENY'}")
print("✓ ACL validation test complete")
def test_failover_scenario():
"""Test network failover simulation"""
print("\n=== Test 3: Failover Scenario ===")
client = BatfishClient(use_batfish=True)
# Test if network survives device failure
survives = client.test_failover_scenario(
failed_device="core-sw-01",
src="10.0.10.10",
dst="10.0.20.20"
)
print(f"Network survives core-sw-01 failure: {survives}")
print("✓ Failover test complete")
def test_pipeline_with_batfish():
"""Test pipeline integration with Batfish"""
print("\n=== Test 4: Pipeline with Batfish ===")
pipeline = OvergrowthPipeline()
# Create network intent
intent = NetworkIntent(
description="Enterprise campus network with redundancy",
business_requirements=["99.99% uptime", "Scalable to 1000 devices"],
constraints=["Cisco only", "OSPF for routing"],
budget="$50000",
timeline="1 month"
)
# Generate source of truth
model = pipeline.stage2_generate_sot(intent)
print(f"\nGenerated Network Model:")
print(f" Name: {model.name}")
print(f" VLANs: {len(model.vlans)}")
print(f" Subnets: {len(model.subnets)}")
print(f" Devices: {len(model.devices)}")
# Run pre-flight with Batfish
preflight = pipeline.stage0_preflight(model)
print(f"\nPre-flight Results:")
print(f" Schema valid: {preflight['schema_valid']}")
print(f" Policy passed: {preflight['policy_passed']}")
print(f" Batfish passed: {preflight['batfish_passed']}")
print(f" Ready to deploy: {preflight['ready_to_deploy']}")
if 'batfish_analysis' in preflight:
bf = preflight['batfish_analysis']
print(f"\nBatfish Analysis:")
print(f" Mock mode: {bf.get('mock_mode', False)}")
print(f" All passed: {bf.get('all_passed', False)}")
if bf.get('recommendations'):
print("\nRecommendations:")
for rec in bf['recommendations'][:3]:
print(f" - {rec}")
print("\n✓ Pipeline integration test complete")
def test_config_generation():
"""Test config generation for Batfish"""
print("\n=== Test 5: Config Generation ===")
from agent.pipeline_engine import NetworkModel, Device, NetworkIntent
# Create a simple network model
intent = NetworkIntent(
description="Test network",
business_requirements=[],
constraints=[]
)
model = NetworkModel(
name="test-network",
version="1.0.0",
intent=intent,
devices=[
Device(
name="sw-01",
role="access",
model="Catalyst 2960",
vendor="cisco",
mgmt_ip="10.0.10.10",
location="IDF1",
interfaces=[]
)
],
vlans=[
{"id": 10, "name": "Management"},
{"id": 20, "name": "Users"}
],
subnets=[
{"network": "10.0.10.0/24", "gateway": "10.0.10.1", "vlan": 10}
],
routing={"protocol": "ospf", "process_id": 1, "networks": ["10.0.0.0/8"]},
services=["DHCP", "DNS"]
)
pipeline = OvergrowthPipeline()
configs = pipeline._generate_configs_for_batfish(model)
print(f"\nGenerated {len(configs)} device configs:")
for device, config in configs.items():
print(f"\n{device}:")
print("-" * 40)
print(config[:200] + "..." if len(config) > 200 else config)
print("\n✓ Config generation test complete")
def test_real_batfish_if_available():
"""Test with real Batfish service if running"""
print("\n=== Test 6: Real Batfish (Optional) ===")
import os
# Check if Batfish service is available
batfish_host = os.getenv("BATFISH_HOST", "localhost")
try:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(2)
result = sock.connect_ex((batfish_host, 9996))
sock.close()
if result != 0:
print("⊘ Batfish service not running - skipping")
print(" Start Batfish with: docker run -d -p 9996:9996 -p 9997:9997 batfish/batfish")
return
# Batfish is running - test real analysis
client = BatfishClient(host=batfish_host, use_batfish=True)
configs = {
"test-router": """
hostname test-router
interface GigabitEthernet0/0
ip address 10.0.0.1 255.255.255.0
no shutdown
!
router ospf 1
network 10.0.0.0 0.0.0.255 area 0
!
"""
}
analysis = client.analyze_configs(configs, network_name="real-test")
print(f"✓ Real Batfish analysis complete:")
print(f" Mock mode: {client.mock_mode}")
print(f" All passed: {analysis.all_passed}")
except ImportError:
print("⊘ pybatfish not installed - install with: pip install pybatfish")
except Exception as e:
print(f"⊘ Batfish test skipped: {e}")
if __name__ == "__main__":
print("\n" + "="*60)
print("Batfish Integration & Digital Twin Test Suite")
print("="*60)
try:
test_batfish_mock_mode()
test_acl_validation()
test_failover_scenario()
test_config_generation()
test_pipeline_with_batfish()
test_real_batfish_if_available()
print("\n" + "="*60)
print("✓ All Batfish tests completed!")
print("="*60 + "\n")
except Exception as e:
print(f"\n✗ Test failed: {e}")
import traceback
traceback.print_exc()
exit(1)