Merge branch 'mattieserver:master' into update-git-ignore
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
IMPORTANT:
|
||||
==========
|
||||
Thank you for contributing to NetBox Topology Views! Please
|
||||
note that our contribution guidelines require a feature
|
||||
request or bug report to be labeled "status: accepted". This
|
||||
helps avoid waste time and effort on a proposed change that
|
||||
we might not be able to accept.
|
||||
|
||||
Please prefix the title of your pull request according to
|
||||
it's type. You should use "Closes #FEATURE_ID: ..." for feature
|
||||
requests and "Fixes #ISSUE_ID: ..." for issues. This helps
|
||||
us closing issues automatically after merging.
|
||||
|
||||
To sum it up:
|
||||
1. Create Bug Report or Feature Request
|
||||
2. Wait until the issue has been labeled "status: accepted"
|
||||
3. Start working on the issue in a seperate branch
|
||||
3. Create a pull request and name the title with a prefix
|
||||
according to the issue ID.
|
||||
-->
|
||||
@@ -1,93 +0,0 @@
|
||||
name: Docker
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '35 10 * * *'
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@7e0881f8fe90b25e305bbf0309761e9314607e25
|
||||
with:
|
||||
cosign-release: 'v1.9.0'
|
||||
|
||||
|
||||
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
# Sign the resulting Docker image digest except on PRs.
|
||||
# This will only write to the public Rekor transparency log when the Docker
|
||||
# repository is public to avoid leaking data. If you would like to publish
|
||||
# transparency data even for private images, pass --force to cosign below.
|
||||
# https://github.com/sigstore/cosign
|
||||
- name: Sign the published Docker image
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
# This step uses the identity token to provision an ephemeral certificate
|
||||
# against the sigstore community Fulcio instance.
|
||||
run: cosign sign ${{ steps.meta.outputs.tags }}@${{ steps.build-and-push.outputs.digest }}
|
||||
@@ -1,4 +0,0 @@
|
||||
FROM netboxcommunity/netbox:latest
|
||||
|
||||
COPY ./docker/plugin_requirements.txt /
|
||||
RUN /opt/netbox/venv/bin/pip install --no-warn-script-location -r /plugin_requirements.txt
|
||||
@@ -12,6 +12,8 @@ Support to filter on name, site, tag and device role.
|
||||
|
||||
## Install
|
||||
|
||||
**_NOTE:_** For docker please see: [Docker install](https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins)
|
||||
|
||||
The plugin is available as a Python package and can be installed with pip.
|
||||
|
||||
Run `pip install netbox-topology-views` in your virtual env.
|
||||
@@ -44,6 +46,7 @@ python3 manage.py collectstatic --no-input
|
||||
|
||||
| netbox version | netbox-topology-views version |
|
||||
| -------------- | ----------------------------- |
|
||||
| >= 3.4.0 | >= v3.X.X |
|
||||
| >= 3.3.0 | >= v3.0.0 |
|
||||
| >= 3.2.0 | >= v1.1.0 |
|
||||
| >= 3.1.8 | >= v1.0.0 |
|
||||
@@ -85,6 +88,9 @@ PLUGINS_CONFIG = {
|
||||
| ignore_cable_type | [] | The cable types that you want to ignore in the views |
|
||||
| preselected_tags | [] | The name of tags you want to preload |
|
||||
| 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) |
|
||||
| hide_single_cable_logical_conns | False | (bool) Set to True if you want to hide duplicate cables & logical connections |
|
||||
|
||||
|
||||
|
||||
### Custom Images
|
||||
|
||||
@@ -94,6 +100,31 @@ To change image with associated device use the `Images` page - it allows to map
|
||||
|
||||
Go to the plugins tab in the navbar and click topology or go to `$NETBOX_URL/plugins/netbox_topology_views/` to view your topologies
|
||||
|
||||
Select your options for the topology view:
|
||||
|
||||

