topo / Decipher /validator.py
sae8d's picture
Upload 16 files
55e2289 verified
import ipaddress
from typing import List, Dict, Set
from Decipher.models import NetworkTopology
def validate_topology(topology: NetworkTopology) -> List[str]:
"""
Performs logical validation on the network topology and returns a list of warnings/errors.
"""
issues = []
device_names = {d.name for d in topology.devices}
ip_to_interface = {} # ip -> (device, interface)
# 1. Check for IP Conflicts and basic interface sanity
for device in topology.devices:
for intf in device.interfaces:
if intf.ip:
# Check for duplicate IPs
if intf.ip in ip_to_interface:
other_dev, other_intf = ip_to_interface[intf.ip]
issues.append(f"IP Conflict: {intf.ip} is assigned to both {device.name} ({intf.name}) and {other_dev} ({other_intf})")
else:
ip_to_interface[intf.ip] = (device.name, intf.name)
# Basic IP format validation
try:
if "/" in intf.ip:
ipaddress.ip_interface(intf.ip)
else:
ipaddress.ip_address(intf.ip)
except ValueError:
issues.append(f"Invalid IP Format: '{intf.ip}' on {device.name} ({intf.name})")
# 2. Ensure Link Endpoints Exist
for link in topology.physical_links:
if link.from_device not in device_names:
issues.append(f"Broken Link: Source device '{link.from_device}' does not exist.")
if link.to_device not in device_names:
issues.append(f"Broken Link: Destination device '{link.to_device}' does not exist.")
# 3. Verify Routing Protocols (Basic Sanity)
for device in topology.devices:
if device.routing and device.routing.ospf:
device_ips = [intf.ip for intf in device.interfaces if intf.ip]
for ospf in device.routing.ospf:
for net_statement in ospf.networks:
# Check if the OSPF network statement matches at least one interface IP segment
try:
# network statement is usually "network 10.0.0.0 0.0.0.255 area 0"
# we extract the network and wildcard mask
parts = net_statement.split()
if "network" in parts:
net_addr = parts[parts.index("network") + 1]
# Simple check: does any device IP start with the same prefix?
# This is a heuristic check
found_match = False
for ip in device_ips:
if ip.split('.')[0:2] == net_addr.split('.')[0:2]:
found_match = True
break
if not found_match:
issues.append(f"OSPF Warning: Network statement '{net_statement}' on {device.name} doesn't seem to match any local interface IPs.")
except Exception:
pass
return issues