Compare commits
11
Commits
v3.0.0-beta.2
...
v3.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ce13cd1d2 | ||
|
|
b4e2f2418e | ||
|
|
5563e3c084 | ||
|
|
77a8a2c0d6 | ||
|
|
010179742c | ||
|
|
f2bbaed317 | ||
|
|
8c56f21f16 | ||
|
|
eb627bdedc | ||
|
|
291c033e34 | ||
|
|
1f389b3faa | ||
|
|
3d3ddaa7c4 |
@@ -29,7 +29,15 @@ Once installed, the plugin needs to be enabled in your `configuration.py`
|
|||||||
PLUGINS = ["netbox_topology_views"]
|
PLUGINS = ["netbox_topology_views"]
|
||||||
```
|
```
|
||||||
|
|
||||||
Then run `python3 manage.py collectstatic --no-input`
|
First run `source /opt/netbox/venv/bin/activate` to enter the Python virtual environment.
|
||||||
|
|
||||||
|
|
||||||
|
Then run
|
||||||
|
```bash
|
||||||
|
cd /opt/netbox/netbox
|
||||||
|
pip3 install netbox-topology-views
|
||||||
|
python3 manage.py collectstatic --no-input
|
||||||
|
```
|
||||||
|
|
||||||
### Versions
|
### Versions
|
||||||
|
|
||||||
@@ -48,7 +56,10 @@ There is also support for custom fields.
|
|||||||
|
|
||||||
If you create a custom field "coordinates" for "dcim > device" and "Circuits > circuit" with type "text" and name "coordinates" you will see the same layout every time.
|
If you create a custom field "coordinates" for "dcim > device" and "Circuits > circuit" with type "text" and name "coordinates" you will see the same layout every time.
|
||||||
|
|
||||||
The coordinates can then be provided as: "X;Y"
|
The coordinates can then be provided as: "X;Y".
|
||||||
|
|
||||||
|
Please read the "Configure" chapter to set the `allow_coordinates_saving` option to True.
|
||||||
|
You might also set the `always_save_coordinates` option to True.
|
||||||
|
|
||||||
## Configure
|
## Configure
|
||||||
|
|
||||||
|
|||||||
@@ -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 = '3.0.0-beta.2'
|
version = '3.0.1'
|
||||||
author = 'Mattijs Vanhaverbeke'
|
author = 'Mattijs Vanhaverbeke'
|
||||||
author_email = 'author@example.com'
|
author_email = 'author@example.com'
|
||||||
base_url = 'netbox_topology_views'
|
base_url = 'netbox_topology_views'
|
||||||
|
|||||||
@@ -41,10 +41,9 @@ class DeviceFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=DeviceStatusChoices,
|
choices=DeviceStatusChoices,
|
||||||
null_value=None
|
null_value=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = ['id', 'name']
|
fields = ['id', 'name']
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
model = Device
|
model = Device
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'hide_unconnected', 'save_coords', 'show_circuit', 'show_power' ,)),
|
(None, ('q', 'hide_unconnected', 'save_coords', 'show_circuit', 'show_power' ,)),
|
||||||
(None, ('tenant_group_id', 'tenant_id')),
|
(None, ('tenant_group_id', 'tenant_id',)),
|
||||||
(None, ('region_id', 'site_id', 'location_id')),
|
(None, ('region_id', 'site_id', 'location_id',)),
|
||||||
(None, ('device_role_id', )),
|
(None, ('device_role_id','id','status', )),
|
||||||
(None, ('tag', 'status')),
|
(None, ('tag', )),
|
||||||
)
|
)
|
||||||
|
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
@@ -38,6 +38,17 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Device Role')
|
label=_('Device Role')
|
||||||
)
|
)
|
||||||
|
id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Device.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Device'),
|
||||||
|
query_params={
|
||||||
|
'location_id' : '$location_id',
|
||||||
|
'region_id': '$region_id',
|
||||||
|
'site_id': '$site_id',
|
||||||
|
'role_id': '$device_role_id',
|
||||||
|
},
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@@ -66,7 +77,7 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
initial=False
|
initial=False
|
||||||
)
|
)
|
||||||
show_power = forms.BooleanField(
|
show_power = forms.BooleanField(
|
||||||
label=_("Show Power panel/feed"),
|
label=_("Show Power Feeds"),
|
||||||
required=False,
|
required=False,
|
||||||
initial=False
|
initial=False
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from platform import node
|
from django.shortcuts import render
|
||||||
from django.shortcuts import get_object_or_404, render
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
@@ -7,18 +6,17 @@ from django.conf import settings
|
|||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
|
|
||||||
from .forms import DeviceFilterForm
|
from .forms import DeviceFilterForm
|
||||||
from .filters import DeviceFilterSet
|
from .filters import DeviceFilterSet
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from dcim.models import Device, Cable, CableTermination, DeviceRole, PathEndpoint, Interface, FrontPort, RearPort, PowerPanel, PowerFeed
|
from dcim.models import Device, CableTermination, DeviceRole, Interface, FrontPort, RearPort, PowerPanel, PowerFeed
|
||||||
from circuits.models import CircuitTermination, ProviderNetwork
|
from circuits.models import CircuitTermination
|
||||||
from wireless.models import WirelessLink
|
from wireless.models import WirelessLink
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
|
||||||
supported_termination_types = ["interface", "front port", "rear port", "power outlet", "power port"]
|
supported_termination_types = ["interface", "front port", "rear port", "power outlet", "power port", "console port", "console server port"]
|
||||||
|
|
||||||
def create_node(device, save_coords, circuit = None, powerpanel = None, powerfeed= None):
|
def create_node(device, save_coords, circuit = None, powerpanel = None, powerfeed= None):
|
||||||
|
|
||||||
@@ -170,9 +168,9 @@ def get_topology_data(queryset, hide_unconnected, save_coords, show_circuit, sho
|
|||||||
ignore_cable_type = settings.PLUGINS_CONFIG["netbox_topology_views"]["ignore_cable_type"]
|
ignore_cable_type = settings.PLUGINS_CONFIG["netbox_topology_views"]["ignore_cable_type"]
|
||||||
|
|
||||||
device_ids = [d.id for d in queryset]
|
device_ids = [d.id for d in queryset]
|
||||||
|
site_ids = [d.site.id for d in queryset]
|
||||||
|
|
||||||
if show_circuit:
|
if show_circuit:
|
||||||
site_ids = [d.site.id for d in queryset]
|
|
||||||
circuits = CircuitTermination.objects.filter( Q(site_id__in=site_ids) | Q( provider_network__isnull=False) ).prefetch_related("provider_network", "circuit")
|
circuits = CircuitTermination.objects.filter( Q(site_id__in=site_ids) | Q( provider_network__isnull=False) ).prefetch_related("provider_network", "circuit")
|
||||||
for circuit in circuits:
|
for circuit in circuits:
|
||||||
if not hide_unconnected and circuit.circuit.id not in nodes_circuits:
|
if not hide_unconnected and circuit.circuit.id not in nodes_circuits:
|
||||||
@@ -224,16 +222,18 @@ def get_topology_data(queryset, hide_unconnected, save_coords, show_circuit, sho
|
|||||||
if power_feed.power_panel.id not in nodes_powerpanel:
|
if power_feed.power_panel.id not in nodes_powerpanel:
|
||||||
nodes_powerpanel[power_feed.power_panel.id] = power_feed.power_panel
|
nodes_powerpanel[power_feed.power_panel.id] = power_feed.power_panel
|
||||||
|
|
||||||
|
power_link_name = ""
|
||||||
if power_feed.id not in nodes_powerfeed:
|
if power_feed.id not in nodes_powerfeed:
|
||||||
if hide_unconnected:
|
if hide_unconnected:
|
||||||
if power_feed.link_peers[0].device.id in device_ids:
|
if power_feed.link_peers[0].device.id in device_ids:
|
||||||
nodes_powerfeed[power_feed.id] = power_feed
|
nodes_powerfeed[power_feed.id] = power_feed
|
||||||
|
power_link_name =power_feed.link_peers[0].name
|
||||||
else:
|
else:
|
||||||
nodes_powerfeed[power_feed.id] = power_feed
|
nodes_powerfeed[power_feed.id] = power_feed
|
||||||
|
|
||||||
edge_ids += 1
|
edge_ids += 1
|
||||||
termination_a = { "termination_name": power_feed.power_panel.name, "termination_device_name": str(power_feed.power_panel.id), "device_id": "p{}".format(power_feed.power_panel.id) }
|
termination_a = { "termination_name": power_feed.power_panel.name, "termination_device_name": "", "device_id": "p{}".format(power_feed.power_panel.id) }
|
||||||
termination_b = { "termination_name": power_feed.name, "termination_device_name": str(termination.id), "device_id": "f{}".format(power_feed.id) }
|
termination_b = { "termination_name": power_feed.name, "termination_device_name": power_link_name, "device_id": "f{}".format(power_feed.id) }
|
||||||
edges.append(create_edge(edge_id=edge_ids, termination_a=termination_a, termination_b=termination_b, power=True))
|
edges.append(create_edge(edge_id=edge_ids, termination_a=termination_a, termination_b=termination_b, power=True))
|
||||||
|
|
||||||
if power_feed.cable_id is not None:
|
if power_feed.cable_id is not None:
|
||||||
@@ -259,14 +259,16 @@ def get_topology_data(queryset, hide_unconnected, save_coords, show_circuit, sho
|
|||||||
if link.cable.id not in cable_ids:
|
if link.cable.id not in cable_ids:
|
||||||
cable_ids[link.cable.id] = {}
|
cable_ids[link.cable.id] = {}
|
||||||
else:
|
else:
|
||||||
if cable_ids[link.cable.id]['B'] is not None:
|
if 'B' in cable_ids[link.cable.id]:
|
||||||
complete_link = True
|
if cable_ids[link.cable.id]['B'] is not None:
|
||||||
|
complete_link = True
|
||||||
elif link.cable_end == "B":
|
elif link.cable_end == "B":
|
||||||
if link.cable.id not in cable_ids:
|
if link.cable.id not in cable_ids:
|
||||||
cable_ids[link.cable.id] = {}
|
cable_ids[link.cable.id] = {}
|
||||||
else:
|
else:
|
||||||
if cable_ids[link.cable.id]['A'] is not None:
|
if 'A' in cable_ids[link.cable.id]:
|
||||||
complete_link = True
|
if cable_ids[link.cable.id]['A'] is not None:
|
||||||
|
complete_link = True
|
||||||
else:
|
else:
|
||||||
print("Unkown cable end")
|
print("Unkown cable end")
|
||||||
cable_ids[link.cable.id][link.cable_end] = link
|
cable_ids[link.cable.id][link.cable_end] = link
|
||||||
@@ -288,7 +290,7 @@ def get_topology_data(queryset, hide_unconnected, save_coords, show_circuit, sho
|
|||||||
termination_a = cable_ids[link.cable.id]["A"]
|
termination_a = cable_ids[link.cable.id]["A"]
|
||||||
|
|
||||||
edges.append(create_edge(edge_id=edge_ids, cable=link.cable, termination_a=termination_a, termination_b=termination_b))
|
edges.append(create_edge(edge_id=edge_ids, cable=link.cable, termination_a=termination_a, termination_b=termination_b))
|
||||||
|
|
||||||
for wlan_link in wlan_links:
|
for wlan_link in wlan_links:
|
||||||
if wlan_link.interface_a.device.id not in nodes_devices:
|
if wlan_link.interface_a.device.id not in nodes_devices:
|
||||||
nodes_devices[wlan_link.interface_a.device.id] = wlan_link.interface_a.device
|
nodes_devices[wlan_link.interface_a.device.id] = wlan_link.interface_a.device
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ with open(path.join(top_level_directory, 'README.md'), encoding='utf-8') as file
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='netbox-topology-views',
|
name='netbox-topology-views',
|
||||||
version='3.0.0-beta.2',
|
version='3.0.1',
|
||||||
description='An NetBox plugin to create Topology maps',
|
description='An NetBox plugin to create Topology maps',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
|
|||||||
Reference in New Issue
Block a user