From 45a2f1741ee21d1eacbdcbe682c4beffdcdc2d62 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 10:48:21 -0400 Subject: [PATCH 01/12] Introduce template filters for checking dynamic permissions --- netbox/utilities/templatetags/perms.py | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 netbox/utilities/templatetags/perms.py diff --git a/netbox/utilities/templatetags/perms.py b/netbox/utilities/templatetags/perms.py new file mode 100644 index 000000000..f1bbf7549 --- /dev/null +++ b/netbox/utilities/templatetags/perms.py @@ -0,0 +1,30 @@ +from django import template + +register = template.Library() + + +def _check_permission(user, instance, action): + return user.has_perm( + perm=f'{instance._meta.app_label}.{action}_{instance._meta.model_name}', + obj=instance + ) + + +@register.filter() +def can_view(user, instance): + return _check_permission(user, instance, 'view') + + +@register.filter() +def can_add(user, instance): + return _check_permission(user, instance, 'add') + + +@register.filter() +def can_change(user, instance): + return _check_permission(user, instance, 'change') + + +@register.filter() +def can_delete(user, instance): + return _check_permission(user, instance, 'delete') From 54b4e942604e1d268ebb12a97fc04f524304de80 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 10:49:30 -0400 Subject: [PATCH 02/12] Tweak url_name template filter to work with URLs which need a PK --- netbox/utilities/templatetags/helpers.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py index a70e917d8..425a2fca2 100644 --- a/netbox/utilities/templatetags/helpers.py +++ b/netbox/utilities/templatetags/helpers.py @@ -5,7 +5,6 @@ import re import yaml from django import template from django.conf import settings -from django.urls import NoReverseMatch, reverse from django.utils.html import strip_tags from django.utils.safestring import mark_safe from markdown import markdown @@ -79,14 +78,7 @@ def url_name(model, action): """ Return the URL name for the given model and action, or None if invalid. """ - url_name = '{}:{}_{}'.format(model._meta.app_label, model._meta.model_name, action) - try: - # Validate and return the URL name. We don't return the actual URL yet because many of the templates - # are written to pass a name to {% url %}. - reverse(url_name) - return url_name - except NoReverseMatch: - return None + return '{}:{}_{}'.format(model._meta.app_label, model._meta.model_name, action) @register.filter() From 547317ea7b560f911d0a68b5a26da2b44cbd8c1c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 10:53:00 -0400 Subject: [PATCH 03/12] Extend ObjectView to provide a default get() method --- netbox/utilities/views.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index cf282a8c0..0fdb2f89e 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -127,6 +127,25 @@ class ObjectView(ObjectPermissionRequiredMixin, View): def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'view') + def get_template_name(self): + """ + Return self.template_name if set. Otherwise, resolve the template path by model app_label and name. + """ + if hasattr(self, 'template_name'): + return self.template_name + model_opts = self.queryset.model._meta + return f'{model_opts.app_label}/{model_opts.model_name}.html' + + def get(self, request, pk): + """ + Generic GET handler for accessing an object by PK + """ + instance = get_object_or_404(self.queryset, pk=pk) + + return render(request, self.get_template_name(), { + 'instance': instance, + }) + class ObjectListView(ObjectPermissionRequiredMixin, View): """ From 261082ef9b06c1dfee4ef128da5a79b1aff7abbf Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 11:00:25 -0400 Subject: [PATCH 04/12] Add/update device component templates --- netbox/templates/dcim/consoleport.html | 95 +++++++++++++++++ netbox/templates/dcim/consoleserverport.html | 95 +++++++++++++++++ netbox/templates/dcim/device_component.html | 39 +++++++ netbox/templates/dcim/devicebay.html | 62 +++++++++++ netbox/templates/dcim/frontport.html | 83 +++++++++++++++ netbox/templates/dcim/interface.html | 79 ++++---------- netbox/templates/dcim/poweroutlet.html | 103 +++++++++++++++++++ netbox/templates/dcim/powerport.html | 103 +++++++++++++++++++ netbox/templates/dcim/rearport.html | 77 ++++++++++++++ 9 files changed, 679 insertions(+), 57 deletions(-) create mode 100644 netbox/templates/dcim/consoleport.html create mode 100644 netbox/templates/dcim/consoleserverport.html create mode 100644 netbox/templates/dcim/device_component.html create mode 100644 netbox/templates/dcim/devicebay.html create mode 100644 netbox/templates/dcim/frontport.html create mode 100644 netbox/templates/dcim/poweroutlet.html create mode 100644 netbox/templates/dcim/powerport.html create mode 100644 netbox/templates/dcim/rearport.html diff --git a/netbox/templates/dcim/consoleport.html b/netbox/templates/dcim/consoleport.html new file mode 100644 index 000000000..f1f2c4c1c --- /dev/null +++ b/netbox/templates/dcim/consoleport.html @@ -0,0 +1,95 @@ +{% extends 'dcim/device_component.html' %} +{% load helpers %} + +{% block content %} +
+
+
+
+ Console Port +
+ + + + + + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Description{{ instance.description|placeholder }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} +
+
+
+
+ Connection +
+ {% if instance.cable %} + + {% if instance.connected_endpoint %} + + + + + + + + + + + + + + + + + {% endif %} + + + + + + + + +
Device + {{ instance.connected_endpoint.device }} +
Name + {{ instance.connected_endpoint.name }} +
Type{{ instance.connected_endpoint.get_type_display|placeholder }}
Description{{ instance.connected_endpoint.description|placeholder }}
Cable + {{ instance.cable }} + + + +
Connection Status + {% if instance.connection_status %} + {{ instance.get_connection_status_display }} + {% else %} + {{ instance.get_connection_status_display }} + {% endif %} +
+ {% else %} +
+ Not connected +
+ {% endif %} +
+
+
+{% endblock %} diff --git a/netbox/templates/dcim/consoleserverport.html b/netbox/templates/dcim/consoleserverport.html new file mode 100644 index 000000000..8d7ca0b43 --- /dev/null +++ b/netbox/templates/dcim/consoleserverport.html @@ -0,0 +1,95 @@ +{% extends 'dcim/device_component.html' %} +{% load helpers %} + +{% block content %} +
+
+
+
+ Console Server Port +
+ + + + + + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Description{{ instance.description|placeholder }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} +
+
+
+
+ Connection +
+ {% if instance.cable %} + + {% if instance.connected_endpoint %} + + + + + + + + + + + + + + + + + {% endif %} + + + + + + + + +
Device + {{ instance.connected_endpoint.device }} +
Name + {{ instance.connected_endpoint.name }} +
Type{{ instance.connected_endpoint.get_type_display|placeholder }}
Description{{ instance.connected_endpoint.description|placeholder }}
Cable + {{ instance.cable }} + + + +
Connection Status + {% if instance.connection_status %} + {{ instance.get_connection_status_display }} + {% else %} + {{ instance.get_connection_status_display }} + {% endif %} +
+ {% else %} +
+ Not connected +
+ {% endif %} +
+
+
+{% endblock %} diff --git a/netbox/templates/dcim/device_component.html b/netbox/templates/dcim/device_component.html new file mode 100644 index 000000000..d2a1ad660 --- /dev/null +++ b/netbox/templates/dcim/device_component.html @@ -0,0 +1,39 @@ +{% extends 'base.html' %} +{% load helpers %} +{% load perms %} + +{% block header %} + +
+ {% if request.user|can_change:instance %} + + Edit + + {% endif %} + {% if request.user|can_delete:instance %} + + Delete + + {% endif %} +
+

{% block title %}{{ instance.device }} / {{ instance }}{% endblock %}

