Compare commits

..
16 Commits
14 changed files with 103 additions and 110 deletions
+1 -2
View File
@@ -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 |
+2 -2
View File
@@ -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.1.0' version = '1.0.0-beta.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'
@@ -15,7 +15,7 @@ class TopologyViewsConfig(PluginConfig):
'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': True,
'draw_default_layout': False 'draw_default_layout': False
} }
+3
View File
@@ -0,0 +1,3 @@
from django.contrib import admin
#no models yet
+7 -12
View File
@@ -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."""
+11 -17
View File
@@ -1,30 +1,22 @@
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 django import forms
from tenancy.models import TenantGroup, Tenant
from tenancy.forms import TenancyFilterForm
from django.conf import settings from django.conf import settings
from netbox.forms import NetBoxModelFilterSetForm
from utilities.forms import (TagFilterField, DynamicModelMultipleChoiceField)
from utilities.forms import (TagFilterField, DynamicModelMultipleChoiceField, FilterForm)
allow_coordinates_saving = settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"] allow_coordinates_saving = settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"]
class DeviceFilterForm(FilterForm):
class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = Device model = Device
fieldsets = ( field_groups = [
(None, ('q', 'hide_unconnected', 'save_coords')), ['q', 'hide_unconnected', 'save_coords'],
(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,6 +45,7 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
}, },
label=_('Location') label=_('Location')
) )
hide_unconnected = forms.BooleanField( hide_unconnected = forms.BooleanField(
label=_("Hide Unconnected"), label=_("Hide Unconnected"),
required=False, required=False,
@@ -63,4 +56,5 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
required=False, required=False,
disabled=(not allow_coordinates_saving), disabled=(not allow_coordinates_saving),
initial=False) initial=False)
tag = TagFilterField(model) tag = TagFilterField(model)
+3
View File
@@ -0,0 +1,3 @@
from django.db import models
# no modeles yet
+9 -1
View File
@@ -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 @@
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"}},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 n=document.cookie.split(";"),o=0;o<n.length;o++){var a=n[o].trim();if(a.substring(0,e.length+1)===e+"="){t=decodeURIComponent(a.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],handleLoadData(),downloadButton=document.getElementById("btnDownloadImage"),btnFullView=document.getElementById("btnFullView"),coord_save_checkbox=document.getElementById("id_save_coords")},!1)}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],graph.on("afterDrawing",function(){var e=canvas.toDataURL(MIME_TYPE);downloadButton.href=e,downloadButton.download="topology"}),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,n=new XMLHttpRequest;n.open("PATCH","/api/plugins/netbox_topology_views/save-coords/save_coords/"),n.setRequestHeader("X-CSRFToken",csrftoken),n.setRequestHeader("Accept","application/json"),n.setRequestHeader("Content-Type","application/json"),n.onreadystatechange=function(){4===n.readyState&&(console.log(n.status),console.log(n.responseText))};var o=JSON.stringify({node_id:t,x:dragged[t].x,y:dragged[t].y});n.send(o)}}))} 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"}},physics:{solver:"forceAtlas2Based"}},selected_regions=[],selected_sites=[],coord_save_checkbox=null;function getCookie(e){var t=null;if(document.cookie&&""!==document.cookie)for(var n=document.cookie.split(";"),o=0;o<n.length;o++){var a=n[o].trim();if(a.substring(0,e.length+1)===e+"="){t=decodeURIComponent(a.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"),handleLoadData(),downloadButton=document.getElementById("btnDownloadImage"),btnFullView=document.getElementById("btnFullView"),coord_save_checkbox=document.getElementById("id_save_coords")},!1)}function handleLoadData(){null!==topology_data&&(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"}),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,n=new XMLHttpRequest;n.open("PATCH","/api/plugins/netbox_topology_views/save-coords/save_coords/"),n.setRequestHeader("X-CSRFToken",csrftoken),n.setRequestHeader("Accept","application/json"),n.setRequestHeader("Content-Type","application/json"),n.onreadystatechange=function(){4===n.readyState&&(console.log(n.status),console.log(n.responseText))};var o=JSON.stringify({node_id:t,x:dragged[t].x,y:dragged[t].y});n.send(o)}}))}
@@ -35,7 +35,6 @@ var options = {
var selected_regions = []; var selected_regions = [];
var selected_sites = []; var selected_sites = [];
var coord_save_checkbox = null; var coord_save_checkbox = null;
var htmlElement = null;
function getCookie(name) { function getCookie(name) {
var cookieValue = null; var cookieValue = null;
@@ -74,7 +73,6 @@ function iniPlotboxIndex() {
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
csrftoken = getCookie('csrftoken'); csrftoken = getCookie('csrftoken');
container = document.getElementById('visgraph'); container = document.getElementById('visgraph');
htmlElement = document.getElementsByTagName("html")[0];
handleLoadData(); handleLoadData();
downloadButton = document.getElementById('btnDownloadImage'); downloadButton = document.getElementById('btnDownloadImage');
btnFullView = document.getElementById('btnFullView'); btnFullView = document.getElementById('btnFullView');
@@ -84,11 +82,6 @@ function iniPlotboxIndex() {
function handleLoadData() { function handleLoadData() {
if (topology_data !== null) { 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();
edges = new vis.DataSet(); edges = new vis.DataSet();
@@ -36,7 +36,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,7 +45,7 @@
{% 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">
@@ -65,11 +65,10 @@
{% 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 %}
-1
View File
@@ -66,7 +66,6 @@ def get_topology_data(queryset, hide_unconnected):
device_has_connections = True device_has_connections = True
else: else:
if settings.PLUGINS_CONFIG["netbox_topology_views"]["enable_circuit_terminations"]: if settings.PLUGINS_CONFIG["netbox_topology_views"]["enable_circuit_terminations"]:
if link_from.termination_a_type.name == "circuit termination":
if link_from.termination_a.circuit.id not in circuit_ids: if link_from.termination_a.circuit.id not in circuit_ids:
circuit_ids.append(link_from.termination_a.circuit.id) circuit_ids.append(link_from.termination_a.circuit.id)
edge_ids += 1 edge_ids += 1
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "netbox_topology_views", "name": "netbox_topology_views",
"version": "1.1.0", "version": "1.0.0-beta.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "netbox_topology_views", "name": "netbox_topology_views",
"version": "1.1.0", "version": "1.0.0-beta.1",
"scripts": { "scripts": {
"resources": "gulp build", "resources": "gulp build",
"resources_dev": "gulp build_dev" "resources_dev": "gulp build_dev"
+1 -1
View File
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
setup( setup(
name='netbox-topology-views', name='netbox-topology-views',
version='1.1.0', version='1.0.0-beta.1',
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',