Compare commits
12
Commits
v4.0.0-beta.2
...
v4.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8c6e9e193 | ||
|
|
19adcab4a9 | ||
|
|
d2d2106246 | ||
|
|
9072faf7cf | ||
|
|
6f43b959bd | ||
|
|
1048feff52 | ||
|
|
7ff0a9650f | ||
|
|
8d5de5c49f | ||
|
|
96afdf8d83 | ||
|
|
aba0598947 | ||
|
|
142de460b9 | ||
|
|
2600209724 |
@@ -50,6 +50,7 @@ python3 manage.py collectstatic --no-input
|
|||||||
|
|
||||||
| netbox version | netbox-topology-views version |
|
| netbox version | netbox-topology-views version |
|
||||||
| -------------- | ----------------------------- |
|
| -------------- | ----------------------------- |
|
||||||
|
| >= 4.0.0 | >= v4.0.0 |
|
||||||
| >= 3.7.0 | >= v3.9.0 |
|
| >= 3.7.0 | >= v3.9.0 |
|
||||||
| >= 3.6.4 | >= v3.8.1 |
|
| >= 3.6.4 | >= v3.8.1 |
|
||||||
| >= 3.6.0 | >= v3.7.X |
|
| >= 3.6.0 | >= v3.7.X |
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class TopologyViewsConfig(PluginConfig):
|
|||||||
name = "netbox_topology_views"
|
name = "netbox_topology_views"
|
||||||
verbose_name = "Topology views"
|
verbose_name = "Topology views"
|
||||||
description = "An plugin to render topology maps"
|
description = "An plugin to render topology maps"
|
||||||
version = "4.0.0-beta.2"
|
version = "4.0.1"
|
||||||
author = "Mattijs Vanhaverbeke"
|
author = "Mattijs Vanhaverbeke"
|
||||||
author_email = "author@example.com"
|
author_email = "author@example.com"
|
||||||
base_url = "netbox_topology_views"
|
base_url = "netbox_topology_views"
|
||||||
|
|||||||
@@ -50,4 +50,4 @@ class PowerFeedCoordinateSerializer(NetBoxModelSerializer):
|
|||||||
class IndividualOptionsSerializer(NetBoxModelSerializer):
|
class IndividualOptionsSerializer(NetBoxModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IndividualOptions
|
model = IndividualOptions
|
||||||
fields = ("ignore_cable_type", "save_coords", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_neighbors", "show_circuit", "show_power", "show_wireless", "group_sites", "group_locations", "group_racks", "draw_default_layout")
|
fields = ("ignore_cable_type", "save_coords", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_neighbors", "show_circuit", "show_power", "show_wireless", "group_sites", "group_locations", "group_racks", "group_virtualchassis", "draw_default_layout", "straight_cables", "grid_size")
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from netbox.api.viewsets import BaseViewSet
|
||||||
|
|
||||||
from circuits.models import Circuit
|
from circuits.models import Circuit
|
||||||
from dcim.models import Device, DeviceRole, PowerFeed, PowerPanel
|
from dcim.models import Device, DeviceRole, PowerFeed, PowerPanel
|
||||||
from extras.models import SavedFilter
|
from extras.models import SavedFilter
|
||||||
@@ -22,7 +24,7 @@ from netbox_topology_views.views import get_topology_data
|
|||||||
from netbox_topology_views.utils import get_image_from_url, export_data_to_xml, get_query_settings
|
from netbox_topology_views.utils import get_image_from_url, export_data_to_xml, get_query_settings
|
||||||
from netbox_topology_views.filters import DeviceFilterSet
|
from netbox_topology_views.filters import DeviceFilterSet
|
||||||
|
|
||||||
class SaveCoordsViewSet(PermissionRequiredMixin, ReadOnlyModelViewSet):
|
class SaveCoordsViewSet(BaseViewSet, ReadOnlyModelViewSet):
|
||||||
permission_required = 'netbox_topology_views.change_coordinate'
|
permission_required = 'netbox_topology_views.change_coordinate'
|
||||||
|
|
||||||
queryset = Device.objects.none()
|
queryset = Device.objects.none()
|
||||||
@@ -89,16 +91,14 @@ class SaveCoordsViewSet(PermissionRequiredMixin, ReadOnlyModelViewSet):
|
|||||||
|
|
||||||
return Response({"status": "saved coords"})
|
return Response({"status": "saved coords"})
|
||||||
|
|
||||||
class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
|
class ExportTopoToXML(BaseViewSet, ViewSet):
|
||||||
permission_required = ("dcim.view_site", "dcim.view_device")
|
|
||||||
|
|
||||||
queryset = Device.objects.none()
|
queryset = Device.objects.none()
|
||||||
serializer_class = TopologyDummySerializer
|
serializer_class = TopologyDummySerializer
|
||||||
|
|
||||||
def list(self, request):
|
def list(self, request):
|
||||||
|
|
||||||
self.filterset = DeviceFilterSet
|
self.filterset = DeviceFilterSet
|
||||||
self.queryset = Device.objects.all().select_related(
|
self.queryset = Device.objects.restrict(request.user, 'view').select_related(
|
||||||
"device_type", "role"
|
"device_type", "role"
|
||||||
)
|
)
|
||||||
self.queryset = self.filterset(request.GET, self.queryset).qs
|
self.queryset = self.filterset(request.GET, self.queryset).qs
|
||||||
@@ -109,7 +109,7 @@ class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
|
|||||||
|
|
||||||
if request.GET:
|
if request.GET:
|
||||||
|
|
||||||
filter_id, save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, group_sites, group_locations, group_racks,show_neighbors = get_query_settings(request)
|
filter_id, ignore_cable_type, save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, group_sites, group_locations, group_racks, group_virtualchassis, group, show_neighbors, straight_cables, grid_size = get_query_settings(request)
|
||||||
|
|
||||||
# Read options from saved filters as NetBox does not handle custom plugin filters
|
# Read options from saved filters as NetBox does not handle custom plugin filters
|
||||||
if "filter_id" in request.GET and request.GET["filter_id"] != '':
|
if "filter_id" in request.GET and request.GET["filter_id"] != '':
|
||||||
@@ -117,6 +117,7 @@ class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
|
|||||||
saved_filter = SavedFilter.objects.get(pk=filter_id)
|
saved_filter = SavedFilter.objects.get(pk=filter_id)
|
||||||
saved_filter_params = getattr(saved_filter, 'parameters')
|
saved_filter_params = getattr(saved_filter, 'parameters')
|
||||||
|
|
||||||
|
if ignore_cable_type == () and 'ignore_cable_type' in saved_filter_params: ignore_cable_type = saved_filter_params['ignore_cable_type']
|
||||||
if save_coords == False and 'save_coords' in saved_filter_params: save_coords = saved_filter_params['save_coords']
|
if save_coords == False and 'save_coords' in saved_filter_params: save_coords = saved_filter_params['save_coords']
|
||||||
if show_power == False and 'show_power' in saved_filter_params: show_power = saved_filter_params['show_power']
|
if show_power == False and 'show_power' in saved_filter_params: show_power = saved_filter_params['show_power']
|
||||||
if show_circuit == False and 'show_circuit' in saved_filter_params: show_circuit = saved_filter_params['show_circuit']
|
if show_circuit == False and 'show_circuit' in saved_filter_params: show_circuit = saved_filter_params['show_circuit']
|
||||||
@@ -127,19 +128,26 @@ class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
|
|||||||
if group_sites == False and 'group_sites' in saved_filter_params: group_sites = saved_filter_params['group_sites']
|
if group_sites == False and 'group_sites' in saved_filter_params: group_sites = saved_filter_params['group_sites']
|
||||||
if group_locations == False and 'group_locations' in saved_filter_params: group_locations = saved_filter_params['group_locations']
|
if group_locations == False and 'group_locations' in saved_filter_params: group_locations = saved_filter_params['group_locations']
|
||||||
if group_racks == False and 'group_racks' in saved_filter_params: group_racks = saved_filter_params['group_racks']
|
if group_racks == False and 'group_racks' in saved_filter_params: group_racks = saved_filter_params['group_racks']
|
||||||
|
if group_virtualchassis == False and 'group_virtualchassis' in saved_filter_params: group_virtualchassis = saved_filter_params['group_virtualchassis']
|
||||||
if show_neighbors == False and 'show_neighbors' in saved_filter_params: show_neighbors = saved_filter_params['show_neighbors']
|
if show_neighbors == False and 'show_neighbors' in saved_filter_params: show_neighbors = saved_filter_params['show_neighbors']
|
||||||
|
if straight_cables == False and 'straight_cables' in saved_filter_params: show_neighbors = saved_filter_params['straight_cables']
|
||||||
|
if grid_size == 0 and 'grid_size' in saved_filter_params: grid_size = saved_filter_params['grid_size']
|
||||||
except SavedFilter.DoesNotExist: # filter_id not found
|
except SavedFilter.DoesNotExist: # filter_id not found
|
||||||
pass
|
pass
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
print(type(inst))
|
print(type(inst))
|
||||||
|
|
||||||
if 'group' not in request.query_params:
|
if 'group' not in request.query_params:
|
||||||
group_id = "default"
|
if 'saved_filter_params' in locals() and "group" in saved_filter_params:
|
||||||
|
group_id = saved_filter_params['group'][0]
|
||||||
|
else:
|
||||||
|
group_id = "default"
|
||||||
else:
|
else:
|
||||||
group_id = request.query_params["group"]
|
group_id = request.query_params["group"]
|
||||||
topo_data = get_topology_data(
|
topo_data = get_topology_data(
|
||||||
queryset=self.queryset,
|
queryset=self.queryset,
|
||||||
individualOptions=individualOptions,
|
individualOptions=individualOptions,
|
||||||
|
ignore_cable_type=ignore_cable_type,
|
||||||
save_coords=save_coords,
|
save_coords=save_coords,
|
||||||
show_unconnected=show_unconnected,
|
show_unconnected=show_unconnected,
|
||||||
show_cables=show_cables,
|
show_cables=show_cables,
|
||||||
@@ -152,7 +160,10 @@ class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
|
|||||||
group_sites=group_sites,
|
group_sites=group_sites,
|
||||||
group_locations=group_locations,
|
group_locations=group_locations,
|
||||||
group_racks=group_racks,
|
group_racks=group_racks,
|
||||||
|
group_virtualchassis=group_virtualchassis,
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
|
straight_cables=straight_cables,
|
||||||
|
grid_size=grid_size,
|
||||||
)
|
)
|
||||||
xml_data = export_data_to_xml(topo_data).decode('utf-8')
|
xml_data = export_data_to_xml(topo_data).decode('utf-8')
|
||||||
|
|
||||||
@@ -162,7 +173,7 @@ class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
|
|||||||
{"status": "Missing or malformed request parameters"}, status=400
|
{"status": "Missing or malformed request parameters"}, status=400
|
||||||
)
|
)
|
||||||
|
|
||||||
class SaveRoleImageViewSet(PermissionRequiredMixin, ReadOnlyModelViewSet):
|
class SaveRoleImageViewSet(BaseViewSet, ReadOnlyModelViewSet):
|
||||||
queryset = DeviceRole.objects.none()
|
queryset = DeviceRole.objects.none()
|
||||||
serializer_class = RoleImageSerializer
|
serializer_class = RoleImageSerializer
|
||||||
permission_required = (
|
permission_required = (
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class DeviceFilterForm(
|
|||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('q', 'filter_id', 'tag'),
|
FieldSet('q', 'filter_id', 'tag'),
|
||||||
FieldSet(
|
FieldSet(
|
||||||
'group', 'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections',
|
'group', 'ignore_cable_type', 'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections',
|
||||||
'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power', 'show_wireless',
|
'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power', 'show_wireless',
|
||||||
'group_sites', 'group_locations', 'group_racks', name=_("Options")
|
'group_sites', 'group_locations', 'group_racks', 'group_virtualchassis', 'straight_cables', 'grid_size', name=_("Options")
|
||||||
),
|
),
|
||||||
FieldSet('id', name=_("Device")),
|
FieldSet('id', name=_("Device")),
|
||||||
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_("Location")),
|
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_("Location")),
|
||||||
@@ -229,6 +229,11 @@ class DeviceFilterForm(
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
# options
|
# options
|
||||||
|
ignore_cable_type = forms.MultipleChoiceField(
|
||||||
|
label=_('Ignore Termination Types'),
|
||||||
|
required=False,
|
||||||
|
choices=IndividualOptions.CHOICES
|
||||||
|
)
|
||||||
save_coords = forms.NullBooleanField(
|
save_coords = forms.NullBooleanField(
|
||||||
label=_('Save Coordinates'),
|
label=_('Save Coordinates'),
|
||||||
required=False,
|
required=False,
|
||||||
@@ -310,6 +315,26 @@ class DeviceFilterForm(
|
|||||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
group_virtualchassis = forms.NullBooleanField(
|
||||||
|
label =_('Group Virtual Chassis'), required=False, initial=False,
|
||||||
|
widget=forms.Select(
|
||||||
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
straight_cables = forms.BooleanField(
|
||||||
|
label=_('Straight Cables'), required=False, initial=False,
|
||||||
|
widget=forms.Select(
|
||||||
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
grid_size = forms.IntegerField(
|
||||||
|
label=_('Grid Size'),
|
||||||
|
required=False,
|
||||||
|
initial=0,
|
||||||
|
min_value=0,
|
||||||
|
max_value=1000,
|
||||||
|
help_text=_('Show grid and snap dragged icons to grid. Set to 0 to disable grid and snapping.')
|
||||||
|
)
|
||||||
|
|
||||||
class CoordinateGroupsForm(NetBoxModelForm):
|
class CoordinateGroupsForm(NetBoxModelForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -501,7 +526,10 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
'group_sites',
|
'group_sites',
|
||||||
'group_locations',
|
'group_locations',
|
||||||
'group_racks',
|
'group_racks',
|
||||||
|
'group_virtualchassis',
|
||||||
'draw_default_layout',
|
'draw_default_layout',
|
||||||
|
'straight_cables',
|
||||||
|
'grid_size',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -622,6 +650,13 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
help_text=_('Draws a rectangle around Devices that belong to the '
|
help_text=_('Draws a rectangle around Devices that belong to the '
|
||||||
'same rack.')
|
'same rack.')
|
||||||
)
|
)
|
||||||
|
group_virtualchassis = forms.BooleanField(
|
||||||
|
label =_('Group Virtual Chassis'),
|
||||||
|
required=False,
|
||||||
|
initial=False,
|
||||||
|
help_text=_('Draws a rectangle around Devices that belong to the '
|
||||||
|
'same virtual chassis.')
|
||||||
|
)
|
||||||
draw_default_layout = forms.BooleanField(
|
draw_default_layout = forms.BooleanField(
|
||||||
label = ('Draw Default Layout'),
|
label = ('Draw Default Layout'),
|
||||||
required=False,
|
required=False,
|
||||||
@@ -629,6 +664,23 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
help_text=_('Enable this option if you want to draw the topology on '
|
help_text=_('Enable this option if you want to draw the topology on '
|
||||||
'the initial load (when you go to the topology plugin page).')
|
'the initial load (when you go to the topology plugin page).')
|
||||||
)
|
)
|
||||||
|
straight_cables = forms.BooleanField(
|
||||||
|
label=('Use Straight Cables'),
|
||||||
|
required=False,
|
||||||
|
initial=False,
|
||||||
|
help_text=_('Enable this option if you want to draw cables as straight lines '
|
||||||
|
'instead of curves.')
|
||||||
|
)
|
||||||
|
grid_size = forms.IntegerField(
|
||||||
|
label=_('Grid Size'),
|
||||||
|
required=True,
|
||||||
|
initial=0,
|
||||||
|
min_value=0,
|
||||||
|
max_value=1000,
|
||||||
|
help_text=_('Default grid value. Set to 0 to disable grid. '
|
||||||
|
'Integers between 0 and 1000 are allowed. Snap to grid will be '
|
||||||
|
'automatically enabled for values > 0.')
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IndividualOptions
|
model = IndividualOptions
|
||||||
@@ -636,5 +688,6 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags',
|
'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags',
|
||||||
'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections',
|
'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections',
|
||||||
'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power',
|
'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power',
|
||||||
'show_wireless', 'group_sites', 'group_locations', 'group_racks', 'draw_default_layout'
|
'show_wireless', 'group_sites', 'group_locations', 'group_racks', 'group_virtualchassis', 'draw_default_layout',
|
||||||
|
'straight_cables', 'grid_size'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.10 on 2024-04-29 14:37
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('netbox_topology_views', '0007_individualoptions_group_locations_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='individualoptions',
|
||||||
|
name='straight_cables',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2024-07-29 08:54
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('netbox_topology_views', '0008_straight_cables'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='individualoptions',
|
||||||
|
name='group_virtualchassis',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2024-08-14 09:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('netbox_topology_views', '0009_individualoptions_group_virtualchassis'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='individualoptions',
|
||||||
|
name='grid_size',
|
||||||
|
field=models.PositiveSmallIntegerField(default=0),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -331,12 +331,12 @@ class PowerFeedCoordinate(NetBoxModel):
|
|||||||
class IndividualOptions(NetBoxModel):
|
class IndividualOptions(NetBoxModel):
|
||||||
CHOICES = (
|
CHOICES = (
|
||||||
('interface', 'interface'),
|
('interface', 'interface'),
|
||||||
('front port', 'front port'),
|
('frontport', 'frontport'),
|
||||||
('rear port', 'rear port'),
|
('rearport', 'rearport'),
|
||||||
('power outlet', 'power outlet'),
|
('poweroutlet', 'poweroutlet'),
|
||||||
('power port', 'power port'),
|
('powerport', 'powerport'),
|
||||||
('console port', 'console port'),
|
('consoleport', 'consoleport'),
|
||||||
('console server port', 'console server port'),
|
('consoleserverport', 'consoleserverport'),
|
||||||
)
|
)
|
||||||
|
|
||||||
user_id = models.IntegerField(
|
user_id = models.IntegerField(
|
||||||
@@ -395,9 +395,18 @@ class IndividualOptions(NetBoxModel):
|
|||||||
group_racks = models.BooleanField(
|
group_racks = models.BooleanField(
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
group_virtualchassis = models.BooleanField(
|
||||||
|
default=False
|
||||||
|
)
|
||||||
draw_default_layout = models.BooleanField(
|
draw_default_layout = models.BooleanField(
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
straight_cables = models.BooleanField(
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
grid_size = models.PositiveSmallIntegerField(
|
||||||
|
default=0
|
||||||
|
)
|
||||||
|
|
||||||
_netbox_private = True
|
_netbox_private = True
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -73,11 +73,131 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|||||||
const group_sites = topologyData.options.group_sites
|
const group_sites = topologyData.options.group_sites
|
||||||
const group_locations = topologyData.options.group_locations
|
const group_locations = topologyData.options.group_locations
|
||||||
const group_racks = topologyData.options.group_racks
|
const group_racks = topologyData.options.group_racks
|
||||||
|
const group_virtualchassis = topologyData.options.group_virtualchassis
|
||||||
|
|
||||||
|
const gridSize = parseInt(topologyData.options.grid_size[0]);
|
||||||
|
var dragMode = false;
|
||||||
|
|
||||||
graph = new Network(container, { nodes, edges }, options)
|
graph = new Network(container, { nodes, edges }, options)
|
||||||
graph.fit()
|
graph.fit()
|
||||||
|
|
||||||
|
function getGridPosition(nodeId, gridSize) {
|
||||||
|
x = graph.getPosition(nodeId).x;
|
||||||
|
y = graph.getPosition(nodeId).y;
|
||||||
|
|
||||||
|
if(x >= 0) {
|
||||||
|
if((x % gridSize) > (gridSize / 2)) {
|
||||||
|
x += gridSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if((-x % gridSize) > (gridSize / 2)) {
|
||||||
|
x -= gridSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x = x - x % gridSize;
|
||||||
|
|
||||||
|
if(y >= 0) {
|
||||||
|
if((y % gridSize) > (gridSize / 2)) {
|
||||||
|
y += gridSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if((-y % gridSize) > (gridSize / 2)) {
|
||||||
|
y -= gridSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y = y - y % gridSize;
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: x,
|
||||||
|
y: y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGrid(canvascontext) {
|
||||||
|
// Canvas can be zoomed. It then contains more or less virtual pixels than the real number of pixels
|
||||||
|
const zoomFactor = graph.getScale() * window.devicePixelRatio;
|
||||||
|
const virtualWidth = canvascontext.canvas.width / zoomFactor;
|
||||||
|
const virtualHeight = canvascontext.canvas.height / zoomFactor;
|
||||||
|
|
||||||
|
// Canvas can be moved. Get the center of the virtual canvas. Take the grid into account
|
||||||
|
const virtualCenter = graph.getViewPosition();
|
||||||
|
const rasterizedCenterX = virtualCenter.x - virtualCenter.x % gridSize;
|
||||||
|
const rasterizedCenterY = virtualCenter.y - virtualCenter.y % gridSize;
|
||||||
|
|
||||||
|
// Calculate virtual space for the grid
|
||||||
|
const hSpace = (virtualWidth / 2) - (virtualWidth / 2) % gridSize + gridSize;
|
||||||
|
const vSpace = (virtualHeight / 2) - (virtualHeight / 2) % gridSize + gridSize;
|
||||||
|
|
||||||
|
// Calculate virtual position for the grid
|
||||||
|
const left = rasterizedCenterX - gridSize - hSpace;
|
||||||
|
const right = rasterizedCenterX + gridSize + hSpace;
|
||||||
|
const top = rasterizedCenterY - gridSize - vSpace;
|
||||||
|
const bottom = rasterizedCenterY + gridSize + vSpace;
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
canvascontext.beginPath();
|
||||||
|
|
||||||
|
for (let x = left; x < right; x += gridSize) {
|
||||||
|
canvascontext.moveTo(x, top);
|
||||||
|
canvascontext.lineTo(x, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let y = top; y < bottom; y += gridSize) {
|
||||||
|
canvascontext.moveTo(left, y);
|
||||||
|
canvascontext.lineTo(right, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvascontext.strokeStyle = '#777777';
|
||||||
|
canvascontext.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGridSnapHint(canvascontext) {
|
||||||
|
// Draw grid hinting line and circle
|
||||||
|
if(gridSize > 0 && dragMode == true && graph.getSelectedNodes().length > 0) {
|
||||||
|
for(i = 0; i < graph.getSelectedNodes().length; i++) {
|
||||||
|
id = graph.getSelectedNodes()[i];
|
||||||
|
if(window.nodes.get(id).x != graph.getPosition(id).x || window.nodes.get(id).y != graph.getPosition(id).y) {
|
||||||
|
pos = getGridPosition(graph.getSelectedNodes()[i], gridSize);
|
||||||
|
|
||||||
|
canvascontext.beginPath();
|
||||||
|
canvascontext.arc(graph.getPosition(graph.getSelectedNodes()[i]).x, graph.getPosition(graph.getSelectedNodes()[i]).y, 5, 0, 2 * Math.PI);
|
||||||
|
canvascontext.fillStyle = '#FF3D3D';
|
||||||
|
canvascontext.fill();
|
||||||
|
|
||||||
|
canvascontext.beginPath();
|
||||||
|
canvascontext.moveTo(graph.getPosition(graph.getSelectedNodes()[i]).x, graph.getPosition(graph.getSelectedNodes()[i]).y);
|
||||||
|
canvascontext.lineTo(pos.x, pos.y);
|
||||||
|
canvascontext.strokeStyle = '#FF3D3D';
|
||||||
|
canvascontext.stroke();
|
||||||
|
|
||||||
|
canvascontext.beginPath();
|
||||||
|
canvascontext.arc(pos.x, pos.y, 10, 0, 2 * Math.PI);
|
||||||
|
canvascontext.fillStyle = '#9C0000';
|
||||||
|
canvascontext.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.on('dragStart', (params) => {
|
||||||
|
dragMode = true;
|
||||||
|
})
|
||||||
|
|
||||||
graph.on('dragEnd', (params) => {
|
graph.on('dragEnd', (params) => {
|
||||||
|
dragMode = false;
|
||||||
|
// Place icon on the grid
|
||||||
|
if(gridSize > 0 && graph.getSelectedNodes().length > 0) {
|
||||||
|
for(i = 0; i < graph.getSelectedNodes().length; i++) {
|
||||||
|
id = graph.getSelectedNodes()[i];
|
||||||
|
if(window.nodes.get(id).x != graph.getPosition(id).x || window.nodes.get(id).y != graph.getPosition(id).y) {
|
||||||
|
pos = getGridPosition(graph.getSelectedNodes()[i], gridSize);
|
||||||
|
window.nodes.update({id: graph.getSelectedNodes()[i], x: pos.x, y: pos.y});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (coordSaveCheckbox.options[coordSaveCheckbox.selectedIndex].text != "Yes") return
|
if (coordSaveCheckbox.options[coordSaveCheckbox.selectedIndex].text != "Yes") return
|
||||||
|
|
||||||
Promise.allSettled(
|
Promise.allSettled(
|
||||||
@@ -138,21 +258,30 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
graph.on('beforeDrawing', (canvascontext) => {
|
||||||
|
if (gridSize > 0) {
|
||||||
|
drawGrid(canvascontext);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
graph.on('afterDrawing', (canvascontext) => {
|
graph.on('afterDrawing', (canvascontext) => {
|
||||||
allRectangles = [];
|
allRectangles = [];
|
||||||
if(group_sites != null && group_sites == 'on') { drawGroupRectangles(canvascontext, groupedNodeSites, siteRectParams); }
|
if(group_sites != null && group_sites == 'on') { drawGroupRectangles(canvascontext, groupedNodeSites, siteRectParams); }
|
||||||
if(group_locations != null && group_locations == 'on') { drawGroupRectangles(canvascontext, groupedNodeLocations, locationRectParams); }
|
if(group_locations != null && group_locations == 'on') { drawGroupRectangles(canvascontext, groupedNodeLocations, locationRectParams); }
|
||||||
if(group_racks != null && group_racks == 'on') { drawGroupRectangles(canvascontext, groupedNodeRacks, rackRectParams); }
|
if(group_racks != null && group_racks == 'on') { drawGroupRectangles(canvascontext, groupedNodeRacks, rackRectParams); }
|
||||||
|
if(group_virtualchassis != null && group_virtualchassis == 'on') { drawGroupRectangles(canvascontext, groupedNodeVirtualchassis, virtualchassisRectParams); }
|
||||||
|
|
||||||
|
drawGridSnapHint(canvascontext);
|
||||||
})
|
})
|
||||||
|
|
||||||
graph.on('click', (canvascontext) => {
|
graph.on('click', (canvascontext) => {
|
||||||
allRectangles.forEach(key => {
|
allRectangles.forEach(key => {
|
||||||
// Is the mouse pointer inside of the current rectangle?
|
// Is the mouse pointer inside of the current rectangle?
|
||||||
if(canvascontext.pointer.canvas.x > (key.x1 - key.border / 2 - 1) && canvascontext.pointer.canvas.x < (key.x2 + key.border / 2 + 1)
|
if(canvascontext.pointer.canvas.x > (key.x1 - key.border / 2 - 3) && canvascontext.pointer.canvas.x < (key.x2 + key.border / 2 + 3)
|
||||||
&& canvascontext.pointer.canvas.y > (key.y1 - key.border / 2 - 1) && canvascontext.pointer.canvas.y < (key.y2 + key.border / 2 + 1)) {
|
&& canvascontext.pointer.canvas.y > (key.y1 - key.border / 2 - 3) && canvascontext.pointer.canvas.y < (key.y2 + key.border / 2 + 3)) {
|
||||||
// We just want to react when the border has been clicked, not the whole rectangle
|
// We just want to react when the border has been clicked, not the whole rectangle
|
||||||
if (canvascontext.pointer.canvas.x < (key.x1 + key.border / 2 + 1) || canvascontext.pointer.canvas.x > (key.x2 - key.border / 2 - 1)
|
if (canvascontext.pointer.canvas.x < (key.x1 + key.border / 2 + 3) || canvascontext.pointer.canvas.x > (key.x2 - key.border / 2 - 3)
|
||||||
|| canvascontext.pointer.canvas.y < (key.y1 + key.border / 2 + 1) || canvascontext.pointer.canvas.y > (key.y2 - key.border / 2 - 1)) {
|
|| canvascontext.pointer.canvas.y < (key.y1 + key.border / 2 + 3) || canvascontext.pointer.canvas.y > (key.y2 - key.border / 2 - 3)) {
|
||||||
// Generate an array of affected nodes in order to pass it to the select.Nodes() function
|
// Generate an array of affected nodes in order to pass it to the select.Nodes() function
|
||||||
let arr = [];
|
let arr = [];
|
||||||
if(key.category == "Site") {
|
if(key.category == "Site") {
|
||||||
@@ -182,13 +311,22 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if(key.category == "Virtual Chassis") {
|
||||||
|
groupedNodeVirtualchassis.forEach(subArray => {
|
||||||
|
subArray.forEach(element => {
|
||||||
|
if (element[1] === key.id) {
|
||||||
|
arr.push(element[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
graph.selectNodes(arr);
|
graph.selectNodes(arr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add information on which node belongs to which group (site/location/rack).
|
// Add information on which node belongs to which group (site/location/rack/virtualchassis).
|
||||||
// Create an array for each group in order to loop through that arrays later
|
// Create an array for each group in order to loop through that arrays later
|
||||||
function combineNodeInfo(typeId, type) {
|
function combineNodeInfo(typeId, type) {
|
||||||
let nodesArray = [];
|
let nodesArray = [];
|
||||||
@@ -238,7 +376,7 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|||||||
allRectangles.push({category: rectangle.category, id: rectangle.id, x1: rectangle.x, y1: rectangle.y, x2: rectangle.x + rectangle.width, y2: rectangle.y + rectangle.height, border: rectangle.lineWidth})
|
allRectangles.push({category: rectangle.category, id: rectangle.id, x1: rectangle.x, y1: rectangle.y, x2: rectangle.x + rectangle.width, y2: rectangle.y + rectangle.height, border: rectangle.lineWidth})
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw all rectangles of a given group (site/location/rack)
|
/* Draw all rectangles of a given group (site/location//virtualchassis)
|
||||||
rectParams expects an object that consists of the following keys:
|
rectParams expects an object that consists of the following keys:
|
||||||
lineWidth: border width (string)
|
lineWidth: border width (string)
|
||||||
color: border color (string)
|
color: border color (string)
|
||||||
@@ -303,8 +441,8 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|||||||
color: "#337ab7",
|
color: "#337ab7",
|
||||||
paddingX: 77,
|
paddingX: 77,
|
||||||
paddingY: 77,
|
paddingY: 77,
|
||||||
textPaddingX: 12,
|
textPaddingX: 22,
|
||||||
textPaddingY: 22,
|
textPaddingY: 29,
|
||||||
font: "14px helvetica",
|
font: "14px helvetica",
|
||||||
category: "Location"
|
category: "Location"
|
||||||
}
|
}
|
||||||
@@ -315,11 +453,23 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|||||||
color: "green",
|
color: "green",
|
||||||
paddingX: 70,
|
paddingX: 70,
|
||||||
paddingY: 70,
|
paddingY: 70,
|
||||||
textPaddingX: 8,
|
textPaddingX: 15,
|
||||||
textPaddingY: 30,
|
textPaddingY: 36,
|
||||||
font: "14px helvetica",
|
font: "14px helvetica",
|
||||||
category: "Rack"
|
category: "Rack"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let groupedNodeVirtualchassis = combineNodeInfo('virtual_chassis_id', 'virtual_chassis');
|
||||||
|
let virtualchassisRectParams = {
|
||||||
|
lineWidth: "5",
|
||||||
|
color: "orange",
|
||||||
|
paddingX: 63,
|
||||||
|
paddingY: 63,
|
||||||
|
textPaddingX: 8,
|
||||||
|
textPaddingY: 43,
|
||||||
|
font: "14px helvetica",
|
||||||
|
category: "Virtual Chassis"
|
||||||
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
// Download Graph
|
// Download Graph
|
||||||
|
|||||||
+15
-12
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "netbox_topology_views",
|
"name": "netbox_topology_views",
|
||||||
"version": "4.0.0-beta.1",
|
"version": "4.0.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "netbox_topology_views",
|
"name": "netbox_topology_views",
|
||||||
"version": "4.0.0-beta.1",
|
"version": "4.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vis-data": "^7.1.9",
|
"vis-data": "^7.1.9",
|
||||||
"vis-network": "^9.1.9",
|
"vis-network": "^9.1.9",
|
||||||
@@ -561,9 +561,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/immutable": {
|
"node_modules/immutable": {
|
||||||
"version": "4.3.6",
|
"version": "4.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
|
||||||
"integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==",
|
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/is-binary-path": {
|
"node_modules/is-binary-path": {
|
||||||
@@ -579,12 +579,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.13.1",
|
"version": "2.15.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
|
||||||
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
|
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hasown": "^2.0.0"
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@@ -682,9 +685,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.77.4",
|
"version": "1.77.8",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.4.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz",
|
||||||
"integrity": "sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==",
|
"integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "netbox_topology_views",
|
"name": "netbox_topology_views",
|
||||||
"version": "4.0.0-beta.2",
|
"version": "4.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bundle": "node bundle.js",
|
"bundle": "node bundle.js",
|
||||||
"bundle:styles": "node bundle.js --styles",
|
"bundle:styles": "node bundle.js --styles",
|
||||||
|
|||||||
@@ -123,6 +123,10 @@ def get_query_settings(request):
|
|||||||
save_coords = True
|
save_coords = True
|
||||||
|
|
||||||
# Individual options
|
# Individual options
|
||||||
|
ignore_cable_type = ()
|
||||||
|
if "ignore_cable_type" in request.GET:
|
||||||
|
ignore_cable_type = request.GET.getlist('ignore_cable_type')
|
||||||
|
|
||||||
show_unconnected = False
|
show_unconnected = False
|
||||||
if "show_unconnected" in request.GET:
|
if "show_unconnected" in request.GET:
|
||||||
if request.GET["show_unconnected"] == "True":
|
if request.GET["show_unconnected"] == "True":
|
||||||
@@ -173,12 +177,30 @@ def get_query_settings(request):
|
|||||||
if request.GET["group_racks"] == "True" :
|
if request.GET["group_racks"] == "True" :
|
||||||
group_racks = True
|
group_racks = True
|
||||||
|
|
||||||
|
group_virtualchassis = False
|
||||||
|
if "group_virtualchassis" in request.GET:
|
||||||
|
if request.GET["group_virtualchassis"] == "True" :
|
||||||
|
group_virtualchassis = True
|
||||||
|
|
||||||
|
group = False
|
||||||
|
if "group" in request.GET:
|
||||||
|
group = request.GET["group"]
|
||||||
|
|
||||||
show_neighbors = False
|
show_neighbors = False
|
||||||
if "show_neighbors" in request.GET:
|
if "show_neighbors" in request.GET:
|
||||||
if request.GET["show_neighbors"] == "True" :
|
if request.GET["show_neighbors"] == "True" :
|
||||||
show_neighbors = True
|
show_neighbors = True
|
||||||
|
|
||||||
return filter_id, save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, group_sites, group_locations, group_racks, show_neighbors
|
straight_cables = False
|
||||||
|
if "straight_cables" in request.GET:
|
||||||
|
if request.GET["straight_cables"] == "True":
|
||||||
|
straight_cables = True
|
||||||
|
|
||||||
|
grid_size = 0
|
||||||
|
if "grid_size" in request.GET:
|
||||||
|
grid_size = request.GET.getlist('grid_size')
|
||||||
|
|
||||||
|
return filter_id, ignore_cable_type, save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, group_sites, group_locations, group_racks, group_virtualchassis, group, show_neighbors, straight_cables, grid_size
|
||||||
|
|
||||||
class LinePattern():
|
class LinePattern():
|
||||||
wireless = [2, 10, 2, 10]
|
wireless = [2, 10, 2, 10]
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ def create_node(
|
|||||||
if device.rack is not None:
|
if device.rack is not None:
|
||||||
node["rack"] = device.rack.name
|
node["rack"] = device.rack.name
|
||||||
node["rack_id"] = device.rack_id
|
node["rack_id"] = device.rack_id
|
||||||
|
if device.virtual_chassis is not None:
|
||||||
|
node["virtual_chassis"] = device.virtual_chassis.name
|
||||||
|
node["virtual_chassis_id"] = device.virtual_chassis_id
|
||||||
|
|
||||||
if device.role.color != "":
|
if device.role.color != "":
|
||||||
node["color.border"] = "#" + device.role.color
|
node["color.border"] = "#" + device.role.color
|
||||||
@@ -238,6 +241,7 @@ def create_edge(
|
|||||||
edge_id: int,
|
edge_id: int,
|
||||||
termination_a: Dict,
|
termination_a: Dict,
|
||||||
termination_b: Dict,
|
termination_b: Dict,
|
||||||
|
straight_cables: bool,
|
||||||
circuit: Optional[Dict] = None,
|
circuit: Optional[Dict] = None,
|
||||||
cable: Optional[Cable] = None,
|
cable: Optional[Cable] = None,
|
||||||
wireless: Optional[Dict] = None,
|
wireless: Optional[Dict] = None,
|
||||||
@@ -300,6 +304,9 @@ def create_edge(
|
|||||||
if hasattr(cable, 'color') and cable.color != "":
|
if hasattr(cable, 'color') and cable.color != "":
|
||||||
edge["color"] = "#" + cable.color
|
edge["color"] = "#" + cable.color
|
||||||
|
|
||||||
|
# if straight_cables == True: edge["smooth"] = False
|
||||||
|
edge["smooth"] = not straight_cables
|
||||||
|
|
||||||
return edge
|
return edge
|
||||||
|
|
||||||
|
|
||||||
@@ -327,6 +334,7 @@ def get_topology_data(
|
|||||||
queryset: QuerySet,
|
queryset: QuerySet,
|
||||||
individualOptions: IndividualOptions,
|
individualOptions: IndividualOptions,
|
||||||
show_unconnected: bool,
|
show_unconnected: bool,
|
||||||
|
ignore_cable_type: list,
|
||||||
save_coords: bool,
|
save_coords: bool,
|
||||||
show_cables: bool,
|
show_cables: bool,
|
||||||
show_circuit: bool,
|
show_circuit: bool,
|
||||||
@@ -338,7 +346,10 @@ def get_topology_data(
|
|||||||
group_sites: bool,
|
group_sites: bool,
|
||||||
group_locations: bool,
|
group_locations: bool,
|
||||||
group_racks: bool,
|
group_racks: bool,
|
||||||
|
group_virtualchassis: bool,
|
||||||
group_id,
|
group_id,
|
||||||
|
straight_cables: bool,
|
||||||
|
grid_size: list,
|
||||||
):
|
):
|
||||||
|
|
||||||
supported_termination_types = []
|
supported_termination_types = []
|
||||||
@@ -360,8 +371,6 @@ def get_topology_data(
|
|||||||
cable_ids = DefaultDict(dict)
|
cable_ids = DefaultDict(dict)
|
||||||
interface_ids = DefaultDict(dict)
|
interface_ids = DefaultDict(dict)
|
||||||
|
|
||||||
ignore_cable_type = individualOptions.ignore_cable_type
|
|
||||||
|
|
||||||
device_ids = [d.pk for d in queryset]
|
device_ids = [d.pk for d in queryset]
|
||||||
site_ids = [d.site_id for d in queryset]
|
site_ids = [d.site_id for d in queryset]
|
||||||
|
|
||||||
@@ -436,6 +445,7 @@ def get_topology_data(
|
|||||||
circuit=circuit_model,
|
circuit=circuit_model,
|
||||||
termination_a=termination_a,
|
termination_a=termination_a,
|
||||||
termination_b=termination_b,
|
termination_b=termination_b,
|
||||||
|
straight_cables=straight_cables,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -505,6 +515,7 @@ def get_topology_data(
|
|||||||
termination_a=termination_a,
|
termination_a=termination_a,
|
||||||
termination_b=termination_b,
|
termination_b=termination_b,
|
||||||
power=True,
|
power=True,
|
||||||
|
straight_cables=straight_cables,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -543,7 +554,7 @@ def get_topology_data(
|
|||||||
edge_ids += 1
|
edge_ids += 1
|
||||||
termination_a = { "termination_name": interface.name, "termination_device_name": interface.device.name, "device_id": interface.device.id }
|
termination_a = { "termination_name": interface.name, "termination_device_name": interface.device.name, "device_id": interface.device.id }
|
||||||
termination_b = { "termination_name": destination.name, "termination_device_name": destination.device.name, "device_id": destination.device.id }
|
termination_b = { "termination_name": destination.name, "termination_device_name": destination.device.name, "device_id": destination.device.id }
|
||||||
edges.append(create_edge(edge_id=edge_ids, termination_a=termination_a, termination_b=termination_b, interface=interface))
|
edges.append(create_edge(edge_id=edge_ids, termination_a=termination_a, termination_b=termination_b, interface=interface, straight_cables=straight_cables))
|
||||||
nodes_devices[interface.device.id] = interface.device
|
nodes_devices[interface.device.id] = interface.device
|
||||||
nodes_devices[destination.device.id] = destination.device
|
nodes_devices[destination.device.id] = destination.device
|
||||||
|
|
||||||
@@ -553,11 +564,11 @@ def get_topology_data(
|
|||||||
).select_related("termination_type")
|
).select_related("termination_type")
|
||||||
|
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.termination_type.name in ignore_cable_type:
|
if link.termination_type.model in ignore_cable_type:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Normal device cables
|
# Normal device cables
|
||||||
if link.termination_type.name in supported_termination_types:
|
if link.termination_type.model in supported_termination_types:
|
||||||
complete_link = False
|
complete_link = False
|
||||||
if link.cable_end == "A":
|
if link.cable_end == "A":
|
||||||
if link.cable_id not in cable_ids:
|
if link.cable_id not in cable_ids:
|
||||||
@@ -623,6 +634,7 @@ def get_topology_data(
|
|||||||
cable=link.cable,
|
cable=link.cable,
|
||||||
termination_a=termination_a,
|
termination_a=termination_a,
|
||||||
termination_b=termination_b,
|
termination_b=termination_b,
|
||||||
|
straight_cables=straight_cables,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -662,6 +674,7 @@ def get_topology_data(
|
|||||||
termination_a=termination_a,
|
termination_a=termination_a,
|
||||||
termination_b=termination_b,
|
termination_b=termination_b,
|
||||||
wireless=wireless,
|
wireless=wireless,
|
||||||
|
straight_cables=straight_cables,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -671,6 +684,12 @@ def get_topology_data(
|
|||||||
options['group_racks'] = 'on'
|
options['group_racks'] = 'on'
|
||||||
if group_sites:
|
if group_sites:
|
||||||
options['group_sites'] = 'on'
|
options['group_sites'] = 'on'
|
||||||
|
if group_virtualchassis:
|
||||||
|
options['group_virtualchassis'] = 'on'
|
||||||
|
if grid_size:
|
||||||
|
options['grid_size'] = grid_size
|
||||||
|
else:
|
||||||
|
options['grid_size'] = list('0')
|
||||||
|
|
||||||
for qs_device in queryset:
|
for qs_device in queryset:
|
||||||
if qs_device.pk not in nodes_devices and show_unconnected:
|
if qs_device.pk not in nodes_devices and show_unconnected:
|
||||||
@@ -697,7 +716,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
self.filterset = DeviceFilterSet
|
self.filterset = DeviceFilterSet
|
||||||
self.queryset = Device.objects.all().select_related(
|
self.queryset = Device.objects.restrict(request.user, 'view').select_related(
|
||||||
"device_type", "role"
|
"device_type", "role"
|
||||||
)
|
)
|
||||||
self.queryset = self.filterset(request.GET, self.queryset).qs
|
self.queryset = self.filterset(request.GET, self.queryset).qs
|
||||||
@@ -710,7 +729,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
|
|
||||||
if request.GET:
|
if request.GET:
|
||||||
|
|
||||||
filter_id, save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, group_sites, group_locations, group_racks, show_neighbors = get_query_settings(request)
|
filter_id, ignore_cable_type, save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, group_sites, group_locations, group_racks, group_virtualchassis, group, show_neighbors, straight_cables, grid_size = get_query_settings(request)
|
||||||
|
|
||||||
# Read options from saved filters as NetBox does not handle custom plugin filters
|
# Read options from saved filters as NetBox does not handle custom plugin filters
|
||||||
if "filter_id" in request.GET and request.GET["filter_id"] != '':
|
if "filter_id" in request.GET and request.GET["filter_id"] != '':
|
||||||
@@ -718,6 +737,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
saved_filter = SavedFilter.objects.get(pk=filter_id)
|
saved_filter = SavedFilter.objects.get(pk=filter_id)
|
||||||
saved_filter_params = getattr(saved_filter, 'parameters')
|
saved_filter_params = getattr(saved_filter, 'parameters')
|
||||||
|
|
||||||
|
if ignore_cable_type == () and 'ignore_cable_type' in saved_filter_params: ignore_cable_type = saved_filter_params['ignore_cable_type']
|
||||||
if save_coords == False and 'save_coords' in saved_filter_params: save_coords = saved_filter_params['save_coords']
|
if save_coords == False and 'save_coords' in saved_filter_params: save_coords = saved_filter_params['save_coords']
|
||||||
if show_power == False and 'show_power' in saved_filter_params: show_power = saved_filter_params['show_power']
|
if show_power == False and 'show_power' in saved_filter_params: show_power = saved_filter_params['show_power']
|
||||||
if show_circuit == False and 'show_circuit' in saved_filter_params: show_circuit = saved_filter_params['show_circuit']
|
if show_circuit == False and 'show_circuit' in saved_filter_params: show_circuit = saved_filter_params['show_circuit']
|
||||||
@@ -728,14 +748,20 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
if group_sites == False and 'group_sites' in saved_filter_params: group_sites = saved_filter_params['group_sites']
|
if group_sites == False and 'group_sites' in saved_filter_params: group_sites = saved_filter_params['group_sites']
|
||||||
if group_locations == False and 'group_locations' in saved_filter_params: group_locations = saved_filter_params['group_locations']
|
if group_locations == False and 'group_locations' in saved_filter_params: group_locations = saved_filter_params['group_locations']
|
||||||
if group_racks == False and 'group_racks' in saved_filter_params: group_racks = saved_filter_params['group_racks']
|
if group_racks == False and 'group_racks' in saved_filter_params: group_racks = saved_filter_params['group_racks']
|
||||||
|
if group_virtualchassis == False and 'group_virtualchassis' in saved_filter_params: group_virtualchassis = saved_filter_params['group_virtualchassis']
|
||||||
if show_neighbors == False and 'show_neighbors' in saved_filter_params: show_neighbors = saved_filter_params['show_neighbors']
|
if show_neighbors == False and 'show_neighbors' in saved_filter_params: show_neighbors = saved_filter_params['show_neighbors']
|
||||||
|
if straight_cables == False and 'straight_cables' in saved_filter_params: straight_cables = saved_filter_params['straight_cables']
|
||||||
|
if grid_size == 0 and 'grid_size' in saved_filter_params: grid_size = saved_filter_params['grid_size']
|
||||||
except SavedFilter.DoesNotExist: # filter_id not found
|
except SavedFilter.DoesNotExist: # filter_id not found
|
||||||
pass
|
pass
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
print(type(inst))
|
print(type(inst))
|
||||||
|
|
||||||
if "group" not in request.GET:
|
if "group" not in request.GET:
|
||||||
group_id = "default"
|
if 'saved_filter_params' in locals() and "group" in saved_filter_params:
|
||||||
|
group_id = saved_filter_params['group'][0]
|
||||||
|
else:
|
||||||
|
group_id = "default"
|
||||||
else:
|
else:
|
||||||
group_id = request.GET["group"]
|
group_id = request.GET["group"]
|
||||||
|
|
||||||
@@ -743,6 +769,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
topo_data = get_topology_data(
|
topo_data = get_topology_data(
|
||||||
queryset=self.queryset,
|
queryset=self.queryset,
|
||||||
individualOptions=individualOptions,
|
individualOptions=individualOptions,
|
||||||
|
ignore_cable_type=ignore_cable_type,
|
||||||
save_coords=save_coords,
|
save_coords=save_coords,
|
||||||
show_unconnected=show_unconnected,
|
show_unconnected=show_unconnected,
|
||||||
show_cables=show_cables,
|
show_cables=show_cables,
|
||||||
@@ -755,17 +782,23 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
group_sites=group_sites,
|
group_sites=group_sites,
|
||||||
group_locations=group_locations,
|
group_locations=group_locations,
|
||||||
group_racks=group_racks,
|
group_racks=group_racks,
|
||||||
|
group_virtualchassis=group_virtualchassis,
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
|
straight_cables=straight_cables,
|
||||||
|
grid_size=grid_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# No GET-Request in URL. We most likely came here from the navigation menu.
|
# No GET-Request in URL. We most likely came here from the navigation menu.
|
||||||
preselected_device_roles = IndividualOptions.objects.get(id=individualOptions.id).preselected_device_roles.all().values_list('id', flat=True)
|
preselected_device_roles = IndividualOptions.objects.get(id=individualOptions.id).preselected_device_roles.all().values_list('id', flat=True)
|
||||||
preselected_tags = IndividualOptions.objects.get(id=individualOptions.id).preselected_tags.all().values_list(Lower('name'), flat=True)
|
preselected_tags = IndividualOptions.objects.get(id=individualOptions.id).preselected_tags.all().values_list(Lower('name'), flat=True)
|
||||||
|
ignore_cable_type = IndividualOptions.objects.get(id=individualOptions.id).ignore_cable_type.translate({ord(i): None for i in '[]\''}).split(', ')
|
||||||
|
if ignore_cable_type == ['']: ignore_cable_type = []
|
||||||
|
|
||||||
q = QueryDict(mutable=True)
|
q = QueryDict(mutable=True)
|
||||||
q.setlist("role_id", list(preselected_device_roles))
|
q.setlist("role_id", list(preselected_device_roles))
|
||||||
q.setlist("tag", list(preselected_tags))
|
q.setlist("tag", list(preselected_tags))
|
||||||
|
q.setlist("ignore_cable_type", ignore_cable_type)
|
||||||
|
|
||||||
if individualOptions.save_coords: q['save_coords'] = "True"
|
if individualOptions.save_coords: q['save_coords'] = "True"
|
||||||
if individualOptions.show_unconnected: q['show_unconnected'] = "True"
|
if individualOptions.show_unconnected: q['show_unconnected'] = "True"
|
||||||
@@ -779,6 +812,9 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
if individualOptions.group_sites: q['group_sites'] = "True"
|
if individualOptions.group_sites: q['group_sites'] = "True"
|
||||||
if individualOptions.group_locations: q['group_locations'] = "True"
|
if individualOptions.group_locations: q['group_locations'] = "True"
|
||||||
if individualOptions.group_racks: q['group_racks'] = "True"
|
if individualOptions.group_racks: q['group_racks'] = "True"
|
||||||
|
if individualOptions.group_virtualchassis: q['group_virtualchassis'] = "True"
|
||||||
|
if individualOptions.straight_cables: q['straight_cables'] = "True"
|
||||||
|
if individualOptions.grid_size: q['grid_size'] = individualOptions.grid_size
|
||||||
if individualOptions.draw_default_layout:
|
if individualOptions.draw_default_layout:
|
||||||
q['draw_init'] = "True"
|
q['draw_init'] = "True"
|
||||||
else:
|
else:
|
||||||
@@ -1136,7 +1172,10 @@ class TopologyIndividualOptionsView(PermissionRequiredMixin, View):
|
|||||||
'group_sites': queryset.group_sites,
|
'group_sites': queryset.group_sites,
|
||||||
'group_locations': queryset.group_locations,
|
'group_locations': queryset.group_locations,
|
||||||
'group_racks': queryset.group_racks,
|
'group_racks': queryset.group_racks,
|
||||||
|
'group_virtualchassis': queryset.group_virtualchassis,
|
||||||
'draw_default_layout': queryset.draw_default_layout,
|
'draw_default_layout': queryset.draw_default_layout,
|
||||||
|
'straight_cables': queryset.straight_cables,
|
||||||
|
'grid_size': queryset.grid_size,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ long_description = readme.read_text()
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="netbox-topology-views",
|
name="netbox-topology-views",
|
||||||
version="4.0.0-beta.2",
|
version="4.0.1",
|
||||||
description="An NetBox plugin to create Topology maps",
|
description="An NetBox plugin to create Topology maps",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
|
|||||||
Reference in New Issue
Block a user