Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fbeb97810 | ||
|
|
f3b822e18d | ||
|
|
555c1fac4c | ||
|
|
cf8641939a | ||
|
|
2893fff111 | ||
|
|
25fdcb6cf4 | ||
|
|
5d330bc830 | ||
|
|
03b8c9c76e | ||
|
|
02a7118a33 | ||
|
|
44e49495fe |
@@ -0,0 +1,15 @@
|
|||||||
|
name: NetBox plugin manifest modified
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ develop ]
|
||||||
|
paths:
|
||||||
|
- netbox-plugin.yaml
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
manifest-modified:
|
||||||
|
uses: netboxlabs/public-workflows/.github/workflows/reusable-plugin-manifest-modified.yml@release
|
||||||
@@ -48,21 +48,19 @@ python3 manage.py collectstatic --no-input
|
|||||||
|
|
||||||
### Versions
|
### Versions
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If the version of netbox is not listed here the plugin will most likely not work. Remove the plugin if you want to update netbox before this plugin has released an update for that netbox version.
|
||||||
|
|
||||||
| netbox version | netbox-topology-views version |
|
| netbox version | netbox-topology-views version |
|
||||||
| -------------- | ----------------------------- |
|
| -------------- | ----------------------------- |
|
||||||
| >= 4.1.0 | >= v4.1.0 |
|
| = 4.2.0 | = v4.2.0 |
|
||||||
| >= 4.0.0 | >= v4.0.0 |
|
| = 4.1.X | = v4.1.0 |
|
||||||
| >= 3.7.0 | >= v3.9.0 |
|
| = 4.0.X | = v4.0.0 |
|
||||||
| >= 3.6.4 | >= v3.8.1 |
|
| = 3.7.0 | = v3.9.0 |
|
||||||
| >= 3.6.0 | >= v3.7.X |
|
| = 3.6.4 | = v3.8.1 |
|
||||||
| >= 3.5.0 | >= v3.4.X |
|
| = 3.6.0 | = v3.7.0 |
|
||||||
| >= 3.4.0 | >= v3.X.X |
|
| = 3.5.0 | = v3.6.2 |
|
||||||
| >= 3.3.0 | >= v3.0.0 |
|
|
||||||
| >= 3.2.0 | >= v1.1.0 |
|
|
||||||
| >= 3.1.8 | >= v1.0.0 |
|
|
||||||
| >= 2.11.1 | >= v0.5.3 |
|
|
||||||
| >= 2.10.0 | >= v0.5.0 |
|
|
||||||
| < 2.10.0 | =< v0.4.10 |
|
|
||||||
|
|
||||||
### Update
|
### Update
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
version: '3.4'
|
version: '3.4'
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:12-alpine
|
image: postgres:16-alpine
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: J5brHrAXFLQSif0K #just for local dev
|
POSTGRES_PASSWORD: J5brHrAXFLQSif0K #just for local dev
|
||||||
POSTGRES_DB: netbox
|
POSTGRES_DB: netbox
|
||||||
@@ -9,7 +9,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- netbox-postgres-data:/var/lib/postgresql/data
|
- netbox-postgres-data-16:/var/lib/postgresql/data
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6-alpine
|
image: redis:6-alpine
|
||||||
@@ -35,7 +35,7 @@ services:
|
|||||||
- "6378:6379"
|
- "6378:6379"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
netbox-postgres-data:
|
netbox-postgres-data-16:
|
||||||
driver: local
|
driver: local
|
||||||
netbox-redis-data:
|
netbox-redis-data:
|
||||||
driver: local
|
driver: local
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
version: 0.1
|
||||||
|
package_name: netbox-topology-views
|
||||||
|
compatibility:
|
||||||
|
- release: 4.2.0
|
||||||
|
netbox_min: 4.2.0
|
||||||
|
netbox_max: 4.2.1
|
||||||
|
- release: 4.1.0
|
||||||
|
netbox_min: 4.1.0
|
||||||
|
netbox_max: 4.1.11
|
||||||
|
- release: 4.0.1
|
||||||
|
netbox_min: 4.0.9
|
||||||
|
netbox_max: 4.0.11
|
||||||
|
- release: 4.0.0
|
||||||
|
netbox_min: 4.0.0
|
||||||
|
netbox_max: 4.0.9
|
||||||
|
- release: 3.9.1
|
||||||
|
netbox_min: 3.7.1
|
||||||
|
netbox_max: 3.7.8
|
||||||
|
- release: 3.9.0
|
||||||
|
netbox_min: 3.7.1
|
||||||
|
netbox_max: 3.7.8
|
||||||
|
- release: 3.8.1
|
||||||
|
netbox_min: 3.6.9
|
||||||
|
netbox_max: 3.6.9
|
||||||
|
- release: 3.6.2
|
||||||
|
netbox_min: 3.5.1
|
||||||
|
netbox_max: 3.5.9
|
||||||
@@ -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.1.0"
|
version = "4.2.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", "group_virtualchassis", "draw_default_layout", "straight_cables", "grid_size")
|
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", "node_label_items")
|
||||||
|
|||||||
@@ -7,5 +7,10 @@ router = NetBoxRouter()
|
|||||||
router.register("save-coords", views.SaveCoordsViewSet)
|
router.register("save-coords", views.SaveCoordsViewSet)
|
||||||
router.register("images", views.SaveRoleImageViewSet)
|
router.register("images", views.SaveRoleImageViewSet)
|
||||||
router.register("xml-export", views.ExportTopoToXML, "xml-export")
|
router.register("xml-export", views.ExportTopoToXML, "xml-export")
|
||||||
|
router.register("coordinate-groups", views.CoordinateGroupViewSet)
|
||||||
|
router.register("coordinate", views.CoordinateViewSet)
|
||||||
|
router.register("circuitcoordinate", views.CircuitCoordinateViewSet)
|
||||||
|
router.register("powerpanelcoordinate", views.PowerPanelCoordinateViewSet)
|
||||||
|
router.register("powerfeedcoordinate", views.PowerFeedCoordinateViewSet)
|
||||||
|
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from netbox.api.viewsets import BaseViewSet
|
from netbox.api.viewsets import BaseViewSet, NetBoxModelViewSet
|
||||||
|
|
||||||
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
|
||||||
@@ -17,6 +17,11 @@ from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
|
|||||||
from netbox_topology_views.api.serializers import (
|
from netbox_topology_views.api.serializers import (
|
||||||
RoleImageSerializer,
|
RoleImageSerializer,
|
||||||
TopologyDummySerializer,
|
TopologyDummySerializer,
|
||||||
|
CoordinateGroupSerializer,
|
||||||
|
CoordinateSerializer,
|
||||||
|
CircuitCoordinateSerializer,
|
||||||
|
PowerPanelCoordinateSerializer,
|
||||||
|
PowerFeedCoordinateSerializer,
|
||||||
)
|
)
|
||||||
import netbox_topology_views.models
|
import netbox_topology_views.models
|
||||||
from netbox_topology_views.models import RoleImage, IndividualOptions, CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate
|
from netbox_topology_views.models import RoleImage, IndividualOptions, CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate
|
||||||
@@ -109,7 +114,7 @@ class ExportTopoToXML(BaseViewSet, ViewSet):
|
|||||||
|
|
||||||
if request.GET:
|
if request.GET:
|
||||||
|
|
||||||
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)
|
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, node_label_items = 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"] != '':
|
||||||
@@ -119,6 +124,7 @@ class ExportTopoToXML(BaseViewSet, ViewSet):
|
|||||||
|
|
||||||
if ignore_cable_type == () and 'ignore_cable_type' in saved_filter_params: ignore_cable_type = saved_filter_params['ignore_cable_type']
|
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_unconnected == False and 'show_unconnected' in saved_filter_params: show_unconnected = saved_filter_params['show_unconnected']
|
||||||
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']
|
||||||
if show_logical_connections == False and 'show_logical_connections' in saved_filter_params: show_logical_connections = saved_filter_params['show_logical_connections']
|
if show_logical_connections == False and 'show_logical_connections' in saved_filter_params: show_logical_connections = saved_filter_params['show_logical_connections']
|
||||||
@@ -132,6 +138,7 @@ class ExportTopoToXML(BaseViewSet, ViewSet):
|
|||||||
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 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']
|
if grid_size == 0 and 'grid_size' in saved_filter_params: grid_size = saved_filter_params['grid_size']
|
||||||
|
if node_label_items == () and 'node_label_items' in saved_filter_params: node_label_items = saved_filter_params['node_label_items']
|
||||||
except SavedFilter.DoesNotExist: # filter_id not found
|
except SavedFilter.DoesNotExist: # filter_id not found
|
||||||
pass
|
pass
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
@@ -164,8 +171,9 @@ class ExportTopoToXML(BaseViewSet, ViewSet):
|
|||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
straight_cables=straight_cables,
|
straight_cables=straight_cables,
|
||||||
grid_size=grid_size,
|
grid_size=grid_size,
|
||||||
|
node_label_items=node_label_items,
|
||||||
)
|
)
|
||||||
xml_data = export_data_to_xml(topo_data).decode('utf-8')
|
xml_data = export_data_to_xml(topo_data).decode('utf-8').replace('\n', '
')
|
||||||
|
|
||||||
return HttpResponse(xml_data, content_type="application/xml; charset=utf-8")
|
return HttpResponse(xml_data, content_type="application/xml; charset=utf-8")
|
||||||
else:
|
else:
|
||||||
@@ -251,3 +259,23 @@ class SaveRoleImageViewSet(BaseViewSet, ReadOnlyModelViewSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return JsonResponse({"status": "Ok"})
|
return JsonResponse({"status": "Ok"})
|
||||||
|
|
||||||
|
class CoordinateGroupViewSet(NetBoxModelViewSet):
|
||||||
|
queryset = CoordinateGroup.objects.all()
|
||||||
|
serializer_class = CoordinateGroupSerializer
|
||||||
|
|
||||||
|
class CoordinateViewSet(NetBoxModelViewSet):
|
||||||
|
queryset = Coordinate.objects.all()
|
||||||
|
serializer_class = CoordinateSerializer
|
||||||
|
|
||||||
|
class CircuitCoordinateViewSet(NetBoxModelViewSet):
|
||||||
|
queryset = CircuitCoordinate.objects.all()
|
||||||
|
serializer_class = CircuitCoordinateSerializer
|
||||||
|
|
||||||
|
class PowerPanelCoordinateViewSet(NetBoxModelViewSet):
|
||||||
|
queryset = PowerPanelCoordinate.objects.all()
|
||||||
|
serializer_class = PowerPanelCoordinateSerializer
|
||||||
|
|
||||||
|
class PowerFeedCoordinateViewSet(NetBoxModelViewSet):
|
||||||
|
queryset = PowerFeedCoordinate.objects.all()
|
||||||
|
serializer_class = PowerFeedCoordinateSerializer
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from utilities.choices import ChoiceSet
|
||||||
|
|
||||||
|
class NodeLabelItems(ChoiceSet):
|
||||||
|
DEVICE_NAME = 'devicename'
|
||||||
|
PRIMARY_IPV4 = 'primaryipv4'
|
||||||
|
PRIMARY_IPV6 = 'primaryipv6'
|
||||||
|
OUT_OF_BAND_IP = 'outofbandip'
|
||||||
|
|
||||||
|
CHOICES = [
|
||||||
|
(DEVICE_NAME, 'Device Name'),
|
||||||
|
(PRIMARY_IPV4, 'Primary IPv4'),
|
||||||
|
(PRIMARY_IPV6, 'Primary IPv6'),
|
||||||
|
(OUT_OF_BAND_IP, 'Out-of-band IP'),
|
||||||
|
]
|
||||||
@@ -71,7 +71,7 @@ class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilter
|
|||||||
null_value=None,
|
null_value=None,
|
||||||
)
|
)
|
||||||
mac_address = MultiValueMACAddressFilter(
|
mac_address = MultiValueMACAddressFilter(
|
||||||
field_name='interfaces__mac_address',
|
field_name='interfaces__mac_addresses__mac_address',
|
||||||
label="MAC address",
|
label="MAC address",
|
||||||
)
|
)
|
||||||
serial = MultiValueCharFilter(
|
serial = MultiValueCharFilter(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from utilities.forms.fields import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from netbox_topology_views.models import IndividualOptions, CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate
|
from netbox_topology_views.models import IndividualOptions, CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate
|
||||||
|
from netbox_topology_views.choices import NodeLabelItems
|
||||||
|
|
||||||
class DeviceFilterForm(
|
class DeviceFilterForm(
|
||||||
LocalConfigContextFilterForm,
|
LocalConfigContextFilterForm,
|
||||||
@@ -36,7 +37,7 @@ class DeviceFilterForm(
|
|||||||
FieldSet(
|
FieldSet(
|
||||||
'group', 'ignore_cable_type', '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', 'group_virtualchassis', 'straight_cables', 'grid_size', name=_("Options")
|
'group_sites', 'group_locations', 'group_racks', 'group_virtualchassis', 'straight_cables', 'grid_size', 'node_label_items', 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")),
|
||||||
@@ -335,6 +336,11 @@ class DeviceFilterForm(
|
|||||||
max_value=1000,
|
max_value=1000,
|
||||||
help_text=_('Show grid and snap dragged icons to grid. Set to 0 to disable grid and snapping.')
|
help_text=_('Show grid and snap dragged icons to grid. Set to 0 to disable grid and snapping.')
|
||||||
)
|
)
|
||||||
|
node_label_items = forms.MultipleChoiceField(
|
||||||
|
label=_('Node Label Items'),
|
||||||
|
required=False,
|
||||||
|
choices=NodeLabelItems
|
||||||
|
)
|
||||||
|
|
||||||
class CoordinateGroupsForm(NetBoxModelForm):
|
class CoordinateGroupsForm(NetBoxModelForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -530,6 +536,7 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
'draw_default_layout',
|
'draw_default_layout',
|
||||||
'straight_cables',
|
'straight_cables',
|
||||||
'grid_size',
|
'grid_size',
|
||||||
|
'node_label_items',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -681,6 +688,12 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
'Integers between 0 and 1000 are allowed. Snap to grid will be '
|
'Integers between 0 and 1000 are allowed. Snap to grid will be '
|
||||||
'automatically enabled for values > 0.')
|
'automatically enabled for values > 0.')
|
||||||
)
|
)
|
||||||
|
node_label_items = forms.MultipleChoiceField(
|
||||||
|
label=_('Node Label Items'),
|
||||||
|
required=False,
|
||||||
|
choices=NodeLabelItems,
|
||||||
|
help_text=_('Choose Label Items that you want to be displayed.')
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IndividualOptions
|
model = IndividualOptions
|
||||||
@@ -689,5 +702,5 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
'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', 'group_virtualchassis', 'draw_default_layout',
|
'show_wireless', 'group_sites', 'group_locations', 'group_racks', 'group_virtualchassis', 'draw_default_layout',
|
||||||
'straight_cables', 'grid_size'
|
'straight_cables', 'grid_size', 'node_label_items'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.9 on 2024-09-27 18:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('netbox_topology_views', '0010_individualoptions_grid_size'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='individualoptions',
|
||||||
|
name='node_label_items',
|
||||||
|
field=models.CharField(blank=True, max_length=255),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -25,6 +25,7 @@ from netbox_topology_views.utils import (
|
|||||||
image_static_url,
|
image_static_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from netbox_topology_views.choices import NodeLabelItems
|
||||||
|
|
||||||
class RoleImage(ChangeLoggingMixin, ExportTemplatesMixin, EventRulesMixin):
|
class RoleImage(ChangeLoggingMixin, ExportTemplatesMixin, EventRulesMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -407,7 +408,11 @@ class IndividualOptions(NetBoxModel):
|
|||||||
grid_size = models.PositiveSmallIntegerField(
|
grid_size = models.PositiveSmallIntegerField(
|
||||||
default=0
|
default=0
|
||||||
)
|
)
|
||||||
|
node_label_items = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
_netbox_private = True
|
_netbox_private = True
|
||||||
|
|
||||||
def __str___(self):
|
def __str___(self):
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -511,12 +511,12 @@ function performXmlDownload() {
|
|||||||
if (curr_url.includes(sites_prefix)) {
|
if (curr_url.includes(sites_prefix)) {
|
||||||
var site_id = curr_url.split(sites_prefix)[1];
|
var site_id = curr_url.split(sites_prefix)[1];
|
||||||
site_id = site_id.split('/')[0]
|
site_id = site_id.split('/')[0]
|
||||||
xml_search_options = 'site_id=' + site_id + '&show_cables=on&show_unconnected=on'
|
xml_search_options = 'site_id=' + site_id + '&show_cables=True&show_unconnected=True'
|
||||||
}
|
}
|
||||||
else if (curr_url.includes(location_prefix)) {
|
else if (curr_url.includes(location_prefix)) {
|
||||||
var location_id = curr_url.split(location_prefix)[1];
|
var location_id = curr_url.split(location_prefix)[1];
|
||||||
location_id = location_id.split('/')[0]
|
location_id = location_id.split('/')[0]
|
||||||
xml_search_options = 'location_id=' + location_id + '&show_cables=on&show_unconnected=on'
|
xml_search_options = 'location_id=' + location_id + '&show_cables=True&show_unconnected=True'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+1300
-432
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "netbox_topology_views",
|
"name": "netbox_topology_views",
|
||||||
"version": "4.1.0",
|
"version": "4.2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bundle": "node bundle.js",
|
"bundle": "node bundle.js",
|
||||||
"bundle:styles": "node bundle.js --styles",
|
"bundle:styles": "node bundle.js --styles",
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@egjs/hammerjs": "^2.0.0",
|
"@egjs/hammerjs": "^2.0.0",
|
||||||
"component-emitter": "^1.3.0",
|
"component-emitter": "^1.3.0",
|
||||||
"esbuild": "^0.12.24",
|
"esbuild": "^0.25.0",
|
||||||
"esbuild-sass-plugin": "^1.5.2",
|
"esbuild-sass-plugin": "^3.3.1",
|
||||||
"keycharm": "^0.4.0",
|
"keycharm": "^0.4.0",
|
||||||
"timsort": "^0.3.0",
|
"timsort": "^0.3.0",
|
||||||
"uuid": "^8.0.0"
|
"uuid": "^8.0.0"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from netbox.plugins import PluginTemplateExtension
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
NETBOX_CURRENT_VERSION = version.parse(settings.VERSION)
|
NETBOX_CURRENT_VERSION = version.parse(settings.RELEASE.version)
|
||||||
|
|
||||||
|
|
||||||
class SiteButtons(PluginTemplateExtension):
|
class SiteButtons(PluginTemplateExtension):
|
||||||
|
|||||||
@@ -40,5 +40,6 @@
|
|||||||
var is_htmx = true;
|
var is_htmx = true;
|
||||||
var brokenImage = '{{ broken_image }}';
|
var brokenImage = '{{ broken_image }}';
|
||||||
var topologyData = {{ topology_data | safe }};
|
var topologyData = {{ topology_data | safe }};
|
||||||
|
const basePath = '{{ basepath }}';
|
||||||
</script>
|
</script>
|
||||||
<script src="{% static 'netbox_topology_views/js/app.js' %}?ver={{ epoch }} " defer></script>
|
<script src="{% static 'netbox_topology_views/js/app.js' %}?ver={{ epoch }} " defer></script>
|
||||||
|
|||||||
@@ -200,7 +200,11 @@ def get_query_settings(request):
|
|||||||
if "grid_size" in request.GET:
|
if "grid_size" in request.GET:
|
||||||
grid_size = request.GET.getlist('grid_size')
|
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
|
node_label_items = ()
|
||||||
|
if "node_label_items" in request.GET:
|
||||||
|
node_label_items = request.GET.getlist('node_label_items')
|
||||||
|
|
||||||
|
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, node_label_items
|
||||||
|
|
||||||
class LinePattern():
|
class LinePattern():
|
||||||
wireless = [2, 10, 2, 10]
|
wireless = [2, 10, 2, 10]
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ from netbox_topology_views.utils import (
|
|||||||
IMAGE_FILETYPES
|
IMAGE_FILETYPES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from netbox_topology_views.choices import NodeLabelItems
|
||||||
|
|
||||||
def get_image_for_entity(entity: Union[Device, Circuit, PowerPanel, PowerFeed]):
|
def get_image_for_entity(entity: Union[Device, Circuit, PowerPanel, PowerFeed]):
|
||||||
is_device = isinstance(entity, Device)
|
is_device = isinstance(entity, Device)
|
||||||
@@ -97,7 +97,10 @@ def get_image_for_entity(entity: Union[Device, Circuit, PowerPanel, PowerFeed]):
|
|||||||
|
|
||||||
|
|
||||||
def create_node(
|
def create_node(
|
||||||
device: Union[Device, Circuit, PowerPanel, PowerFeed], save_coords: bool, group_id="default"
|
device: Union[Device, Circuit, PowerPanel, PowerFeed],
|
||||||
|
save_coords: bool,
|
||||||
|
node_label_items: list,
|
||||||
|
group_id="default"
|
||||||
):
|
):
|
||||||
node = {}
|
node = {}
|
||||||
node_content = ""
|
node_content = ""
|
||||||
@@ -229,7 +232,22 @@ def create_node(
|
|||||||
dev_title = "<table><tbody> %s</tbody></table>" % (node_content)
|
dev_title = "<table><tbody> %s</tbody></table>" % (node_content)
|
||||||
node["title"] = dev_title
|
node["title"] = dev_title
|
||||||
node["name"] = dev_name
|
node["name"] = dev_name
|
||||||
node["label"] = dev_name
|
|
||||||
|
# Create a list of possible label items. Omit None types
|
||||||
|
label_mapping = {
|
||||||
|
NodeLabelItems.DEVICE_NAME: dev_name,
|
||||||
|
NodeLabelItems.PRIMARY_IPV4: getattr(device, 'primary_ip4', None),
|
||||||
|
NodeLabelItems.PRIMARY_IPV6: getattr(device, 'primary_ip6', None),
|
||||||
|
NodeLabelItems.OUT_OF_BAND_IP: getattr(device, 'oob_ip', None),
|
||||||
|
}
|
||||||
|
|
||||||
|
label_items = []
|
||||||
|
for item in node_label_items:
|
||||||
|
if label_mapping[item] is not None:
|
||||||
|
label_items.append(str(label_mapping[item]))
|
||||||
|
node_label = '\n'.join(label_items)
|
||||||
|
|
||||||
|
node["label"] = node_label
|
||||||
node["shape"] = "image"
|
node["shape"] = "image"
|
||||||
node["href"] = device.get_absolute_url()
|
node["href"] = device.get_absolute_url()
|
||||||
node["image"] = get_image_for_entity(device)
|
node["image"] = get_image_for_entity(device)
|
||||||
@@ -350,6 +368,7 @@ def get_topology_data(
|
|||||||
group_id,
|
group_id,
|
||||||
straight_cables: bool,
|
straight_cables: bool,
|
||||||
grid_size: list,
|
grid_size: list,
|
||||||
|
node_label_items: list,
|
||||||
):
|
):
|
||||||
|
|
||||||
supported_termination_types = []
|
supported_termination_types = []
|
||||||
@@ -402,8 +421,8 @@ def get_topology_data(
|
|||||||
|
|
||||||
if show_circuit:
|
if show_circuit:
|
||||||
circuit_terminations = CircuitTermination.objects.filter(
|
circuit_terminations = CircuitTermination.objects.filter(
|
||||||
Q(site_id__in=site_ids) | Q(provider_network__isnull=False)
|
Q(_site_id__in=site_ids) | Q(_provider_network__isnull=False)
|
||||||
).prefetch_related("provider_network", "circuit")
|
)
|
||||||
for circuit_termination in circuit_terminations:
|
for circuit_termination in circuit_terminations:
|
||||||
circuit_termination: CircuitTermination
|
circuit_termination: CircuitTermination
|
||||||
if (
|
if (
|
||||||
@@ -472,7 +491,7 @@ def get_topology_data(
|
|||||||
] = circuit_termination.circuit
|
] = circuit_termination.circuit
|
||||||
|
|
||||||
for d in nodes_circuits.values():
|
for d in nodes_circuits.values():
|
||||||
nodes.append(create_node(d, save_coords, group_id))
|
nodes.append(create_node(d, save_coords, node_label_items, group_id))
|
||||||
|
|
||||||
if show_power:
|
if show_power:
|
||||||
power_panels_ids = PowerPanel.objects.filter(
|
power_panels_ids = PowerPanel.objects.filter(
|
||||||
@@ -523,10 +542,10 @@ def get_topology_data(
|
|||||||
cable_ids[power_feed.cable_id][power_feed.cable_end] = termination_b
|
cable_ids[power_feed.cable_id][power_feed.cable_end] = termination_b
|
||||||
|
|
||||||
for d in nodes_powerfeed.values():
|
for d in nodes_powerfeed.values():
|
||||||
nodes.append(create_node(d, save_coords, group_id))
|
nodes.append(create_node(d, save_coords, node_label_items ,group_id))
|
||||||
|
|
||||||
for d in nodes_powerpanel.values():
|
for d in nodes_powerpanel.values():
|
||||||
nodes.append(create_node(d, save_coords, group_id))
|
nodes.append(create_node(d, save_coords, node_label_items, group_id))
|
||||||
|
|
||||||
if show_logical_connections:
|
if show_logical_connections:
|
||||||
interfaces = Interface.objects.filter(
|
interfaces = Interface.objects.filter(
|
||||||
@@ -698,7 +717,7 @@ def get_topology_data(
|
|||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
for d in nodes_devices.values():
|
for d in nodes_devices.values():
|
||||||
nodes.append(create_node(d, save_coords, group_id))
|
nodes.append(create_node(d, save_coords, node_label_items, group_id))
|
||||||
|
|
||||||
results["nodes"] = nodes
|
results["nodes"] = nodes
|
||||||
results["edges"] = edges
|
results["edges"] = edges
|
||||||
@@ -729,7 +748,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
|
|
||||||
if request.GET:
|
if request.GET:
|
||||||
|
|
||||||
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)
|
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, node_label_items = 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"] != '':
|
||||||
@@ -739,6 +758,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
|
|
||||||
if ignore_cable_type == () and 'ignore_cable_type' in saved_filter_params: ignore_cable_type = saved_filter_params['ignore_cable_type']
|
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_unconnected == False and 'show_unconnected' in saved_filter_params: show_unconnected = saved_filter_params['show_unconnected']
|
||||||
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']
|
||||||
if show_logical_connections == False and 'show_logical_connections' in saved_filter_params: show_logical_connections = saved_filter_params['show_logical_connections']
|
if show_logical_connections == False and 'show_logical_connections' in saved_filter_params: show_logical_connections = saved_filter_params['show_logical_connections']
|
||||||
@@ -752,6 +772,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
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 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']
|
if grid_size == 0 and 'grid_size' in saved_filter_params: grid_size = saved_filter_params['grid_size']
|
||||||
|
if node_label_items == () and 'node_label_items' in saved_filter_params: node_label_items = saved_filter_params['node_label_items']
|
||||||
except SavedFilter.DoesNotExist: # filter_id not found
|
except SavedFilter.DoesNotExist: # filter_id not found
|
||||||
pass
|
pass
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
@@ -786,6 +807,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
straight_cables=straight_cables,
|
straight_cables=straight_cables,
|
||||||
grid_size=grid_size,
|
grid_size=grid_size,
|
||||||
|
node_label_items=node_label_items,
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -815,6 +837,10 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
if individualOptions.group_virtualchassis: q['group_virtualchassis'] = "True"
|
if individualOptions.group_virtualchassis: q['group_virtualchassis'] = "True"
|
||||||
if individualOptions.straight_cables: q['straight_cables'] = "True"
|
if individualOptions.straight_cables: q['straight_cables'] = "True"
|
||||||
if individualOptions.grid_size: q['grid_size'] = individualOptions.grid_size
|
if individualOptions.grid_size: q['grid_size'] = individualOptions.grid_size
|
||||||
|
node_label_items = IndividualOptions.objects.get(id=individualOptions.id).node_label_items.translate({ord(i): None for i in '[]\''}).split(', ')
|
||||||
|
if node_label_items == ['']: node_label_items = []
|
||||||
|
q.setlist("node_label_items", node_label_items)
|
||||||
|
|
||||||
if individualOptions.draw_default_layout:
|
if individualOptions.draw_default_layout:
|
||||||
q['draw_init'] = "True"
|
q['draw_init'] = "True"
|
||||||
else:
|
else:
|
||||||
@@ -1176,6 +1202,7 @@ class TopologyIndividualOptionsView(PermissionRequiredMixin, View):
|
|||||||
'draw_default_layout': queryset.draw_default_layout,
|
'draw_default_layout': queryset.draw_default_layout,
|
||||||
'straight_cables': queryset.straight_cables,
|
'straight_cables': queryset.straight_cables,
|
||||||
'grid_size': queryset.grid_size,
|
'grid_size': queryset.grid_size,
|
||||||
|
'node_label_items': tuple(queryset.node_label_items.translate({ord(i): None for i in '[]\''}).split(', ')),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ long_description = readme.read_text()
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="netbox-topology-views",
|
name="netbox-topology-views",
|
||||||
version="4.1.0",
|
version="4.2.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