feat: declare NetBox 4.7.x support and add wireless/power toggle to Kabeltopologie
Adds min_version/max_version to the plugin config for NetBox 4.7.x and extends the rack view's cable topology with optional wireless and power cable display, rendered with distinct colors and dash patterns from regular network cables. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ from typing import DefaultDict, Dict, Optional, Union
|
||||
from itertools import chain
|
||||
|
||||
from circuits.models import Circuit, CircuitTermination, ProviderNetwork
|
||||
from dcim.choices import CableTypeChoices
|
||||
from dcim.models import (
|
||||
Cable,
|
||||
CableTermination,
|
||||
@@ -1008,6 +1009,14 @@ class RackElevationView(PermissionRequiredMixin, View):
|
||||
} for device in rack.elevation_devices],
|
||||
})
|
||||
|
||||
def _flag_enabled(name):
|
||||
if not request.GET:
|
||||
return True
|
||||
return request.GET.get(name) in ("on", "True", "true", "1")
|
||||
|
||||
show_wireless = _flag_enabled("show_wireless")
|
||||
show_power = _flag_enabled("show_power")
|
||||
|
||||
cable_rows = []
|
||||
topology_nodes = {}
|
||||
topology_edges = []
|
||||
@@ -1017,6 +1026,9 @@ class RackElevationView(PermissionRequiredMixin, View):
|
||||
).values_list("cable_id", flat=True).distinct()
|
||||
cables = Cable.objects.restrict(request.user, "view").filter(pk__in=cable_ids)
|
||||
for cable in cables.order_by("label", "pk"):
|
||||
is_power_cable = cable.type == CableTypeChoices.TYPE_POWER
|
||||
if is_power_cable and not show_power:
|
||||
continue
|
||||
a_terms, b_terms = list(cable.a_terminations), list(cable.b_terminations)
|
||||
for index in range(max(len(a_terms), len(b_terms), 1)):
|
||||
a = _rack_endpoint(a_terms[index]) if index < len(a_terms) else None
|
||||
@@ -1027,65 +1039,67 @@ class RackElevationView(PermissionRequiredMixin, View):
|
||||
if endpoint:
|
||||
topology_nodes[endpoint["node_id"]] = endpoint
|
||||
if a and b:
|
||||
default_color = "#f59e0b" if is_power_cable else "#1685fc"
|
||||
topology_edges.append({
|
||||
"from": a["node_id"], "to": b["node_id"],
|
||||
"from_device": a["device_name"], "to_device": b["device_name"],
|
||||
"from_port": a["name"], "to_port": b["name"],
|
||||
"label": cable.label or str(cable),
|
||||
"color": f"#{cable.color}" if cable.color else "#1685fc",
|
||||
"color": f"#{cable.color}" if cable.color else default_color,
|
||||
"url": cable.get_absolute_url(),
|
||||
"kind": "cable",
|
||||
"kind": "power" if is_power_cable else "cable",
|
||||
})
|
||||
|
||||
wireless_links = WirelessLink.objects.restrict(request.user, "view").filter(
|
||||
Q(interface_a__device_id__in=device_ids) |
|
||||
Q(interface_b__device_id__in=device_ids)
|
||||
).select_related(
|
||||
"interface_a__device__rack", "interface_a__device__role", "interface_a__device__device_type",
|
||||
"interface_b__device__rack", "interface_b__device__role", "interface_b__device__device_type",
|
||||
)
|
||||
for wireless_link in wireless_links:
|
||||
a = _rack_endpoint(wireless_link.interface_a)
|
||||
b = _rack_endpoint(wireless_link.interface_b)
|
||||
topology_nodes[a["node_id"]] = a
|
||||
topology_nodes[b["node_id"]] = b
|
||||
topology_edges.append({
|
||||
"from": a["node_id"], "to": b["node_id"],
|
||||
"from_device": a["device_name"], "to_device": b["device_name"],
|
||||
"from_port": a["name"], "to_port": b["name"],
|
||||
"label": wireless_link.ssid or str(wireless_link),
|
||||
"color": "#8b5cf6", "url": wireless_link.get_absolute_url(),
|
||||
"kind": "wireless",
|
||||
})
|
||||
|
||||
wireless_lans = WirelessLAN.objects.restrict(request.user, "view").filter(
|
||||
interfaces__device_id__in=device_ids
|
||||
).distinct()
|
||||
for wireless_lan in wireless_lans:
|
||||
wlan_node_id = f"wlan-{wireless_lan.pk}"
|
||||
topology_nodes[wlan_node_id] = {
|
||||
"node_id": wlan_node_id, "device_id": None,
|
||||
"device_name": f"WLAN · {wireless_lan.ssid}",
|
||||
"name": wireless_lan.ssid, "subtitle": "Multi-Access WLAN",
|
||||
"rack_id": None, "rack_name": "Funknetze",
|
||||
"url": wireless_lan.get_absolute_url(), "color": "8b5cf6",
|
||||
"position": None, "height": 1, "face": "front", "rack_u_height": None,
|
||||
}
|
||||
wlan_interfaces = Interface.objects.restrict(request.user, "view").filter(
|
||||
wireless_lans=wireless_lan
|
||||
).select_related("device__rack", "device__role", "device__device_type")
|
||||
for interface in wlan_interfaces:
|
||||
endpoint = _rack_endpoint(interface)
|
||||
topology_nodes[endpoint["node_id"]] = endpoint
|
||||
if show_wireless:
|
||||
wireless_links = WirelessLink.objects.restrict(request.user, "view").filter(
|
||||
Q(interface_a__device_id__in=device_ids) |
|
||||
Q(interface_b__device_id__in=device_ids)
|
||||
).select_related(
|
||||
"interface_a__device__rack", "interface_a__device__role", "interface_a__device__device_type",
|
||||
"interface_b__device__rack", "interface_b__device__role", "interface_b__device__device_type",
|
||||
)
|
||||
for wireless_link in wireless_links:
|
||||
a = _rack_endpoint(wireless_link.interface_a)
|
||||
b = _rack_endpoint(wireless_link.interface_b)
|
||||
topology_nodes[a["node_id"]] = a
|
||||
topology_nodes[b["node_id"]] = b
|
||||
topology_edges.append({
|
||||
"from": endpoint["node_id"], "to": wlan_node_id,
|
||||
"from_device": endpoint["device_name"],
|
||||
"to_device": f"WLAN · {wireless_lan.ssid}",
|
||||
"from_port": endpoint["name"], "to_port": wireless_lan.ssid,
|
||||
"label": wireless_lan.ssid, "color": "#8b5cf6",
|
||||
"url": wireless_lan.get_absolute_url(), "kind": "wireless-lan",
|
||||
"from": a["node_id"], "to": b["node_id"],
|
||||
"from_device": a["device_name"], "to_device": b["device_name"],
|
||||
"from_port": a["name"], "to_port": b["name"],
|
||||
"label": wireless_link.ssid or str(wireless_link),
|
||||
"color": "#8b5cf6", "url": wireless_link.get_absolute_url(),
|
||||
"kind": "wireless",
|
||||
})
|
||||
|
||||
wireless_lans = WirelessLAN.objects.restrict(request.user, "view").filter(
|
||||
interfaces__device_id__in=device_ids
|
||||
).distinct()
|
||||
for wireless_lan in wireless_lans:
|
||||
wlan_node_id = f"wlan-{wireless_lan.pk}"
|
||||
topology_nodes[wlan_node_id] = {
|
||||
"node_id": wlan_node_id, "device_id": None,
|
||||
"device_name": f"WLAN · {wireless_lan.ssid}",
|
||||
"name": wireless_lan.ssid, "subtitle": "Multi-Access WLAN",
|
||||
"rack_id": None, "rack_name": "Funknetze",
|
||||
"url": wireless_lan.get_absolute_url(), "color": "8b5cf6",
|
||||
"position": None, "height": 1, "face": "front", "rack_u_height": None,
|
||||
}
|
||||
wlan_interfaces = Interface.objects.restrict(request.user, "view").filter(
|
||||
wireless_lans=wireless_lan
|
||||
).select_related("device__rack", "device__role", "device__device_type")
|
||||
for interface in wlan_interfaces:
|
||||
endpoint = _rack_endpoint(interface)
|
||||
topology_nodes[endpoint["node_id"]] = endpoint
|
||||
topology_edges.append({
|
||||
"from": endpoint["node_id"], "to": wlan_node_id,
|
||||
"from_device": endpoint["device_name"],
|
||||
"to_device": f"WLAN · {wireless_lan.ssid}",
|
||||
"from_port": endpoint["name"], "to_port": wireless_lan.ssid,
|
||||
"label": wireless_lan.ssid, "color": "#8b5cf6",
|
||||
"url": wireless_lan.get_absolute_url(), "kind": "wireless-lan",
|
||||
})
|
||||
|
||||
return render(request, "netbox_topology_views/rack_elevation.html", {
|
||||
"filter_form": RackElevationFilterForm(request.GET or None),
|
||||
"racks": rack_list, "cables": cable_rows,
|
||||
|
||||
Reference in New Issue
Block a user