Compare commits

..
16 Commits
Author SHA1 Message Date
mattieserver 0340522f59 update jquery version to match netbox v2.9.1 (#43)
* Update full.html

* Update setup.py

* Update package.json

* Update __init__.py
2020-08-23 11:53:52 +02:00
Mattijs Vanhaverbeke 2edd597871 bump version 2020-07-20 15:49:06 +02:00
mattieserver 5f4f97c360 Merge pull request #41 from mattieserver/32
added Filter topology via URL Parameter
2020-07-20 15:00:10 +02:00
Mattijs Vanhaverbeke 17ae8b4a1d #32 2020-07-20 14:57:09 +02:00
mattieserver bb9765e011 Merge pull request #40 from mattieserver/37
add filter by region
2020-07-20 12:38:15 +02:00
Mattijs Vanhaverbeke 5bfc47775b #37 2020-07-20 12:36:38 +02:00
mattieserver 216508170f Merge pull request #39 from mattieserver/dependabot/npm_and_yarn/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19
2020-07-20 11:07:20 +02:00
dependabot[bot] 0faa135978 Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-20 07:57:54 +00:00
mattieserver 9ba61cce28 Merge pull request #28 from mattieserver/22052020_dev
22052020 dev
2020-05-24 18:31:07 +02:00
mattieserver 9824a3f659 Merge branch 'master' into 22052020_dev 2020-05-24 18:30:59 +02:00
mattieserver 4115584306 Update README.md 2020-05-23 18:20:04 +02:00
mattieserver cd239ac6ae Update README.md 2020-05-23 18:19:13 +02:00
mattieserver 030844f2fb Merge pull request #27 from ryanmerolle/rm-issue-26
Update README to include local_requirements setup
2020-05-23 18:15:56 +02:00
ryanmerolle 2e6dd7de23 Update README to include local_requirements setup 2020-05-23 12:04:19 -04:00
Mattijs Vanhaverbeke de5707ff06 PATCH instead of POST 2020-05-22 12:57:58 +02:00
Mattijs Vanhaverbeke 371c5c3502 added download button, fixed perms 2020-05-22 12:07:46 +02:00
14 changed files with 933 additions and 254 deletions
+25 -2
View File
@@ -10,9 +10,22 @@ Support to filter on name, site, tag and device role.
## Install
The plugin is available as a Python package and can be installed with pip.
Run `pip install netbox-topology-views` in your virtual env.
Add 'netbox_topology_views' to the PLUGINS array in your django configuration file.
To ensure NetBox Topology Views plugin is automatically re-installed during future upgrades, create a file named `local_requirements.txt` (if not already existing) in the NetBox root directory (alongside `requirements.txt`) and list the `netbox-topology-views` package:
```no-highlight
# echo netbox-topology-views >> local_requirements.txt
```
Once installed, the plugin needs to be enabled in your `configuration.py`
```python
# In your configuration.py
PLUGINS = ["netbox_topology_views"]
```
Then run `python3 manage.py collectstatic --no-input`
@@ -63,4 +76,14 @@ Run `pip install netbox-topology-views --upgrade` in your venv.
Run `python3 manage.py collectstatic --no-input`
Clear your browser cache.
Clear you browser cache.
### Permissions
To view `/plugins/topology-views/` you need the following permissions:
+ dcim | device | can view device
+ dcim | site | can view site
+ extras | tag | can view tag
+ dcim | device role | can view device role
+1 -1
View File
@@ -4,7 +4,7 @@ class TopologyViewsConfig(PluginConfig):
name = 'netbox_topology_views'
verbose_name = 'Topology views'
description = 'An plugin to render toplogoy maps'
version = '0.4.8'
version = '0.4.10'
author = 'Mattijs Vanhaverbeke'
author_email = 'author@example.com'
base_url = 'topology-views'
+27 -22
View File
@@ -30,11 +30,11 @@ class PreSelectTagsViewSet(ReadOnlyModelViewSet):
queryset = Tag.objects.filter(name__in=preselected_tags)
serializer_class = PreTagSerializer
class SaveCoordsViewSet(GenericViewSet):
class SaveCoordsViewSet(ReadOnlyModelViewSet):
queryset = Device.objects.all()
serializer_class = TopologyDummySerializer
@action(detail=False, methods=['post'])
@action(detail=False, methods=['patch'])
def save_coords(self, request):
results = {}
if settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"]:
@@ -76,14 +76,14 @@ class SaveCoordsViewSet(GenericViewSet):
results["status"] = "not allowed to save coords"
return Response(results, status=500)
class SearchViewSet(GenericViewSet):
class SearchViewSet(ReadOnlyModelViewSet):
#_ignore_model_permissions = True
#permission_classes = [IsAuthenticatedOrLoginNotRequired]
queryset = Device.objects.all()
serializer_class = TopologyDummySerializer
def _filter(self, site, role, name, tags):
def _filter(self, site, role, name, tags, region):
filter_devices = Device.objects.all()
if name is not None:
filter_devices = filter_devices.filter(name__contains=name)
@@ -93,29 +93,34 @@ class SearchViewSet(GenericViewSet):
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)
return filter_devices
@action(detail=False, methods=['post'])
@action(detail=False, methods=['get'])
def search(self, request):
name = None
sites = None
devicerole = None
tags = None
if "devicerole" in request.data:
if request.data["devicerole"]:
devicerole = request.data["devicerole"]
if "sites" in request.data:
if request.data["sites"]:
sites = request.data["sites"]
if "name" in request.data:
if request.data["name"]:
name = request.data["name"]
if "tags" in request.data:
if request.data["tags"]:
tags = request.data["tags"]
name = request.query_params.get('name', None)
if name == "":
name = None
devices = self._filter(sites, devicerole, name, tags)
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
devices = self._filter(sites, devicerole, name, tags, regions)
nodes = []
edges = []
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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];
}
});
}
+91 -10
View File
@@ -1,5 +1,8 @@
var graph = null;
var container = null;
var downloadButton = null;
var MIME_TYPE = "image/png";
var canvas = null;
var csrftoken = null;
var nodes = new vis.DataSet();
var edges = new vis.DataSet();
@@ -29,6 +32,7 @@ var options = {
solver: 'forceAtlas2Based'
}
};
var selected_regions = [];
function iniPlotboxIndex() {
document.addEventListener('DOMContentLoaded', function () {
@@ -36,6 +40,8 @@ function iniPlotboxIndex() {
csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
startLoadSearchBar();
handleButtonPress();
downloadButton = document.getElementById('btnDownloadImage');
btnFullView = document.getElementById('btnFullView');
}, false);
}
@@ -73,11 +79,23 @@ function startLoadSearchBar() {
theme: "bootstrap",
multiple: true,
ajax: {
url: "../../api/dcim/sites/?brief=true",
url: function (params) {
var base_url = "../../api/dcim/sites/?brief=true";
if (selected_regions.length == 0) {
return base_url;
}
else {
for (var i = 0; i < selected_regions.length; i++) {
var tmp = "&region_id=" + selected_regions[i];
base_url = base_url + tmp;
}
return base_url;
}
},
dataType: "json",
type: "GET",
data: function (params) {
var queryParameters = {
q: params.term
}
@@ -122,6 +140,53 @@ function startLoadSearchBar() {
}
}
});
$('#regions').select2({
allowClear: true,
placeholder: "---------",
theme: "bootstrap",
multiple: true,
ajax: {
url: "../../api/dcim/regions/?brief=true",
dataType: "json",
type: "GET",
data: function (params) {
var queryParameters = {
q: params.term
}
return queryParameters;
},
processResults: function (data) {
return {
results: $.map(data.results, function (item) {
return {
text: item.name,
id: item.id
}
})
};
}
}
});
$('#regions').on('select2:select', function (e) {
var data = e.params.data;
var index = selected_regions.indexOf(data.id.toString());
if (index == -1) {
selected_regions.push(data.id.toString());
}
});
$('#regions').on('select2:unselect', function (e) {
var data = e.params.data;
var index = selected_regions.indexOf(data.id.toString());
console.log(index);
if (index > -1) {
selected_regions.splice(index, 1);
}
});
$('#regions').on('select2:clear', function (e) {
selected_regions = [];
});
var deviceRolesSelect = $('#device-roles');
$.ajax({
@@ -166,19 +231,29 @@ function handleButtonPress() {
var value2 = $("#device-roles").val();
var value3 = $("#sites").val();
var value4 = $("#tags").val();
var value5 = $("#regions").val();
$.ajax({
type: "POST",
type: "GET",
url: "../../api/plugins/topology-views/search/search/",
data: JSON.stringify({
data: {
'name': value,
'devicerole': value2,
'sites': value3,
'tags': value4
}),
'devicerole[]': value2,
'sites[]': value3,
'tags[]': value4,
'regions[]': value5
},
headers: { "X-CSRFToken": csrftoken },
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data_result) {
beforeSend: function(){
new_url = this.url.split("../../api/plugins/topology-views/search/search/?");
new_url = new_url[1];
new_url = "../../plugins/topology-views/full?" + new_url
btnFullView.classList.remove("disabled");
btnFullView.href = new_url;
},
success: function (data_result, status, xhr) {
$("#status").html('<span class="label label-info">Drawing network</span>');
graph = null;
nodes = new vis.DataSet();
@@ -191,9 +266,13 @@ function handleButtonPress() {
edges.add(edge);
});
graph.fit();
canvas = document.getElementById('visgraph').getElementsByTagName('canvas')[0];
graph.on('afterDrawing', function () {
$("#status").html('<span class="label label-success">Ready</span>');
var image = canvas.toDataURL(MIME_TYPE);
downloadButton.href = image;
downloadButton.download = "topology";
});
graph.on("dragEnd", function (params) {
@@ -204,10 +283,11 @@ function handleButtonPress() {
//nodes.update({ id: node_id, physics: false });
$.ajax({
url: "../../api/plugins/topology-views/save-coords/save_coords/",
type: 'POST',
type: 'PATCH',
dataType: 'json',
headers: { "X-CSRFToken": csrftoken },
contentType: "application/json; charset=utf-8",
processData: false,
data: JSON.stringify({
'node_id': node_id,
'x': coordinates.x,
@@ -223,6 +303,7 @@ function handleButtonPress() {
}
});
});
},
error: function (error_result) {
$("#status").html('<span class="label label-warning">Something went wrong</span>');
@@ -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 %}
@@ -12,6 +12,14 @@
<div class="panel panel-default">
<div class="panel-heading">
<strong>Network</strong> <span id="status"></span>
<div class="buttons pull-right">
<a id="btnDownloadImage" class="btn btn-xs btn-info">
<i class="fa fa-download"></i>
</a>
<a id="btnFullView" class="btn btn-xs btn-info disabled" target="_blank">
<i class="fa fa-share"></i>
</a>
</div>
</div>
<div class="panel-body">
<div id="visgraph" class=""></div>
@@ -41,6 +49,10 @@
<div class="form-group">
<label for="tags">Tags</label>
<select class="form-control" multiple="multiple" id="tags"></select>
</div>
<div class="form-group">
<label for="regions">Regions</label>
<select class="form-control" multiple="multiple" id="regions"></select>
</div>
<hr class="mb-4">
<button class="btn btn-primary btn-lg btn-block" type="submit" id="start-search">Search</button>
+1
View File
@@ -7,4 +7,5 @@ from . import views
# a specific view so that it can be accessed by users.
urlpatterns = (
path('', views.TopologyHomeView.as_view(), name='home'),
path('full', views.TopologyFullView.as_view(), name='full'),
)
+10 -1
View File
@@ -9,4 +9,13 @@ class TopologyHomeView(PermissionRequiredMixin, View):
Show the home page
"""
def get(self, request):
return render(request, 'netbox_topology_views/index.html')
return render(request, 'netbox_topology_views/index.html')
class TopologyFullView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_site', 'dcim.view_device')
"""
Show the full view page
"""
def get(self, request):
return render(request, 'netbox_topology_views/full.html')
+692 -211
View File
File diff suppressed because it is too large Load Diff
+14 -4
View File
@@ -1,7 +1,7 @@
{
"private": true,
"name": "netbox_topology_views",
"version": "0.4.8",
"version": "0.4.10",
"scripts": {
"resources": "gulp build",
"resources_dev": "gulp build_dev"
@@ -9,10 +9,20 @@
"dependencies": {},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-clean-css": "^4.2.0",
"gulp-clean-css": "^4.3.0",
"gulp-concat": "^2.6.1",
"gulp-sass": "^4.0.2",
"gulp-sass": "^4.1.0",
"gulp-uglify": "^3.0.2",
"vis-network": "^7.6.4"
"vis-network": "^7.10.0",
"keycharm": "^0.3.0",
"moment": "^2.24.0",
"timsort": "^0.3.0",
"vis-data": "^6.2.1",
"vis-util": "^4.0.0",
"uuid": "7.0.0",
"@egjs/hammerjs" : "^2.0.0"
},
"peerDependencies": {
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
setup(
name='netbox-topology-views',
version='0.4.8',
version='0.4.10',
description='An NetBox plugin to create Topology maps',
url='https://github.com/mattieserver/netbox-topology-views',
author='Mattijs Vanhaverbeke',