Netbox v3.3 Support (#145)

* init

* added circuit (or at least part of)

* refactor circuits + fix js

* small changes

* small changes

* added power + rework settings

* small changes
This commit is contained in:
mattieserver
2022-09-01 18:44:03 +02:00
committed by GitHub
parent 5e1d5d2937
commit 318b8495b7
11 changed files with 296 additions and 231 deletions
+2 -4
View File
@@ -45,7 +45,7 @@ Then run `python3 manage.py collectstatic --no-input`
There is also support for custom fields.
If you create a custom field "coordinates" for "dcim > device" 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"
@@ -67,12 +67,10 @@ PLUGINS_CONFIG = {
| ------------- |-------------| -----|
| 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'] | The slug of the device roles that you have a image for. |
| preselected_device_roles | ['Firewall', 'Router', 'Distribution Switch', 'Core Switch', 'Internal Switch', 'Access Switch', 'Server', 'Storage', 'Backup', 'Wireless AP'] | The full name of the device roles you want to pre select in the global view. Note that this is case sensitive|
| preselected_intermediate_dev_roles | ['Patch-panel'] | The full name of the device roles you want to display in the global view when using end-to-end connections mode. Note that this is case sensitive|
| allow_coordinates_saving | False | (bool) Set to true if you use the custom coordinates fields and want to save the coordinates |
| always_save_coordinates | False | (bool) Set if you want to enable the option to save coordinates by default |
| ignore_cable_type | ['power outlet', 'power port'] | The cable types that you want to ignore in the views |
| ignore_cable_type | [] | The cable types that you want to ignore in the views |
| preselected_tags | '[]' | The name of tags you want to preload |
| enable_circuit_terminations | False | (bool) Set to true if you want to see circuit terminations in the topology |
| draw_default_layout | False | (bool) Set to True if you want to load draw the topology on the initial load (when you go to the topology plugin page) |
### Custom Images
+1 -4
View File
@@ -11,14 +11,11 @@ class TopologyViewsConfig(PluginConfig):
required_settings = []
default_settings = {
'preselected_device_roles': ['Firewall', 'Router', 'Distribution Switch', 'Core Switch', 'Internal Switch', 'Access Switch', 'Server', 'Storage', 'Backup', 'Wireless AP'],
'preselected_intermediate_dev_roles' : [],
'end2end_connections': False,
'ignore_cable_type': ['power outlet','power port'],
'ignore_cable_type': [],
'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,
'always_save_coordinates': False,
'preselected_tags' : [],
'enable_circuit_terminations': False,
'draw_default_layout': False
}
+13 -3
View File
@@ -7,7 +7,7 @@ from rest_framework.routers import APIRootView
from .serializers import TopologyDummySerializer
from django.conf import settings
from dcim.models import DeviceRole, Device, Cable
from dcim.models import DeviceRole, Device, Cable , PowerPanel, PowerFeed
from circuits.models import Circuit
from extras.models import Tag
@@ -37,8 +37,18 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
if "y" in request.data:
if request.data["y"]:
y_coord = request.data["y"]
actual_device= Device.objects.get(id=device_id)
if device_id.startswith("c"):
device_id = device_id.lstrip('c')
actual_device= Circuit.objects.get(id=device_id)
elif device_id.startswith("p"):
device_id = device_id.lstrip('p')
actual_device= PowerPanel.objects.get(id=device_id)
elif device_id.startswith("f"):
device_id = device_id.lstrip('f')
actual_device= PowerFeed.objects.get(id=device_id)
else:
actual_device= Device.objects.get(id=device_id)
if "coordinates" in actual_device.custom_field_data:
actual_device.custom_field_data["coordinates"] = "%s;%s" % (x_coord,y_coord)
+12 -14
View File
@@ -16,16 +16,15 @@ from netbox.forms import NetBoxModelFilterSetForm
from utilities.forms import (TagFilterField, DynamicModelMultipleChoiceField, MultipleChoiceField)
allow_coordinates_saving = bool(settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"])
end2end = settings.PLUGINS_CONFIG["netbox_topology_views"]["end2end_connections"]
class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = Device
fieldsets = (
(None, ('q', 'hide_unconnected', 'save_coords', 'end2end_connections')),
(None, ('q', 'hide_unconnected', 'save_coords', 'show_circuit', 'show_power' ,)),
(None, ('tenant_group_id', 'tenant_id')),
(None, ('region_id', 'site_id', 'location_id')),
(None, ('device_role_id', 'intermediate_dev_role_id')),
(None, ('device_role_id', )),
(None, ('tag', 'status')),
)
@@ -39,17 +38,6 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
required=False,
label=_('Device Role')
)
end2end_connections = forms.BooleanField(
label=_("Display end-to-end connections"),
required=False,
initial=end2end
)
intermediate_dev_role_id = DynamicModelMultipleChoiceField(
queryset=DeviceRole.objects.all(),
required=False,
label=_('Intermediate Devices Role'),
help_text='Intermediate devices to display when using end-to-end connections mode, even if they do not match the query'
)
site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(),
required=False,
@@ -72,6 +60,16 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
required=False,
initial=False
)
show_circuit = forms.BooleanField(
label=_("Show Circuit Terminations"),
required=False,
initial=False
)
show_power = forms.BooleanField(
label=_("Show Power panel/feed"),
required=False,
initial=False
)
save_coords = forms.BooleanField(
label=_("Save Coordinates"),
required=False,
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long
+19 -7
View File
@@ -40,8 +40,6 @@ var options = {
solver: 'forceAtlas2Based'
}
};
var selected_regions = [];
var selected_sites = [];
var coord_save_checkbox = null;
var htmlElement = null;
@@ -69,12 +67,12 @@ export function htmlTitle(html) {
};
export function addEdge(item) {
item.title = htmlTitle( item.title );
item.title = htmlTitle(item.title);
edges.add(item);
};
export function addNode(item) {
item.title = htmlTitle( item.title );
item.title = htmlTitle(item.title);
nodes.add(item);
}
@@ -137,7 +135,6 @@ export function handleLoadData() {
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.status);
console.log(xhr.responseText);
}};
var data = JSON.stringify({
@@ -154,7 +151,23 @@ export function handleLoadData() {
graph.on("doubleClick", function (params) {
let selected_devices = params.nodes;
for (let selected_device in selected_devices) {
let url = "/dcim/devices/" + selected_devices[selected_device] + "/";
let url = ""
if(String(selected_devices[selected_device]).startsWith("c")) {
cid = selected_devices[selected_device].substring(1);
url = "/circuits/circuits/" + cid + "/";
}
else if (String(selected_devices[selected_device]).startsWith("p")) {
cid = selected_devices[selected_device].substring(1);
url = "/dcim/power-panels/" + cid + "/";
}
else if (String(selected_devices[selected_device]).startsWith("f")) {
cid = selected_devices[selected_device].substring(1);
url = "/dcim/power-feeds/" + cid + "/";
}
else {
url = "/dcim/devices/" + selected_devices[selected_device] + "/";
}
window.open(url, "_blank");
}
@@ -164,7 +177,6 @@ export function handleLoadData() {
export function load_doc() {
if (document.readyState !== 'loading') {
console.log("test");
iniPlotboxIndex();
} else {
document.addEventListener('DOMContentLoaded', iniPlotboxIndex);
+248 -198
View File
@@ -1,3 +1,5 @@
from platform import node
from tkinter.messagebox import NO
from django.shortcuts import get_object_or_404, render
from django.db.models import Q
from django.views.generic import View
@@ -6,61 +8,101 @@ from django.conf import settings
from django.http import QueryDict
from django.http import HttpResponseRedirect
from .forms import DeviceFilterForm
from .filters import DeviceFilterSet
import json
from dcim.models import Device, Cable, DeviceRole, PathEndpoint
from dcim.models import Device, Cable, CableTermination, DeviceRole, PathEndpoint, Interface, FrontPort, RearPort, PowerPanel, PowerFeed
from circuits.models import CircuitTermination, ProviderNetwork
from wireless.models import WirelessLink
from extras.models import Tag
supported_termination_types = ["interface", "front port", "rear port", "power outlet", "power port"]
def create_node(device, save_coords):
dev_name = device.name
if dev_name is None:
dev_name = "device name unknown"
node_content = ""
if device.device_type is not None:
node_content += "<tr><th>Type: </th><td>" + device.device_type.model + "</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>"
if device.site is not None:
node_content += "<tr><th>Site: </th><td>" + device.site.name + "</td></tr>"
if device.location is not None:
node_content += "<tr><th>Location: </th><td>" + device.location.name + "</td></tr>"
if device.rack is not None:
node_content += "<tr><th>Rack: </th><td>" + device.rack.name + "</td></tr>"
if device.position is not None:
if device.face is not None:
node_content += "<tr><th>Position: </th><td> {} ({}) </td></tr>".format(device.position, device.face)
else:
node_content += "<tr><th>Position: </th><td>" + device.position + "</td></tr>"
dev_title = "<table> %s </table>" % (node_content)
def create_node(device, save_coords, circuit = None, powerpanel = None, powerfeed= None):
node = {}
node["id"] = device.id
node_content = ""
if circuit:
dev_name = "Circuit " + str(device.cid)
node["image"] = "../../static/netbox_topology_views/img/circuit.png"
node["id"] = "c{}".format(device.id)
if device.provider is not None:
node_content += "<tr><th>Provider: </th><td>" + device.provider.name + "</td></tr>"
if device.type is not None:
node_content += "<tr><th>Type: </th><td>" + device.type.name + "</td></tr>"
elif powerpanel:
dev_name = "Power Panel " + str(device.id)
node["image"] = "../../static/netbox_topology_views/img/power-panel.png"
node["id"] = "p{}".format(device.id)
if device.site is not None:
node_content += "<tr><th>Site: </th><td>" + device.site.name + "</td></tr>"
if device.location is not None:
node_content += "<tr><th>Location: </th><td>" + device.location.name + "</td></tr>"
elif powerfeed:
dev_name = "Power Feed " + str(device.id)
node["image"] = "../../static/netbox_topology_views/img/power-feed.png"
node["id"] = "f{}".format(device.id)
if device.power_panel is not None:
node_content += "<tr><th>Power Panel: </th><td>" + device.power_panel.name + "</td></tr>"
if device.type is not None:
node_content += "<tr><th>Type: </th><td>" + device.type + "</td></tr>"
if device.supply is not None:
node_content += "<tr><th>Supply: </th><td>" + device.supply + "</td></tr>"
if device.phase is not None:
node_content += "<tr><th>Phase: </th><td>" + device.phase + "</td></tr>"
if device.amperage is not None:
node_content += "<tr><th>Amperage: </th><td>" + str(device.amperage )+ "</td></tr>"
if device.voltage is not None:
node_content += "<tr><th>Voltage: </th><td>" + str(device.voltage) + "</td></tr>"
else:
dev_name = device.name
if dev_name is None:
dev_name = "device name unknown"
if device.device_type is not None:
node_content += "<tr><th>Type: </th><td>" + device.device_type.model + "</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>"
if device.site is not None:
node_content += "<tr><th>Site: </th><td>" + device.site.name + "</td></tr>"
if device.location is not None:
node_content += "<tr><th>Location: </th><td>" + device.location.name + "</td></tr>"
if device.rack is not None:
node_content += "<tr><th>Rack: </th><td>" + device.rack.name + "</td></tr>"
if device.position is not None:
if device.face is not None:
node_content += "<tr><th>Position: </th><td> {} ({}) </td></tr>".format(device.position, device.face)
else:
node_content += "<tr><th>Position: </th><td>" + device.position + "</td></tr>"
node["id"] = device.id
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
dev_title = "<table><tbody> %s</tbody></table>" % (node_content)
node["title"] = dev_title
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
node["physics"] = True
node["physics"] = True
if "coordinates" in device.custom_field_data:
if device.custom_field_data["coordinates"] is not None:
if ";" in device.custom_field_data["coordinates"]:
@@ -75,191 +117,202 @@ def create_node(device, save_coords):
node["physics"] = True
return node
def create_edge(edge_id, cable, termination_a, termination_b, path = None, circuit = None):
cable_a_dev_name = "device A name unknown" if termination_a.device.name is None else termination_a.device.name
cable_a_name = "device A name unknown" if termination_a.name is None else termination_a.name
cable_b_dev_name = "device A name unknown" if termination_b.device.name is None else termination_b.device.name
cable_b_name = "cable B name unknown" if termination_b.name is None else termination_b.name
def create_edge(edge_id, termination_a, termination_b, circuit = None, cable = None, wireless = None, power=None):
cable_a_name = "device A name unknown" if termination_a["termination_name"] is None else termination_a["termination_name"]
cable_a_dev_name = "device A name unknown" if termination_a["termination_device_name"] is None else termination_a["termination_device_name"]
cable_b_name= "device A name unknown" if termination_b["termination_name"] is None else termination_b["termination_name"]
cable_b_dev_name = "cable B name unknown" if termination_b["termination_device_name"] is None else termination_b["termination_device_name"]
edge = {}
edge["id"] = edge_id
edge["from"] = termination_a.device.id
edge["to"] = termination_b.device.id
edge["from"] = termination_a["device_id"]
edge["to"] = termination_b["device_id"]
if circuit is not None:
edge["dashes"] = True
edge["title"] = "Circuit provider: " + circuit.provider.name + "<br>"
edge["title"] = "Circuit provider: " + circuit["provider_name"] + "<br>"
edge["title"] += "Termination between <br>"
edge["title"] += cable_b_dev_name + " [" + cable_b_name + "]<br>"
edge["title"] += cable_a_dev_name + " [" + cable_a_name + "]"
elif wireless is not None:
edge["dashes"] = [2, 10, 2, 10]
edge["title"] = "Wireless Connection between <br> " + cable_a_dev_name + " [" + cable_a_name + "]<br>" + cable_b_dev_name + " [" + cable_b_name + "]"
elif power is not None:
edge["dashes"] = [5, 5, 3, 3]
edge["title"] = "Power Connection between <br> " + cable_a_dev_name + " [" + cable_a_name + "]<br>" + cable_b_dev_name + " [" + cable_b_name + "]"
else:
edge["title"] = "Cable between <br> " + cable_a_dev_name + " [" + cable_a_name + "]<br>" + cable_b_dev_name + " [" + cable_b_name + "]"
if path is not None:
edge["title"] += "" if len(path) <= 0 else "<br>Through " + "/".join(path)
if cable is not None and cable.color != "":
edge["color"] = "#" + cable.color
return edge
def get_topology_data(queryset, hide_unconnected, save_coords, intermediate_dev_role_ids, end2end_connections):
def create_circuit_termination(termination):
if isinstance(termination, CircuitTermination):
return { "termination_name": termination.circuit.provider.name, "termination_device_name": termination.circuit.cid, "device_id": "c{}".format(termination.circuit.id) }
if isinstance(termination, Interface) or isinstance(termination, FrontPort) or isinstance(termination, RearPort):
return { "termination_name": termination.name, "termination_device_name": termination.device.name, "device_id": termination.device.id }
return None
def get_topology_data(queryset, hide_unconnected, save_coords, show_circuit, show_power):
nodes_devices = {}
edges = []
nodes = []
edge_ids = 0
cable_ids = []
circuit_ids = []
nodes_circuits = {}
nodes_powerpanel = {}
nodes_powerfeed = {}
nodes_provider_networks = {}
cable_ids = {}
if not queryset:
return None
ignore_cable_type = settings.PLUGINS_CONFIG["netbox_topology_views"]["ignore_cable_type"]
enable_circuit_terminations = settings.PLUGINS_CONFIG["netbox_topology_views"]["enable_circuit_terminations"]
device_ids = [d.id for d in queryset]
links = Cable.objects.filter( Q(_termination_a_device_id__in=device_ids) | Q(_termination_b_device_id__in=device_ids) ) \
.select_related("termination_a_type", "termination_b_type") \
.prefetch_related("termination_a", "termination_b")
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")
for circuit in circuits:
if not hide_unconnected and circuit.circuit.id not in nodes_circuits:
nodes_circuits[circuit.circuit.id] = circuit.circuit
termination_a = {}
termination_b = {}
circuit_model = {}
if circuit.cable is not None:
termination_a = create_circuit_termination(circuit.cable.a_terminations[0])
termination_b = create_circuit_termination(circuit.cable.b_terminations[0])
elif circuit.provider_network is not None:
if circuit.provider_network.id not in nodes_provider_networks:
nodes_provider_networks[circuit.provider_network.id] = circuit.provider_network
if bool(termination_a) and bool(termination_b):
circuit_model = {"provider_name": circuit.circuit.provider.name}
edge_ids += 1
edges.append(create_edge(edge_id=edge_ids,circuit=circuit_model, termination_a=termination_a, termination_b=termination_b))
circuit_has_connections = False
for termination in [circuit.cable.a_terminations[0], circuit.cable.b_terminations[0]]:
if not isinstance(termination, CircuitTermination):
if termination.device.id not in nodes_devices and termination.device.id in device_ids:
nodes_devices[termination.device.id] = termination.device
circuit_has_connections = True
else:
if termination.device.id in device_ids:
circuit_has_connections = True
if circuit_has_connections and hide_unconnected:
if circuit.circuit.id not in nodes_circuits:
nodes_circuits[circuit.circuit.id] = circuit.circuit
for d in nodes_circuits.values():
nodes.append(create_node(d, save_coords, circuit=True))
links = CableTermination.objects.filter( Q(_device_id__in=device_ids) ).select_related("termination_type")
wlan_links = WirelessLink.objects.filter( Q(_interface_a_device_id__in=device_ids) & Q(_interface_b_device_id__in=device_ids))
if show_power:
power_panels = PowerPanel.objects.filter( Q (site_id__in=site_ids))
power_panels_ids = [d.id for d in power_panels]
power_feeds = PowerFeed.objects.filter( Q (power_panel_id__in=power_panels_ids))
for power_feed in power_feeds:
if not hide_unconnected or (hide_unconnected and power_feed.cable_id is not None):
if power_feed.power_panel.id not in nodes_powerpanel:
nodes_powerpanel[power_feed.power_panel.id] = power_feed.power_panel
if power_feed.id not in nodes_powerfeed:
if hide_unconnected:
if power_feed.link_peers[0].device.id in device_ids:
nodes_powerfeed[power_feed.id] = power_feed
else:
nodes_powerfeed[power_feed.id] = power_feed
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_b = { "termination_name": power_feed.name, "termination_device_name": str(termination.id), "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))
if power_feed.cable_id is not None:
if power_feed.cable.id not in cable_ids:
cable_ids[power_feed.cable.id] = {}
cable_ids[power_feed.cable.id][power_feed.cable_end] = termination_b
for d in nodes_powerfeed.values():
nodes.append(create_node(d, save_coords, powerfeed = True))
for d in nodes_powerpanel.values():
nodes.append(create_node(d, save_coords, powerpanel = True))
for link in links:
if link.termination_a_type.name in ignore_cable_type or link.termination_b_type.name in ignore_cable_type \
or link.id in cable_ids:
if link.termination_type.name in ignore_cable_type :
continue
a_is_path_endoint = isinstance(link.termination_a, PathEndpoint)
b_is_path_endoint = isinstance(link.termination_b, PathEndpoint)
if not end2end_connections or (not a_is_path_endoint and not b_is_path_endoint):
if isinstance(link.termination_a, CircuitTermination):
if enable_circuit_terminations and link.termination_a.circuit.id not in circuit_ids:
circuit = link.termination_a.circuit
circuit_ids.append(circuit.id)
cable_ids.append(link.id)
path_destination = circuit.termination_z if link.termination_a.term_side == "A" else circuit.termination_a
if path_destination is not None and path_destination.provider_network is None:
# ProviderNetwork not supported at the moment
# $path_destination.cable would be none : there is no cable between a CircuitTermination and ProviderNetwork
if not hasattr(link.termination_b, 'device'):
#CircuitTermination B Missing Device
continue
origin_device = link.termination_b.device
destination_device = path_destination.cable.termination_b.device
if origin_device.id in device_ids and destination_device.id in device_ids:
if origin_device.id not in nodes_devices:
nodes_devices[origin_device.id] = origin_device
if destination_device.id not in nodes_devices:
nodes_devices[destination_device.id] = destination_device
edge_ids += 1
edges.append(create_edge(edge_ids, link, link.termination_b, path_destination.cable.termination_b, [circuit.cid], circuit))
elif link.termination_a.device.id in device_ids and link.termination_b.device.id in device_ids:
if link.termination_a.device.id not in nodes_devices:
nodes_devices[link.termination_a.device.id] = link.termination_a.device
if link.termination_b.device.id not in nodes_devices:
nodes_devices[link.termination_b.device.id] = link.termination_b.device
cable_ids.append(link.id)
edge_ids += 1
edges.append(create_edge(edge_ids, link, link.termination_a, link.termination_b))
else:
# termination_a can be a CircuitTermination (no trace()) while termination_b is a PathEndpoint
# If so, we swap them so we can follow the path using PathEndpoint#trace()
path_start = link.termination_a if a_is_path_endoint else link.termination_b
if path_start.device is not None and path_start.device.id not in device_ids:
# device not in queryset, skip
continue
# Ignore incomplete path when in end-to-end connection mode
if path_start.path is not None:
if not path_start.path.is_active or path_start.path.is_split:
continue
#Normal device cables
if link.termination_type.name in supported_termination_types:
complete_link = False
if link.cable_end == "A":
if link.cable.id not in cable_ids:
cable_ids[link.cable.id] = {}
else:
if cable_ids[link.cable.id]['B'] is not None:
complete_link = True
elif link.cable_end == "B":
if link.cable.id not in cable_ids:
cable_ids[link.cable.id] = {}
else:
if cable_ids[link.cable.id]['A'] is not None:
complete_link = True
else:
continue
print("Unkown cable end")
cable_ids[link.cable.id][link.cable_end] = link
path_destination = path_start.path.destination
if hasattr(path_destination, "device") and path_destination.device is not None and path_destination.device.id not in device_ids:
# device not in queryset, skip
continue
if complete_link:
edge_ids += 1
if isinstance(cable_ids[link.cable.id]["B"], CableTermination):
if cable_ids[link.cable.id]["B"]._device_id not in nodes_devices:
nodes_devices[cable_ids[link.cable.id]["B"]._device_id] = cable_ids[link.cable.id]["B"].termination.device
termination_b = { "termination_name": cable_ids[link.cable.id]["B"].termination.name, "termination_device_name": cable_ids[link.cable.id]["B"].termination.device.name, "device_id": cable_ids[link.cable.id]["B"].termination.device.id }
else:
termination_b = cable_ids[link.cable.id]["B"]
valid_path = False
if isinstance(cable_ids[link.cable.id]["A"], CableTermination):
if cable_ids[link.cable.id]["A"]._device_id not in nodes_devices:
nodes_devices[cable_ids[link.cable.id]["A"]._device_id] = cable_ids[link.cable.id]["A"].termination.device
termination_a = { "termination_name": cable_ids[link.cable.id]["A"].termination.name, "termination_device_name": cable_ids[link.cable.id]["A"].termination.device.name, "device_id": cable_ids[link.cable.id]["A"].termination.device.id }
else:
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))
for wlan_link in wlan_links:
if wlan_link.interface_a.device.id not in nodes_devices:
nodes_devices[wlan_link.interface_a.device.id] = wlan_link.interface_a.device
if wlan_link.interface_b.device.id not in nodes_devices:
nodes_devices[wlan_link.interface_b.device.id] = wlan_link.interface_b.device
termination_a = {"termination_name": wlan_link.interface_a.name, "termination_device_name": wlan_link.interface_a.device.name, "device_id": wlan_link.interface_a.device.id}
termination_b = {"termination_name": wlan_link.interface_b.name, "termination_device_name": wlan_link.interface_b.device.name, "device_id": wlan_link.interface_b.device.id}
wireless = {"ssid": wlan_link.ssid }
# variables needed for trace() browsing
origin = None
circuit = None
tmp_path = []
edge_ids += 1
edges.append(create_edge(edge_id=edge_ids, termination_a=termination_a, termination_b=termination_b,wireless=wireless))
# trace() : Return the path as a list of three-tuples (A termination, cable, B termination)
# TODO device qui ne s"affichent pas si hide_unconnected
for (termination_a, cable, termination_b) in path_start.trace():
if origin is None:
# New part of the link
origin = termination_a
circuit = None
tmp_path = []
if isinstance(termination_b, ProviderNetwork):
# ProviderNetwork not supported at the moment
# It would need to manage several kind of nodes (Device, ProviderNetwork)
# and maps javascript nodes ID with Devices IDs and ProviderNetworks IDs
# When $termination_b is a ProviderNetwork instance, $cable will be None
break
if cable is not None and (cable.termination_a_type in ignore_cable_type or cable.termination_b_type in ignore_cable_type):
# Ignore this path
break
if isinstance(termination_b, CircuitTermination):
if enable_circuit_terminations:
circuit = termination_b.circuit
tmp_path.append(circuit.cid)
if circuit.id not in circuit_ids:
circuit_ids.append(circuit.id)
else:
# Ignore this path
break
elif end2end_connections and termination_b != path_destination \
and termination_b.device.device_role.id not in intermediate_dev_role_ids \
and termination_b.device.id not in device_ids:
# Skip this intermediate device, origin device is keep in $origin for next iteration
tmp_path.append(termination_b.device.name)
elif cable is not None and termination_b is not None:
if cable.id not in cable_ids:
# New part of the link we want to display
cable_ids.append(cable.id)
edge_ids += 1
edges.append(create_edge(edge_ids, cable, origin, termination_b, tmp_path, circuit))
if not valid_path:
valid_path = True
if termination_b.device.id not in nodes_devices and \
(termination_b.device.device_role.id in intermediate_dev_role_ids or termination_b.device.id in device_ids) :
nodes_devices[termination_b.device.id] = termination_b.device
# Reset for next iteration
origin = None
# endfor (trace)
if valid_path and path_start.device.id not in nodes_devices:
nodes_devices[path_start.device.id] = path_start.device
# endfor (link)
for qs_device in queryset:
if qs_device.id not in nodes_devices and not hide_unconnected:
nodes_devices[qs_device.id] = qs_device
results = {}
results["nodes"] = [create_node(d, save_coords) for d in nodes_devices.values()]
for d in nodes_devices.values():
nodes.append(create_node(d, save_coords))
results["nodes"] = nodes
results["edges"] = edges
return results
@@ -286,34 +339,31 @@ class TopologyHomeView(PermissionRequiredMixin, View):
if request.GET["hide_unconnected"] == "on" :
hide_unconnected = True
if "intermediate_dev_role_id" in request.GET:
intermediate_dev_role_ids = list(map(int, request.GET.getlist("intermediate_dev_role_id")))
else:
intermediate_dev_role_ids = []
show_power = False
if "show_power" in request.GET:
if request.GET["show_power"] == "on" :
show_power = True
show_circuit = False
if "show_circuit" in request.GET:
if request.GET["show_circuit"] == "on" :
show_circuit = True
end2end_connections = False
if "end2end_connections" in request.GET:
if request.GET["end2end_connections"] == "on" :
end2end_connections = True
if "draw_init" in request.GET:
if request.GET["draw_init"].lower() == "true":
topo_data = get_topology_data(self.queryset, hide_unconnected, save_coords, intermediate_dev_role_ids, end2end_connections)
topo_data = get_topology_data(self.queryset, hide_unconnected, save_coords, show_circuit, show_power)
else:
topo_data = get_topology_data(self.queryset, hide_unconnected, save_coords, intermediate_dev_role_ids, end2end_connections)
topo_data = get_topology_data(self.queryset, hide_unconnected, save_coords, show_circuit, show_power)
else:
preselected_device_roles = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_device_roles"]
preselected_intermediate_dev_roles = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_intermediate_dev_roles"]
preselected_tags = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_tags"]
always_save_coordinates = bool(settings.PLUGINS_CONFIG["netbox_topology_views"]["always_save_coordinates"])
q_device_role_id = DeviceRole.objects.filter(name__in=preselected_device_roles).values_list("id", flat=True)
q_intermediate_dev_role_id = DeviceRole.objects.filter(name__in=preselected_intermediate_dev_roles).values_list("id", flat=True)
q_tags = Tag.objects.filter(name__in=preselected_tags).values_list("name", flat=True)
q = QueryDict(mutable=True)
q.setlist("device_role_id", list(q_device_role_id))
q.setlist("intermediate_dev_role_id", list(q_intermediate_dev_role_id))
q.setlist("tag", list(q_tags))
q["draw_init"] = settings.PLUGINS_CONFIG["netbox_topology_views"]["draw_default_layout"]
if always_save_coordinates: