Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b53ceed2ae | ||
|
|
064751f727 | ||
|
|
deedee78bd | ||
|
|
683e125cd5 |
@@ -8,7 +8,7 @@ Support to filter on name, site, tag and device role.
|
|||||||
|
|
||||||
## Preview
|
## Preview
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -35,8 +35,7 @@ Then run `python3 manage.py collectstatic --no-input`
|
|||||||
|
|
||||||
| netbox version | netbox-topology-views version |
|
| netbox version | netbox-topology-views version |
|
||||||
| ------------- |-------------|
|
| ------------- |-------------|
|
||||||
| >= 3.2.0 | >= v1.1.0 |
|
| >= 3.1.8 | >= v1.0.0a1 |
|
||||||
| >= 3.1.8 | >= v1.0.0 |
|
|
||||||
| >= 2.11.1 | >= v0.5.3 |
|
| >= 2.11.1 | >= v0.5.3 |
|
||||||
| >= 2.10.0 | >= v0.5.0 |
|
| >= 2.10.0 | >= v0.5.0 |
|
||||||
| < 2.10.0 | =< v0.4.10 |
|
| < 2.10.0 | =< v0.4.10 |
|
||||||
@@ -81,7 +80,7 @@ If you add your own image you also need to add the slug to the `device_img` sett
|
|||||||
|
|
||||||
## Use
|
## Use
|
||||||
|
|
||||||
Go to the plugins tab in the navbar and click topology or go to `$NETBOX_URL/plugins/netbox_topology_views/` to view your topologies
|
Go to the plugins tab in the navbar and click topology or go to `$NETBOX_URL/plugins/topology-views/` to view your topologies
|
||||||
|
|
||||||
### Update
|
### Update
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB |
@@ -4,7 +4,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 = '1.2.0'
|
version = '1.0.0-alpha.2'
|
||||||
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'
|
||||||
@@ -12,7 +12,7 @@ class TopologyViewsConfig(PluginConfig):
|
|||||||
default_settings = {
|
default_settings = {
|
||||||
'preselected_device_roles': ['Firewall', 'Router', 'Distribution Switch', 'Core Switch', 'Internal Switch', 'Access Switch', 'Server', 'Storage', 'Backup', 'Wireless AP'],
|
'preselected_device_roles': ['Firewall', 'Router', 'Distribution Switch', 'Core Switch', 'Internal Switch', 'Access Switch', 'Server', 'Storage', 'Backup', 'Wireless AP'],
|
||||||
'ignore_cable_type': ['power outlet','power port'],
|
'ignore_cable_type': ['power outlet','power port'],
|
||||||
'device_img': ['access-switch', 'core-switch', 'firewall', 'router', 'distribution-switch', 'backup', 'storage', 'wan-network', 'wireless-ap', 'server', 'internal-switch', 'isp-cpe-material', 'non-racked-devices', 'power-units'],
|
'device_img': ['access-switch', 'core-switch', 'firewall', 'router', 'distribution-switch', 'backup', 'storage,wan-network', 'wireless-ap', 'server', 'internal-switch', 'isp-cpe-material', 'non-racked-devices', 'power-units'],
|
||||||
'allow_coordinates_saving': False,
|
'allow_coordinates_saving': False,
|
||||||
'preselected_tags' : [],
|
'preselected_tags' : [],
|
||||||
'enable_circuit_terminations': False,
|
'enable_circuit_terminations': False,
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
#no models yet
|
||||||
@@ -3,6 +3,18 @@ from rest_framework.serializers import ModelSerializer
|
|||||||
from dcim.models import DeviceRole, Device
|
from dcim.models import DeviceRole, Device
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
|
||||||
|
class PreDeviceRoleSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = DeviceRole
|
||||||
|
fields = ('id', 'name')
|
||||||
|
|
||||||
|
class PreTagSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Tag
|
||||||
|
fields = ('id', 'name')
|
||||||
|
|
||||||
|
|
||||||
class TopologyDummySerializer(ModelSerializer):
|
class TopologyDummySerializer(ModelSerializer):
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ from . import views
|
|||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
|
|
||||||
|
router.register('preselectdeviceroles', views.PreSelectDeviceRolesViewSet)
|
||||||
|
router.register('preselecttags', views.PreSelectTagsViewSet)
|
||||||
|
router.register('search', views.SearchViewSet, basename='search')
|
||||||
router.register('save-coords', views.SaveCoordsViewSet, basename='save_coords')
|
router.register('save-coords', views.SaveCoordsViewSet, basename='save_coords')
|
||||||
|
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
@@ -4,7 +4,7 @@ from rest_framework.response import Response
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
|
|
||||||
from .serializers import TopologyDummySerializer
|
from .serializers import PreDeviceRoleSerializer, TopologyDummySerializer, PreTagSerializer
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Device, Cable
|
from dcim.models import DeviceRole, Device, Cable
|
||||||
@@ -12,10 +12,26 @@ from circuits.models import Circuit
|
|||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
|
||||||
|
|
||||||
|
ignore_cable_type_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["ignore_cable_type"]
|
||||||
|
ignore_cable_type = ignore_cable_type_raw.split(",")
|
||||||
|
|
||||||
|
preselected_device_roles_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_device_roles"]
|
||||||
|
preselected_device_roles = preselected_device_roles_raw.split(",")
|
||||||
|
|
||||||
|
preselected_tags_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_tags"]
|
||||||
|
preselected_tags = preselected_tags_raw.split(",")
|
||||||
|
|
||||||
class TopologyViewsRootView(APIRootView):
|
class TopologyViewsRootView(APIRootView):
|
||||||
def get_view_name(self):
|
def get_view_name(self):
|
||||||
return 'TopologyViews'
|
return 'TopologyViews'
|
||||||
|
|
||||||
|
class PreSelectDeviceRolesViewSet(ReadOnlyModelViewSet):
|
||||||
|
queryset = DeviceRole.objects.filter(name__in=preselected_device_roles)
|
||||||
|
serializer_class = PreDeviceRoleSerializer
|
||||||
|
|
||||||
|
class PreSelectTagsViewSet(ReadOnlyModelViewSet):
|
||||||
|
queryset = Tag.objects.filter(name__in=preselected_tags)
|
||||||
|
serializer_class = PreTagSerializer
|
||||||
|
|
||||||
class SaveCoordsViewSet(ReadOnlyModelViewSet):
|
class SaveCoordsViewSet(ReadOnlyModelViewSet):
|
||||||
queryset = Device.objects.all()
|
queryset = Device.objects.all()
|
||||||
@@ -58,3 +74,200 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
|
|||||||
results["status"] = "not allowed to save coords"
|
results["status"] = "not allowed to save coords"
|
||||||
return Response(results, status=500)
|
return Response(results, status=500)
|
||||||
|
|
||||||
|
class SearchViewSet(ReadOnlyModelViewSet):
|
||||||
|
queryset = Device.objects.all()
|
||||||
|
serializer_class = TopologyDummySerializer
|
||||||
|
|
||||||
|
def _filter(self, site, role, name, tags, region, location):
|
||||||
|
filter_devices = Device.objects.all()
|
||||||
|
if name is not None:
|
||||||
|
filter_devices = filter_devices.filter(name__contains=name)
|
||||||
|
if site is not None:
|
||||||
|
filter_devices = filter_devices.filter(site__id__in=site)
|
||||||
|
if role is not None:
|
||||||
|
filter_devices = filter_devices.filter(device_role__id__in=role)
|
||||||
|
if tags is not None:
|
||||||
|
filter_devices = filter_devices.filter(tags__id__in=tags)
|
||||||
|
if region is not None:
|
||||||
|
filter_devices = filter_devices.filter(site__region__id__in=region)
|
||||||
|
if location is not None:
|
||||||
|
filter_devices = filter_devices.filter(location__id__in=location)
|
||||||
|
return filter_devices
|
||||||
|
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'])
|
||||||
|
def search(self, request):
|
||||||
|
name = request.query_params.get('name', None)
|
||||||
|
if name == "":
|
||||||
|
name = None
|
||||||
|
|
||||||
|
sites = request.query_params.getlist('sites[]', None)
|
||||||
|
if sites == []:
|
||||||
|
sites = None
|
||||||
|
|
||||||
|
devicerole = request.query_params.getlist('devicerole[]', None)
|
||||||
|
if devicerole == []:
|
||||||
|
devicerole = None
|
||||||
|
|
||||||
|
tags = request.query_params.getlist('tags[]', None)
|
||||||
|
if tags == []:
|
||||||
|
tags = None
|
||||||
|
|
||||||
|
regions = request.query_params.getlist('regions[]', None)
|
||||||
|
if regions == []:
|
||||||
|
regions = None
|
||||||
|
|
||||||
|
locations = request.query_params.getlist('locations[]', None)
|
||||||
|
if locations == []:
|
||||||
|
locations = None
|
||||||
|
|
||||||
|
hide_unconnected = request.query_params.get('hide_unconnected', None)
|
||||||
|
if hide_unconnected == "":
|
||||||
|
hide_unconnected = None
|
||||||
|
|
||||||
|
devices = self._filter(sites, devicerole, name, tags, regions, locations)
|
||||||
|
|
||||||
|
nodes = []
|
||||||
|
edges = []
|
||||||
|
edge_ids = 0
|
||||||
|
cable_ids = []
|
||||||
|
circuit_ids = []
|
||||||
|
for device in devices:
|
||||||
|
cables = device.get_cables()
|
||||||
|
if cables.exists():
|
||||||
|
device_has_connections = True
|
||||||
|
for cable in cables:
|
||||||
|
if cable.termination_a_type.name != "circuit termination" and cable.termination_b_type.name != "circuit termination":
|
||||||
|
if cable.id not in cable_ids:
|
||||||
|
if cable.termination_a_type.name not in ignore_cable_type and cable.termination_b_type.name not in ignore_cable_type:
|
||||||
|
cable_ids.append(cable.id)
|
||||||
|
edge_ids += 1
|
||||||
|
|
||||||
|
cable_a_dev_name = cable.termination_a.device.name
|
||||||
|
if cable_a_dev_name is None:
|
||||||
|
cable_a_dev_name = "device A name unknown"
|
||||||
|
cable_a_name = cable.termination_a.name
|
||||||
|
if cable_a_name is None:
|
||||||
|
cable_a_name = "cable A name unknown"
|
||||||
|
cable_b_dev_name = cable.termination_b.device.name
|
||||||
|
if cable_b_dev_name is None:
|
||||||
|
cable_b_dev_name = "device B name unknown"
|
||||||
|
cable_b_name = cable.termination_b.name
|
||||||
|
if cable_b_name is None:
|
||||||
|
cable_b_name = "cable B name unknown"
|
||||||
|
|
||||||
|
edge = {}
|
||||||
|
edge["id"] = edge_ids
|
||||||
|
edge["from"] = cable.termination_a.device.id
|
||||||
|
edge["to"] = cable.termination_b.device.id
|
||||||
|
edge["title"] = "Cable between <br> " + cable_a_dev_name + " [" + cable_a_name + "]<br>" + cable_b_dev_name + " [" + cable_b_name + "]"
|
||||||
|
if cable.color != "":
|
||||||
|
edge["color"] = "#" + cable.color
|
||||||
|
edges.append(edge)
|
||||||
|
else:
|
||||||
|
if cable.termination_a_type.name == "circuit termination":
|
||||||
|
if settings.PLUGINS_CONFIG["netbox_topology_views"]["enable_circuit_terminations"]:
|
||||||
|
if cable.termination_a.circuit.id not in circuit_ids:
|
||||||
|
circuit_ids.append(cable.termination_a.circuit.id)
|
||||||
|
edge_ids += 1
|
||||||
|
|
||||||
|
cable_b_dev_name = cable.termination_b.device.name
|
||||||
|
if cable_b_dev_name is None:
|
||||||
|
cable_b_dev_name = "device B name unknown"
|
||||||
|
cable_b_name = cable.termination_b.name
|
||||||
|
if cable_b_name is None:
|
||||||
|
cable_b_name = "cable B name unknown"
|
||||||
|
|
||||||
|
edge = {}
|
||||||
|
edge["id"] = edge_ids
|
||||||
|
edge["to"] = cable.termination_b.device.id
|
||||||
|
edge["dashes"] = True
|
||||||
|
title = ""
|
||||||
|
|
||||||
|
title += "Circuit provider: " + cable.termination_a.circuit.provider.name + "<br>"
|
||||||
|
title += "Termination between <br>"
|
||||||
|
title += cable_b_dev_name + " [" + cable_b_name + "]<br>"
|
||||||
|
# To Many if's
|
||||||
|
if cable.termination_a.circuit.termination_a is not None:
|
||||||
|
if cable.termination_a.circuit.termination_a.cable is not None:
|
||||||
|
if cable.termination_a.circuit.termination_a.cable.id != cable.id:
|
||||||
|
if cable.termination_a.circuit.termination_a.cable.termination_b is not None:
|
||||||
|
if cable.termination_a.circuit.termination_a.cable.termination_b.device is not None:
|
||||||
|
edge["from"] = cable.termination_a.circuit.termination_a.cable.termination_b.device.id
|
||||||
|
|
||||||
|
cable_a_dev_name = cable.termination_a.circuit.termination_a.cable.termination_b.device.name
|
||||||
|
if cable_a_dev_name is None:
|
||||||
|
cable_a_dev_name = "device B name unknown"
|
||||||
|
cable_b_name = cable.termination_a.circuit.termination_a.cable.termination_b.name
|
||||||
|
if cable_a_name is None:
|
||||||
|
cable_a_name = "cable B name unknown"
|
||||||
|
title += cable_a_dev_name + " [" + cable_a_name + "]<br>"
|
||||||
|
edge["title"] = title
|
||||||
|
edges.append(edge)
|
||||||
|
# To Many if's
|
||||||
|
if cable.termination_a.circuit.termination_z is not None:
|
||||||
|
if cable.termination_a.circuit.termination_z.cable is not None:
|
||||||
|
if cable.termination_a.circuit.termination_z.cable.id != cable.id:
|
||||||
|
if cable.termination_a.circuit.termination_z.cable.termination_b is not None:
|
||||||
|
if cable.termination_a.circuit.termination_z.cable.termination_b.device is not None:
|
||||||
|
edge["from"] = cable.termination_a.circuit.termination_z.cable.termination_b.device.id
|
||||||
|
|
||||||
|
cable_a_dev_name = cable.termination_a.circuit.termination_z.cable.termination_b.device.name
|
||||||
|
if cable_a_dev_name is None:
|
||||||
|
cable_a_dev_name = "device B name unknown"
|
||||||
|
cable_a_name = cable.termination_a.circuit.termination_z.cable.termination_b.name
|
||||||
|
if cable_a_name is None:
|
||||||
|
cable_a_name = "cable B name unknown"
|
||||||
|
title += cable_a_dev_name + " [" + cable_a_name + "]<br>"
|
||||||
|
edge["title"] = title
|
||||||
|
edges.append(edge)
|
||||||
|
else:
|
||||||
|
device_has_connections = False
|
||||||
|
|
||||||
|
if hide_unconnected == 'false' or (hide_unconnected == 'true' and device_has_connections is True):
|
||||||
|
dev_name = device.name
|
||||||
|
if dev_name is None:
|
||||||
|
dev_name = "device name unknown"
|
||||||
|
|
||||||
|
node_content = ""
|
||||||
|
|
||||||
|
if device.device_type.display_name is not None:
|
||||||
|
node_content += "<tr><th>Type: </th><td>" + device.device_type.display_name + "</td></tr>"
|
||||||
|
if device.device_role.name is not None:
|
||||||
|
node_content += "<tr><th>Role: </th><td>" + device.device_role.name + "</td></tr>"
|
||||||
|
if device.serial != "":
|
||||||
|
node_content += "<tr><th>Serial: </th><td>" + device.serial + "</td></tr>"
|
||||||
|
if device.primary_ip is not None:
|
||||||
|
node_content += "<tr><th>IP Address: </th><td>" + str(device.primary_ip.address) + "</td></tr>"
|
||||||
|
|
||||||
|
dev_title = "<table> %s </table>" % (node_content)
|
||||||
|
|
||||||
|
node = {}
|
||||||
|
node["id"] = device.id
|
||||||
|
node["name"] = dev_name
|
||||||
|
node["label"] = dev_name
|
||||||
|
node["title"] = dev_title
|
||||||
|
node["shape"] = 'image'
|
||||||
|
if device.device_role.slug in settings.PLUGINS_CONFIG["netbox_topology_views"]["device_img"]:
|
||||||
|
node["image"] = '../../static/netbox_topology_views/img/' + device.device_role.slug + ".png"
|
||||||
|
else:
|
||||||
|
node["image"] = "../../static/netbox_topology_views/img/role-unknown.png"
|
||||||
|
|
||||||
|
if device.device_role.color != "":
|
||||||
|
node["color.border"] = "#" + device.device_role.color
|
||||||
|
|
||||||
|
if "coordinates" in device.custom_field_data:
|
||||||
|
if device.custom_field_data["coordinates"] != "":
|
||||||
|
cords = device.custom_field_data["coordinates"].split(";")
|
||||||
|
node["x"] = int(cords[0])
|
||||||
|
node["y"] = int(cords[1])
|
||||||
|
node["physics"] = False
|
||||||
|
|
||||||
|
nodes.append(node)
|
||||||
|
|
||||||
|
results = {}
|
||||||
|
results["nodes"] = nodes
|
||||||
|
results["edges"] = edges
|
||||||
|
|
||||||
|
return Response(results)
|
||||||
|
|
||||||
@@ -1,21 +1,16 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
|
from extras.filters import TagFilter
|
||||||
|
from dcim.models import Device, DeviceRole, Region, Site, Location
|
||||||
|
from utilities.filters import TreeNodeMultipleChoiceFilter
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from dcim.models import Device, DeviceRole, Region, Site, Location
|
class DeviceFilterSet(django_filters.FilterSet):
|
||||||
|
|
||||||
from netbox.filtersets import NetBoxModelFilterSet
|
|
||||||
|
|
||||||
from tenancy.models import TenantGroup, Tenant
|
|
||||||
from tenancy.filtersets import TenancyFilterSet
|
|
||||||
|
|
||||||
from utilities.filters import TreeNodeMultipleChoiceFilter
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label='Search',
|
||||||
)
|
)
|
||||||
|
tag = TagFilter()
|
||||||
|
|
||||||
device_role_id = django_filters.ModelMultipleChoiceFilter(
|
device_role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device_role_id',
|
field_name='device_role_id',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
@@ -40,7 +35,7 @@ class DeviceFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = ['id', 'name']
|
fields = ['id', 'name', ]
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
"""Perform the filtered search."""
|
"""Perform the filtered search."""
|
||||||
@@ -49,4 +44,4 @@ class DeviceFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|||||||
qs_filter = (
|
qs_filter = (
|
||||||
Q(name__icontains=value)
|
Q(name__icontains=value)
|
||||||
)
|
)
|
||||||
return queryset.filter(qs_filter)
|
return queryset.filter(qs_filter)
|
||||||
@@ -1,30 +1,21 @@
|
|||||||
import imp
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from extras.models import Tag
|
||||||
from dcim.models import Device, Site, Region, DeviceRole, Location
|
from dcim.models import Device, Site, Region, DeviceRole, Location
|
||||||
|
from extras.forms import CustomFieldModelFilterForm
|
||||||
|
|
||||||
from django import forms
|
from utilities.forms import (TagFilterField, DynamicModelMultipleChoiceField, BootstrapMixin)
|
||||||
from tenancy.models import TenantGroup, Tenant
|
|
||||||
from tenancy.forms import TenancyFilterForm
|
|
||||||
from django.conf import settings
|
|
||||||
from netbox.forms import NetBoxModelFilterSetForm
|
|
||||||
from utilities.forms import (TagFilterField, DynamicModelMultipleChoiceField)
|
|
||||||
|
|
||||||
allow_coordinates_saving = settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"]
|
class DeviceFilterForm(CustomFieldModelFilterForm):
|
||||||
|
|
||||||
|
|
||||||
class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
||||||
model = Device
|
model = Device
|
||||||
fieldsets = (
|
field_groups = [
|
||||||
(None, ('q', 'hide_unconnected', 'save_coords')),
|
['q'],
|
||||||
(None, ('tenant_group_id', 'tenant_id')),
|
['region_id', 'site_id', 'location_id'],
|
||||||
(None, ('region_id', 'site_id', 'location_id')),
|
['device_role_id'],
|
||||||
(None, ('device_role_id',)),
|
['tag'],
|
||||||
(None, ('tag',)),
|
]
|
||||||
)
|
|
||||||
|
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@@ -53,14 +44,5 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
},
|
},
|
||||||
label=_('Location')
|
label=_('Location')
|
||||||
)
|
)
|
||||||
hide_unconnected = forms.BooleanField(
|
|
||||||
label=_("Hide Unconnected"),
|
|
||||||
required=False,
|
|
||||||
initial=False)
|
|
||||||
|
|
||||||
save_coords = forms.BooleanField(
|
|
||||||
label=_("Save Coordinates"),
|
|
||||||
required=False,
|
|
||||||
disabled=(not allow_coordinates_saving),
|
|
||||||
initial=False)
|
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# no modeles yet
|
||||||
@@ -4,6 +4,14 @@ from utilities.choices import ButtonColorChoices
|
|||||||
menu_items = (
|
menu_items = (
|
||||||
PluginMenuItem(
|
PluginMenuItem(
|
||||||
link='plugins:netbox_topology_views:home',
|
link='plugins:netbox_topology_views:home',
|
||||||
link_text='Topology'
|
link_text='Topology',
|
||||||
|
buttons=(
|
||||||
|
PluginMenuButton(
|
||||||
|
link='plugins:netbox_topology_views:home',
|
||||||
|
title='Topology View',
|
||||||
|
icon_class='mdi mdi-plus-thick',
|
||||||
|
permissions=[],
|
||||||
|
),
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -1 +1 @@
|
|||||||
#visgraph{height:70vh}html[data-netbox-color-mode=dark] #visgraph{background-color:#212529}
|
#visgraph{height:70vh}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
var graph=null,container=null,downloadButton=null,MIME_TYPE="image/png",canvas=null,csrftoken=null,nodes=new vis.DataSet,edges=new vis.DataSet,options={interaction:{hover:!0,hoverConnectedEdges:!0,multiselect:!1},nodes:{shape:"image",brokenImage:"../../static/netbox_topology_views/img/role-unknown.png",size:35,font:{multi:"md",face:"helvetica"}},edges:{length:100,width:2,font:{face:"helvetica"},shadow:{enabled:!0}},physics:{solver:"forceAtlas2Based"}},selected_regions=[],selected_sites=[],coord_save_checkbox=null,htmlElement=null;function getCookie(e){var t=null;if(document.cookie&&""!==document.cookie)for(var o=document.cookie.split(";"),n=0;n<o.length;n++){var d=o[n].trim();if(d.substring(0,e.length+1)===e+"="){t=decodeURIComponent(d.substring(e.length+1));break}}return t}function htmlTitle(e){return(container=document.createElement("div")).innerHTML=e,container}function addEdge(e){e.title=htmlTitle(e.title),edges.add(e)}function addNode(e){e.title=htmlTitle(e.title),nodes.add(e)}function iniPlotboxIndex(){document.addEventListener("DOMContentLoaded",function(){csrftoken=getCookie("csrftoken"),container=document.getElementById("visgraph"),htmlElement=document.getElementsByTagName("html")[0],downloadButton=document.getElementById("btnDownloadImage"),handleLoadData(),btnFullView=document.getElementById("btnFullView"),coord_save_checkbox=document.getElementById("id_save_coords")},!1)}function performGraphDownload(){var e=document.createElement("a"),t=canvas.toDataURL(MIME_TYPE);e.href=t,e.download="topology",document.body.appendChild(e),e.click(),document.body.removeChild(e)}function handleLoadData(){null!==topology_data&&("dark"==htmlElement.dataset.netboxColorMode&&(options.nodes.font.color="#fff"),graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),topology_data.edges.forEach(addEdge),topology_data.nodes.forEach(addNode),graph.fit(),canvas=document.getElementById("visgraph").getElementsByTagName("canvas")[0],downloadButton.onclick=function(e){return performGraphDownload(),!1},graph.on("dragEnd",function(e){if(dragged=this.getPositions(e.nodes),coord_save_checkbox.checked&&0!==Object.keys(dragged).length)for(dragged_device in dragged){var t=dragged_device,o=new XMLHttpRequest;o.open("PATCH","/api/plugins/netbox_topology_views/save-coords/save_coords/"),o.setRequestHeader("X-CSRFToken",csrftoken),o.setRequestHeader("Accept","application/json"),o.setRequestHeader("Content-Type","application/json"),o.onreadystatechange=function(){4===o.readyState&&(console.log(o.status),console.log(o.responseText))};var n=JSON.stringify({node_id:t,x:dragged[t].x,y:dragged[t].y});o.send(n)}}))}
|
var graph=null,container=null;function iniPlotboxFull(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("fullvisgraph"),startRender()},!1)}function startRender(){var e=location.search;$.ajax({type:"GET",url:"../../api/plugins/topology-views/search/search/"+e,contentType:"application/json; charset=utf-8",success:function(e,n,t){graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,n){nodes.add(n)}),$.each(e.edges,function(e,n){edges.add(n)}),graph.fit(),canvas=document.getElementById("fullvisgraph").getElementsByTagName("canvas")[0]}})}graph=null,container=null;var downloadButton=null,MIME_TYPE="image/png",canvas=null,csrftoken=null,nodes=new vis.DataSet,edges=new vis.DataSet,options={interaction:{hover:!0,hoverConnectedEdges:!0,multiselect:!1},nodes:{shape:"image",brokenImage:"../../static/netbox_topology_views/img/role-unknown.png",size:35,font:{multi:"md",face:"helvetica"}},edges:{length:100,width:2,font:{face:"helvetica"}},physics:{solver:"forceAtlas2Based"}},selected_regions=[],selected_sites=[];function addEdge(e){edges.add(e)}function addNode(e){nodes.add(e)}function iniPlotboxIndex(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("visgraph"),handleLoadData(),downloadButton=document.getElementById("btnDownloadImage"),btnFullView=document.getElementById("btnFullView")},!1)}function handleLoadData(){graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),topology_data.edges.forEach(addEdge),topology_data.nodes.forEach(addNode),graph.fit(),canvas=document.getElementById("visgraph").getElementsByTagName("canvas")[0],graph.on("afterDrawing",function(){var e=canvas.toDataURL(MIME_TYPE);downloadButton.href=e,downloadButton.download="topology"})}
|
||||||
File diff suppressed because one or more lines are too long
@@ -2,6 +2,3 @@
|
|||||||
height: 70vh;
|
height: 70vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-netbox-color-mode=dark] #visgraph {
|
|
||||||
background-color: #212529;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
var graph = null;
|
||||||
|
var container = null;
|
||||||
|
|
||||||
|
function iniPlotboxFull() {
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
container = document.getElementById('fullvisgraph');
|
||||||
|
startRender();
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startRender() {
|
||||||
|
var url = location.search;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: "../../api/plugins/topology-views/search/search/" + url,
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
success: function (data_result, status, xhr) {
|
||||||
|
graph = null;
|
||||||
|
nodes = new vis.DataSet();
|
||||||
|
edges = new vis.DataSet();
|
||||||
|
graph = new vis.Network(container, { nodes: nodes, edges: edges }, options);
|
||||||
|
$.each(data_result["nodes"], function (index, device) {
|
||||||
|
nodes.add(device);
|
||||||
|
});
|
||||||
|
$.each(data_result["edges"], function (index, edge) {
|
||||||
|
edges.add(edge);
|
||||||
|
});
|
||||||
|
graph.fit();
|
||||||
|
canvas = document.getElementById('fullvisgraph').getElementsByTagName('canvas')[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -27,9 +27,6 @@ var options = {
|
|||||||
font: {
|
font: {
|
||||||
face: 'helvetica',
|
face: 'helvetica',
|
||||||
},
|
},
|
||||||
shadow: {
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
physics: {
|
physics: {
|
||||||
solver: 'forceAtlas2Based'
|
solver: 'forceAtlas2Based'
|
||||||
@@ -37,71 +34,25 @@ var options = {
|
|||||||
};
|
};
|
||||||
var selected_regions = [];
|
var selected_regions = [];
|
||||||
var selected_sites = [];
|
var selected_sites = [];
|
||||||
var coord_save_checkbox = null;
|
|
||||||
var htmlElement = null;
|
|
||||||
|
|
||||||
function getCookie(name) {
|
|
||||||
var cookieValue = null;
|
|
||||||
if (document.cookie && document.cookie !== '') {
|
|
||||||
var cookies = document.cookie.split(';');
|
|
||||||
for (var i = 0; i < cookies.length; i++) {
|
|
||||||
var cookie = cookies[i].trim();
|
|
||||||
// Does this cookie string begin with the name we want?
|
|
||||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
||||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cookieValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function htmlTitle(html) {
|
|
||||||
container = document.createElement("div");
|
|
||||||
container.innerHTML = html;
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEdge(item) {
|
function addEdge(item) {
|
||||||
item.title = htmlTitle( item.title );
|
|
||||||
edges.add(item);
|
edges.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNode(item) {
|
function addNode(item) {
|
||||||
item.title = htmlTitle( item.title );
|
|
||||||
nodes.add(item);
|
nodes.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
function iniPlotboxIndex() {
|
function iniPlotboxIndex() {
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
csrftoken = getCookie('csrftoken');
|
|
||||||
container = document.getElementById('visgraph');
|
container = document.getElementById('visgraph');
|
||||||
htmlElement = document.getElementsByTagName("html")[0];
|
|
||||||
downloadButton = document.getElementById('btnDownloadImage');
|
|
||||||
handleLoadData();
|
handleLoadData();
|
||||||
|
downloadButton = document.getElementById('btnDownloadImage');
|
||||||
btnFullView = document.getElementById('btnFullView');
|
btnFullView = document.getElementById('btnFullView');
|
||||||
coord_save_checkbox = document.getElementById('id_save_coords');
|
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function performGraphDownload() {
|
|
||||||
var tempDownloadLink = document.createElement('a');
|
|
||||||
var generatedImageUrl = canvas.toDataURL(MIME_TYPE);
|
|
||||||
|
|
||||||
tempDownloadLink.href = generatedImageUrl;
|
|
||||||
tempDownloadLink.download = "topology";
|
|
||||||
document.body.appendChild(tempDownloadLink);
|
|
||||||
tempDownloadLink.click();
|
|
||||||
document.body.removeChild(tempDownloadLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleLoadData() {
|
function handleLoadData() {
|
||||||
if (topology_data !== null) {
|
|
||||||
|
|
||||||
if (htmlElement.dataset.netboxColorMode == "dark") {
|
|
||||||
options.nodes.font.color = "#fff";
|
|
||||||
}
|
|
||||||
|
|
||||||
graph = null;
|
graph = null;
|
||||||
nodes = new vis.DataSet();
|
nodes = new vis.DataSet();
|
||||||
@@ -114,38 +65,10 @@ function handleLoadData() {
|
|||||||
graph.fit();
|
graph.fit();
|
||||||
canvas = document.getElementById('visgraph').getElementsByTagName('canvas')[0];
|
canvas = document.getElementById('visgraph').getElementsByTagName('canvas')[0];
|
||||||
|
|
||||||
downloadButton.onclick = function(e) { performGraphDownload(); return false; };
|
graph.on('afterDrawing', function () {
|
||||||
|
var image = canvas.toDataURL(MIME_TYPE);
|
||||||
graph.on("dragEnd", function (params) {
|
downloadButton.href = image;
|
||||||
dragged = this.getPositions(params.nodes);
|
downloadButton.download = "topology";
|
||||||
|
|
||||||
if (coord_save_checkbox.checked) {
|
|
||||||
if (Object.keys(dragged).length !== 0) {
|
|
||||||
for (dragged_device in dragged) {
|
|
||||||
var node_id = dragged_device;
|
|
||||||
|
|
||||||
var url = "/api/plugins/netbox_topology_views/save-coords/save_coords/";
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("PATCH", url);
|
|
||||||
xhr.setRequestHeader('X-CSRFToken', csrftoken );
|
|
||||||
xhr.setRequestHeader("Accept", "application/json");
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
|
||||||
|
|
||||||
xhr.onreadystatechange = function () {
|
|
||||||
if (xhr.readyState === 4) {
|
|
||||||
console.log(xhr.status);
|
|
||||||
console.log(xhr.responseText);
|
|
||||||
}};
|
|
||||||
|
|
||||||
var data = JSON.stringify({
|
|
||||||
'node_id': node_id,
|
|
||||||
'x': dragged[node_id].x,
|
|
||||||
'y': dragged[node_id].y});
|
|
||||||
|
|
||||||
xhr.send(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{% load static %}
|
||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% with config=settings.PLUGINS_CONFIG.netbox_topology_views %}
|
||||||
|
<link rel="stylesheet" href="{% static 'netbox_topology_views/css/vendor.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'netbox_topology_views/css/app.css' %}">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="panel-body">
|
||||||
|
<div id="fullvisgraph" class=""></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endwith %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascript %}
|
||||||
|
<script src="{% static 'jquery/jquery-3.5.1.min.js' %}"></script>
|
||||||
|
<script src="{% static 'netbox_topology_views/js/vendor.js' %}"></script>
|
||||||
|
<script src="{% static 'netbox_topology_views/js/app.js' %}"></script>
|
||||||
|
|
||||||
|
<script type="application/javascript"> iniPlotboxFull()</script>
|
||||||
|
{% endblock %}
|
||||||
@@ -16,10 +16,14 @@
|
|||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
{% block extra_controls %}{% endblock %}
|
{% block extra_controls %}{% endblock %}
|
||||||
<a id="btnDownloadImage" class="btn btn-sm btn-info" href="#">
|
<a id="btnDownloadImage" class="btn btn-sm btn-info">
|
||||||
<i class="mdi mdi-download"></i>
|
<i class="mdi mdi-download"></i>
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
|
<a id="btnFullView" class="btn btn-sm btn-info disabled" target="_blank">
|
||||||
|
<i class="mdi mdi-share"></i>
|
||||||
|
Share
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock controls %}
|
{% endblock controls %}
|
||||||
@@ -36,7 +40,7 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" id="filters-form-tab" data-bs-toggle="tab" data-bs-target="#filters-form" type="button" role="tab" aria-controls="object-list" aria-selected="false">
|
<button class="nav-link" id="filters-form-tab" data-bs-toggle="tab" data-bs-target="#filters-form" type="button" role="tab" aria-controls="object-list" aria-selected="false">
|
||||||
Filters
|
Filters
|
||||||
{% if filter_form %}{% badge filter_form.changed_data|length bg_color="blue" %}{% endif %}
|
{% if filter_form %}{% badge filter_form.changed_data|length bg_class="primary" %}{% endif %}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -45,31 +49,30 @@
|
|||||||
{% endblock tabs %}
|
{% endblock tabs %}
|
||||||
|
|
||||||
{% block content-wrapper %}
|
{% block content-wrapper %}
|
||||||
{% with config=settings.PLUGINS_CONFIG.netbox_topology_views %}
|
{% with config=settings.PLUGINS_CONFIG.netbox_topology_views %}
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
||||||
<div class="tab-pane show active" id="networks" role="tabpanel" aria-labelledby="network-tab">
|
<div class="tab-pane show active" id="networks" role="tabpanel" aria-labelledby="network-tab">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div id="visgraph" class=""></div>
|
<div id="visgraph" class=""></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var topology_data = {{ topology_data | safe }};
|
var topology_data = {{ topology_data | safe }};
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if filter_form %}
|
{% if filter_form %}
|
||||||
<div class="tab-pane show" id="filters-form" role="tabpanel" aria-labelledby="filters-form-tab">
|
<div class="tab-pane show" id="filters-form" role="tabpanel" aria-labelledby="filters-form-tab">
|
||||||
{% include 'inc/filter_list.html' %}
|
{% include 'inc/filter_list.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
|
||||||
{% endblock content-wrapper %}
|
|
||||||
|
|
||||||
{% block javascript %}
|
<script src="{% static 'netbox_topology_views/js/vendor.js' %}"></script>
|
||||||
<script src="{% static 'netbox_topology_views/js/vendor.js' %}"></script>
|
|
||||||
<script src="{% static 'netbox_topology_views/js/app.js' %}"></script>
|
<script src="{% static 'netbox_topology_views/js/app.js' %}"></script>
|
||||||
<script type="application/javascript">iniPlotboxIndex()</script>
|
|
||||||
{% endblock javascript %}
|
<script type="application/javascript"> iniPlotboxIndex()</script>
|
||||||
|
{% endwith %}
|
||||||
|
{% endblock content-wrapper %}
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ from .forms import DeviceFilterForm
|
|||||||
from .filters import DeviceFilterSet
|
from .filters import DeviceFilterSet
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import distutils
|
||||||
|
|
||||||
from dcim.models import Device, Cable, DeviceRole, DeviceType
|
from dcim.models import Device, Cable, DeviceRole, DeviceType
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
|
||||||
def get_topology_data(queryset, hide_unconnected):
|
def get_topology_data(queryset):
|
||||||
nodes = []
|
nodes = []
|
||||||
nodes_ids = []
|
nodes_ids = []
|
||||||
edges = []
|
edges = []
|
||||||
@@ -24,98 +25,85 @@ def get_topology_data(queryset, hide_unconnected):
|
|||||||
if not queryset:
|
if not queryset:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ignore_cable_type = settings.PLUGINS_CONFIG["netbox_topology_views"]["ignore_cable_type"]
|
|
||||||
|
|
||||||
device_ids = [d.id for d in queryset]
|
|
||||||
|
|
||||||
for qs_device in queryset:
|
for qs_device in queryset:
|
||||||
device_has_connections = False
|
device_has_connections = False
|
||||||
|
links_from_device = Cable.objects.filter(Q(_termination_a_device_id=qs_device.id) | Q(_termination_b_device_id=qs_device.id) )
|
||||||
links_device = Cable.objects.filter(Q(_termination_a_device_id=qs_device.id) | Q(_termination_b_device_id=qs_device.id) )
|
for link_from in links_from_device:
|
||||||
for link_from in links_device:
|
device_has_connections = True
|
||||||
if link_from.termination_a_type.name != "circuit termination" and link_from.termination_b_type.name != "circuit termination":
|
if link_from.termination_a_type.name != "circuit termination" and link_from.termination_b_type.name != "circuit termination":
|
||||||
if link_from.termination_a_type.name not in ignore_cable_type and link_from.termination_b_type.name not in ignore_cable_type:
|
if link_from.id not in cable_ids:
|
||||||
if link_from.id not in cable_ids:
|
cable_ids.append(link_from.id)
|
||||||
if link_from.termination_a.device.id in device_ids and link_from.termination_b.device.id in device_ids:
|
edge_ids += 1
|
||||||
device_has_connections = True
|
cable_a_dev_name = link_from.termination_a.device.name
|
||||||
cable_ids.append(link_from.id)
|
if cable_a_dev_name is None:
|
||||||
edge_ids += 1
|
cable_a_dev_name = "device A name unknown"
|
||||||
cable_a_dev_name = link_from.termination_a.device.name
|
cable_a_name = link_from.termination_a.name
|
||||||
if cable_a_dev_name is None:
|
if cable_a_name is None:
|
||||||
cable_a_dev_name = "device A name unknown"
|
cable_a_name = "cable A name unknown"
|
||||||
cable_a_name = link_from.termination_a.name
|
cable_b_dev_name = link_from.termination_b.device.name
|
||||||
if cable_a_name is None:
|
if cable_b_dev_name is None:
|
||||||
cable_a_name = "cable A name unknown"
|
cable_b_dev_name = "device B name unknown"
|
||||||
cable_b_dev_name = link_from.termination_b.device.name
|
cable_b_name = link_from.termination_b.name
|
||||||
if cable_b_dev_name is None:
|
if cable_b_name is None:
|
||||||
cable_b_dev_name = "device B name unknown"
|
cable_b_name = "cable B name unknown"
|
||||||
cable_b_name = link_from.termination_b.name
|
|
||||||
if cable_b_name is None:
|
|
||||||
cable_b_name = "cable B name unknown"
|
|
||||||
|
|
||||||
edge = {}
|
edge = {}
|
||||||
edge["id"] = edge_ids
|
edge["id"] = edge_ids
|
||||||
edge["from"] = link_from.termination_a.device.id
|
edge["from"] = link_from.termination_a.device.id
|
||||||
edge["to"] = link_from.termination_b.device.id
|
edge["to"] = link_from.termination_b.device.id
|
||||||
edge["title"] = "Cable between <br> " + cable_a_dev_name + " [" + cable_a_name + "]<br>" + cable_b_dev_name + " [" + cable_b_name + "]"
|
edge["title"] = "Cable between <br> " + cable_a_dev_name + " [" + cable_a_name + "]<br>" + cable_b_dev_name + " [" + cable_b_name + "]"
|
||||||
if link_from.color != "":
|
if link_from.color != "":
|
||||||
edge["color"] = "#" + link_from.color
|
edge["color"] = "#" + link_from.color
|
||||||
edges.append(edge)
|
edges.append(edge)
|
||||||
else:
|
|
||||||
if link_from.termination_a.device.id in device_ids and link_from.termination_b.device.id in device_ids:
|
|
||||||
device_has_connections = True
|
|
||||||
else:
|
else:
|
||||||
if settings.PLUGINS_CONFIG["netbox_topology_views"]["enable_circuit_terminations"]:
|
if link_from.termination_a.circuit.id not in circuit_ids:
|
||||||
if link_from.termination_a_type.name == "circuit termination":
|
circuit_ids.append(link_from.termination_a.circuit.id)
|
||||||
if link_from.termination_a.circuit.id not in circuit_ids:
|
edge_ids += 1
|
||||||
circuit_ids.append(link_from.termination_a.circuit.id)
|
|
||||||
edge_ids += 1
|
|
||||||
|
|
||||||
cable_b_dev_name = link_from.termination_b.device.name
|
cable_b_dev_name = link_from.termination_b.device.name
|
||||||
if cable_b_dev_name is None:
|
if cable_b_dev_name is None:
|
||||||
cable_b_dev_name = "device B name unknown"
|
cable_b_dev_name = "device B name unknown"
|
||||||
cable_b_name = link_from.termination_b.name
|
cable_b_name = link_from.termination_b.name
|
||||||
if cable_b_name is None:
|
if cable_b_name is None:
|
||||||
cable_b_name = "cable B name unknown"
|
cable_b_name = "cable B name unknown"
|
||||||
|
|
||||||
edge = {}
|
edge = {}
|
||||||
edge["id"] = edge_ids
|
edge["id"] = edge_ids
|
||||||
edge["to"] = link_from.termination_b.device.id
|
edge["to"] = link_from.termination_b.device.id
|
||||||
edge["dashes"] = True
|
edge["dashes"] = True
|
||||||
title = ""
|
title = ""
|
||||||
|
|
||||||
title += "Circuit provider: " + link_from.termination_a.circuit.provider.name + "<br>"
|
title += "Circuit provider: " + link_from.termination_a.circuit.provider.name + "<br>"
|
||||||
title += "Termination between <br>"
|
title += "Termination between <br>"
|
||||||
title += cable_b_dev_name + " [" + cable_b_name + "]<br>"
|
title += cable_b_dev_name + " [" + cable_b_name + "]<br>"
|
||||||
|
|
||||||
if link_from.termination_a.circuit.termination_a is not None and link_from.termination_a.circuit.termination_a.cable is not None and link_from.termination_a.circuit.termination_a.cable.id != link_from.id and link_from.termination_a.circuit.termination_a.cable.termination_b is not None and link_from.termination_a.circuit.termination_a.cable.termination_b.device is not None:
|
if link_from.termination_a.circuit.termination_a is not None and link_from.termination_a.circuit.termination_a.cable is not None and link_from.termination_a.circuit.termination_a.cable.id != link_from.id and link_from.termination_a.circuit.termination_a.cable.termination_b is not None and link_from.termination_a.circuit.termination_a.cable.termination_b.device is not None:
|
||||||
edge["from"] = link_from.termination_a.circuit.termination_a.cable.termination_b.device.id
|
edge["from"] = link_from.termination_a.circuit.termination_a.cable.termination_b.device.id
|
||||||
|
|
||||||
cable_a_dev_name = link_from.termination_a.circuit.termination_a.cable.termination_b.device.name
|
cable_a_dev_name = link_from.termination_a.circuit.termination_a.cable.termination_b.device.name
|
||||||
if cable_a_dev_name is None:
|
if cable_a_dev_name is None:
|
||||||
cable_a_dev_name = "device B name unknown"
|
cable_a_dev_name = "device B name unknown"
|
||||||
cable_b_name = link_from.termination_a.circuit.termination_a.cable.termination_b.name
|
cable_b_name = link_from.termination_a.circuit.termination_a.cable.termination_b.name
|
||||||
if cable_a_name is None:
|
if cable_a_name is None:
|
||||||
cable_a_name = "cable B name unknown"
|
cable_a_name = "cable B name unknown"
|
||||||
title += cable_a_dev_name + " [" + cable_a_name + "]<br>"
|
title += cable_a_dev_name + " [" + cable_a_name + "]<br>"
|
||||||
edge["title"] = title
|
edge["title"] = title
|
||||||
edges.append(edge)
|
edges.append(edge)
|
||||||
|
|
||||||
if link_from.termination_a.circuit.termination_z is not None and link_from.termination_a.circuit.termination_z.cable is not None and link_from.termination_a.circuit.termination_z.cable.id != link_from.id and link_from.termination_a.circuit.termination_z.cable.termination_b is not None and link_from.termination_a.circuit.termination_z.cable.termination_b.device is not None:
|
if link_from.termination_a.circuit.termination_z is not None and link_from.termination_a.circuit.termination_z.cable is not None and link_from.termination_a.circuit.termination_z.cable.id != link_from.id and link_from.termination_a.circuit.termination_z.cable.termination_b is not None and link_from.termination_a.circuit.termination_z.cable.termination_b.device is not None:
|
||||||
edge["from"] = link_from.termination_a.circuit.termination_z.cable.termination_b.device.id
|
edge["from"] = link_from.termination_a.circuit.termination_z.cable.termination_b.device.id
|
||||||
|
|
||||||
cable_a_dev_name = link_from.termination_a.circuit.termination_z.cable.termination_b.device.name
|
cable_a_dev_name = link_from.termination_a.circuit.termination_z.cable.termination_b.device.name
|
||||||
if cable_a_dev_name is None:
|
if cable_a_dev_name is None:
|
||||||
cable_a_dev_name = "device B name unknown"
|
cable_a_dev_name = "device B name unknown"
|
||||||
cable_a_name = link_from.termination_a.circuit.termination_z.cable.termination_b.name
|
cable_a_name = link_from.termination_a.circuit.termination_z.cable.termination_b.name
|
||||||
if cable_a_name is None:
|
if cable_a_name is None:
|
||||||
cable_a_name = "cable B name unknown"
|
cable_a_name = "cable B name unknown"
|
||||||
title += cable_a_dev_name + " [" + cable_a_name + "]<br>"
|
title += cable_a_dev_name + " [" + cable_a_name + "]<br>"
|
||||||
edge["title"] = title
|
edge["title"] = title
|
||||||
edges.append(edge)
|
edges.append(edge)
|
||||||
|
|
||||||
if qs_device.id not in nodes_ids:
|
if qs_device.id not in nodes_ids:
|
||||||
if hide_unconnected == None or (hide_unconnected is True and device_has_connections is True):
|
|
||||||
nodes_ids.append(qs_device.id)
|
nodes_ids.append(qs_device.id)
|
||||||
|
|
||||||
dev_name = qs_device.name
|
dev_name = qs_device.name
|
||||||
@@ -151,11 +139,10 @@ def get_topology_data(queryset, hide_unconnected):
|
|||||||
|
|
||||||
if "coordinates" in qs_device.custom_field_data:
|
if "coordinates" in qs_device.custom_field_data:
|
||||||
if qs_device.custom_field_data["coordinates"] is not None:
|
if qs_device.custom_field_data["coordinates"] is not None:
|
||||||
if ';' in qs_device.custom_field_data["coordinates"]:
|
cords = qs_device.custom_field_data["coordinates"].split(";")
|
||||||
cords = qs_device.custom_field_data["coordinates"].split(";")
|
node["x"] = int(cords[0])
|
||||||
node["x"] = int(cords[0])
|
node["y"] = int(cords[1])
|
||||||
node["y"] = int(cords[1])
|
node["physics"] = False
|
||||||
node["physics"] = False
|
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
@@ -177,16 +164,11 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
topo_data = None
|
topo_data = None
|
||||||
|
|
||||||
if request.GET:
|
if request.GET:
|
||||||
hide_unconnected = None
|
|
||||||
if 'hide_unconnected' in request.GET:
|
|
||||||
if request.GET["hide_unconnected"] == "on" :
|
|
||||||
hide_unconnected = True
|
|
||||||
|
|
||||||
if 'draw_init' in request.GET:
|
if 'draw_init' in request.GET:
|
||||||
if request.GET["draw_init"].lower() == 'true':
|
if bool(distutils.util.strtobool(request.GET["draw_init"])):
|
||||||
topo_data = get_topology_data(self.queryset, hide_unconnected)
|
topo_data = get_topology_data(self.queryset)
|
||||||
else:
|
else:
|
||||||
topo_data = get_topology_data(self.queryset, hide_unconnected)
|
topo_data = get_topology_data(self.queryset)
|
||||||
else:
|
else:
|
||||||
preselected_device_roles = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_device_roles"]
|
preselected_device_roles = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_device_roles"]
|
||||||
preselected_tags = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_tags"]
|
preselected_tags = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_tags"]
|
||||||
@@ -199,6 +181,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
q.setlist('tag', list(q_tags))
|
q.setlist('tag', list(q_tags))
|
||||||
q['draw_init'] = settings.PLUGINS_CONFIG["netbox_topology_views"]["draw_default_layout"]
|
q['draw_init'] = settings.PLUGINS_CONFIG["netbox_topology_views"]["draw_default_layout"]
|
||||||
query_string = q.urlencode()
|
query_string = q.urlencode()
|
||||||
|
print(query_string)
|
||||||
return HttpResponseRedirect(request.path + "?" + query_string)
|
return HttpResponseRedirect(request.path + "?" + query_string)
|
||||||
|
|
||||||
return render(request, 'netbox_topology_views/index.html' , {
|
return render(request, 'netbox_topology_views/index.html' , {
|
||||||
|
|||||||
Generated
+79
-53
@@ -1,9 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "netbox_topology_views",
|
"name": "netbox_topology_views",
|
||||||
"version": "1.2.0",
|
"version": "1.0.0-alpha.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@egjs/hammerjs": {
|
||||||
|
"version": "2.0.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
|
||||||
|
"integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/hammerjs": "^2.0.36"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/hammerjs": {
|
||||||
|
"version": "2.0.38",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.38.tgz",
|
||||||
|
"integrity": "sha512-wuwDzWW1JWh3BZoRftBlKcctjNzR75QFY4/b4zAz7sH1EesA8HBJzke+bF5dxCATNdHHs3X1P5UWanbbUT6chw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"abbrev": {
|
"abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
@@ -1300,21 +1315,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"copy-props": {
|
"copy-props": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz",
|
||||||
"integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==",
|
"integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"each-props": "^1.3.2",
|
"each-props": "^1.3.0",
|
||||||
"is-plain-object": "^5.0.0"
|
"is-plain-object": "^2.0.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"is-plain-object": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
@@ -2024,9 +2031,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"glob-watcher": {
|
"glob-watcher": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz",
|
||||||
"integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==",
|
"integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"anymatch": "^2.0.0",
|
"anymatch": "^2.0.0",
|
||||||
@@ -2034,16 +2041,7 @@
|
|||||||
"chokidar": "^2.0.0",
|
"chokidar": "^2.0.0",
|
||||||
"is-negated-glob": "^1.0.0",
|
"is-negated-glob": "^1.0.0",
|
||||||
"just-debounce": "^1.0.0",
|
"just-debounce": "^1.0.0",
|
||||||
"normalize-path": "^3.0.0",
|
|
||||||
"object.defaults": "^1.1.0"
|
"object.defaults": "^1.1.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"normalize-path": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"global-modules": {
|
"global-modules": {
|
||||||
@@ -2171,13 +2169,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-sass": {
|
"gulp-sass": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-4.1.0.tgz",
|
||||||
"integrity": "sha512-bg7mfgsgho0Ej0WXE9Cd2sq/YxeKxOjagrMmM40zvOYXHtZvi5ED84wIpqCUvJLz66kFNkv+jS/rQXolmgXrUQ==",
|
"integrity": "sha512-xIiwp9nkBLcJDpmYHbEHdoWZv+j+WtYaKD6Zil/67F3nrAaZtWYN5mDwerdo7EvcdBenSAj7Xb2hx2DqURLGdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chalk": "^2.3.0",
|
"chalk": "^2.3.0",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.11",
|
||||||
"node-sass": "^4.8.3",
|
"node-sass": "^4.8.3",
|
||||||
"plugin-error": "^1.0.1",
|
"plugin-error": "^1.0.1",
|
||||||
"replace-ext": "^1.0.0",
|
"replace-ext": "^1.0.0",
|
||||||
@@ -2629,9 +2627,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"json-schema": {
|
"json-schema": {
|
||||||
"version": "0.4.0",
|
"version": "0.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"json-schema-traverse": {
|
"json-schema-traverse": {
|
||||||
@@ -2653,14 +2651,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsprim": {
|
"jsprim": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||||
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
|
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"assert-plus": "1.0.0",
|
"assert-plus": "1.0.0",
|
||||||
"extsprintf": "1.3.0",
|
"extsprintf": "1.3.0",
|
||||||
"json-schema": "0.4.0",
|
"json-schema": "0.2.3",
|
||||||
"verror": "1.10.0"
|
"verror": "1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2670,6 +2668,12 @@
|
|||||||
"integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=",
|
"integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"keycharm": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.3.1.tgz",
|
||||||
|
"integrity": "sha512-zn47Ti4FJT9zdF+YBBLWJsfKF/fYQHkrYlBeB5Ez5e2PjW7SoIxr43yehAne2HruulIoid4NKZZxO0dHBygCtQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"kind-of": {
|
"kind-of": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||||
@@ -2930,9 +2934,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mixin-deep": {
|
"mixin-deep": {
|
||||||
@@ -2965,6 +2969,12 @@
|
|||||||
"minimist": "^1.2.5"
|
"minimist": "^1.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.29.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
@@ -3389,9 +3399,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"path-parse": {
|
"path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"path-root": {
|
"path-root": {
|
||||||
@@ -3795,7 +3805,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"camelcase": {
|
"camelcase": {
|
||||||
@@ -4333,6 +4344,12 @@
|
|||||||
"integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
|
"integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"timsort": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"to-absolute-glob": {
|
"to-absolute-glob": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
|
||||||
@@ -4579,6 +4596,12 @@
|
|||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-LNUrNsXdI/fUsypJbWM8Jt4DgQdFAZh41p9C7WE9Cn+CULOEkoG2lgQyH68v3wnIy5K3fN4jdSt270K6IFA3MQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"v8flags": {
|
"v8flags": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz",
|
||||||
@@ -4679,19 +4702,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vis-data": {
|
"vis-data": {
|
||||||
"version": "7.1.2",
|
"version": "6.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vis-data/-/vis-data-6.6.1.tgz",
|
||||||
"integrity": "sha512-RPSegFxEcnp3HUEJSzhS2vBdbJ2PSsrYYuhRlpHp2frO/MfRtTYbIkkLZmPkA/Sg3pPfBlR235gcoKbtdm4mbw=="
|
"integrity": "sha512-xmujDB2Dzf8T04rGFJ9OP4OA6zRVrz8R9hb0CVKryBrZRCljCga9JjSfgctA8S7wdZu7otDtUIwX4ZOgfV/57w==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"vis-network": {
|
"vis-network": {
|
||||||
"version": "9.1.0",
|
"version": "7.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/vis-network/-/vis-network-7.10.2.tgz",
|
||||||
"integrity": "sha512-rx96L144RJWcqOa6afjiFyxZKUerRRbT/YaNMpsusHdwzxrVTO2LlduR45PeJDEztrAf3AU5l2zmiG+1ydUZCw=="
|
"integrity": "sha512-KDx2agbDnaiE0Bye4AcCRqTn5mxzDKhdUNpKkzSn0AOLBmdhNtPGjxAFluAmvFVyiSK5R6Q5KIWdLjeIMu/PAQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"vis-util": {
|
"vis-util": {
|
||||||
"version": "5.0.2",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/vis-util/-/vis-util-4.3.4.tgz",
|
||||||
"integrity": "sha512-oPDmPc4o0uQLoKpKai2XD1DjrhYsA7MRz75Wx9KmfX84e9LLgsbno7jVL5tR0K9eNVQkD6jf0Ei8NtbBHDkF1A=="
|
"integrity": "sha512-hJIZNrwf4ML7FYjs+m+zjJfaNvhjk3/1hbMdQZVnwwpOFJS/8dMG8rdbOHXcKoIEM6U5VOh3HNpaDXxGkOZGpw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
|
|||||||
+12
-13
@@ -1,27 +1,26 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "netbox_topology_views",
|
"name": "netbox_topology_views",
|
||||||
"version": "1.2.0",
|
"version": "1.0.0-alpha.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"resources": "gulp build",
|
"resources": "gulp build",
|
||||||
"resources_dev": "gulp build_dev"
|
"resources_dev": "gulp build_dev"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
"vis-data": "^7.1.2",
|
|
||||||
"vis-network": "^9.1.0",
|
|
||||||
"vis-util": "^5.0.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@egjs/hammerjs": "^2.0.0",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"gulp-clean-css": "^4.3.0",
|
"gulp-clean-css": "^4.3.0",
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
"gulp-sass": "^4.1.0",
|
"gulp-sass": "^4.1.0",
|
||||||
"gulp-uglify": "^3.0.2"
|
"gulp-uglify": "^3.0.2",
|
||||||
|
"keycharm": "^0.3.0",
|
||||||
|
"moment": "^2.24.0",
|
||||||
|
"timsort": "^0.3.0",
|
||||||
|
"uuid": "7.0.0",
|
||||||
|
"vis-data": "^6.2.1",
|
||||||
|
"vis-network": "^7.10.2",
|
||||||
|
"vis-util": "^4.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {}
|
||||||
"@egjs/hammerjs": "^2.0.0",
|
|
||||||
"uuid": "^8.0.0",
|
|
||||||
"keycharm": "^0.4.0",
|
|
||||||
"timsort": "^0.3.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='netbox-topology-views',
|
name='netbox-topology-views',
|
||||||
version='1.2.0',
|
version='1.0.0-alpha.2',
|
||||||
description='An NetBox plugin to create Topology maps',
|
description='An NetBox plugin to create Topology maps',
|
||||||
url='https://github.com/mattieserver/netbox-topology-views',
|
url='https://github.com/mattieserver/netbox-topology-views',
|
||||||
author='Mattijs Vanhaverbeke',
|
author='Mattijs Vanhaverbeke',
|
||||||
|
|||||||
Reference in New Issue
Block a user