From 7119f0d3135c58a85781c5b46bf5973b8ae24edb Mon Sep 17 00:00:00 2001 From: Andrea Dainese Date: Wed, 19 Jul 2023 21:59:52 +0200 Subject: [PATCH 1/3] Add site diagram --- netdoc/models.py | 1 + netdoc/static/netdoc/img/site.png | Bin 0 -> 556 bytes netdoc/topologies.py | 123 ++++++++++++++++++++++++++++++ netdoc/utils.py | 9 +++ 4 files changed, 133 insertions(+) create mode 100644 netdoc/static/netdoc/img/site.png diff --git a/netdoc/models.py b/netdoc/models.py index 9dee185..b5d7efa 100644 --- a/netdoc/models.py +++ b/netdoc/models.py @@ -63,6 +63,7 @@ class DiagramModeChoices(ChoiceSet): CHOICES = [ ("l2", "L2"), ("l3", "L3"), + ("site", "Site connections"), # ("stp", "STP"), ] diff --git a/netdoc/static/netdoc/img/site.png b/netdoc/static/netdoc/img/site.png new file mode 100644 index 0000000000000000000000000000000000000000..281278980819d06e62fb9d1b591d779465be6a86 GIT binary patch literal 556 zcmV+{0@MA8P)`5=FYrp^WA2@_w#$Zmnl8f?7QbWr2WZko?O{`7x%C@Gnra0mBuSLM9S4V zly}xrOQg7dz(f+|J@!bV+|k|4|Mp0;-GSrMqt@ut6IobYS#o|i!D40f>sN#8nqYAs zN{`?o%NvJK$j^GnG~4fB<+A-!ZGzo8y@u_x8|Av#2e6Nc7GQ!6Bx0ZMdWhg34E7vB zgn|{DQ(@7f_{c;kSSG?AadU(cE+%2fp_RJO(lrt+%c8o7szt$yaETkM4XuyQFTlF4 zd&Wjb`m>7(7MmybAYSt72dy%&a^t@aAE;QTNK|X2 zbxp9WwJ0L)uQ9QUMhwf=n_jBR1Pgg@9)>YO>_mcwCwY^qUX>Ukl)DzcM#@Cuys?r` zB6Zefk7z72lw{`|36tf1S}VeOz&R4ddfj(9M|1|{(BjbQS%7ndbHug4(6rY6C4R3p u6*1*{9}wW3Nzhv2UPX1d5M~p2c<~JnbwqeeuN$oZ0000 """ +SITE_TEMPLATE = """ + + + + + + + + + + + +
From: [{{ from_interface.device.site.name }}] {{ from_interface.device.name }}:{{ from_interface.name }}
To: [{{ to_interface.device.site.name }}] {{ to_interface.device.name }}:{{ to_interface.name }}
+""" + NETWORK_TEMPLATE = """ @@ -347,3 +362,111 @@ def get_l3_drawio_topology(queryset, diagram): ) return drawio_o.dump_xml() + + +def get_site_topology_data(queryset, details): + """Create a site vis.js topology data from an Interface queryset.""" + sites = {} + links = {} + + # Filter out unconnected interfaces + queryset = queryset.filter(cable__isnull=False) + + for interface_o in queryset: + # Create link + cable_o = interface_o.cable + from_interface_o = cable_o.terminations.first().interface.first() + from_interface_id = from_interface_o.id + to_interface_o = cable_o.terminations.last().interface.first() + to_interface_id = to_interface_o.id + from_site_o = from_interface_o.device.site + from_site_id = from_site_o.id + to_site_o = to_interface_o.device.site + to_site_id = to_site_o.id + link_id = f"{from_interface_id}-{to_interface_id}" if from_interface_id <= to_interface_id else f"{to_interface_id}-{from_interface_id}" + + if link_id not in links and from_site_id != to_site_id: + # Add link only if intra-site + links[link_id] = { + "id": link_id, + "from": from_site_id, + "from_label": f"{from_interface_o.device.name}:{from_interface_o.label}", + "to": to_site_id, + "to_label": f"{to_interface_o.device.name}:{to_interface_o.label}", + "title": Template( + SITE_TEMPLATE, autoescape=JINJA_AUTOESCAPE + ).render( + from_interface=from_interface_o, to_interface=to_interface_o + ), + } + + # Add source site + if from_site_id not in sites: + sites[from_site_id] = { + "id": from_site_id, + "label": from_site_o.name, + "image": "/static/netdoc/img/site.png", + "shape": "image", + "title": from_site_o.name, + } + # Set position + if "positions" in sites and str(from_site_o.id) in details["positions"]: + sites[from_site_id]["x"] = details["positions"][str(from_site_id)].get("x") + sites[from_site_id]["y"] = details["positions"][str(from_site_id)].get("y") + + # Add destination site + if to_site_id not in sites: + sites[to_site_id] = { + "id": to_site_id, + "label": to_site_o.name, + "image": "/static/netdoc/img/site.png", + "shape": "image", + "title": to_site_o.name, + } + # Set position + if "positions" in sites and str(from_site_o.id) in details["positions"]: + sites[to_site_id]["x"] = details["positions"][str(to_site_id)].get("x") + sites[to_site_id]["y"] = details["positions"][str(to_site_id)].get("y") + + return { + "nodes": list(sites.values()), + "edges": list(links.values()), + } + + +def get_site_drawio_topology(queryset, diagram): + """Create a site DrawIO topology data from an Interface queryset.""" + data = get_site_topology_data(queryset, diagram.details) + nodes = data.get("nodes") + links = data.get("edges") + + # Transform node list into dict using id a key + nodes_dict = {item["id"]: item for item in nodes} + + # Create diagram + drawio_o = drawio_diagram() + drawio_o.add_diagram("Page-1") + + for node in nodes: + # Add node + node_label = node.get("label") + node_style = "site" + drawio_o.add_node( + id=node_label, + url="Page-1", + x_pos=node.get("x") if node.get("x") else None, + y_pos=node.get("y") if node.get("x") else None, + **DRAWIO_ROLE_MAP[node_style], + ) + + for link in links: + # Add link (using node labels) + drawio_o.add_link( + nodes_dict[link["from"]]["label"], + nodes_dict[link["to"]]["label"], + src_label=link["from_label"], + trgt_label=link["to_label"], + link_id=link["id"], + ) + + return drawio_o.dump_xml() diff --git a/netdoc/utils.py b/netdoc/utils.py index e5751b7..2fcf7ac 100644 --- a/netdoc/utils.py +++ b/netdoc/utils.py @@ -144,6 +144,15 @@ "width": 50, "height": 50, }, + "site": { + "style": "points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[1,0.25,0],[1,0.5,0]," + + "[1,0.75,0],[1,1,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,1,0],[0,0.75,0],[0,0.5,0]," + + "[0,0.25,0]];verticalLabelPosition=bottom;sketch=0;html=1;verticalAlign=top;" + + "aspect=fixed;align=center;pointerEvents=1;shape=mxgraph.cisco19.branch;" + + "fillColor=#005073;strokeColor=none;", + "width": 50, + "height": 50, + }, "storage": { "style": "sketch=0;points=[[0.015,0.015,0],[0.985,0.015,0],[0.985,0.985,0],[0.015,0.985,0]," + "[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0]," From e4045aa8f802b466ed6c9b0ab2df0369cdc6ad0b Mon Sep 17 00:00:00 2001 From: Andrea Dainese Date: Wed, 19 Jul 2023 22:10:39 +0200 Subject: [PATCH 2/3] Fix positions --- netdoc/topologies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netdoc/topologies.py b/netdoc/topologies.py index 3d93131..dc65221 100644 --- a/netdoc/topologies.py +++ b/netdoc/topologies.py @@ -424,7 +424,7 @@ def get_site_topology_data(queryset, details): "title": to_site_o.name, } # Set position - if "positions" in sites and str(from_site_o.id) in details["positions"]: + if "positions" in details and str(from_site_o.id) in details["positions"]: sites[to_site_id]["x"] = details["positions"][str(to_site_id)].get("x") sites[to_site_id]["y"] = details["positions"][str(to_site_id)].get("y") From 0c4d6a91c6842bcbcd35fc7c9e246036559e9f04 Mon Sep 17 00:00:00 2001 From: Andrea Dainese Date: Wed, 19 Jul 2023 22:11:22 +0200 Subject: [PATCH 3/3] Linting --- netdoc/topologies.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/netdoc/topologies.py b/netdoc/topologies.py index dc65221..4ba9f5b 100644 --- a/netdoc/topologies.py +++ b/netdoc/topologies.py @@ -383,7 +383,11 @@ def get_site_topology_data(queryset, details): from_site_id = from_site_o.id to_site_o = to_interface_o.device.site to_site_id = to_site_o.id - link_id = f"{from_interface_id}-{to_interface_id}" if from_interface_id <= to_interface_id else f"{to_interface_id}-{from_interface_id}" + link_id = ( + f"{from_interface_id}-{to_interface_id}" + if from_interface_id <= to_interface_id + else f"{to_interface_id}-{from_interface_id}" + ) if link_id not in links and from_site_id != to_site_id: # Add link only if intra-site @@ -393,9 +397,7 @@ def get_site_topology_data(queryset, details): "from_label": f"{from_interface_o.device.name}:{from_interface_o.label}", "to": to_site_id, "to_label": f"{to_interface_o.device.name}:{to_interface_o.label}", - "title": Template( - SITE_TEMPLATE, autoescape=JINJA_AUTOESCAPE - ).render( + "title": Template(SITE_TEMPLATE, autoescape=JINJA_AUTOESCAPE).render( from_interface=from_interface_o, to_interface=to_interface_o ), } @@ -411,8 +413,12 @@ def get_site_topology_data(queryset, details): } # Set position if "positions" in sites and str(from_site_o.id) in details["positions"]: - sites[from_site_id]["x"] = details["positions"][str(from_site_id)].get("x") - sites[from_site_id]["y"] = details["positions"][str(from_site_id)].get("y") + sites[from_site_id]["x"] = details["positions"][str(from_site_id)].get( + "x" + ) + sites[from_site_id]["y"] = details["positions"][str(from_site_id)].get( + "y" + ) # Add destination site if to_site_id not in sites: