diff --git a/README.md b/README.md
index f37c33c..ac5c326 100644
--- a/README.md
+++ b/README.md
@@ -99,15 +99,13 @@ Go to the plugins tab in the navbar and click topology or go to `$NETBOX_URL/plu
Select your options for the topology view:
-
+
- Save Coordinates
- Save the coordinates of devices in the topology view.
- Show Unconnected
- Show devices that have no connections or for which no connection is displayed. This option depends on other parameters like 'Show Cables' and 'Show Logical Connections'.
- - Show Circuit Terminations
- - Show connections which end at a circuit termination in the topology view. These connections are displayed as blue dashed lines.
- Show Cables
- Show connections between interfaces, front / rear ports, etc., that are connected with one or more cables. These connections are displayed as solid lines in the color of the cable.
- Show Logical Connections
@@ -116,6 +114,10 @@ Select your options for the topology view:
intermediate front / rear port connections, etc. This is similar to what was referred to as 'end-to-end' connections in previous versions. These connections are displayed as yellow dotted lines.
- Show redundant Cable and Locigal Connection
- Shows a logical connection (in addition to a cable), even if a cable is directly connected. Leaving this option disabled prevents that redundant display. This option only has an effect if 'Show Logical Connections' is activated.
+ - Show Neighbors
+ - Adds neighbors to the filter result set automatically. Link peers will be added if 'Show Cables' is ticked, far-end terminations will be added if 'Show Logical Connections' is ticked.
+ - Show Circuit Terminations
+ - Show connections which end at a circuit termination in the topology view. These connections are displayed as blue dashed lines.
- Show Power Feeds
- Displays connections between power outlets and power ports. These connections are displayed as solid lines in the color of the cable. This option depends on 'Show Cables'.
- Show Wireless Links
diff --git a/netbox_topology_views/api/serializers.py b/netbox_topology_views/api/serializers.py
index b8608aa..82963eb 100644
--- a/netbox_topology_views/api/serializers.py
+++ b/netbox_topology_views/api/serializers.py
@@ -25,4 +25,5 @@ class DeviceRoleSerializer(ModelSerializer):
class IndividualOptionsSerializer(NetBoxModelSerializer):
class Meta:
model = IndividualOptions
- fields = ("ignore_cable_type", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_circuit", "show_power", "show_wireless", "draw_default_layout")
\ No newline at end of file
+ fields = ("ignore_cable_type", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_neighbors", "show_circuit", "show_power", "show_wireless", "draw_default_layout")
+
\ No newline at end of file
diff --git a/netbox_topology_views/api/views.py b/netbox_topology_views/api/views.py
index 736f86c..026b8be 100644
--- a/netbox_topology_views/api/views.py
+++ b/netbox_topology_views/api/views.py
@@ -80,23 +80,29 @@ class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
user_id=request.user.id,
)
- save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless = get_query_settings(request)
- topo_data = get_topology_data(
- self.queryset,
- individualOptions,
- show_unconnected,
- save_coords,
- show_cables,
- show_circuit,
- show_logical_connections,
- show_single_cable_logical_conns,
- show_power,
- show_wireless,
- )
- xml_data = export_data_to_xml(topo_data).decode('utf-8')
+ if request.GET:
- return HttpResponse(xml_data, content_type="application/xml; charset=utf-8")
+ save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors = get_query_settings(request)
+ topo_data = get_topology_data(
+ queryset=self.queryset,
+ individualOptions=individualOptions,
+ save_coords=save_coords,
+ show_unconnected=show_unconnected,
+ show_cables=show_cables,
+ show_logical_connections=show_logical_connections,
+ show_single_cable_logical_conns=show_single_cable_logical_conns,
+ show_neighbors=show_neighbors,
+ show_circuit=show_circuit,
+ show_power=show_power,
+ show_wireless=show_wireless,
+ )
+ xml_data = export_data_to_xml(topo_data).decode('utf-8')
+ return HttpResponse(xml_data, content_type="application/xml; charset=utf-8")
+ else:
+ return JsonResponse(
+ {"status": "Missing or malformed request parameters"}, status=400
+ )
class SaveRoleImageViewSet(PermissionRequiredMixin, ViewSet):
queryset = DeviceRole.objects.none()
diff --git a/netbox_topology_views/forms.py b/netbox_topology_views/forms.py
index d6dfc99..af1ab06 100644
--- a/netbox_topology_views/forms.py
+++ b/netbox_topology_views/forms.py
@@ -33,9 +33,10 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
"save_coords",
"show_unconnected",
"show_cables",
- "show_circuit",
"show_logical_connections",
"show_single_cable_logical_conns",
+ "show_neighbors",
+ "show_circuit",
"show_power",
"show_wireless",
),
@@ -118,17 +119,17 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
show_unconnected = forms.BooleanField(
label=_("Show Unconnected"), required=False, initial=False
)
+ show_cables = forms.BooleanField(
+ label =_("Show Cables"), required=False, initial=False
+ )
show_logical_connections = forms.BooleanField(
label =_("Show Logical Connections"), required=False, initial=False
)
show_single_cable_logical_conns = forms.BooleanField(
label =_("Show redundant Cable and Locigal Connection"), required=False, initial=False
)
- show_cables = forms.BooleanField(
- label =_("Show Cables"), required=False, initial=False
- )
- show_wireless = forms.BooleanField(
- label =_("Show Wireless Links"), required=False, initial=False
+ show_neighbors = forms.BooleanField(
+ label =_("Show Neighbors"), required=False, initial=False
)
show_circuit = forms.BooleanField(
label=_("Show Circuit Terminations"), required=False, initial=False
@@ -136,6 +137,9 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
show_power = forms.BooleanField(
label=_("Show Power Feeds"), required=False, initial=False
)
+ show_wireless = forms.BooleanField(
+ label =_("Show Wireless Links"), required=False, initial=False
+ )
class IndividualOptionsForm(NetBoxModelForm):
fieldsets = (
@@ -148,9 +152,10 @@ class IndividualOptionsForm(NetBoxModelForm):
"preselected_tags",
"show_unconnected",
"show_cables",
- "show_circuit",
"show_logical_connections",
"show_single_cable_logical_conns",
+ "show_neighbors",
+ "show_circuit",
"show_power",
"show_wireless",
"draw_default_layout",
@@ -215,6 +220,14 @@ class IndividualOptionsForm(NetBoxModelForm):
"disabled prevents that redundant display. This option only "
"has an effect if 'Show Logical Connections' is activated.")
)
+ show_neighbors = forms.BooleanField(
+ label =_("Show Neighbors"),
+ required=False,
+ initial=False,
+ help_text=_("Adds neighbors to the filter result set automatically. "
+ "Link peers will be added if 'Show Cables' is ticked, far-end "
+ "terminations will be added if 'Show Logical Connections' is ticked.")
+ )
show_circuit = forms.BooleanField(
label=_("Show Circuit Terminations"),
required=False,
@@ -248,5 +261,6 @@ class IndividualOptionsForm(NetBoxModelForm):
class Meta:
model = IndividualOptions
fields = [
- 'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags', 'show_unconnected', 'show_cables', 'show_logical_connections', 'show_single_cable_logical_conns', 'show_circuit', 'show_power', 'show_wireless', 'draw_default_layout'
- ]
\ No newline at end of file
+ 'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags', 'show_unconnected', 'show_cables', 'show_logical_connections', 'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power', 'show_wireless', 'draw_default_layout'
+ ]
+
\ No newline at end of file
diff --git a/netbox_topology_views/migrations/0003_individualoptions_show_neighbors.py b/netbox_topology_views/migrations/0003_individualoptions_show_neighbors.py
new file mode 100644
index 0000000..080dd9b
--- /dev/null
+++ b/netbox_topology_views/migrations/0003_individualoptions_show_neighbors.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.1.8 on 2023-04-29 09:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('netbox_topology_views', '0002_individualoptions'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='individualoptions',
+ name='show_neighbors',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/netbox_topology_views/models.py b/netbox_topology_views/models.py
index e95d020..941ef34 100644
--- a/netbox_topology_views/models.py
+++ b/netbox_topology_views/models.py
@@ -137,6 +137,9 @@ class IndividualOptions(NetBoxModel):
show_single_cable_logical_conns = models.BooleanField(
default=False
)
+ show_neighbors = models.BooleanField(
+ default=False
+ )
show_circuit = models.BooleanField(
default=False
)
diff --git a/netbox_topology_views/utils.py b/netbox_topology_views/utils.py
index d802035..b427cea 100644
--- a/netbox_topology_views/utils.py
+++ b/netbox_topology_views/utils.py
@@ -154,8 +154,13 @@ def get_query_settings(request):
if "show_wireless" in request.GET:
if request.GET["show_wireless"] == "on" :
show_wireless = True
+
+ show_neighbors = False
+ if "show_neighbors" in request.GET:
+ if request.GET["show_neighbors"] == "on" :
+ show_neighbors = True
- return save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless
+ return save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors
class LinePattern():
wireless = [2, 10, 2, 10]
diff --git a/netbox_topology_views/views.py b/netbox_topology_views/views.py
index 65a21c6..890999e 100644
--- a/netbox_topology_views/views.py
+++ b/netbox_topology_views/views.py
@@ -2,6 +2,7 @@ import json
from functools import reduce
from typing import DefaultDict, Dict, Optional, Union
import time
+from itertools import chain
from utilities.htmx import is_htmx
from circuits.models import Circuit, CircuitTermination
@@ -263,6 +264,7 @@ def get_topology_data(
show_circuit: bool,
show_logical_connections: bool,
show_single_cable_logical_conns: bool,
+ show_neighbors: bool,
show_power: bool,
show_wireless: bool,
):
@@ -290,6 +292,31 @@ def get_topology_data(
device_ids = [d.pk for d in queryset]
site_ids = [d.site_id for d in queryset]
+ if show_neighbors:
+ interfaces = Interface.objects.filter(
+ Q(device_id__in=device_ids)
+ )
+ frontports = FrontPort.objects.filter(
+ Q(device_id__in=device_ids)
+ )
+ rearports = RearPort.objects.filter(
+ Q(device_id__in=device_ids)
+ )
+
+ ports = chain(interfaces, frontports, rearports)
+ for port in ports:
+ for link_peer in port.link_peers:
+ if link_peer.device.id not in device_ids:
+ device_ids.append(link_peer.device.id)
+
+ if show_logical_connections:
+ path_complete_interfaces = Interface.objects.filter(
+ Q(_path__is_complete=True) & Q(device_id__in=device_ids)
+ )
+ for path_complete_interface in path_complete_interfaces:
+ for connected_endpoint in path_complete_interface.connected_endpoints:
+ device_ids.append(connected_endpoint.device.id)
+
if show_circuit:
circuit_terminations = CircuitTermination.objects.filter(
Q(site_id__in=site_ids) | Q(provider_network__isnull=False)
@@ -600,20 +627,21 @@ class TopologyHomeView(PermissionRequiredMixin, View):
if request.GET:
- save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless = get_query_settings(request)
+ save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors = get_query_settings(request)
if not "draw_init" in request.GET or "draw_init" in request.GET and request.GET["draw_init"].lower() == "true":
topo_data = get_topology_data(
- self.queryset,
- individualOptions,
- show_unconnected,
- save_coords,
- show_cables,
- show_circuit,
- show_logical_connections,
- show_single_cable_logical_conns,
- show_power,
- show_wireless,
+ queryset=self.queryset,
+ individualOptions=individualOptions,
+ save_coords=save_coords,
+ show_unconnected=show_unconnected,
+ show_cables=show_cables,
+ show_logical_connections=show_logical_connections,
+ show_single_cable_logical_conns=show_single_cable_logical_conns,
+ show_neighbors=show_neighbors,
+ show_circuit=show_circuit,
+ show_power=show_power,
+ show_wireless=show_wireless,
)
else:
@@ -629,6 +657,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
if individualOptions.show_cables: q['show_cables'] = "on"
if individualOptions.show_logical_connections: q['show_logical_connections'] = "on"
if individualOptions.show_single_cable_logical_conns: q['show_single_cable_logical_conns'] = "on"
+ if individualOptions.show_neighbors: q['show_neighbors'] = "on"
if individualOptions.show_circuit: q['show_circuit'] = "on"
if individualOptions.show_power: q['show_power'] = "on"
if individualOptions.show_wireless: q['show_wireless'] = "on"
@@ -751,6 +780,7 @@ class TopologyIndividualOptionsView(PermissionRequiredMixin, View):
'show_cables': queryset.show_cables,
'show_logical_connections': queryset.show_logical_connections,
'show_single_cable_logical_conns': queryset.show_single_cable_logical_conns,
+ 'show_neighbors': queryset.show_neighbors,
'show_circuit': queryset.show_circuit,
'show_power': queryset.show_power,
'show_wireless': queryset.show_wireless,