|
||||
|
||||
<dl>
|
||||
<dt>Hide Unconnected</dt>
|
||||
<dd>Hide devices which have no connections.</dd>
|
||||
<dt>Save Coordinates</dt>
|
||||
<dd>Save the coordinates of devices in the topology view.</dd>
|
||||
<dd>Please read the "Configure" chapter to set the allow_coordinates_saving option to True.</dd>
|
||||
<dt>Show Cables</dt>
|
||||
<dd>Show cable connections between devices, including cables connected to interfaces, front / rear ports, etc., in the topology view</dd>
|
||||
<dt>Show Logical Connections</dt>
|
||||
<dd>Show logical connections between interfaces (referred to as Interface Connections in NetBox) in the topology view. Where the path between
|
||||
interfaces includes multiple cables (e.g., via patch panels), only the end interface connections are shown, not the
|
||||
intermediate front / rear port connections, etc. This is similar to what was referred to as 'end-to-end' connections in previous versions. </dd>
|
||||
<dd><i>Selecting both 'Show Cables' and 'Show Logical Interfaces' will sometimes result in a topology view with 2 cables per connection as these devices are diretly connected. Select only one of the options or set `hide_single_cable_logical_conns` to True.</i></dd>
|
||||
<dt>Show Circuit Terminations</dt>
|
||||
<dd>Show connections which end at a circuit termination in the topology view.</dd>
|
||||
<dt>Show Wireless Links</dt>
|
||||
<dd>Show wireless connections in the topology view.</dd>
|
||||
<dt>Show Power Feeds</dt>
|
||||
<dd>Show power connections from power feeds in the topology view.</dd>
|
||||
</dl>
|
||||
|
||||
### Update
|
||||
|
||||
Run `pip install netbox-topology-views --upgrade` in your venv.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
@@ -1 +0,0 @@
|
||||
netbox-topology-views
|
||||
@@ -29,6 +29,7 @@ class TopologyViewsConfig(PluginConfig):
|
||||
"always_save_coordinates": False,
|
||||
"preselected_tags": [],
|
||||
"draw_default_layout": False,
|
||||
"hide_single_cable_logical_conns": False,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,10 +31,14 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||
None,
|
||||
(
|
||||
"q",
|
||||
"filter_id",
|
||||
"hide_unconnected",
|
||||
"save_coords",
|
||||
"show_cables",
|
||||
"show_circuit",
|
||||
"show_logical_connections",
|
||||
"show_power",
|
||||
"show_wireless",
|
||||
),
|
||||
),
|
||||
(
|
||||
@@ -103,6 +107,15 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||
hide_unconnected = forms.BooleanField(
|
||||
label=_("Hide Unconnected"), required=False, initial=False
|
||||
)
|
||||
show_logical_connections = forms.BooleanField(
|
||||
label =_("Show Logical Connections"), required=False, initial=False
|
||||
)
|
||||
show_cables = forms.BooleanField(
|
||||
label =_("Show Cables"), required=False, initial=False
|
||||
)
|
||||
show_wireless = forms.BooleanField(
|
||||
label =_("Show Wireless Links"), required=False, initial=False
|
||||
)
|
||||
show_circuit = forms.BooleanField(
|
||||
label=_("Show Circuit Terminations"), required=False, initial=False
|
||||
)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from extras.plugins import PluginMenuItem
|
||||
from extras.plugins import PluginMenu, PluginMenuItem
|
||||
|
||||
menu_items = (
|
||||
PluginMenuItem(link="plugins:netbox_topology_views:home", link_text="Topology"),
|
||||
PluginMenuItem(
|
||||
link="plugins:netbox_topology_views:images", link_text="Topology Images"
|
||||
menu = PluginMenu(
|
||||
label='Topology Views',
|
||||
groups=(
|
||||
('Topology', (PluginMenuItem(link="plugins:netbox_topology_views:home", link_text="Topology"),),),
|
||||
('CUSTOMIZATION', (PluginMenuItem(link="plugins:netbox_topology_views:images", link_text="Topology Images"),),),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1 +1 @@
|
||||
#visgraph{height:70vh}html[data-netbox-color-mode=dark] #visgraph{background-color:#212529}.image-dropdown img{width:64px;height:64px}.image-dropdown-content{display:flex;flex-wrap:wrap;gap:.5rem;padding-inline:.5rem;width:50vw;max-width:32rem}.image-dropdown-content>img{cursor:pointer}
|
||||
#visgraph{height:64vh}html[data-netbox-color-mode=dark] #visgraph{background-color:#212529}.image-dropdown img{width:64px;height:64px}.image-dropdown-content{display:flex;flex-wrap:wrap;gap:.5rem;padding-inline:.5rem;width:50vw;max-width:32rem}.image-dropdown-content>img{cursor:pointer}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
#visgraph {
|
||||
height: 70vh;
|
||||
height: 64vh;
|
||||
}
|
||||
|
||||
html[data-netbox-color-mode=dark] #visgraph {
|
||||
|
||||
@@ -100,9 +100,16 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
||||
})
|
||||
|
||||
graph.on('doubleClick', (params) => {
|
||||
params.nodes.forEach((node) => {
|
||||
window.open(nodes.get(node).href, '_blank')
|
||||
})
|
||||
if (params.nodes.length > 0) {
|
||||
params.nodes.forEach((node) => {
|
||||
window.open(nodes.get(node).href, '_blank')
|
||||
})
|
||||
}
|
||||
else {
|
||||
params.edges.forEach((edge) => {
|
||||
window.open(edges.get(edge).href, '_blank')
|
||||
})
|
||||
}
|
||||
})
|
||||
})()
|
||||
|
||||
|
||||
@@ -48,6 +48,12 @@
|
||||
{% with config=settings.PLUGINS_CONFIG.netbox_topology_views %}
|
||||
<div class="tab-content">
|
||||
|
||||
{# Applied filters #}
|
||||
{% if filter_form %}
|
||||
{% applied_filters model filter_form request.GET %}
|
||||
<hr style="margin-top: 0px;">
|
||||
{% endif %}
|
||||
|
||||
<div class="tab-pane show active" id="networks" role="tabpanel" aria-labelledby="network-tab">
|
||||
<div class="panel-body">
|
||||
<div id="visgraph"></div>
|
||||
|
||||
+192
-115
@@ -9,6 +9,7 @@ from dcim.models import (
|
||||
Cable,
|
||||
CableTermination,
|
||||
Device,
|
||||
device_components,
|
||||
DeviceRole,
|
||||
FrontPort,
|
||||
Interface,
|
||||
@@ -177,6 +178,7 @@ def create_edge(
|
||||
cable: Optional[Cable] = None,
|
||||
wireless: Optional[Dict] = None,
|
||||
power: Optional[bool] = None,
|
||||
interface: Optional[bool] = None,
|
||||
):
|
||||
cable_a_name = (
|
||||
"device A name unknown"
|
||||
@@ -217,12 +219,21 @@ def create_edge(
|
||||
edge["dashes"] = [5, 5, 3, 3]
|
||||
title = "Power Connection"
|
||||
|
||||
elif interface is not None:
|
||||
title = "Interface Connection"
|
||||
edge["width"] = 3
|
||||
edge["dashes"] = [1, 10, 1, 10]
|
||||
edge["arrows"] = {"to": {"enabled": True, "scaleFactor": 0.5}, "from": {"enabled": True, "scaleFactor": 0.5}}
|
||||
edge["color"] = '#f1c232'
|
||||
|
||||
edge[
|
||||
"title"
|
||||
] = f"{title} between<br>{cable_a_dev_name} [{cable_a_name}]<br>{cable_b_dev_name} [{cable_b_name}]"
|
||||
|
||||
if cable is not None and cable.color != "":
|
||||
edge["color"] = "#" + cable.color
|
||||
if cable is not None:
|
||||
edge["href"] = cable.get_absolute_url()
|
||||
if hasattr(cable, 'color') and cable.color != "":
|
||||
edge["color"] = "#" + cable.color
|
||||
|
||||
return edge
|
||||
|
||||
@@ -251,8 +262,11 @@ def get_topology_data(
|
||||
queryset: QuerySet,
|
||||
hide_unconnected: bool,
|
||||
save_coords: bool,
|
||||
show_cables: bool,
|
||||
show_circuit: bool,
|
||||
show_logical_connections: bool,
|
||||
show_power: bool,
|
||||
show_wireless: bool,
|
||||
):
|
||||
if not queryset:
|
||||
return None
|
||||
@@ -266,9 +280,15 @@ def get_topology_data(
|
||||
nodes_powerfeed: Dict[int, PowerFeed] = {}
|
||||
nodes_provider_networks = {}
|
||||
cable_ids = DefaultDict(dict)
|
||||
interface_ids = DefaultDict(dict)
|
||||
ignore_cable_type = settings.PLUGINS_CONFIG["netbox_topology_views"][
|
||||
"ignore_cable_type"
|
||||
]
|
||||
hide_single_cable_logical_conns = bool(
|
||||
settings.PLUGINS_CONFIG["netbox_topology_views"][
|
||||
"hide_single_cable_logical_conns"
|
||||
]
|
||||
)
|
||||
|
||||
device_ids = [d.pk for d in queryset]
|
||||
site_ids = [d.site_id for d in queryset]
|
||||
@@ -314,6 +334,7 @@ def get_topology_data(
|
||||
edges.append(
|
||||
create_edge(
|
||||
edge_id=edge_ids,
|
||||
cable=circuit_termination.cable,
|
||||
circuit=circuit_model,
|
||||
termination_a=termination_a,
|
||||
termination_b=termination_b,
|
||||
@@ -345,15 +366,6 @@ def get_topology_data(
|
||||
for d in nodes_circuits.values():
|
||||
nodes.append(create_node(d, save_coords))
|
||||
|
||||
links: QuerySet[CableTermination] = CableTermination.objects.filter(
|
||||
Q(_device_id__in=device_ids)
|
||||
).select_related("termination_type")
|
||||
|
||||
wlan_links: QuerySet[WirelessLink] = WirelessLink.objects.filter(
|
||||
Q(_interface_a_device_id__in=device_ids)
|
||||
& Q(_interface_b_device_id__in=device_ids)
|
||||
)
|
||||
|
||||
if show_power:
|
||||
power_panels_ids = PowerPanel.objects.filter(
|
||||
Q(site_id__in=site_ids)
|
||||
@@ -407,112 +419,154 @@ def get_topology_data(
|
||||
for d in nodes_powerpanel.values():
|
||||
nodes.append(create_node(d, save_coords))
|
||||
|
||||
for link in links:
|
||||
if link.termination_type.name in ignore_cable_type:
|
||||
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 "B" in cable_ids[link.cable_id]:
|
||||
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 "A" in cable_ids[link.cable_id]:
|
||||
if cable_ids[link.cable_id]["A"] is not None:
|
||||
complete_link = True
|
||||
else:
|
||||
print("Unkown cable end")
|
||||
cable_ids[link.cable_id][link.cable_end] = link
|
||||
|
||||
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"]
|
||||
|
||||
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.pk
|
||||
] = wlan_link.interface_a.device
|
||||
if wlan_link.interface_b.device_id not in nodes_devices:
|
||||
nodes_devices[
|
||||
wlan_link.interface_b.device.pk
|
||||
] = 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}
|
||||
|
||||
edge_ids += 1
|
||||
edges.append(
|
||||
create_edge(
|
||||
edge_id=edge_ids,
|
||||
termination_a=termination_a,
|
||||
termination_b=termination_b,
|
||||
wireless=wireless,
|
||||
)
|
||||
if show_logical_connections:
|
||||
interfaces = Interface.objects.filter(
|
||||
Q(_path__is_complete=True) & Q(device_id__in=device_ids)
|
||||
)
|
||||
|
||||
for interface in interfaces:
|
||||
# print('{} {} {} {}'.format(interface.device.name, interface.name, interface._path.destinations[0].device.name, interface._path.destinations[0].name))
|
||||
for destination in interface._path.destinations:
|
||||
if isinstance(destination, device_components.Interface):
|
||||
if destination.device.id not in device_ids:
|
||||
# print('Destination interface not in device queryset, ignoring')
|
||||
continue
|
||||
|
||||
if destination.id in interface_ids:
|
||||
# we've already captured the destination interface, ignore this connection
|
||||
# print('Destination interface already exists, ignoring')
|
||||
continue
|
||||
|
||||
if hide_single_cable_logical_conns and interface.cable_id==destination.cable_id and show_cables:
|
||||
# interface connection is the same as the cable connection, ignore this connection
|
||||
continue
|
||||
|
||||
interface_ids[interface.id]=interface
|
||||
edge_ids += 1
|
||||
termination_a = { "termination_name": interface.name, "termination_device_name": interface.device.name, "device_id": interface.device.id }
|
||||
termination_b = { "termination_name": destination.name, "termination_device_name": destination.device.name, "device_id": destination.device.id }
|
||||
edges.append(create_edge(edge_id=edge_ids, termination_a=termination_a, termination_b=termination_b, interface=True))
|
||||
nodes_devices[interface.device.id] = interface.device
|
||||
nodes_devices[destination.device.id] = destination.device
|
||||
|
||||
if show_cables:
|
||||
links: QuerySet[CableTermination] = CableTermination.objects.filter(
|
||||
Q(_device_id__in=device_ids)
|
||||
).select_related("termination_type")
|
||||
|
||||
for link in links:
|
||||
if link.termination_type.name in ignore_cable_type:
|
||||
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 "B" in cable_ids[link.cable_id]:
|
||||
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 "A" in cable_ids[link.cable_id]:
|
||||
if cable_ids[link.cable_id]["A"] is not None:
|
||||
complete_link = True
|
||||
else:
|
||||
print("Unkown cable end")
|
||||
cable_ids[link.cable_id][link.cable_end] = link
|
||||
|
||||
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"]
|
||||
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
if show_wireless:
|
||||
wlan_links: QuerySet[WirelessLink] = WirelessLink.objects.filter(
|
||||
Q(_interface_a_device_id__in=device_ids)
|
||||
& Q(_interface_b_device_id__in=device_ids)
|
||||
)
|
||||
|
||||
for wlan_link in wlan_links:
|
||||
if wlan_link.interface_a.device_id not in nodes_devices:
|
||||
nodes_devices[
|
||||
wlan_link.interface_a.device.pk
|
||||
] = wlan_link.interface_a.device
|
||||
if wlan_link.interface_b.device_id not in nodes_devices:
|
||||
nodes_devices[
|
||||
wlan_link.interface_b.device.pk
|
||||
] = 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}
|
||||
|
||||
edge_ids += 1
|
||||
edges.append(
|
||||
create_edge(
|
||||
edge_id=edge_ids,
|
||||
cable=wlan_link,
|
||||
termination_a=termination_a,
|
||||
termination_b=termination_b,
|
||||
wireless=wireless,
|
||||
)
|
||||
)
|
||||
|
||||
for qs_device in queryset:
|
||||
if qs_device.pk not in nodes_devices and not hide_unconnected:
|
||||
nodes_devices[qs_device.pk] = qs_device
|
||||
@@ -540,6 +594,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
||||
"device_type", "device_role"
|
||||
)
|
||||
self.queryset = self.filterset(request.GET, self.queryset).qs
|
||||
self.model = self.queryset.model
|
||||
topo_data = None
|
||||
|
||||
if request.GET:
|
||||
@@ -563,22 +618,43 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
||||
if request.GET["show_circuit"] == "on":
|
||||
show_circuit = True
|
||||
|
||||
show_logical_connections = False
|
||||
if "show_logical_connections" in request.GET:
|
||||
if request.GET["show_logical_connections"] == "on" :
|
||||
show_logical_connections = True
|
||||
|
||||
show_cables = False
|
||||
if "show_cables" in request.GET:
|
||||
if request.GET["show_cables"] == "on" :
|
||||
show_cables = True
|
||||
|
||||
show_wireless = False
|
||||
if "show_wireless" in request.GET:
|
||||
if request.GET["show_wireless"] == "on" :
|
||||
show_wireless = 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,
|
||||
show_cables,
|
||||
show_circuit,
|
||||
show_logical_connections,
|
||||
show_power,
|
||||
show_wireless,
|
||||
)
|
||||
else:
|
||||
topo_data = get_topology_data(
|
||||
self.queryset,
|
||||
hide_unconnected,
|
||||
save_coords,
|
||||
show_cables,
|
||||
show_circuit,
|
||||
show_logical_connections,
|
||||
show_power,
|
||||
show_wireless,
|
||||
)
|
||||
else:
|
||||
preselected_device_roles = settings.PLUGINS_CONFIG["netbox_topology_views"][
|
||||
@@ -630,6 +706,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
||||
"filter_form": DeviceFilterForm(request.GET, label_suffix=""),
|
||||
"topology_data": json.dumps(topo_data),
|
||||
"broken_image": find_image_url("role-unknown"),
|
||||
"model": self.model,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user