1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #3485: Enable embedded graphs for devices

This commit is contained in:
Jeremy Stretch
2019-09-18 15:59:52 -04:00
parent a0545568cd
commit 51fb0b59ec
7 changed files with 30 additions and 10 deletions

View File

@ -6,6 +6,7 @@ v2.6.4 (FUTURE)
* [#3027](https://github.com/netbox-community/netbox/issues/3028) - Add `local_context_data` boolean filter for devices * [#3027](https://github.com/netbox-community/netbox/issues/3028) - Add `local_context_data` boolean filter for devices
* [#3318](https://github.com/netbox-community/netbox/issues/3318) - Increase length of platform name and slug to 100 characters * [#3318](https://github.com/netbox-community/netbox/issues/3318) - Increase length of platform name and slug to 100 characters
* [#3341](https://github.com/netbox-community/netbox/issues/3341) - Enable inline VLAN assignment while editing an interface * [#3341](https://github.com/netbox-community/netbox/issues/3341) - Enable inline VLAN assignment while editing an interface
* [#3485](https://github.com/netbox-community/netbox/issues/3485) - Enable embedded graphs for devices
* [#3510](https://github.com/netbox-community/netbox/issues/3510) - Add minimum/maximum prefix length enforcement for `IPNetworkVar` * [#3510](https://github.com/netbox-community/netbox/issues/3510) - Add minimum/maximum prefix length enforcement for `IPNetworkVar`
## Bug Fixes ## Bug Fixes

View File

@ -35,7 +35,7 @@ class ProviderViewSet(CustomFieldModelViewSet):
filterset_class = filters.ProviderFilter filterset_class = filters.ProviderFilter
@action(detail=True) @action(detail=True)
def graphs(self, request, pk=None): def graphs(self, request, pk):
""" """
A convenience method for rendering graphs for a particular provider. A convenience method for rendering graphs for a particular provider.
""" """

View File

@ -23,7 +23,8 @@ from dcim.models import (
) )
from extras.api.serializers import RenderedGraphSerializer from extras.api.serializers import RenderedGraphSerializer
from extras.api.views import CustomFieldModelViewSet from extras.api.views import CustomFieldModelViewSet
from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE from extras.constants import GRAPH_TYPE_DEVICE, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
from extras.models import Graph
from ipam.models import Prefix, VLAN from ipam.models import Prefix, VLAN
from utilities.api import ( from utilities.api import (
get_serializer_for_model, IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable, get_serializer_for_model, IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable,
@ -123,7 +124,7 @@ class SiteViewSet(CustomFieldModelViewSet):
filterset_class = filters.SiteFilter filterset_class = filters.SiteFilter
@action(detail=True) @action(detail=True)
def graphs(self, request, pk=None): def graphs(self, request, pk):
""" """
A convenience method for rendering graphs for a particular site. A convenience method for rendering graphs for a particular site.
""" """
@ -346,6 +347,17 @@ class DeviceViewSet(CustomFieldModelViewSet):
return serializers.DeviceWithConfigContextSerializer return serializers.DeviceWithConfigContextSerializer
@action(detail=True)
def graphs(self, request, pk):
"""
A convenience method for rendering graphs for a particular Device.
"""
device = get_object_or_404(Device, pk=pk)
queryset = Graph.objects.filter(type=GRAPH_TYPE_DEVICE)
serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': device})
return Response(serializer.data)
@action(detail=True, url_path='napalm') @action(detail=True, url_path='napalm')
def napalm(self, request, pk): def napalm(self, request, pk):
""" """
@ -458,7 +470,7 @@ class InterfaceViewSet(CableTraceMixin, ModelViewSet):
filterset_class = filters.InterfaceFilter filterset_class = filters.InterfaceFilter
@action(detail=True) @action(detail=True)
def graphs(self, request, pk=None): def graphs(self, request, pk):
""" """
A convenience method for rendering graphs for a particular interface. A convenience method for rendering graphs for a particular interface.
""" """

View File

@ -16,7 +16,8 @@ from django.utils.safestring import mark_safe
from django.views.generic import View from django.views.generic import View
from circuits.models import Circuit from circuits.models import Circuit
from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE from extras.constants import GRAPH_TYPE_DEVICE, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
from extras.models import Graph, TopologyMap
from extras.views import ObjectConfigContextView from extras.views import ObjectConfigContextView
from ipam.models import Prefix, VLAN from ipam.models import Prefix, VLAN
from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
@ -972,9 +973,6 @@ class DeviceView(PermissionRequiredMixin, View):
'rack', 'device_type__manufacturer' 'rack', 'device_type__manufacturer'
)[:10] )[:10]
# Show graph button on interfaces only if at least one graph has been created.
show_graphs = Graph.objects.filter(type=GRAPH_TYPE_INTERFACE).exists()
return render(request, 'dcim/device.html', { return render(request, 'dcim/device.html', {
'device': device, 'device': device,
'console_ports': console_ports, 'console_ports': console_ports,
@ -989,7 +987,8 @@ class DeviceView(PermissionRequiredMixin, View):
'secrets': secrets, 'secrets': secrets,
'vc_members': vc_members, 'vc_members': vc_members,
'related_devices': related_devices, 'related_devices': related_devices,
'show_graphs': show_graphs, 'show_graphs': Graph.objects.filter(type=GRAPH_TYPE_DEVICE).exists(),
'show_interface_graphs': Graph.objects.filter(type=GRAPH_TYPE_INTERFACE).exists(),
}) })

View File

@ -88,10 +88,12 @@ BUTTON_CLASS_CHOICES = (
# Graph types # Graph types
GRAPH_TYPE_INTERFACE = 100 GRAPH_TYPE_INTERFACE = 100
GRAPH_TYPE_DEVICE = 150
GRAPH_TYPE_PROVIDER = 200 GRAPH_TYPE_PROVIDER = 200
GRAPH_TYPE_SITE = 300 GRAPH_TYPE_SITE = 300
GRAPH_TYPE_CHOICES = ( GRAPH_TYPE_CHOICES = (
(GRAPH_TYPE_INTERFACE, 'Interface'), (GRAPH_TYPE_INTERFACE, 'Interface'),
(GRAPH_TYPE_DEVICE, 'Device'),
(GRAPH_TYPE_PROVIDER, 'Provider'), (GRAPH_TYPE_PROVIDER, 'Provider'),
(GRAPH_TYPE_SITE, 'Site'), (GRAPH_TYPE_SITE, 'Site'),
) )

View File

@ -35,6 +35,12 @@
</div> </div>
</div> </div>
<div class="pull-right noprint"> <div class="pull-right noprint">
{% if show_graphs %}
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ device.name }}" data-url="{% url 'dcim-api:device-graphs' pk=device.pk %}" title="Show graphs">
<i class="fa fa-signal" aria-hidden="true"></i>
Graphs
</button>
{% endif %}
{% if perms.dcim.change_device %} {% if perms.dcim.change_device %}
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

View File

@ -135,7 +135,7 @@
{# Buttons #} {# Buttons #}
<td class="text-right text-nowrap noprint"> <td class="text-right text-nowrap noprint">
{% if show_graphs %} {% if show_interface_graphs %}
{% if iface.connected_endpoint %} {% if iface.connected_endpoint %}
<button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ device.name }} - {{ iface.name }}" data-url="{% url 'dcim-api:interface-graphs' pk=iface.pk %}" title="Show graphs"> <button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ device.name }} - {{ iface.name }}" data-url="{% url 'dcim-api:interface-graphs' pk=iface.pk %}" title="Show graphs">
<i class="glyphicon glyphicon-signal" aria-hidden="true"></i> <i class="glyphicon glyphicon-signal" aria-hidden="true"></i>