From de13989759a497278ac8eaa4969d0c89c868134d Mon Sep 17 00:00:00 2001 From: Dmitrii Gudov Date: Sat, 21 Feb 2026 22:54:19 +0300 Subject: [PATCH] t --- data/output/diagram.png | Bin 0 -> 22145 bytes requirements.txt | 1 + src/netdiag/base.py | 14 +++++--------- src/netdiag/output/__init__.py | 0 src/netdiag/output/file_convert.py | 30 +++++++++++++++++++++++++++++ src/netdiag/output/graphviz.py | 26 +++++++++++++++++++++++++ src/netdiag/parse/convert_raw.py | 4 ++++ 7 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 data/output/diagram.png create mode 100644 requirements.txt create mode 100644 src/netdiag/output/__init__.py create mode 100644 src/netdiag/output/file_convert.py create mode 100644 src/netdiag/output/graphviz.py diff --git a/data/output/diagram.png b/data/output/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..27959a13c58790ce0620e28923aa92a1e3f2bcfa GIT binary patch literal 22145 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 None: + pass diff --git a/src/netdiag/output/graphviz.py b/src/netdiag/output/graphviz.py new file mode 100644 index 0000000..67c159b --- /dev/null +++ b/src/netdiag/output/graphviz.py @@ -0,0 +1,26 @@ +from ..domain.models import Topology +import graphviz + + +def generate_diagram(topology: Topology, output_path: str) -> None: + dot = graphviz.Graph(name="Network Topology", format="png", engine="neato") + dot.attr(overlap="false", splines="true") + + for device in topology.devices.values(): + shape = "box" + dot.node(device.name, label=device.name, shape=shape) + + for network in topology.networks.values(): + interfaces_with_device = [ + iface for iface in network.interfaces if iface.device is not None + ] + + for i, iface_a in enumerate(interfaces_with_device): + for iface_b in interfaces_with_device[i + 1 :]: + label = network.name or "" + dot.edge(iface_a.device.name, iface_b.device.name, label=label) + + output_path = output_path[:-4] if output_path.endswith(".png") else output_path + dot.render(output_path, cleanup=True) + + return diff --git a/src/netdiag/parse/convert_raw.py b/src/netdiag/parse/convert_raw.py index 1c37d48..08b3dbe 100644 --- a/src/netdiag/parse/convert_raw.py +++ b/src/netdiag/parse/convert_raw.py @@ -10,6 +10,7 @@ from ..domain.models import ( ) from . import RawDevices +import logging name_matching = { "DEVICE_TYPE": "Role", @@ -136,12 +137,15 @@ def assign_interfaces_to_networks( def convert_raw_topology(raw_devices: list[RawDevices]) -> Topology: topology = Topology() + logging.info(f"Parsing {len(raw_devices)} raw devices to devices") devices = parse_devices(raw_devices) + logging.info(f"Parsing interfaces from raw devices") add_interfaces(devices, raw_devices) for device in devices: topology.add_device(device) + logging.info(f"Parsing networks from raw devices") networks = parse_networks(devices, raw_devices) assign_interfaces_to_networks(networks, devices)