+ +{% endblock %} diff --git a/netbox/templates/dcim/devicebay.html b/netbox/templates/dcim/devicebay.html new file mode 100644 index 000000000..b257cd471 --- /dev/null +++ b/netbox/templates/dcim/devicebay.html @@ -0,0 +1,62 @@ +{% extends 'dcim/device_component.html' %} +{% load helpers %} + +{% block content %} +
+
+
+
+ Device Bay +
+ + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Description{{ instance.description|placeholder }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} +
+
+
+
+ Installed Device +
+ {% if instance.installed_device %} + {% with device=instance.installed_device %} + + + + + + + + + +
Device + {{ device }} +
Device Type{{ device.device_type }}
+ {% endwith %} + {% else %} +
+ None +
+ {% endif %} +
+
+
+{% endblock %} diff --git a/netbox/templates/dcim/frontport.html b/netbox/templates/dcim/frontport.html new file mode 100644 index 000000000..33ce03eb8 --- /dev/null +++ b/netbox/templates/dcim/frontport.html @@ -0,0 +1,83 @@ +{% extends 'dcim/device_component.html' %} +{% load helpers %} + +{% block content %} +
+
+
+
+ Front Port +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Rear Port + {{ instance.rear_port }} +
Rear Port Position{{ instance.rear_port_position }}
Description{{ instance.description|placeholder }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} +
+
+
+
+ Connection +
+ {% if instance.cable %} + + + + + + + + + +
Cable + {{ instance.cable }} + + + +
Connection Status + {% if instance.cable.status %} + {{ instance.cable.get_status_display }} + {% else %} + {{ instance.cable.get_status_display }} + {% endif %} +
+ {% else %} +
+ Not connected +
+ {% endif %} +
+
+
+{% endblock %} diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index 5165169ff..b3163c413 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -1,41 +1,6 @@ -{% extends 'base.html' %} +{% extends 'dcim/device_component.html' %} {% load helpers %} -{% block header %} -
-
- -
-
-
- {% if perms.dcim.change_interface %} - - Edit - - {% endif %} - {% if perms.dcim.delete_interface %} - - Delete - - {% endif %} -
-

{% block title %}{{ interface.device }} / {{ interface.name }}{% endblock %}

