added search by tag (#8)
This commit is contained in:
@@ -43,6 +43,7 @@ PLUGINS_CONFIG = {
|
||||
| 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|
|
||||
| allow_coordinates_saving | False | (bool) Set to true if you use the custom coordinates fields and want to save the coordinates |
|
||||
| ignore_cable_type | 'poweroutlet,powerport' | The cable types that you want to ignore in the views |
|
||||
| preselected_tags | '' | The name of tags you want to preload |
|
||||
|
||||
|
||||
### Custom Images
|
||||
|
||||
@@ -13,7 +13,8 @@ class TopologyViewsConfig(PluginConfig):
|
||||
'preselected_device_roles': 'Firewall,Router,Distribution Switch,Core Switch,Internal Switch,Access Switch,Server,Storage,Backup,Wireless AP',
|
||||
'ignore_cable_type': 'poweroutlet,powerport',
|
||||
'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' : ''
|
||||
}
|
||||
|
||||
config = TopologyViewsConfig
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
from dcim.models import DeviceRole, Device
|
||||
from extras.models import Tag
|
||||
|
||||
class PreDeviceRoleSerializer(ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = DeviceRole
|
||||
fields = ('id', 'name')
|
||||
|
||||
class PreTagSerializer(ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = ('id', 'name')
|
||||
|
||||
|
||||
class TopologyDummySerializer(ModelSerializer):
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from rest_framework import routers
|
||||
|
||||
from .views import PreSelectDeviceRolesViewSet, SearchViewSet, SaveCoordsViewSet
|
||||
from .views import PreSelectDeviceRolesViewSet, SearchViewSet, SaveCoordsViewSet, PreSelectTagsViewSet
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register('preselectdeviceroles', PreSelectDeviceRolesViewSet)
|
||||
router.register('preselecttags', PreSelectTagsViewSet)
|
||||
router.register('search', SearchViewSet, basename='search')
|
||||
router.register('save-coords', SaveCoordsViewSet, basename='save_coords')
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ from rest_framework.viewsets import ModelViewSet, ViewSet, ReadOnlyModelViewSet,
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from .serializers import PreDeviceRoleSerializer, TopologyDummySerializer
|
||||
from .serializers import PreDeviceRoleSerializer, TopologyDummySerializer, PreTagSerializer
|
||||
from django.conf import settings
|
||||
|
||||
from utilities.api import IsAuthenticatedOrLoginNotRequired
|
||||
|
||||
from dcim.models import DeviceRole, Device, Cable
|
||||
from extras.models import Tag
|
||||
|
||||
ignore_cable_type_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["ignore_cable_type"]
|
||||
ignore_cable_type = ignore_cable_type_raw.split(",")
|
||||
@@ -15,11 +16,18 @@ ignore_cable_type = ignore_cable_type_raw.split(",")
|
||||
preselected_device_roles_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_device_roles"]
|
||||
preselected_device_roles = preselected_device_roles_raw.split(",")
|
||||
|
||||
preselected_tags_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_tags"]
|
||||
preselected_tags = preselected_tags_raw.split(",")
|
||||
|
||||
|
||||
class PreSelectDeviceRolesViewSet(ReadOnlyModelViewSet):
|
||||
queryset = DeviceRole.objects.filter(name__in=preselected_device_roles)
|
||||
serializer_class = PreDeviceRoleSerializer
|
||||
|
||||
class PreSelectTagsViewSet(ReadOnlyModelViewSet):
|
||||
queryset = Tag.objects.filter(name__in=preselected_tags)
|
||||
serializer_class = PreTagSerializer
|
||||
|
||||
class SaveCoordsViewSet(GenericViewSet):
|
||||
queryset = Device.objects.all()
|
||||
serializer_class = TopologyDummySerializer
|
||||
@@ -64,7 +72,7 @@ class SearchViewSet(GenericViewSet):
|
||||
queryset = Device.objects.all()
|
||||
serializer_class = TopologyDummySerializer
|
||||
|
||||
def _filter(self, site, role, name):
|
||||
def _filter(self, site, role, name, tags):
|
||||
filter_devices = Device.objects.all()
|
||||
if name is not None:
|
||||
filter_devices = filter_devices.filter(name__contains=name)
|
||||
@@ -72,6 +80,8 @@ class SearchViewSet(GenericViewSet):
|
||||
filter_devices = filter_devices.filter(site__id__in=site)
|
||||
if role is not None:
|
||||
filter_devices = filter_devices.filter(device_role__id__in=role)
|
||||
if tags is not None:
|
||||
filter_devices = filter_devices.filter(tags__id__in=tags)
|
||||
return filter_devices
|
||||
|
||||
|
||||
@@ -80,6 +90,7 @@ class SearchViewSet(GenericViewSet):
|
||||
name = None
|
||||
sites = None
|
||||
devicerole = None
|
||||
tags = None
|
||||
if "devicerole" in request.data:
|
||||
if request.data["devicerole"]:
|
||||
devicerole = request.data["devicerole"]
|
||||
@@ -89,8 +100,11 @@ class SearchViewSet(GenericViewSet):
|
||||
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"]
|
||||
|
||||
devices = self._filter(sites, devicerole, name)
|
||||
devices = self._filter(sites, devicerole, name, tags)
|
||||
|
||||
nodes = []
|
||||
edges = []
|
||||
|
||||
@@ -1 +1 @@
|
||||
var graph=null,container=null,csrftoken=null,nodes=new vis.DataSet,edges=new vis.DataSet,options={interaction:{hover:!0,hoverConnectedEdges:!0,multiselect:!0},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"}};function iniPlotboxIndex(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("visgraph"),csrftoken=jQuery("[name=csrfmiddlewaretoken]").val(),startLoadSearchBar(),handleButtonPress()},!1)}function startLoadSearchBar(){$("#device-roles").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/device-roles/?brief=true",dataType:"json",type:"GET",data:function(e){return{term:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}}),$("#sites").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/sites/?brief=true",dataType:"json",type:"GET",data:function(e){return{term:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}});var n=$("#device-roles");$.ajax({type:"GET",url:"../../api/plugins/topology-views/preselectdeviceroles/"}).then(function(e){$.each(e.results,function(e,t){var a=new Option(t.name,t.id,!0,!0);n.append(a).trigger("change"),n.trigger({type:"select2:select",params:{data:t}})})})}function handleButtonPress(){$("#search-form").submit(function(e){$("#status").html('<span class="label label-info">Loading data</span>'),e.preventDefault();var t=$("#name").val(),a=$("#device-roles").val(),n=$("#sites").val();$.ajax({type:"POST",url:"../../api/plugins/topology-views/search/search/",data:JSON.stringify({name:t,devicerole:a,sites:n}),headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",dataType:"json",success:function(e){$("#status").html('<span class="label label-info">Drawing network</span>'),graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,t){nodes.add(t)}),$.each(e.edges,function(e,t){edges.add(t)}),graph.fit(),graph.on("afterDrawing",function(){$("#status").html('<span class="label label-success">Ready</span>')}),graph.on("dragEnd",function(e){dragged=this.getPositions(e.nodes),$.each(dragged,function(e,t){$("#coordstatus").html(""),$("#checkSaveCoordinates").is(":checked")&&(nodes.update({id:e,physics:!1}),$.ajax({url:"../../api/plugins/topology-views/save-coords/save_coords/",type:"POST",dataType:"json",headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",data:JSON.stringify({node_id:e,x:t.x,y:t.y}),error:function(e){$("#coordstatus").html('<span class="label label-warning">Failed to update coordinates</span>')},success:function(e){$("#coordstatus").html('<span class="label label-success">Updated coordinates</span>')}}))})})},error:function(e){$("#status").html('<span class="label label-warning">Something went wrong</span>')}})})}
|
||||
var graph=null,container=null,csrftoken=null,nodes=new vis.DataSet,edges=new vis.DataSet,options={interaction:{hover:!0,hoverConnectedEdges:!0,multiselect:!0},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"}};function iniPlotboxIndex(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("visgraph"),csrftoken=jQuery("[name=csrfmiddlewaretoken]").val(),startLoadSearchBar(),handleButtonPress()},!1)}function startLoadSearchBar(){$("#device-roles").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/device-roles/?brief=true",dataType:"json",type:"GET",data:function(e){return{term:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}}),$("#sites").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/sites/?brief=true",dataType:"json",type:"GET",data:function(e){return{term:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}}),$("#tags").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/extras/tags/?brief=true",dataType:"json",type:"GET",data:function(e){return{term:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}});var s=$("#device-roles");$.ajax({type:"GET",url:"../../api/plugins/topology-views/preselectdeviceroles/"}).then(function(e){$.each(e.results,function(e,t){var a=new Option(t.name,t.id,!0,!0);s.append(a).trigger("change"),s.trigger({type:"select2:select",params:{data:t}})})});var n=$("#tags");$.ajax({type:"GET",url:"../../api/plugins/topology-views/preselecttags/"}).then(function(e){$.each(e.results,function(e,t){var a=new Option(t.name,t.id,!0,!0);n.append(a).trigger("change"),n.trigger({type:"select2:select",params:{data:t}})})})}function handleButtonPress(){$("#search-form").submit(function(e){$("#status").html('<span class="label label-info">Loading data</span>'),e.preventDefault();var t=$("#name").val(),a=$("#device-roles").val(),s=$("#sites").val(),n=$("#tags").val();$.ajax({type:"POST",url:"../../api/plugins/topology-views/search/search/",data:JSON.stringify({name:t,devicerole:a,sites:s,tags:n}),headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",dataType:"json",success:function(e){$("#status").html('<span class="label label-info">Drawing network</span>'),graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,t){nodes.add(t)}),$.each(e.edges,function(e,t){edges.add(t)}),graph.fit(),graph.on("afterDrawing",function(){$("#status").html('<span class="label label-success">Ready</span>')}),graph.on("dragEnd",function(e){dragged=this.getPositions(e.nodes),$.each(dragged,function(e,t){$("#coordstatus").html(""),$("#checkSaveCoordinates").is(":checked")&&(nodes.update({id:e,physics:!1}),$.ajax({url:"../../api/plugins/topology-views/save-coords/save_coords/",type:"POST",dataType:"json",headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",data:JSON.stringify({node_id:e,x:t.x,y:t.y}),error:function(e){$("#coordstatus").html('<span class="label label-warning">Failed to update coordinates</span>')},success:function(e){$("#coordstatus").html('<span class="label label-success">Updated coordinates</span>')}}))})})},error:function(e){$("#status").html('<span class="label label-warning">Something went wrong</span>')}})})}
|
||||
@@ -95,6 +95,33 @@ function startLoadSearchBar() {
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#tags').select2({
|
||||
allowClear: true,
|
||||
placeholder: "---------",
|
||||
theme: "bootstrap",
|
||||
multiple: true,
|
||||
ajax: {
|
||||
url: "../../api/extras/tags/?brief=true",
|
||||
dataType: "json",
|
||||
type: "GET",
|
||||
data: function (params) {
|
||||
var queryParameters = {
|
||||
term: params.term
|
||||
}
|
||||
return queryParameters;
|
||||
},
|
||||
processResults: function (data) {
|
||||
return {
|
||||
results: $.map(data.results, function (item) {
|
||||
return {
|
||||
text: item.name,
|
||||
id: item.id
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var deviceRolesSelect = $('#device-roles');
|
||||
$.ajax({
|
||||
@@ -112,6 +139,23 @@ function startLoadSearchBar() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var tagsSelect = $('#tags');
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '../../api/plugins/topology-views/preselecttags/'
|
||||
}).then(function (data) {
|
||||
$.each(data.results, function (index, tags_to_preload) {
|
||||
var option = new Option(tags_to_preload.name, tags_to_preload.id, true, true);
|
||||
tagsSelect.append(option).trigger('change');
|
||||
tagsSelect.trigger({
|
||||
type: 'select2:select',
|
||||
params: {
|
||||
data: tags_to_preload
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleButtonPress() {
|
||||
@@ -121,13 +165,15 @@ function handleButtonPress() {
|
||||
var value = $("#name").val();
|
||||
var value2 = $("#device-roles").val();
|
||||
var value3 = $("#sites").val();
|
||||
var value4 = $("#tags").val();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "../../api/plugins/topology-views/search/search/",
|
||||
data: JSON.stringify({
|
||||
'name': value,
|
||||
'devicerole': value2,
|
||||
'sites': value3
|
||||
'sites': value3,
|
||||
'tags': value4
|
||||
}),
|
||||
headers: { "X-CSRFToken": csrftoken },
|
||||
contentType: "application/json; charset=utf-8",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<strong>Search</strong>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form id="search-form" class="form">
|
||||
<form id="search-form" class="form" autocomplete="off">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
@@ -37,6 +37,10 @@
|
||||
<div class="form-group">
|
||||
<label for="sites">Sites</label>
|
||||
<select class="form-control" multiple="multiple" id="sites"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tags">Tags</label>
|
||||
<select class="form-control" multiple="multiple" id="tags"></select>
|
||||
</div>
|
||||
<hr class="mb-4">
|
||||
<button class="btn btn-primary btn-lg btn-block" type="submit" id="start-search">Search</button>
|
||||
|
||||
Reference in New Issue
Block a user