| 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 = {} |
|
|
| |
| for device in topology.devices: |
| for intf in device.interfaces: |
| if intf.ip: |
| |
| 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) |
| |
| |
| 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})") |
|
|
| |
| 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.") |
|
|
| |
| 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: |
| |
| try: |
| |
| |
| parts = net_statement.split() |
| if "network" in parts: |
| net_addr = parts[parts.index("network") + 1] |
| |
| |
| 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 |
|
|