- -{% endblock %} - {% block content %}
@@ -47,25 +12,25 @@ Device - {{ interface.device }} + {{ instance.device }} Name - {{ interface.name }} + {{ instance.name }} Label - {{ interface.label|placeholder }} + {{ instance.label|placeholder }} Type - {{ interface.get_type_display }} + {{ instance.get_type_display }} Enabled - {% if interface.enabled %} + {% if instance.enabled %} {% else %} @@ -75,8 +40,8 @@ LAG - {% if interface.lag%} - {{ interface.lag }} + {% if instance.lag%} + {{ instance.lag }} {% else %} None {% endif %} @@ -84,31 +49,31 @@ Description - {{ interface.description|placeholder }} + {{ instance.description|placeholder }} MTU - {{ interface.mtu|placeholder }} + {{ instance.mtu|placeholder }} MAC Address - {{ interface.mac_address|placeholder }} + {{ instance.mac_address|placeholder }} 802.1Q Mode - {{ interface.get_mode_display }} + {{ instance.get_mode_display }}
- {% include 'extras/inc/tags_panel.html' with tags=interface.tags.all %} + {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %}
- {% if interface.is_connectable %} + {% if instance.is_connectable %}
Connection
- {% if interface.cable %} + {% if instance.cable %} {% if connected_interface %} @@ -182,8 +147,8 @@ @@ -191,10 +156,10 @@ @@ -206,7 +171,7 @@ {% endif %} {% endif %} - {% if interface.is_lag %} + {% if instance.is_lag %}
LAG Members
Cable - {{ interface.cable }} - + {{ instance.cable }} +
Connection Status - {% if interface.connection_status %} - {{ interface.get_connection_status_display }} + {% if instance.connection_status %} + {{ instance.get_connection_status_display }} {% else %} - {{ interface.get_connection_status_display }} + {{ instance.get_connection_status_display }} {% endif %}
@@ -218,7 +183,7 @@ - {% for member in interface.member_interfaces.all %} + {% for member in instance.member_interfaces.all %} {# Type #} diff --git a/netbox/templates/dcim/inc/consoleserverport.html b/netbox/templates/dcim/inc/consoleserverport.html index 0d649f812..dcf168ae7 100644 --- a/netbox/templates/dcim/inc/consoleserverport.html +++ b/netbox/templates/dcim/inc/consoleserverport.html @@ -11,7 +11,8 @@ {# Name #} {# Type #} diff --git a/netbox/templates/dcim/inc/devicebay.html b/netbox/templates/dcim/inc/devicebay.html index 70ce7e8df..ee6a66d8f 100644 --- a/netbox/templates/dcim/inc/devicebay.html +++ b/netbox/templates/dcim/inc/devicebay.html @@ -9,7 +9,8 @@ {# Name #} {# Status #} diff --git a/netbox/templates/dcim/inc/frontport.html b/netbox/templates/dcim/inc/frontport.html index 12915f64d..f267479f3 100644 --- a/netbox/templates/dcim/inc/frontport.html +++ b/netbox/templates/dcim/inc/frontport.html @@ -10,7 +10,8 @@ {# Name #} {# Type #} diff --git a/netbox/templates/dcim/inc/poweroutlet.html b/netbox/templates/dcim/inc/poweroutlet.html index 1c0630310..d9a77d647 100644 --- a/netbox/templates/dcim/inc/poweroutlet.html +++ b/netbox/templates/dcim/inc/poweroutlet.html @@ -11,7 +11,8 @@ {# Name #} {# Type #} diff --git a/netbox/templates/dcim/inc/powerport.html b/netbox/templates/dcim/inc/powerport.html index 045b25dfd..c3293e959 100644 --- a/netbox/templates/dcim/inc/powerport.html +++ b/netbox/templates/dcim/inc/powerport.html @@ -2,7 +2,8 @@ {# Name #} {# Type #} diff --git a/netbox/templates/dcim/inc/rearport.html b/netbox/templates/dcim/inc/rearport.html index 73ccd6b70..c1e5482d0 100644 --- a/netbox/templates/dcim/inc/rearport.html +++ b/netbox/templates/dcim/inc/rearport.html @@ -10,7 +10,8 @@ {# Name #} {# Type #} From e7ba126b804d60d79ac25e6577a868f5bd684638 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 12:22:21 -0400 Subject: [PATCH 10/12] Changelog for #4788 --- docs/release-notes/version-2.9.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.9.md b/docs/release-notes/version-2.9.md index 5b98dabb0..39d763bb5 100644 --- a/docs/release-notes/version-2.9.md +++ b/docs/release-notes/version-2.9.md @@ -14,6 +14,7 @@ NetBox v2.9 replaces Django's built-in permissions framework with one that suppo * [#3703](https://github.com/netbox-community/netbox/issues/3703) - Tags must be created administratively before being assigned to an object * [#4615](https://github.com/netbox-community/netbox/issues/4615) - Add `label` field for all device components * [#4742](https://github.com/netbox-community/netbox/issues/4742) - Add tagging for cables, power panels, and rack reservations +* [#4788](https://github.com/netbox-community/netbox/issues/4788) - Add dedicated views for all device components ### Configuration Changes From 522275d433565d5337272db1d97621223991a09e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 13:27:01 -0400 Subject: [PATCH 11/12] Add plugin buttons & content to device component views --- netbox/templates/dcim/consoleport.html | 8 + netbox/templates/dcim/consoleserverport.html | 8 + netbox/templates/dcim/device_component.html | 2 + netbox/templates/dcim/devicebay.html | 8 + netbox/templates/dcim/frontport.html | 8 + netbox/templates/dcim/interface.html | 420 ++++++++++--------- netbox/templates/dcim/poweroutlet.html | 8 + netbox/templates/dcim/powerport.html | 8 + netbox/templates/dcim/rearport.html | 8 + 9 files changed, 272 insertions(+), 206 deletions(-) diff --git a/netbox/templates/dcim/consoleport.html b/netbox/templates/dcim/consoleport.html index f1f2c4c1c..63916bcc5 100644 --- a/netbox/templates/dcim/consoleport.html +++ b/netbox/templates/dcim/consoleport.html @@ -1,5 +1,6 @@ {% extends 'dcim/device_component.html' %} {% load helpers %} +{% load plugins %} {% block content %}
@@ -34,6 +35,7 @@
{{ member.device }} diff --git a/netbox/templates/dcim/poweroutlet.html b/netbox/templates/dcim/poweroutlet.html new file mode 100644 index 000000000..519bd01df --- /dev/null +++ b/netbox/templates/dcim/poweroutlet.html @@ -0,0 +1,103 @@ +{% extends 'dcim/device_component.html' %} +{% load helpers %} + +{% block content %} +
+
+
+
+ Power Outlet +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Description{{ instance.description|placeholder }}
Power Port{{ instance.power_port }}
Feed Leg{{ instance.get_feed_leg_display }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} +
+
+
+
+ Connection +
+ {% if instance.cable %} + + {% if instance.connected_endpoint %} + + + + + + + + + + + + + + + + + {% endif %} + + + + + + + + +
Device + {{ instance.connected_endpoint.device }} +
Name + {{ instance.connected_endpoint.name }} +
Type{{ instance.connected_endpoint.get_type_display|placeholder }}
Description{{ instance.connected_endpoint.description|placeholder }}
Cable + {{ instance.cable }} + + + +
Connection Status + {% if instance.connection_status %} + {{ instance.get_connection_status_display }} + {% else %} + {{ instance.get_connection_status_display }} + {% endif %} +
+ {% else %} +
+ Not connected +
+ {% endif %} +
+
+
+{% endblock %} diff --git a/netbox/templates/dcim/powerport.html b/netbox/templates/dcim/powerport.html new file mode 100644 index 000000000..e7c103c9d --- /dev/null +++ b/netbox/templates/dcim/powerport.html @@ -0,0 +1,103 @@ +{% extends 'dcim/device_component.html' %} +{% load helpers %} + +{% block content %} +
+
+
+
+ Power Port +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Description{{ instance.description|placeholder }}
Maximum Draw{{ instance.maximum_draw|placeholder }}
Allocated Draw{{ instance.allocated_draw|placeholder }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} +
+
+
+
+ Connection +
+ {% if instance.cable %} + + {% if instance.connected_endpoint %} + + + + + + + + + + + + + + + + + {% endif %} + + + + + + + + +
Device + {{ instance.connected_endpoint.device }} +
Name + {{ instance.connected_endpoint.name }} +
Type{{ instance.connected_endpoint.get_type_display|placeholder }}
Description{{ instance.connected_endpoint.description|placeholder }}
Cable + {{ instance.cable }} + + + +
Connection Status + {% if instance.connection_status %} + {{ instance.get_connection_status_display }} + {% else %} + {{ instance.get_connection_status_display }} + {% endif %} +
+ {% else %} +
+ Not connected +
+ {% endif %} +
+
+
+{% endblock %} diff --git a/netbox/templates/dcim/rearport.html b/netbox/templates/dcim/rearport.html new file mode 100644 index 000000000..480f26d2c --- /dev/null +++ b/netbox/templates/dcim/rearport.html @@ -0,0 +1,77 @@ +{% extends 'dcim/device_component.html' %} +{% load helpers %} + +{% block content %} +
+
+
+
+ Rear Port +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Positions{{ instance.positions }}
Description{{ instance.description|placeholder }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} +
+
+
+
+ Connection +
+ {% if instance.cable %} + + + + + + + + + +
Cable + {{ instance.cable }} + + + +
Connection Status + {% if instance.cable.status %} + {{ instance.cable.get_status_display }} + {% else %} + {{ instance.cable.get_status_display }} + {% endif %} +
+ {% else %} +
+ Not connected +
+ {% endif %} +
+
+
+{% endblock %} From 422db035158a75b4d5d9a8b8b62c99019965d636 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 11:01:18 -0400 Subject: [PATCH 05/12] Add individual views for device components --- netbox/dcim/models/device_components.py | 16 ++++++++---- netbox/dcim/urls.py | 34 +++++++++++++++++-------- netbox/dcim/views.py | 30 +++++++++++++++++++++- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 30a276c7d..d94e2484f 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -268,7 +268,7 @@ class ConsolePort(CableTermination, ComponentModel): unique_together = ('device', 'name') def get_absolute_url(self): - return self.device.get_absolute_url() + return reverse('dcim:consoleport', kwargs={'pk': self.pk}) def to_csv(self): return ( @@ -325,7 +325,7 @@ class ConsoleServerPort(CableTermination, ComponentModel): unique_together = ('device', 'name') def get_absolute_url(self): - return self.device.get_absolute_url() + return reverse('dcim:consoleserverport', kwargs={'pk': self.pk}) def to_csv(self): return ( @@ -408,7 +408,7 @@ class PowerPort(CableTermination, ComponentModel): unique_together = ('device', 'name') def get_absolute_url(self): - return self.device.get_absolute_url() + return reverse('dcim:powerport', kwargs={'pk': self.pk}) def to_csv(self): return ( @@ -560,7 +560,7 @@ class PowerOutlet(CableTermination, ComponentModel): unique_together = ('device', 'name') def get_absolute_url(self): - return self.device.get_absolute_url() + return reverse('dcim:poweroutlet', kwargs={'pk': self.pk}) def to_csv(self): return ( @@ -881,6 +881,9 @@ class FrontPort(CableTermination, ComponentModel): def __str__(self): return self.name + def get_absolute_url(self): + return reverse('dcim:frontport', kwargs={'pk': self.pk}) + def to_csv(self): return ( self.device.identifier, @@ -946,6 +949,9 @@ class RearPort(CableTermination, ComponentModel): def __str__(self): return self.name + def get_absolute_url(self): + return reverse('dcim:rearport', kwargs={'pk': self.pk}) + def to_csv(self): return ( self.device.identifier, @@ -1005,7 +1011,7 @@ class DeviceBay(ComponentModel): return '{} - {}'.format(self.device.name, self.name) def get_absolute_url(self): - return self.device.get_absolute_url() + return reverse('dcim:devicebay', kwargs={'pk': self.pk}) def to_csv(self): return ( diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 347ac7064..2014427b7 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -4,9 +4,9 @@ from extras.views import ObjectChangeLogView, ImageAttachmentEditView from ipam.views import ServiceEditView from . import views from .models import ( - Cable, ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, FrontPort, Interface, Manufacturer, Platform, - PowerFeed, PowerPanel, PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, - VirtualChassis, + Cable, ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceRole, DeviceType, FrontPort, Interface, + Manufacturer, Platform, PowerFeed, PowerPanel, PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, + RearPort, Region, Site, VirtualChassis, ) app_name = 'dcim' @@ -189,10 +189,12 @@ urlpatterns = [ path('console-ports/edit/', views.ConsolePortBulkEditView.as_view(), name='consoleport_bulk_edit'), # TODO: Bulk rename, disconnect views for ConsolePorts path('console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), - path('console-ports//connect//', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}), + path('console-ports//', views.ConsolePortView.as_view(), name='consoleport'), path('console-ports//edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'), path('console-ports//delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'), + path('console-ports//changelog/', ObjectChangeLogView.as_view(), name='consoleport_changelog', kwargs={'model': ConsolePort}), path('console-ports//trace/', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}), + path('console-ports//connect//', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}), path('devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'), # Console server ports @@ -203,10 +205,12 @@ urlpatterns = [ path('console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'), path('console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'), path('console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'), - path('console-server-ports//connect//', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}), + path('console-server-ports//', views.ConsoleServerPortView.as_view(), name='consoleserverport'), path('console-server-ports//edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'), path('console-server-ports//delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'), + path('console-server-ports//changelog/', ObjectChangeLogView.as_view(), name='consoleserverport_changelog', kwargs={'model': ConsoleServerPort}), path('console-server-ports//trace/', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}), + path('console-server-ports//connect//', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}), path('devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'), # Power ports @@ -216,10 +220,12 @@ urlpatterns = [ path('power-ports/edit/', views.PowerPortBulkEditView.as_view(), name='powerport_bulk_edit'), # TODO: Bulk rename, disconnect views for PowerPorts path('power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), - path('power-ports//connect//', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}), + path('power-ports//', views.PowerPortView.as_view(), name='powerport'), path('power-ports//edit/', views.PowerPortEditView.as_view(), name='powerport_edit'), path('power-ports//delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'), + path('power-ports//changelog/', ObjectChangeLogView.as_view(), name='powerport_changelog', kwargs={'model': PowerPort}), path('power-ports//trace/', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}), + path('power-ports//connect//', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}), path('devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'), # Power outlets @@ -230,10 +236,12 @@ urlpatterns = [ path('power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'), path('power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'), path('power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), - path('power-outlets//connect//', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}), + path('power-outlets//', views.PowerOutletView.as_view(), name='poweroutlet'), path('power-outlets//edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'), path('power-outlets//delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'), + path('power-outlets//changelog/', ObjectChangeLogView.as_view(), name='poweroutlet_changelog', kwargs={'model': PowerOutlet}), path('power-outlets//trace/', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}), + path('power-outlets//connect//', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}), path('devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'), # Interfaces @@ -244,12 +252,12 @@ urlpatterns = [ path('interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'), path('interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'), path('interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), - path('interfaces//connect//', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}), path('interfaces//', views.InterfaceView.as_view(), name='interface'), path('interfaces//edit/', views.InterfaceEditView.as_view(), name='interface_edit'), path('interfaces//delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'), path('interfaces//changelog/', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}), path('interfaces//trace/', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}), + path('interfaces//connect//', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}), path('devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'), # Front ports @@ -260,10 +268,12 @@ urlpatterns = [ path('front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'), path('front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'), path('front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'), - path('front-ports//connect//', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}), + path('front-ports//', views.FrontPortView.as_view(), name='frontport'), path('front-ports//edit/', views.FrontPortEditView.as_view(), name='frontport_edit'), path('front-ports//delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'), + path('front-ports//changelog/', ObjectChangeLogView.as_view(), name='frontport_changelog', kwargs={'model': FrontPort}), path('front-ports//trace/', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}), + path('front-ports//connect//', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}), # path('devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'), # Rear ports @@ -274,10 +284,12 @@ urlpatterns = [ path('rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'), path('rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'), path('rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'), - path('rear-ports//connect//', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}), + path('rear-ports//', views.RearPortView.as_view(), name='rearport'), path('rear-ports//edit/', views.RearPortEditView.as_view(), name='rearport_edit'), path('rear-ports//delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'), + path('rear-ports//changelog/', ObjectChangeLogView.as_view(), name='rearport_changelog', kwargs={'model': RearPort}), path('rear-ports//trace/', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}), + path('rear-ports//connect//', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}), path('devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'), # Device bays @@ -287,8 +299,10 @@ urlpatterns = [ path('device-bays/edit/', views.DeviceBayBulkEditView.as_view(), name='devicebay_bulk_edit'), path('device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'), path('device-bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'), + path('device-bays//', views.DeviceBayView.as_view(), name='devicebay'), path('device-bays//edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'), path('device-bays//delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'), + path('device-bays//changelog/', ObjectChangeLogView.as_view(), name='devicebay_changelog', kwargs={'model': DeviceBay}), path('device-bays//populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'), path('device-bays//depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'), path('devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 840b9890f..4cf787e3e 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1165,6 +1165,10 @@ class ConsolePortListView(ObjectListView): action_buttons = ('import', 'export') +class ConsolePortView(ObjectView): + queryset = ConsolePort.objects.all() + + class ConsolePortCreateView(ComponentCreateView): queryset = ConsolePort.objects.all() form = forms.ConsolePortCreateForm @@ -1214,6 +1218,10 @@ class ConsoleServerPortListView(ObjectListView): action_buttons = ('import', 'export') +class ConsoleServerPortView(ObjectView): + queryset = ConsoleServerPort.objects.all() + + class ConsoleServerPortCreateView(ComponentCreateView): queryset = ConsoleServerPort.objects.all() form = forms.ConsoleServerPortCreateForm @@ -1273,6 +1281,10 @@ class PowerPortListView(ObjectListView): action_buttons = ('import', 'export') +class PowerPortView(ObjectView): + queryset = PowerPort.objects.all() + + class PowerPortCreateView(ComponentCreateView): queryset = PowerPort.objects.all() form = forms.PowerPortCreateForm @@ -1322,6 +1334,10 @@ class PowerOutletListView(ObjectListView): action_buttons = ('import', 'export') +class PowerOutletView(ObjectView): + queryset = PowerOutlet.objects.all() + + class PowerOutletCreateView(ComponentCreateView): queryset = PowerOutlet.objects.all() form = forms.PowerOutletCreateForm @@ -1409,7 +1425,7 @@ class InterfaceView(ObjectView): ) return render(request, 'dcim/interface.html', { - 'interface': interface, + 'instance': interface, 'connected_interface': interface._connected_interface, 'connected_circuittermination': interface._connected_circuittermination, 'ipaddress_table': ipaddress_table, @@ -1477,6 +1493,10 @@ class FrontPortListView(ObjectListView): action_buttons = ('import', 'export') +class FrontPortView(ObjectView): + queryset = FrontPort.objects.all() + + class FrontPortCreateView(ComponentCreateView): queryset = FrontPort.objects.all() form = forms.FrontPortCreateForm @@ -1536,6 +1556,10 @@ class RearPortListView(ObjectListView): action_buttons = ('import', 'export') +class RearPortView(ObjectView): + queryset = RearPort.objects.all() + + class RearPortCreateView(ComponentCreateView): queryset = RearPort.objects.all() form = forms.RearPortCreateForm @@ -1597,6 +1621,10 @@ class DeviceBayListView(ObjectListView): action_buttons = ('import', 'export') +class DeviceBayView(ObjectView): + queryset = DeviceBay.objects.all() + + class DeviceBayCreateView(ComponentCreateView): queryset = DeviceBay.objects.all() form = forms.DeviceBayCreateForm From fa71921fa57a456d6f3bd010952e9e5dd8c59d1d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 11:04:42 -0400 Subject: [PATCH 06/12] Extend DeviceComponentViewTestCase to include GetObjectViewTestCase --- netbox/dcim/tests/test_views.py | 16 +++++++++++----- netbox/utilities/testing/views.py | 1 + netbox/virtualization/tests/test_views.py | 5 +---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index 5c5e46853..079f26fdf 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -1194,10 +1194,7 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase): ) -class InterfaceTestCase( - ViewTestCases.GetObjectViewTestCase, - ViewTestCases.DeviceComponentViewTestCase, -): +class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): model = Interface @classmethod @@ -1425,7 +1422,16 @@ class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase): ) -class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase): +# TODO: Convert to DeviceComponentViewTestCase? +class InventoryItemTestCase( + ViewTestCases.EditObjectViewTestCase, + ViewTestCases.DeleteObjectViewTestCase, + ViewTestCases.ListObjectsViewTestCase, + ViewTestCases.BulkCreateObjectsViewTestCase, + ViewTestCases.BulkImportObjectsViewTestCase, + ViewTestCases.BulkEditObjectsViewTestCase, + ViewTestCases.BulkDeleteObjectsViewTestCase +): model = InventoryItem @classmethod diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py index 774ceac85..2cf32616c 100644 --- a/netbox/utilities/testing/views.py +++ b/netbox/utilities/testing/views.py @@ -917,6 +917,7 @@ class ViewTestCases: maxDiff = None class DeviceComponentViewTestCase( + GetObjectViewTestCase, EditObjectViewTestCase, DeleteObjectViewTestCase, ListObjectsViewTestCase, diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py index 408558779..ec4159dd4 100644 --- a/netbox/virtualization/tests/test_views.py +++ b/netbox/virtualization/tests/test_views.py @@ -189,10 +189,7 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase): } -class VMInterfaceTestCase( - ViewTestCases.GetObjectViewTestCase, - ViewTestCases.DeviceComponentViewTestCase, -): +class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): model = VMInterface @classmethod From 9e82fbd54f902956e2b8b74383f4ec3a4c5760ab Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 11:56:31 -0400 Subject: [PATCH 07/12] Update and simplify device component tables --- netbox/dcim/tables.py | 238 +++++++++--------------------------------- netbox/dcim/views.py | 50 +++++---- 2 files changed, 75 insertions(+), 213 deletions(-) diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 6aa41ab44..9979bea1a 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -490,18 +490,6 @@ class ConsolePortTemplateTable(ComponentTemplateTable): empty_text = "None" -class ConsolePortImportTable(BaseTable): - device = tables.LinkColumn( - viewname='dcim:device', - args=[Accessor('device.pk')] - ) - - class Meta(BaseTable.Meta): - model = ConsolePort - fields = ('device', 'name', 'description') - empty_text = False - - class ConsoleServerPortTemplateTable(ComponentTemplateTable): actions = tables.TemplateColumn( template_code=get_component_template_actions('consoleserverporttemplate'), @@ -515,18 +503,6 @@ class ConsoleServerPortTemplateTable(ComponentTemplateTable): empty_text = "None" -class ConsoleServerPortImportTable(BaseTable): - device = tables.LinkColumn( - viewname='dcim:device', - args=[Accessor('device.pk')] - ) - - class Meta(BaseTable.Meta): - model = ConsoleServerPort - fields = ('device', 'name', 'description') - empty_text = False - - class PowerPortTemplateTable(ComponentTemplateTable): actions = tables.TemplateColumn( template_code=get_component_template_actions('powerporttemplate'), @@ -540,18 +516,6 @@ class PowerPortTemplateTable(ComponentTemplateTable): empty_text = "None" -class PowerPortImportTable(BaseTable): - device = tables.LinkColumn( - viewname='dcim:device', - args=[Accessor('device.pk')] - ) - - class Meta(BaseTable.Meta): - model = PowerPort - fields = ('device', 'name', 'description', 'maximum_draw', 'allocated_draw') - empty_text = False - - class PowerOutletTemplateTable(ComponentTemplateTable): actions = tables.TemplateColumn( template_code=get_component_template_actions('poweroutlettemplate'), @@ -565,18 +529,6 @@ class PowerOutletTemplateTable(ComponentTemplateTable): empty_text = "None" -class PowerOutletImportTable(BaseTable): - device = tables.LinkColumn( - viewname='dcim:device', - args=[Accessor('device.pk')] - ) - - class Meta(BaseTable.Meta): - model = PowerOutlet - fields = ('device', 'name', 'description', 'power_port', 'feed_leg') - empty_text = False - - class InterfaceTemplateTable(ComponentTemplateTable): mgmt_only = BooleanColumn( verbose_name='Management Only' @@ -593,20 +545,6 @@ class InterfaceTemplateTable(ComponentTemplateTable): empty_text = "None" -class InterfaceImportTable(BaseTable): - device = tables.LinkColumn( - viewname='dcim:device', - args=[Accessor('device.pk')] - ) - - class Meta(BaseTable.Meta): - model = Interface - fields = ( - 'device', 'name', 'description', 'lag', 'type', 'enabled', 'mac_address', 'mtu', 'mgmt_only', 'mode', - ) - empty_text = False - - class FrontPortTemplateTable(ComponentTemplateTable): rear_port_position = tables.Column( verbose_name='Position' @@ -623,18 +561,6 @@ class FrontPortTemplateTable(ComponentTemplateTable): empty_text = "None" -class FrontPortImportTable(BaseTable): - device = tables.LinkColumn( - viewname='dcim:device', - args=[Accessor('device.pk')] - ) - - class Meta(BaseTable.Meta): - model = FrontPort - fields = ('device', 'name', 'description', 'type', 'rear_port', 'rear_port_position') - empty_text = False - - class RearPortTemplateTable(ComponentTemplateTable): actions = tables.TemplateColumn( template_code=get_component_template_actions('rearporttemplate'), @@ -648,18 +574,6 @@ class RearPortTemplateTable(ComponentTemplateTable): empty_text = "None" -class RearPortImportTable(BaseTable): - device = tables.LinkColumn( - viewname='dcim:device', - args=[Accessor('device.pk')] - ) - - class Meta(BaseTable.Meta): - model = RearPort - fields = ('device', 'name', 'description', 'type', 'position') - empty_text = False - - class DeviceBayTemplateTable(ComponentTemplateTable): actions = tables.TemplateColumn( template_code=get_component_template_actions('devicebaytemplate'), @@ -855,144 +769,94 @@ class DeviceImportTable(BaseTable): # Device components # -class DeviceComponentDetailTable(BaseTable): +class DeviceComponentTable(BaseTable): pk = ToggleColumn() - device = tables.LinkColumn() - name = tables.Column(order_by=('_name',)) - cable = tables.LinkColumn() + device = tables.Column( + linkify=True + ) + name = tables.Column( + linkify=True, + order_by=('_name',) + ) + cable = tables.Column( + linkify=True + ) class Meta(BaseTable.Meta): order_by = ('device', 'name') - fields = ('pk', 'device', 'name', 'label', 'type', 'description', 'cable') - sequence = ('pk', 'device', 'name', 'label', 'type', 'description', 'cable') -class ConsolePortTable(BaseTable): - name = tables.Column(order_by=('_name',)) +class ConsolePortTable(DeviceComponentTable): - class Meta(BaseTable.Meta): + class Meta(DeviceComponentTable.Meta): model = ConsolePort - fields = ('name', 'label', 'type') + fields = ('pk', 'device', 'name', 'label', 'type', 'description', 'cable') + default_columns = ('pk', 'device', 'name', 'label', 'type', 'description') -class ConsolePortDetailTable(DeviceComponentDetailTable): +class ConsoleServerPortTable(DeviceComponentTable): - class Meta(DeviceComponentDetailTable.Meta, ConsolePortTable.Meta): - pass - - -class ConsoleServerPortTable(BaseTable): - name = tables.Column(order_by=('_name',)) - - class Meta(BaseTable.Meta): + class Meta(DeviceComponentTable.Meta): model = ConsoleServerPort - fields = ('name', 'label', 'description') + fields = ('pk', 'device', 'name', 'label', 'type', 'description', 'cable') + default_columns = ('pk', 'device', 'name', 'label', 'type', 'description') -class ConsoleServerPortDetailTable(DeviceComponentDetailTable): +class PowerPortTable(DeviceComponentTable): - class Meta(DeviceComponentDetailTable.Meta, ConsoleServerPortTable.Meta): - pass - - -class PowerPortTable(BaseTable): - name = tables.Column(order_by=('_name',)) - - class Meta(BaseTable.Meta): + class Meta(DeviceComponentTable.Meta): model = PowerPort - fields = ('name', 'label', 'type') + fields = ('pk', 'device', 'name', 'label', 'type', 'description', 'maximum_draw', 'allocated_draw', 'cable') + default_columns = ('pk', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description') -class PowerPortDetailTable(DeviceComponentDetailTable): +class PowerOutletTable(DeviceComponentTable): - class Meta(DeviceComponentDetailTable.Meta, PowerPortTable.Meta): - pass - - -class PowerOutletTable(BaseTable): - name = tables.Column(order_by=('_name',)) - - class Meta(BaseTable.Meta): + class Meta(DeviceComponentTable.Meta): model = PowerOutlet - fields = ('name', 'label', 'type', 'description') + fields = ('pk', 'device', 'name', 'label', 'type', 'description', 'power_port', 'feed_leg', 'cable') + default_columns = ('pk', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description') -class PowerOutletDetailTable(DeviceComponentDetailTable): - - class Meta(DeviceComponentDetailTable.Meta, PowerOutletTable.Meta): - pass - - -class InterfaceTable(BaseTable): - - class Meta(BaseTable.Meta): - model = Interface - fields = ('name', 'label', 'type', 'lag', 'enabled', 'mgmt_only', 'description') - - -class InterfaceDetailTable(DeviceComponentDetailTable): +class InterfaceTable(DeviceComponentTable): enabled = BooleanColumn() - class Meta(DeviceComponentDetailTable.Meta, InterfaceTable.Meta): - fields = ('pk', 'device', 'name', 'label', 'enabled', 'type', 'description', 'cable') - sequence = ('pk', 'device', 'name', 'label', 'enabled', 'type', 'description', 'cable') + class Meta(DeviceComponentTable.Meta): + model = Interface + fields = ( + 'pk', 'device', 'name', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', 'mode', 'description', 'cable', + ) + default_columns = ('pk', 'device', 'name', 'label', 'enabled', 'type', 'description') -class FrontPortTable(BaseTable): - name = tables.Column(order_by=('_name',)) +class FrontPortTable(DeviceComponentTable): + rear_port_position = tables.Column( + verbose_name='Position' + ) - class Meta(BaseTable.Meta): + class Meta(DeviceComponentTable.Meta): model = FrontPort - fields = ('name', 'label', 'type', 'rear_port', 'rear_port_position', 'description') - empty_text = "None" + fields = ('pk', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'cable') + default_columns = ('pk', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description') -class FrontPortDetailTable(DeviceComponentDetailTable): +class RearPortTable(DeviceComponentTable): - class Meta(DeviceComponentDetailTable.Meta, FrontPortTable.Meta): - pass - - -class RearPortTable(BaseTable): - name = tables.Column(order_by=('_name',)) - - class Meta(BaseTable.Meta): + class Meta(DeviceComponentTable.Meta): model = RearPort - fields = ('name', 'label', 'type', 'positions', 'description') - empty_text = "None" + fields = ('pk', 'device', 'name', 'label', 'type', 'positions', 'description', 'cable') + default_columns = ('pk', 'device', 'name', 'label', 'type', 'description') -class RearPortDetailTable(DeviceComponentDetailTable): +class DeviceBayTable(DeviceComponentTable): + installed_device = tables.Column( + linkify=True + ) - class Meta(DeviceComponentDetailTable.Meta, RearPortTable.Meta): - pass - - -class DeviceBayTable(BaseTable): - name = tables.Column(order_by=('_name',)) - - class Meta(BaseTable.Meta): + class Meta(DeviceComponentTable.Meta): model = DeviceBay - fields = ('name', 'label', 'description') - - -class DeviceBayDetailTable(DeviceComponentDetailTable): - installed_device = tables.LinkColumn() - - class Meta(DeviceBayTable.Meta): fields = ('pk', 'device', 'name', 'label', 'installed_device', 'description') - sequence = ('pk', 'device', 'name', 'label', 'installed_device', 'description') - exclude = ('cable',) - - -class DeviceBayImportTable(BaseTable): - device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device') - installed_device = tables.LinkColumn('dcim:device', args=[Accessor('installed_device.pk')], verbose_name='Installed Device') - - class Meta(BaseTable.Meta): - model = DeviceBay - fields = ('device', 'name', 'installed_device', 'description') - empty_text = False + default_columns = ('pk', 'device', 'name', 'label', 'installed_device', 'description') # diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 4cf787e3e..1f3b61a42 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1158,10 +1158,10 @@ class DeviceBulkDeleteView(BulkDeleteView): # class ConsolePortListView(ObjectListView): - queryset = ConsolePort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') + queryset = ConsolePort.objects.prefetch_related('device', 'cable') filterset = filters.ConsolePortFilterSet filterset_form = forms.ConsolePortFilterForm - table = tables.ConsolePortDetailTable + table = tables.ConsolePortTable action_buttons = ('import', 'export') @@ -1188,7 +1188,7 @@ class ConsolePortDeleteView(ObjectDeleteView): class ConsolePortBulkImportView(BulkImportView): queryset = ConsolePort.objects.all() model_form = forms.ConsolePortCSVForm - table = tables.ConsolePortImportTable + table = tables.ConsolePortTable default_return_url = 'dcim:consoleport_list' @@ -1211,10 +1211,10 @@ class ConsolePortBulkDeleteView(BulkDeleteView): # class ConsoleServerPortListView(ObjectListView): - queryset = ConsoleServerPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') + queryset = ConsoleServerPort.objects.prefetch_related('device', 'cable') filterset = filters.ConsoleServerPortFilterSet filterset_form = forms.ConsoleServerPortFilterForm - table = tables.ConsoleServerPortDetailTable + table = tables.ConsoleServerPortTable action_buttons = ('import', 'export') @@ -1241,7 +1241,7 @@ class ConsoleServerPortDeleteView(ObjectDeleteView): class ConsoleServerPortBulkImportView(BulkImportView): queryset = ConsoleServerPort.objects.all() model_form = forms.ConsoleServerPortCSVForm - table = tables.ConsoleServerPortImportTable + table = tables.ConsoleServerPortTable default_return_url = 'dcim:consoleserverport_list' @@ -1274,10 +1274,10 @@ class ConsoleServerPortBulkDeleteView(BulkDeleteView): # class PowerPortListView(ObjectListView): - queryset = PowerPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') + queryset = PowerPort.objects.prefetch_related('device', 'cable') filterset = filters.PowerPortFilterSet filterset_form = forms.PowerPortFilterForm - table = tables.PowerPortDetailTable + table = tables.PowerPortTable action_buttons = ('import', 'export') @@ -1304,7 +1304,7 @@ class PowerPortDeleteView(ObjectDeleteView): class PowerPortBulkImportView(BulkImportView): queryset = PowerPort.objects.all() model_form = forms.PowerPortCSVForm - table = tables.PowerPortImportTable + table = tables.PowerPortTable default_return_url = 'dcim:powerport_list' @@ -1327,10 +1327,10 @@ class PowerPortBulkDeleteView(BulkDeleteView): # class PowerOutletListView(ObjectListView): - queryset = PowerOutlet.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') + queryset = PowerOutlet.objects.prefetch_related('device', 'cable') filterset = filters.PowerOutletFilterSet filterset_form = forms.PowerOutletFilterForm - table = tables.PowerOutletDetailTable + table = tables.PowerOutletTable action_buttons = ('import', 'export') @@ -1357,7 +1357,7 @@ class PowerOutletDeleteView(ObjectDeleteView): class PowerOutletBulkImportView(BulkImportView): queryset = PowerOutlet.objects.all() model_form = forms.PowerOutletCSVForm - table = tables.PowerOutletImportTable + table = tables.PowerOutletTable default_return_url = 'dcim:poweroutlet_list' @@ -1390,10 +1390,10 @@ class PowerOutletBulkDeleteView(BulkDeleteView): # class InterfaceListView(ObjectListView): - queryset = Interface.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') + queryset = Interface.objects.prefetch_related('device', 'cable') filterset = filters.InterfaceFilterSet filterset_form = forms.InterfaceFilterForm - table = tables.InterfaceDetailTable + table = tables.InterfaceTable action_buttons = ('import', 'export') @@ -1453,7 +1453,7 @@ class InterfaceDeleteView(ObjectDeleteView): class InterfaceBulkImportView(BulkImportView): queryset = Interface.objects.all() model_form = forms.InterfaceCSVForm - table = tables.InterfaceImportTable + table = tables.InterfaceTable default_return_url = 'dcim:interface_list' @@ -1486,10 +1486,10 @@ class InterfaceBulkDeleteView(BulkDeleteView): # class FrontPortListView(ObjectListView): - queryset = FrontPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') + queryset = FrontPort.objects.prefetch_related('device', 'cable') filterset = filters.FrontPortFilterSet filterset_form = forms.FrontPortFilterForm - table = tables.FrontPortDetailTable + table = tables.FrontPortTable action_buttons = ('import', 'export') @@ -1516,7 +1516,7 @@ class FrontPortDeleteView(ObjectDeleteView): class FrontPortBulkImportView(BulkImportView): queryset = FrontPort.objects.all() model_form = forms.FrontPortCSVForm - table = tables.FrontPortImportTable + table = tables.FrontPortTable default_return_url = 'dcim:frontport_list' @@ -1549,10 +1549,10 @@ class FrontPortBulkDeleteView(BulkDeleteView): # class RearPortListView(ObjectListView): - queryset = RearPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') + queryset = RearPort.objects.prefetch_related('device', 'cable') filterset = filters.RearPortFilterSet filterset_form = forms.RearPortFilterForm - table = tables.RearPortDetailTable + table = tables.RearPortTable action_buttons = ('import', 'export') @@ -1579,7 +1579,7 @@ class RearPortDeleteView(ObjectDeleteView): class RearPortBulkImportView(BulkImportView): queryset = RearPort.objects.all() model_form = forms.RearPortCSVForm - table = tables.RearPortImportTable + table = tables.RearPortTable default_return_url = 'dcim:rearport_list' @@ -1612,12 +1612,10 @@ class RearPortBulkDeleteView(BulkDeleteView): # class DeviceBayListView(ObjectListView): - queryset = DeviceBay.objects.prefetch_related( - 'device', 'device__site', 'installed_device', 'installed_device__site' - ) + queryset = DeviceBay.objects.prefetch_related('device', 'installed_device') filterset = filters.DeviceBayFilterSet filterset_form = forms.DeviceBayFilterForm - table = tables.DeviceBayDetailTable + table = tables.DeviceBayTable action_buttons = ('import', 'export') @@ -1711,7 +1709,7 @@ class DeviceBayDepopulateView(ObjectEditView): class DeviceBayBulkImportView(BulkImportView): queryset = DeviceBay.objects.all() model_form = forms.DeviceBayCSVForm - table = tables.DeviceBayImportTable + table = tables.DeviceBayTable default_return_url = 'dcim:devicebay_list' From 1b807584122d656170118c69602777451ca5de73 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 12:09:56 -0400 Subject: [PATCH 08/12] Fix device component changelog display --- netbox/extras/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index ff8ce75e0..af5106a33 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -296,6 +296,7 @@ class ObjectChangeLogView(View): return render(request, 'extras/object_changelog.html', { object_var: obj, + 'instance': obj, # We'll eventually standardize on 'instance` for the object variable name 'table': objectchanges_table, 'base_template': base_template, 'active_tab': 'changelog', From 9edd27b20c55bf2044eb7b9db1a6619276436209 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 12:21:25 -0400 Subject: [PATCH 09/12] Linkify components under device view --- netbox/templates/dcim/inc/consoleport.html | 3 ++- netbox/templates/dcim/inc/consoleserverport.html | 3 ++- netbox/templates/dcim/inc/devicebay.html | 3 ++- netbox/templates/dcim/inc/frontport.html | 3 ++- netbox/templates/dcim/inc/poweroutlet.html | 3 ++- netbox/templates/dcim/inc/powerport.html | 3 ++- netbox/templates/dcim/inc/rearport.html | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/netbox/templates/dcim/inc/consoleport.html b/netbox/templates/dcim/inc/consoleport.html index 9089f19b4..02afd8f99 100644 --- a/netbox/templates/dcim/inc/consoleport.html +++ b/netbox/templates/dcim/inc/consoleport.html @@ -2,7 +2,8 @@ {# Name #}
- {{ cp }} + + {{ cp }} - {{ csp }} + + {{ csp }} - {{ devicebay.name }} + + {{ devicebay.name }} - {{ frontport }} + + {{ frontport }} - {{ po }} + + {{ po }} - {{ pp }} + + {{ pp }} - {{ rearport }} + + {{ rearport }}
{% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} + {% plugin_left_page instance %}
@@ -90,6 +92,12 @@
{% endif %}
+ {% plugin_right_page instance %} + + +
+
+ {% plugin_full_width_page instance %}
{% endblock %} diff --git a/netbox/templates/dcim/consoleserverport.html b/netbox/templates/dcim/consoleserverport.html index 8d7ca0b43..cdc43142e 100644 --- a/netbox/templates/dcim/consoleserverport.html +++ b/netbox/templates/dcim/consoleserverport.html @@ -1,5 +1,6 @@ {% extends 'dcim/device_component.html' %} {% load helpers %} +{% load plugins %} {% block content %}
@@ -34,6 +35,7 @@
{% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} + {% plugin_left_page instance %}
@@ -90,6 +92,12 @@
{% endif %}
+ {% plugin_right_page instance %} + + +
+
+ {% plugin_full_width_page instance %}
{% endblock %} diff --git a/netbox/templates/dcim/device_component.html b/netbox/templates/dcim/device_component.html index d2a1ad660..616655066 100644 --- a/netbox/templates/dcim/device_component.html +++ b/netbox/templates/dcim/device_component.html @@ -1,6 +1,7 @@ {% extends 'base.html' %} {% load helpers %} {% load perms %} +{% load plugins %} {% block header %}
@@ -14,6 +15,7 @@
@@ -57,6 +59,12 @@
{% endif %}
+ {% plugin_right_page instance %} + + +
+
+ {% plugin_full_width_page instance %}
{% endblock %} diff --git a/netbox/templates/dcim/frontport.html b/netbox/templates/dcim/frontport.html index 33ce03eb8..8ab51cb30 100644 --- a/netbox/templates/dcim/frontport.html +++ b/netbox/templates/dcim/frontport.html @@ -1,5 +1,6 @@ {% extends 'dcim/device_component.html' %} {% load helpers %} +{% load plugins %} {% block content %}
@@ -44,6 +45,7 @@
{% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} + {% plugin_left_page instance %}
@@ -78,6 +80,12 @@
{% endif %}
+ {% plugin_right_page instance %} + + +
+
+ {% plugin_full_width_page instance %}
{% endblock %} diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index b3163c413..4c0b453ad 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -1,219 +1,227 @@ {% extends 'dcim/device_component.html' %} {% load helpers %} +{% load plugins %} {% block content %} -
-
-
-
- Interface -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Device - {{ instance.device }} -
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Enabled - {% if instance.enabled %} - - {% else %} - - {% endif %} -
LAG - {% if instance.lag%} - {{ instance.lag }} - {% else %} - None - {% endif %} -
Description{{ instance.description|placeholder }}
MTU{{ instance.mtu|placeholder }}
MAC Address{{ instance.mac_address|placeholder }}
802.1Q Mode{{ instance.get_mode_display }}
-
- {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} -
-
- {% if instance.is_connectable %} +
+
- Connection + Interface
- {% if instance.cable %} - - {% if connected_interface %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% elif connected_circuittermination %} - {% with ct=connected_circuittermination %} - - - - - - - - - - - - - {% endwith %} - {% endif %} - - - - - - - - -
Device - {{ connected_interface.device }} -
Name - {{ connected_interface.name }} -
Type{{ connected_interface.get_type_display }}
Enabled - {% if connected_interface.enabled %} - - {% else %} - - {% endif %} -
LAG - {% if connected_interface.lag%} - {{ connected_interface.lag }} - {% else %} - None - {% endif %} -
Description{{ connected_interface.description|placeholder }}
MTU{{ connected_interface.mtu|placeholder }}
MAC Address{{ connected_interface.mac_address|placeholder }}
802.1Q Mode{{ connected_interface.get_mode_display }}
Provider{{ ct.circuit.provider }}
Circuit{{ ct.circuit }}
Side{{ ct.term_side }}
Cable - {{ instance.cable }} - - - -
Connection Status - {% if instance.connection_status %} - {{ instance.get_connection_status_display }} - {% else %} - {{ instance.get_connection_status_display }} - {% endif %} -
- {% else %} -
- Not connected -
- {% endif %} -
- {% endif %} - {% if instance.is_lag %} -
-
LAG Members
- - - - - - - - - - {% for member in instance.member_interfaces.all %} - - - - - - {% empty %} - - - - {% endfor %} - +
ParentInterfaceType
- {{ member.device }} - - {{ member }} - - {{ member.get_type_display }} -
No member interfaces
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Device + {{ instance.device }} +
Name{{ instance.name }}
Label{{ instance.label|placeholder }}
Type{{ instance.get_type_display }}
Enabled + {% if instance.enabled %} + + {% else %} + + {% endif %} +
LAG + {% if instance.lag%} + {{ instance.lag }} + {% else %} + None + {% endif %} +
Description{{ instance.description|placeholder }}
MTU{{ instance.mtu|placeholder }}
MAC Address{{ instance.mac_address|placeholder }}
802.1Q Mode{{ instance.get_mode_display }}
- {% endif %} + {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} + {% plugin_left_page instance %} +
+
+ {% if instance.is_connectable %} +
+
+ Connection +
+ {% if instance.cable %} + + {% if connected_interface %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% elif connected_circuittermination %} + {% with ct=connected_circuittermination %} + + + + + + + + + + + + + {% endwith %} + {% endif %} + + + + + + + + +
Device + {{ connected_interface.device }} +
Name + {{ connected_interface.name }} +
Type{{ connected_interface.get_type_display }}
Enabled + {% if connected_interface.enabled %} + + {% else %} + + {% endif %} +
LAG + {% if connected_interface.lag%} + {{ connected_interface.lag }} + {% else %} + None + {% endif %} +
Description{{ connected_interface.description|placeholder }}
MTU{{ connected_interface.mtu|placeholder }}
MAC Address{{ connected_interface.mac_address|placeholder }}
802.1Q Mode{{ connected_interface.get_mode_display }}
Provider{{ ct.circuit.provider }}
Circuit{{ ct.circuit }}
Side{{ ct.term_side }}
Cable + {{ instance.cable }} + + + +
Connection Status + {% if instance.connection_status %} + {{ instance.get_connection_status_display }} + {% else %} + {{ instance.get_connection_status_display }} + {% endif %} +
+ {% else %} +
+ Not connected +
+ {% endif %} +
+ {% endif %} + {% if instance.is_lag %} +
+
LAG Members
+ + + + + + + + + + {% for member in instance.member_interfaces.all %} + + + + + + {% empty %} + + + + {% endfor %} + +
ParentInterfaceType
+ {{ member.device }} + + {{ member }} + + {{ member.get_type_display }} +
No member interfaces
+
+ {% endif %} + {% plugin_right_page device %} +
-
-
-
- {% include 'panel_table.html' with table=ipaddress_table heading="IP Addresses" %} +
+
+ {% include 'panel_table.html' with table=ipaddress_table heading="IP Addresses" %} +
-
-
-
- {% include 'panel_table.html' with table=vlan_table heading="VLANs" %} +
+
+ {% include 'panel_table.html' with table=vlan_table heading="VLANs" %} +
+
+
+
+ {% plugin_full_width_page instance %} +
-
{% endblock %} diff --git a/netbox/templates/dcim/poweroutlet.html b/netbox/templates/dcim/poweroutlet.html index 519bd01df..cddcffd6f 100644 --- a/netbox/templates/dcim/poweroutlet.html +++ b/netbox/templates/dcim/poweroutlet.html @@ -1,5 +1,6 @@ {% extends 'dcim/device_component.html' %} {% load helpers %} +{% load plugins %} {% block content %}
@@ -42,6 +43,7 @@
{% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} + {% plugin_left_page instance %}
@@ -98,6 +100,12 @@
{% endif %}
+ {% plugin_right_page instance %} +
+
+
+
+ {% plugin_full_width_page instance %}
{% endblock %} diff --git a/netbox/templates/dcim/powerport.html b/netbox/templates/dcim/powerport.html index e7c103c9d..8642bd8fb 100644 --- a/netbox/templates/dcim/powerport.html +++ b/netbox/templates/dcim/powerport.html @@ -1,5 +1,6 @@ {% extends 'dcim/device_component.html' %} {% load helpers %} +{% load plugins %} {% block content %}
@@ -42,6 +43,7 @@
{% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} + {% plugin_left_page instance %}
@@ -98,6 +100,12 @@
{% endif %}
+ {% plugin_right_page instance %} + + +
+
+ {% plugin_full_width_page instance %}
{% endblock %} diff --git a/netbox/templates/dcim/rearport.html b/netbox/templates/dcim/rearport.html index 480f26d2c..982d53eaa 100644 --- a/netbox/templates/dcim/rearport.html +++ b/netbox/templates/dcim/rearport.html @@ -1,5 +1,6 @@ {% extends 'dcim/device_component.html' %} {% load helpers %} +{% load plugins %} {% block content %}
@@ -38,6 +39,7 @@
{% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} + {% plugin_left_page instance %}
@@ -72,6 +74,12 @@
{% endif %}
+ {% plugin_right_page instance %} + + +
+
+ {% plugin_full_width_page instance %}
{% endblock %} From fb9ebadee58ae9f49d35ca601a198fc9de164b78 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 25 Jun 2020 13:36:54 -0400 Subject: [PATCH 12/12] Fix typo --- netbox/templates/dcim/interface.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index 4c0b453ad..e3d67eb2c 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -206,7 +206,7 @@ {% endif %} - {% plugin_right_page device %} + {% plugin_right_page instance %}