diff --git a/netbox/dcim/ui/panels.py b/netbox/dcim/ui/panels.py
index fa7ad848a..981644825 100644
--- a/netbox/dcim/ui/panels.py
+++ b/netbox/dcim/ui/panels.py
@@ -1,3 +1,4 @@
+from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from netbox.ui import attrs, panels
@@ -189,6 +190,272 @@ class PlatformPanel(panels.NestedGroupObjectPanel):
config_template = attrs.RelatedObjectAttr('config_template', linkify=True)
+class ConsolePortPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ module = attrs.RelatedObjectAttr('module', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ type = attrs.ChoiceAttr('type')
+ speed = attrs.ChoiceAttr('speed')
+ description = attrs.TextAttr('description')
+
+
+class ConsoleServerPortPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ module = attrs.RelatedObjectAttr('module', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ type = attrs.ChoiceAttr('type')
+ speed = attrs.ChoiceAttr('speed')
+ description = attrs.TextAttr('description')
+
+
+class PowerPortPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ module = attrs.RelatedObjectAttr('module', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ type = attrs.ChoiceAttr('type')
+ description = attrs.TextAttr('description')
+ maximum_draw = attrs.TextAttr('maximum_draw')
+ allocated_draw = attrs.TextAttr('allocated_draw')
+
+
+class PowerOutletPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ module = attrs.RelatedObjectAttr('module', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ type = attrs.ChoiceAttr('type')
+ status = attrs.ChoiceAttr('status')
+ description = attrs.TextAttr('description')
+ color = attrs.ColorAttr('color')
+ power_port = attrs.RelatedObjectAttr('power_port', linkify=True)
+ feed_leg = attrs.ChoiceAttr('feed_leg')
+
+
+class FrontPortPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ module = attrs.RelatedObjectAttr('module', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ type = attrs.ChoiceAttr('type')
+ color = attrs.ColorAttr('color')
+ positions = attrs.TextAttr('positions')
+ description = attrs.TextAttr('description')
+
+
+class RearPortPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ module = attrs.RelatedObjectAttr('module', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ type = attrs.ChoiceAttr('type')
+ color = attrs.ColorAttr('color')
+ positions = attrs.TextAttr('positions')
+ description = attrs.TextAttr('description')
+
+
+class ModuleBayPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ module = attrs.RelatedObjectAttr('module', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ position = attrs.TextAttr('position')
+ description = attrs.TextAttr('description')
+
+
+class DeviceBayPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ description = attrs.TextAttr('description')
+
+
+class InventoryItemPanel(panels.ObjectAttributesPanel):
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ parent = attrs.RelatedObjectAttr('parent', linkify=True, label=_('Parent item'))
+ name = attrs.TextAttr('name')
+ label = attrs.TextAttr('label')
+ status = attrs.ChoiceAttr('status')
+ role = attrs.RelatedObjectAttr('role', linkify=True)
+ component = attrs.GenericForeignKeyAttr('component', linkify=True)
+ manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True)
+ part_id = attrs.TextAttr('part_id', label=_('Part ID'))
+ serial = attrs.TextAttr('serial')
+ asset_tag = attrs.TextAttr('asset_tag')
+ description = attrs.TextAttr('description')
+
+
+class InventoryItemRolePanel(panels.OrganizationalObjectPanel):
+ color = attrs.ColorAttr('color')
+
+
+class CablePanel(panels.ObjectAttributesPanel):
+ type = attrs.ChoiceAttr('type')
+ status = attrs.ChoiceAttr('status')
+ profile = attrs.ChoiceAttr('profile')
+ tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+ label = attrs.TextAttr('label')
+ description = attrs.TextAttr('description')
+ color = attrs.ColorAttr('color')
+ length = attrs.NumericAttr('length', unit_accessor='get_length_unit_display')
+
+
+class VirtualChassisPanel(panels.ObjectAttributesPanel):
+ domain = attrs.TextAttr('domain')
+ master = attrs.RelatedObjectAttr('master', linkify=True)
+ description = attrs.TextAttr('description')
+
+
+class VirtualChassisDetailMembersPanel(panels.ObjectPanel):
+ """
+ A panel which lists all members of a virtual chassis on the VirtualChassis detail view.
+ """
+ template_name = 'dcim/panels/virtual_chassis_detail_members.html'
+ title = _('Members')
+
+ def get_context(self, context):
+ return {
+ **super().get_context(context),
+ 'members': context.get('members'),
+ }
+
+ def render(self, context):
+ ctx = self.get_context(context)
+ return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
+class PowerPanelPanel(panels.ObjectAttributesPanel):
+ site = attrs.RelatedObjectAttr('site', linkify=True)
+ location = attrs.NestedObjectAttr('location', linkify=True)
+ description = attrs.TextAttr('description')
+
+
+class PowerFeedPanel(panels.ObjectAttributesPanel):
+ power_panel = attrs.RelatedObjectAttr('power_panel', linkify=True)
+ rack = attrs.RelatedObjectAttr('rack', linkify=True)
+ type = attrs.ChoiceAttr('type')
+ status = attrs.ChoiceAttr('status')
+ description = attrs.TextAttr('description')
+ tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+ connected_device = attrs.TemplatedAttr(
+ 'connected_endpoints',
+ label=_('Connected device'),
+ template_name='dcim/powerfeed/attrs/connected_device.html',
+ )
+ utilization = attrs.TemplatedAttr(
+ 'connected_endpoints',
+ label=_('Utilization (allocated)'),
+ template_name='dcim/powerfeed/attrs/utilization.html',
+ )
+
+
+class PowerFeedElectricalPanel(panels.ObjectAttributesPanel):
+ title = _('Electrical Characteristics')
+
+ supply = attrs.ChoiceAttr('supply')
+ voltage = attrs.TextAttr('voltage', format_string=_('{}V'))
+ amperage = attrs.TextAttr('amperage', format_string=_('{}A'))
+ phase = attrs.ChoiceAttr('phase')
+ max_utilization = attrs.TextAttr('max_utilization', format_string='{}%')
+
+
+class VirtualDeviceContextPanel(panels.ObjectAttributesPanel):
+ name = attrs.TextAttr('name')
+ device = attrs.RelatedObjectAttr('device', linkify=True)
+ identifier = attrs.TextAttr('identifier')
+ status = attrs.ChoiceAttr('status')
+ primary_ip4 = attrs.TemplatedAttr(
+ 'primary_ip4',
+ label=_('Primary IPv4'),
+ template_name='dcim/device/attrs/ipaddress.html',
+ )
+ primary_ip6 = attrs.TemplatedAttr(
+ 'primary_ip6',
+ label=_('Primary IPv6'),
+ template_name='dcim/device/attrs/ipaddress.html',
+ )
+ tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+
+
+class MACAddressPanel(panels.ObjectAttributesPanel):
+ mac_address = attrs.TextAttr('mac_address', label=_('MAC address'), style='font-monospace', copy_button=True)
+ description = attrs.TextAttr('description')
+ assignment = attrs.TemplatedAttr(
+ 'assigned_object',
+ template_name='dcim/macaddress/attrs/assignment.html',
+ )
+ is_primary = attrs.BooleanAttr('is_primary', label=_('Primary for interface'))
+
+
+class ConnectionPanel(panels.ObjectPanel):
+ """
+ A panel which displays connection information for a cabled object.
+ """
+ template_name = 'dcim/panels/connection.html'
+ title = _('Connection')
+
+ def __init__(self, trace_url_name, connect_options=None, show_endpoints=True, **kwargs):
+ super().__init__(**kwargs)
+ self.trace_url_name = trace_url_name
+ self.connect_options = connect_options or []
+ self.show_endpoints = show_endpoints
+
+ def get_context(self, context):
+ return {
+ **super().get_context(context),
+ 'trace_url_name': self.trace_url_name,
+ 'connect_options': self.connect_options,
+ 'show_endpoints': self.show_endpoints,
+ }
+
+ def render(self, context):
+ ctx = self.get_context(context)
+ return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
+class InventoryItemsPanel(panels.ObjectPanel):
+ """
+ A panel which displays inventory items associated with a component.
+ """
+ template_name = 'dcim/panels/component_inventory_items.html'
+ title = _('Inventory Items')
+
+ def render(self, context):
+ obj = context['object']
+ if not obj.inventory_items.exists():
+ return ''
+ ctx = self.get_context(context)
+ return render_to_string(self.template_name, ctx, request=ctx.get('request'))
+
+
+class PortMappingsPanel(panels.ObjectPanel):
+ """
+ A panel which displays port position mappings for front/rear ports.
+ """
+ template_name = 'dcim/panels/port_mappings.html'
+ title = _('Port Mappings')
+
+ def __init__(self, context_key, label_column, position_field, port_field, port_position_field, **kwargs):
+ super().__init__(**kwargs)
+ self.context_key = context_key
+ self.label_column = label_column
+ self.position_field = position_field
+ self.port_field = port_field
+ self.port_position_field = port_position_field
+
+ def get_context(self, context):
+ return {
+ **super().get_context(context),
+ 'mappings': context.get(self.context_key, []),
+ 'label_column': self.label_column,
+ 'position_field': self.position_field,
+ 'port_field': self.port_field,
+ 'port_position_field': self.port_position_field,
+ }
+
+
class VirtualChassisMembersPanel(panels.ObjectPanel):
"""
A panel which lists all members of a virtual chassis.
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index c90f63753..5fc25ebff 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -2907,6 +2907,28 @@ class ConsolePortListView(generic.ObjectListView):
@register_model_view(ConsolePort)
class ConsolePortView(generic.ObjectView):
queryset = ConsolePort.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.ConsolePortPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ panels.ConnectionPanel(
+ trace_url_name='dcim:consoleport_trace',
+ connect_options=[
+ {
+ 'a_type': 'dcim.consoleport',
+ 'b_type': 'dcim.consoleserverport',
+ 'label': _('Console Server Port'),
+ },
+ {'a_type': 'dcim.consoleport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+ {'a_type': 'dcim.consoleport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+ ],
+ ),
+ panels.InventoryItemsPanel(),
+ ],
+ )
@register_model_view(ConsolePort, 'add', detail=False)
@@ -2978,6 +3000,24 @@ class ConsoleServerPortListView(generic.ObjectListView):
@register_model_view(ConsoleServerPort)
class ConsoleServerPortView(generic.ObjectView):
queryset = ConsoleServerPort.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.ConsoleServerPortPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ panels.ConnectionPanel(
+ trace_url_name='dcim:consoleserverport_trace',
+ connect_options=[
+ {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.consoleport', 'label': _('Console Port')},
+ {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+ {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+ ],
+ ),
+ panels.InventoryItemsPanel(),
+ ],
+ )
@register_model_view(ConsoleServerPort, 'add', detail=False)
@@ -3049,6 +3089,23 @@ class PowerPortListView(generic.ObjectListView):
@register_model_view(PowerPort)
class PowerPortView(generic.ObjectView):
queryset = PowerPort.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.PowerPortPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ panels.ConnectionPanel(
+ trace_url_name='dcim:powerport_trace',
+ connect_options=[
+ {'a_type': 'dcim.powerport', 'b_type': 'dcim.poweroutlet', 'label': _('Power Outlet')},
+ {'a_type': 'dcim.powerport', 'b_type': 'dcim.powerfeed', 'label': _('Power Feed')},
+ ],
+ ),
+ panels.InventoryItemsPanel(),
+ ],
+ )
@register_model_view(PowerPort, 'add', detail=False)
@@ -3120,6 +3177,22 @@ class PowerOutletListView(generic.ObjectListView):
@register_model_view(PowerOutlet)
class PowerOutletView(generic.ObjectView):
queryset = PowerOutlet.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.PowerOutletPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ panels.ConnectionPanel(
+ trace_url_name='dcim:poweroutlet_trace',
+ connect_options=[
+ {'a_type': 'dcim.poweroutlet', 'b_type': 'dcim.powerport', 'label': _('Power Port')},
+ ],
+ ),
+ panels.InventoryItemsPanel(),
+ ],
+ )
@register_model_view(PowerOutlet, 'add', detail=False)
@@ -3329,6 +3402,39 @@ class FrontPortListView(generic.ObjectListView):
@register_model_view(FrontPort)
class FrontPortView(generic.ObjectView):
queryset = FrontPort.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.FrontPortPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ panels.InventoryItemsPanel(),
+ ],
+ right_panels=[
+ panels.ConnectionPanel(
+ trace_url_name='dcim:frontport_trace',
+ show_endpoints=False,
+ connect_options=[
+ {'a_type': 'dcim.frontport', 'b_type': 'dcim.interface', 'label': _('Interface')},
+ {'a_type': 'dcim.frontport', 'b_type': 'dcim.consoleserverport', 'label': _('Console Server Port')},
+ {'a_type': 'dcim.frontport', 'b_type': 'dcim.consoleport', 'label': _('Console Port')},
+ {'a_type': 'dcim.frontport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+ {'a_type': 'dcim.frontport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+ {
+ 'a_type': 'dcim.frontport',
+ 'b_type': 'circuits.circuittermination',
+ 'label': _('Circuit Termination'),
+ },
+ ],
+ ),
+ panels.PortMappingsPanel(
+ context_key='rear_port_mappings',
+ label_column=_('Rear Port'),
+ position_field='front_port_position',
+ port_field='rear_port',
+ port_position_field='rear_port_position',
+ ),
+ ],
+ )
def get_extra_context(self, request, instance):
return {
@@ -3405,6 +3511,37 @@ class RearPortListView(generic.ObjectListView):
@register_model_view(RearPort)
class RearPortView(generic.ObjectView):
queryset = RearPort.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.RearPortPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ panels.InventoryItemsPanel(),
+ ],
+ right_panels=[
+ panels.ConnectionPanel(
+ trace_url_name='dcim:rearport_trace',
+ show_endpoints=False,
+ connect_options=[
+ {'a_type': 'dcim.rearport', 'b_type': 'dcim.interface', 'label': _('Interface')},
+ {'a_type': 'dcim.rearport', 'b_type': 'dcim.frontport', 'label': _('Front Port')},
+ {'a_type': 'dcim.rearport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')},
+ {
+ 'a_type': 'dcim.rearport',
+ 'b_type': 'circuits.circuittermination',
+ 'label': _('Circuit Termination'),
+ },
+ ],
+ ),
+ panels.PortMappingsPanel(
+ context_key='front_port_mappings',
+ label_column=_('Front Port'),
+ position_field='rear_port_position',
+ port_field='front_port',
+ port_position_field='front_port_position',
+ ),
+ ],
+ )
def get_extra_context(self, request, instance):
return {
@@ -3481,6 +3618,19 @@ class ModuleBayListView(generic.ObjectListView):
@register_model_view(ModuleBay)
class ModuleBayView(generic.ObjectView):
queryset = ModuleBay.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.ModuleBayPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ CustomFieldsPanel(),
+ Panel(
+ title=_('Installed Module'),
+ template_name='dcim/panels/installed_module.html',
+ ),
+ ],
+ )
@register_model_view(ModuleBay, 'add', detail=False)
@@ -3543,6 +3693,19 @@ class DeviceBayListView(generic.ObjectListView):
@register_model_view(DeviceBay)
class DeviceBayView(generic.ObjectView):
queryset = DeviceBay.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.DeviceBayPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ Panel(
+ title=_('Installed Device'),
+ template_name='dcim/panels/installed_device.html',
+ ),
+ ],
+ )
@register_model_view(DeviceBay, 'add', detail=False)
@@ -3686,6 +3849,13 @@ class InventoryItemListView(generic.ObjectListView):
@register_model_view(InventoryItem)
class InventoryItemView(generic.ObjectView):
queryset = InventoryItem.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.InventoryItemPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ ],
+ )
@register_model_view(InventoryItem, 'edit')
@@ -3767,13 +3937,19 @@ class InventoryItemRoleListView(generic.ObjectListView):
@register_model_view(InventoryItemRole)
-class InventoryItemRoleView(generic.ObjectView):
+class InventoryItemRoleView(GetRelatedModelsMixin, generic.ObjectView):
queryset = InventoryItemRole.objects.all()
-
- def get_extra_context(self, request, instance):
- return {
- 'inventoryitem_count': InventoryItem.objects.filter(role=instance).count(),
- }
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.InventoryItemRolePanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ RelatedObjectsPanel(),
+ CommentsPanel(),
+ CustomFieldsPanel(),
+ ],
+ )
@register_model_view(InventoryItemRole, 'add', detail=False)
@@ -3940,6 +4116,24 @@ class CableListView(generic.ObjectListView):
@register_model_view(Cable)
class CableView(generic.ObjectView):
queryset = Cable.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.CablePanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ CommentsPanel(),
+ ],
+ right_panels=[
+ Panel(
+ title=_('Termination A'),
+ template_name='dcim/panels/cable_termination_a.html',
+ ),
+ Panel(
+ title=_('Termination B'),
+ template_name='dcim/panels/cable_termination_b.html',
+ ),
+ ],
+ )
@register_model_view(Cable, 'add', detail=False)
@@ -4072,6 +4266,17 @@ class VirtualChassisListView(generic.ObjectListView):
@register_model_view(VirtualChassis)
class VirtualChassisView(generic.ObjectView):
queryset = VirtualChassis.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.VirtualChassisPanel(),
+ TagsPanel(),
+ CustomFieldsPanel(),
+ ],
+ right_panels=[
+ panels.VirtualChassisDetailMembersPanel(),
+ CommentsPanel(),
+ ],
+ )
def get_extra_context(self, request, instance):
members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
@@ -4317,6 +4522,27 @@ class PowerPanelListView(generic.ObjectListView):
@register_model_view(PowerPanel)
class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
queryset = PowerPanel.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.PowerPanelPanel(),
+ TagsPanel(),
+ CommentsPanel(),
+ ],
+ right_panels=[
+ RelatedObjectsPanel(),
+ CustomFieldsPanel(),
+ ImageAttachmentsPanel(),
+ ],
+ bottom_panels=[
+ ObjectsTablePanel(
+ model='dcim.PowerFeed',
+ filters={'power_panel_id': lambda ctx: ctx['object'].pk},
+ actions=[
+ actions.AddObject('dcim.PowerFeed', url_params={'power_panel': lambda ctx: ctx['object'].pk}),
+ ],
+ ),
+ ],
+ )
def get_extra_context(self, request, instance):
return {
@@ -4380,6 +4606,23 @@ class PowerFeedListView(generic.ObjectListView):
@register_model_view(PowerFeed)
class PowerFeedView(generic.ObjectView):
queryset = PowerFeed.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.PowerFeedPanel(),
+ panels.PowerFeedElectricalPanel(),
+ CustomFieldsPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ panels.ConnectionPanel(
+ trace_url_name='dcim:powerfeed_trace',
+ connect_options=[
+ {'a_type': 'dcim.powerfeed', 'b_type': 'dcim.powerport', 'label': _('Power Port')},
+ ],
+ ),
+ CommentsPanel(),
+ ],
+ )
@register_model_view(PowerFeed, 'add', detail=False)
@@ -4448,6 +4691,23 @@ class VirtualDeviceContextListView(generic.ObjectListView):
@register_model_view(VirtualDeviceContext)
class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
queryset = VirtualDeviceContext.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.VirtualDeviceContextPanel(),
+ TagsPanel(),
+ ],
+ right_panels=[
+ RelatedObjectsPanel(),
+ CommentsPanel(),
+ CustomFieldsPanel(),
+ ],
+ bottom_panels=[
+ ObjectsTablePanel(
+ model='dcim.Interface',
+ filters={'vdc_id': lambda ctx: ctx['object'].pk},
+ ),
+ ],
+ )
def get_extra_context(self, request, instance):
return {
@@ -4516,6 +4776,16 @@ class MACAddressListView(generic.ObjectListView):
@register_model_view(MACAddress)
class MACAddressView(generic.ObjectView):
queryset = MACAddress.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.MACAddressPanel(),
+ TagsPanel(),
+ CustomFieldsPanel(),
+ ],
+ right_panels=[
+ CommentsPanel(),
+ ],
+ )
@register_model_view(MACAddress, 'add', detail=False)
diff --git a/netbox/templates/dcim/cable.html b/netbox/templates/dcim/cable.html
index 8e685c514..f15e1d050 100644
--- a/netbox/templates/dcim/cable.html
+++ b/netbox/templates/dcim/cable.html
@@ -1,87 +1 @@
{% extends 'generic/object.html' %}
-{% load buttons %}
-{% load helpers %}
-{% load perms %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Type" %} |
- {{ object.get_type_display|placeholder }} |
-
-
- | {% trans "Status" %} |
- {% badge object.get_status_display bg_color=object.get_status_color %} |
-
-
- | {% trans "Profile" %} |
- {% badge object.get_profile_display %} |
-
-
- | {% trans "Tenant" %} |
-
- {% if object.tenant.group %}
- {{ object.tenant.group|linkify }} /
- {% endif %}
- {{ object.tenant|linkify|placeholder }}
- |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Color" %} |
-
- {% if object.color %}
-
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
- | {% trans "Length" %} |
-
- {% if object.length is not None %}
- {{ object.length|floatformat }} {{ object.get_length_unit_display }}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/comments.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% include 'dcim/inc/cable_termination.html' with terminations=object.a_terminations %}
-
-
-
- {% include 'dcim/inc/cable_termination.html' with terminations=object.b_terminations %}
-
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/consoleport.html b/netbox/templates/dcim/consoleport.html
index 986b38dc8..313d6aec6 100644
--- a/netbox/templates/dcim/consoleport.html
+++ b/netbox/templates/dcim/consoleport.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,88 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Module" %} |
- {{ object.module|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Type" %} |
- {{ object.get_type_display }} |
-
-
- | {% trans "Speed" %} |
- {{ object.get_speed_display }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.mark_connected %}
-
-
- {% trans "Marked as connected" %}
-
- {% elif object.cable %}
- {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:consoleport_trace' %}
- {% else %}
-
- {% trans "Not Connected" %}
- {% if perms.dcim.add_cable %}
-
-
-
-
- {% endif %}
-
- {% endif %}
-
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/consoleserverport.html b/netbox/templates/dcim/consoleserverport.html
index 0ec0ac6fb..80f0e54a2 100644
--- a/netbox/templates/dcim/consoleserverport.html
+++ b/netbox/templates/dcim/consoleserverport.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,88 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Module" %} |
- {{ object.module|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Type" %} |
- {{ object.get_type_display|placeholder }} |
-
-
- | {% trans "Speed" %} |
- {{ object.get_speed_display|placeholder }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.mark_connected %}
-
-
- {% trans "Marked as connected" %}
-
- {% elif object.cable %}
- {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:consoleserverport_trace' %}
- {% else %}
-
- {% trans "Not Connected" %}
- {% if perms.dcim.add_cable %}
-
-
-
-
- {% endif %}
-
- {% endif %}
-
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/devicebay.html b/netbox/templates/dcim/devicebay.html
index 1148a30e3..afc3ad506 100644
--- a/netbox/templates/dcim/devicebay.html
+++ b/netbox/templates/dcim/devicebay.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,63 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.installed_device %}
- {% with device=object.installed_device %}
-
-
- | {% trans "Device" %} |
- {{ device|linkify }} |
-
-
- | {% trans "Device Type" %} |
- {{ device.device_type }} |
-
-
- {% endwith %}
- {% else %}
-
- {% trans "None" %}
-
- {% endif %}
-
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/frontport.html b/netbox/templates/dcim/frontport.html
index f93e8282f..74d42616c 100644
--- a/netbox/templates/dcim/frontport.html
+++ b/netbox/templates/dcim/frontport.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,149 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Module" %} |
- {{ object.module|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Type" %} |
- {{ object.get_type_display }} |
-
-
- | {% trans "Color" %} |
-
- {% if object.color %}
-
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
- | {% trans "Positions" %} |
- {{ object.positions }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.mark_connected %}
-
- {% trans "Marked as Connected" %}
-
- {% elif object.cable %}
-
-
- | {% trans "Cable" %} |
-
- {{ object.cable|linkify }}
-
-
-
- |
-
-
- | {% trans "Connection Status" %} |
-
- {% if object.cable.status %}
- {{ object.cable.get_status_display }}
- {% else %}
- {{ object.cable.get_status_display }}
- {% endif %}
- |
-
-
- {% else %}
-
- {% trans "Not Connected" %}
- {% if perms.dcim.add_cable %}
-
-
-
-
- {% endif %}
-
- {% endif %}
-
-
-
-
- {% if rear_port_mappings %}
-
-
- | {% trans "Position" %} |
- {% trans "Rear Port" %} |
-
-
- {% endif %}
- {% for mapping in rear_port_mappings %}
-
- | {{ mapping.front_port_position }} |
-
- {{ mapping.rear_port }}:{{ mapping.rear_port_position }}
- |
-
- {% empty %}
- {% trans "No mappings defined" %}
- {% endfor %}
-
-
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/inventoryitem.html b/netbox/templates/dcim/inventoryitem.html
index d389abe8e..3d5a2cf27 100644
--- a/netbox/templates/dcim/inventoryitem.html
+++ b/netbox/templates/dcim/inventoryitem.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,74 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Parent Item" %} |
- {{ object.parent|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Status" %} |
- {% badge object.get_status_display bg_color=object.get_status_color %} |
-
-
- | {% trans "Role" %} |
- {{ object.role|linkify|placeholder }} |
-
-
- | {% trans "Component" %} |
- {{ object.component|linkify|placeholder }} |
-
-
- | {% trans "Manufacturer" %} |
- {{ object.manufacturer|linkify|placeholder }} |
-
-
- | {% trans "Part ID" %} |
- {{ object.part_id|placeholder }} |
-
-
- | {% trans "Serial" %} |
- {{ object.serial|placeholder }} |
-
-
- | {% trans "Asset Tag" %} |
- {{ object.asset_tag|placeholder }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/inventoryitemrole.html b/netbox/templates/dcim/inventoryitemrole.html
index 4791e1ab3..f15e1d050 100644
--- a/netbox/templates/dcim/inventoryitemrole.html
+++ b/netbox/templates/dcim/inventoryitemrole.html
@@ -1,53 +1 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block breadcrumbs %}
- {% trans "Inventory Item Roles" %}
-{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Color" %} |
-
-
- |
-
-
- | {% trans "Inventory Items" %} |
-
- {{ inventoryitem_count }}
- |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
- {% include 'inc/panels/comments.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/macaddress.html b/netbox/templates/dcim/macaddress.html
index 489d55c08..f15e1d050 100644
--- a/netbox/templates/dcim/macaddress.html
+++ b/netbox/templates/dcim/macaddress.html
@@ -1,55 +1 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "MAC Address" %} |
-
- {{ object.mac_address|placeholder }}
- {% copy_content object.pk prefix="macaddress_" %}
- |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Assignment" %} |
-
- {% if object.assigned_object %}
- {{ object.assigned_object.parent_object|linkify }} /
- {{ object.assigned_object|linkify }}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
- | {% trans "Primary for interface" %} |
- {% checkmark object.is_primary %} |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% plugin_left_page object %}
-
-
- {% include 'inc/panels/comments.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/macaddress/attrs/assignment.html b/netbox/templates/dcim/macaddress/attrs/assignment.html
new file mode 100644
index 000000000..872788bd2
--- /dev/null
+++ b/netbox/templates/dcim/macaddress/attrs/assignment.html
@@ -0,0 +1,7 @@
+{% load helpers %}
+{% if value %}
+ {{ value.parent_object|linkify }} /
+ {{ value|linkify }}
+{% else %}
+ {{ ''|placeholder }}
+{% endif %}
diff --git a/netbox/templates/dcim/modulebay.html b/netbox/templates/dcim/modulebay.html
index 054a13b1c..ca711665b 100644
--- a/netbox/templates/dcim/modulebay.html
+++ b/netbox/templates/dcim/modulebay.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,83 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
-
- {{ object.device }}
- |
-
-
- | {% trans "Module" %} |
- {{ object.module|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Position" %} |
- {{ object.position|placeholder }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
- {% include 'inc/panels/custom_fields.html' %}
-
-
- {% if object.installed_module %}
- {% with module=object.installed_module %}
-
-
- | {% trans "Module" %} |
- {{ module|linkify }} |
-
-
- | {% trans "Manufacturer" %} |
- {{ module.module_type.manufacturer|linkify }} |
-
-
- | {% trans "Module Type" %} |
- {{ module.module_type|linkify }} |
-
-
- | {% trans "Serial Number" %} |
- {{ module.serial|placeholder }} |
-
-
- | {% trans "Asset Tag" %} |
- {{ module.asset_tag|placeholder }} |
-
-
- {% endwith %}
- {% else %}
-
{% trans "None" %}
- {% endif %}
-
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/panels/cable_termination_a.html b/netbox/templates/dcim/panels/cable_termination_a.html
new file mode 100644
index 000000000..50f7b028d
--- /dev/null
+++ b/netbox/templates/dcim/panels/cable_termination_a.html
@@ -0,0 +1,6 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+ {% include 'dcim/inc/cable_termination.html' with terminations=object.a_terminations %}
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/panels/cable_termination_b.html b/netbox/templates/dcim/panels/cable_termination_b.html
new file mode 100644
index 000000000..bfd56f621
--- /dev/null
+++ b/netbox/templates/dcim/panels/cable_termination_b.html
@@ -0,0 +1,6 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+ {% include 'dcim/inc/cable_termination.html' with terminations=object.b_terminations %}
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/panels/component_inventory_items.html b/netbox/templates/dcim/panels/component_inventory_items.html
new file mode 100644
index 000000000..02b3e1d6a
--- /dev/null
+++ b/netbox/templates/dcim/panels/component_inventory_items.html
@@ -0,0 +1,40 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+
+
+
+ | {% trans "Name" %} |
+ {% trans "Label" %} |
+ {% trans "Role" %} |
+ |
+
+
+
+ {% for item in object.inventory_items.all %}
+
+ | {{ item|linkify:"name" }} |
+ {{ item.label|placeholder }} |
+ {{ item.role|linkify|placeholder }} |
+
+ {% if perms.dcim.change_inventoryitem %}
+
+
+
+ {% endif %}
+ {% if perms.dcim.delete_inventoryitem %}
+
+
+
+ {% endif %}
+ |
+
+ {% empty %}
+
+ | {% trans "None" %} |
+
+ {% endfor %}
+
+
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/panels/connection.html b/netbox/templates/dcim/panels/connection.html
new file mode 100644
index 000000000..454418227
--- /dev/null
+++ b/netbox/templates/dcim/panels/connection.html
@@ -0,0 +1,96 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+ {% if object.mark_connected %}
+
+
+ {% trans "Marked as connected" %}
+
+ {% elif object.cable %}
+ {% if show_endpoints %}
+
+
+ | {% trans "Cable" %} |
+
+ {{ object.cable|linkify }}
+
+
+
+ |
+
+
+ | {% trans "Path Status" %} |
+
+ {% if object.path.is_complete and object.path.is_active %}
+ {% trans "Reachable" %}
+ {% else %}
+ {% trans "Not Reachable" %}
+ {% endif %}
+ |
+
+
+ | {% trans "Path Endpoints" %} |
+
+ {% for endpoint in object.connected_endpoints %}
+ {% if endpoint.parent_object %}
+ {{ endpoint.parent_object|linkify }}
+
+ {% endif %}
+ {{ endpoint|linkify }}
+ {% if not forloop.last %} {% endif %}
+ {% empty %}
+ {{ ''|placeholder }}
+ {% endfor %}
+ |
+
+
+ {% else %}
+
+
+ | {% trans "Cable" %} |
+
+ {{ object.cable|linkify }}
+
+
+
+ |
+
+
+ | {% trans "Connection Status" %} |
+
+ {% if object.cable.status %}
+ {{ object.cable.get_status_display }}
+ {% else %}
+ {{ object.cable.get_status_display }}
+ {% endif %}
+ |
+
+
+ {% endif %}
+ {% else %}
+
+ {% trans "Not Connected" %}
+ {% if perms.dcim.add_cable %}
+ {% if connect_options|length > 1 %}
+
+
+
+
+ {% elif connect_options|length == 1 %}
+
+ {% trans "Connect" %}
+
+ {% endif %}
+ {% endif %}
+
+ {% endif %}
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/panels/installed_device.html b/netbox/templates/dcim/panels/installed_device.html
new file mode 100644
index 000000000..118ba5137
--- /dev/null
+++ b/netbox/templates/dcim/panels/installed_device.html
@@ -0,0 +1,21 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+ {% if object.installed_device %}
+ {% with device=object.installed_device %}
+
+
+ | {% trans "Device" %} |
+ {{ device|linkify }} |
+
+
+ | {% trans "Device Type" %} |
+ {{ device.device_type }} |
+
+
+ {% endwith %}
+ {% else %}
+ {% trans "None" %}
+ {% endif %}
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/panels/installed_module.html b/netbox/templates/dcim/panels/installed_module.html
new file mode 100644
index 000000000..38ee3ecc9
--- /dev/null
+++ b/netbox/templates/dcim/panels/installed_module.html
@@ -0,0 +1,33 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+ {% if object.installed_module %}
+ {% with module=object.installed_module %}
+
+
+ | {% trans "Module" %} |
+ {{ module|linkify }} |
+
+
+ | {% trans "Manufacturer" %} |
+ {{ module.module_type.manufacturer|linkify }} |
+
+
+ | {% trans "Module Type" %} |
+ {{ module.module_type|linkify }} |
+
+
+ | {% trans "Serial Number" %} |
+ {{ module.serial|placeholder }} |
+
+
+ | {% trans "Asset Tag" %} |
+ {{ module.asset_tag|placeholder }} |
+
+
+ {% endwith %}
+ {% else %}
+ {% trans "None" %}
+ {% endif %}
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/panels/port_mappings.html b/netbox/templates/dcim/panels/port_mappings.html
new file mode 100644
index 000000000..3e702c1cc
--- /dev/null
+++ b/netbox/templates/dcim/panels/port_mappings.html
@@ -0,0 +1,31 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+
+ {% if mappings %}
+
+
+ | {% trans "Position" %} |
+ {{ label_column }} |
+
+
+ {% endif %}
+
+ {% for mapping in mappings %}
+
+ {% with position=mapping|getattr:position_field port=mapping|getattr:port_field port_position=mapping|getattr:port_position_field %}
+ | {{ position }} |
+
+ {{ port }}:{{ port_position }}
+ |
+ {% endwith %}
+
+ {% empty %}
+
+ | {% trans "No mappings defined" %} |
+
+ {% endfor %}
+
+
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/panels/virtual_chassis_detail_members.html b/netbox/templates/dcim/panels/virtual_chassis_detail_members.html
new file mode 100644
index 000000000..3fe917884
--- /dev/null
+++ b/netbox/templates/dcim/panels/virtual_chassis_detail_members.html
@@ -0,0 +1,36 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+
+
+
+ | {% trans "Device" %} |
+ {% trans "Position" %} |
+ {% trans "Master" %} |
+ {% trans "Priority" %} |
+
+
+
+ {% for vc_member in members %}
+
+ | {{ vc_member|linkify }} |
+ {% badge vc_member.vc_position show_empty=True %} |
+
+ {% if object.master == vc_member %}
+ {% checkmark True %}
+ {% endif %}
+ |
+ {{ vc_member.vc_priority|placeholder }} |
+
+ {% endfor %}
+
+
+ {% if perms.dcim.change_virtualchassis and object.master %}
+
+ {% endif %}
+{% endblock panel_content %}
diff --git a/netbox/templates/dcim/powerfeed.html b/netbox/templates/dcim/powerfeed.html
index f03ead88b..1304a58db 100644
--- a/netbox/templates/dcim/powerfeed.html
+++ b/netbox/templates/dcim/powerfeed.html
@@ -1,9 +1,5 @@
{% extends 'generic/object.html' %}
-{% load buttons %}
-{% load static %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
+{% load helpers i18n %}
{% block breadcrumbs %}
{{ block.super }}
@@ -13,126 +9,3 @@
{{ object.rack }}
{% endif %}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Power Panel" %} |
- {{ object.power_panel|linkify }} |
-
-
- | {% trans "Rack" %} |
- {{ object.rack|linkify|placeholder }} |
-
-
- | {% trans "Type" %} |
- {% badge object.get_type_display bg_color=object.get_type_color %} |
-
-
- | {% trans "Status" %} |
- {% badge object.get_status_display bg_color=object.get_status_color %} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Tenant" %} |
-
- {% if object.tenant.group %}
- {{ object.tenant.group|linkify }} /
- {% endif %}
- {{ object.tenant|linkify|placeholder }}
- |
-
-
- | {% trans "Connected Device" %} |
-
- {% if object.connected_endpoints %}
- {{ object.connected_endpoints.0.device|linkify }} ({{ object.connected_endpoints.0|linkify:"name" }})
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
- | {% trans "Utilization (Allocated" %}) |
- {% with utilization=object.connected_endpoints.0.get_power_draw %}
- {% if utilization %}
-
- {{ utilization.allocated }}{% trans "VA" %} / {{ object.available_power }}{% trans "VA" %}
- {% if object.available_power > 0 %}
- {% utilization_graph utilization.allocated|percentage:object.available_power %}
- {% endif %}
- |
- {% else %}
- {{ ''|placeholder }} |
- {% endif %}
- {% endwith %}
-
-
-
-
-
-
-
- | {% trans "Supply" %} |
- {{ object.get_supply_display }} |
-
-
- | {% trans "Voltage" %} |
- {{ object.voltage }}{% trans "V" context "Abbreviation for volts" %} |
-
-
- | {% trans "Amperage" %} |
- {{ object.amperage }}{% trans "A" context "Abbreviation for amperes" %} |
-
-
- | {% trans "Phase" %} |
- {{ object.get_phase_display }} |
-
-
- | {% trans "Max Utilization" %} |
- {{ object.max_utilization }}% |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.mark_connected %}
-
-
- {% trans "Marked as connected" %}
-
- {% elif object.cable %}
- {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:powerfeed_trace' %}
- {% else %}
-
- {% trans "Not connected" %}
- {% if perms.dcim.add_cable %}
-
- {% trans "Connect" %}
-
- {% endif %}
-
- {% endif %}
-
- {% include 'inc/panels/comments.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/powerfeed/attrs/connected_device.html b/netbox/templates/dcim/powerfeed/attrs/connected_device.html
new file mode 100644
index 000000000..5a660f5ad
--- /dev/null
+++ b/netbox/templates/dcim/powerfeed/attrs/connected_device.html
@@ -0,0 +1,6 @@
+{% load helpers %}
+{% if value %}
+ {{ value.0.device|linkify }} ({{ value.0|linkify:"name" }})
+{% else %}
+ {{ ''|placeholder }}
+{% endif %}
diff --git a/netbox/templates/dcim/powerfeed/attrs/utilization.html b/netbox/templates/dcim/powerfeed/attrs/utilization.html
new file mode 100644
index 000000000..a0dbf6801
--- /dev/null
+++ b/netbox/templates/dcim/powerfeed/attrs/utilization.html
@@ -0,0 +1,15 @@
+{% load helpers i18n %}
+{% if value %}
+ {% with utilization=value.0.get_power_draw %}
+ {% if utilization %}
+ {{ utilization.allocated }}{% trans "VA" %} / {{ object.available_power }}{% trans "VA" %}
+ {% if object.available_power > 0 %}
+ {% utilization_graph utilization.allocated|percentage:object.available_power %}
+ {% endif %}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ {% endwith %}
+{% else %}
+ {{ ''|placeholder }}
+{% endif %}
diff --git a/netbox/templates/dcim/poweroutlet.html b/netbox/templates/dcim/poweroutlet.html
index a3cbb2a8e..bef31cd55 100644
--- a/netbox/templates/dcim/poweroutlet.html
+++ b/netbox/templates/dcim/poweroutlet.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,93 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Module" %} |
- {{ object.module|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Type" %} |
- {{ object.get_type_display }} |
-
-
- | {% trans "Status" %} |
- {% badge object.get_status_display bg_color=object.get_status_color %} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Color" %} |
-
- {% if object.color %}
-
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
- | {% trans "Power Port" %} |
- {{ object.power_port|linkify|placeholder }} |
-
-
- | {% trans "Feed Leg" %} |
- {{ object.get_feed_leg_display|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.mark_connected %}
-
-
- {% trans "Marked as Connected" %}
-
- {% elif object.cable %}
- {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:poweroutlet_trace' %}
- {% else %}
-
- {% trans "Not Connected" %}
- {% if perms.dcim.add_cable %}
-
- {% trans "Connect" %}
-
- {% endif %}
-
- {% endif %}
-
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/powerpanel.html b/netbox/templates/dcim/powerpanel.html
index a685789f5..55bf70f9c 100644
--- a/netbox/templates/dcim/powerpanel.html
+++ b/netbox/templates/dcim/powerpanel.html
@@ -1,8 +1,5 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
+{% load helpers i18n %}
{% block breadcrumbs %}
{{ block.super }}
@@ -11,72 +8,3 @@
{{ object.location|linkify }}
{% endif %}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Site" %} |
- {{ object.site|linkify }} |
-
-
- | {% trans "Location" %} |
- {{ object.location|linkify|placeholder }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/comments.html' %}
- {% plugin_left_page object %}
-
-
- {% include 'inc/panels/related_objects.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/image_attachments.html' %}
- {% plugin_right_page object %}
-
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/powerport.html b/netbox/templates/dcim/powerport.html
index be05fdae2..3b8c8a00a 100644
--- a/netbox/templates/dcim/powerport.html
+++ b/netbox/templates/dcim/powerport.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,89 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Module" %} |
- {{ object.module|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Type" %} |
- {{ object.get_type_display|placeholder }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Maximum Draw" %} |
- {{ object.maximum_draw|placeholder }} |
-
-
- | {% trans "Allocated Draw" %} |
- {{ object.allocated_draw|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.mark_connected %}
-
-
- {% trans "Marked as Connected" %}
-
- {% elif object.cable %}
- {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:powerport_trace' %}
- {% else %}
-
- {% trans "Not Connected" %}
- {% if perms.dcim.add_cable %}
-
-
-
-
- {% endif %}
-
- {% endif %}
-
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/rearport.html b/netbox/templates/dcim/rearport.html
index a4c111ecf..50cdf9e4e 100644
--- a/netbox/templates/dcim/rearport.html
+++ b/netbox/templates/dcim/rearport.html
@@ -1,6 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
{% load i18n %}
{% block breadcrumbs %}
@@ -9,143 +7,3 @@
{{ object.device }}
{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Module" %} |
- {{ object.module|linkify|placeholder }} |
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Label" %} |
- {{ object.label|placeholder }} |
-
-
- | {% trans "Type" %} |
- {{ object.get_type_display }} |
-
-
- | {% trans "Color" %} |
-
- {% if object.color %}
-
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
-
- | {% trans "Positions" %} |
- {{ object.positions }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
-
- {% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/tags.html' %}
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_left_page object %}
-
-
-
-
- {% if object.mark_connected %}
-
- {% trans "Marked as Connected" %}
-
- {% elif object.cable %}
-
-
- | {% trans "Cable" %} |
-
- {{ object.cable|linkify }}
-
-
-
- |
-
-
- | {% trans "Connection Status" %} |
-
- {% if object.cable.status %}
- {{ object.cable.get_status_display }}
- {% else %}
- {{ object.cable.get_status_display }}
- {% endif %}
- |
-
-
- {% else %}
-
- {% trans "Not connected" %}
- {% if perms.dcim.add_cable %}
-
-
-
-
- {% endif %}
-
- {% endif %}
-
-
-
-
- {% if front_port_mappings %}
-
-
- | {% trans "Position" %} |
- {% trans "Front Port" %} |
-
-
- {% endif %}
- {% for mapping in front_port_mappings %}
-
- | {{ mapping.rear_port_position }} |
-
- {{ mapping.front_port }}:{{ mapping.front_port_position }}
- |
-
- {% empty %}
- {% trans "No mappings defined" %}
- {% endfor %}
-
-
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/virtualchassis.html b/netbox/templates/dcim/virtualchassis.html
index da5a812a2..f15e1d050 100644
--- a/netbox/templates/dcim/virtualchassis.html
+++ b/netbox/templates/dcim/virtualchassis.html
@@ -1,90 +1 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Domain" %} |
- {{ object.domain|placeholder }} |
-
-
- | {% trans "Master" %} |
- {{ object.master|linkify }} |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | Members |
-
- {% if object.member_count %}
- {{ object.member_count }}
- {% else %}
- {{ object.member_count }}
- {% endif %}
- |
-
-
-
- {% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% plugin_left_page object %}
-
-
-
-
-
-
-
- | {% trans "Device" %} |
- {% trans "Position" %} |
- {% trans "Master" %} |
- {% trans "Priority" %} |
-
-
- {% for vc_member in members %}
-
- |
- {{ vc_member|linkify }}
- |
-
- {% badge vc_member.vc_position show_empty=True %}
- |
-
- {% if object.master == vc_member %}
- {% checkmark True %}
- {% endif %}
- |
-
- {{ vc_member.vc_priority|placeholder }}
- |
-
- {% endfor %}
-
-
- {% include 'inc/panels/comments.html' %}
- {% plugin_right_page object %}
-
-
-
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}
diff --git a/netbox/templates/dcim/virtualdevicecontext.html b/netbox/templates/dcim/virtualdevicecontext.html
index 2aec494b8..f15e1d050 100644
--- a/netbox/templates/dcim/virtualdevicecontext.html
+++ b/netbox/templates/dcim/virtualdevicecontext.html
@@ -1,87 +1 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block breadcrumbs %}
- {% trans "Virtual Device Contexts" %}
-{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Name" %} |
- {{ object.name }} |
-
-
- | {% trans "Device" %} |
- {{ object.device|linkify }} |
-
-
- | {% trans "Identifier" %} |
- {{ object.identifier|placeholder }} |
-
-
- | {% trans "Primary IPv4" %} |
-
- {% if object.primary_ip4 %}
- {{ object.primary_ip4 }}
- {% copy_content "primary_ip4" %}
- {% else %}
- —
- {% endif %}
- |
-
-
- | {% trans "Primary IPv6" %} |
-
- {% if object.primary_ip6 %}
- {{ object.primary_ip6 }}
- {% copy_content "primary_ip6" %}
- {% else %}
- —
- {% endif %}
- |
-
-
- | {% trans "Tenant" %} |
-
- {% if object.tenant.group %}
- {{ object.tenant.group|linkify }} /
- {% endif %}
- {{ object.tenant|linkify|placeholder }}
- |
-
-
- | {% trans "Interfaces" %} |
-
- {{ object.interfaces.count }}
- |
-
-
-
- {% plugin_left_page object %}
- {% include 'inc/panels/tags.html' %}
-
-
- {% include 'inc/panels/related_objects.html' %}
- {% include 'inc/panels/comments.html' %}
- {% include 'inc/panels/custom_fields.html' %}
- {% plugin_right_page object %}
-
-
-
-
-
-
- {% htmx_table 'dcim:interface_list' vdc_id=object.pk %}
-
- {% plugin_full_width_page object %}
-
-
-{% endblock %}