From 18779a222e3b48aeae5c389858dd390d457d27c3 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Mar 2026 09:42:30 -0400 Subject: [PATCH] Add layout for InterfaceView --- netbox/dcim/ui/panels.py | 103 +++++ netbox/dcim/views.py | 63 ++- netbox/templates/dcim/interface.html | 436 ------------------ .../dcim/interface/attrs/mac_address.html | 3 + .../templates/dcim/interface/attrs/speed.html | 2 + .../dcim/panels/interface_connection.html | 105 +++++ .../panels/interface_virtual_circuit.html | 35 ++ .../dcim/panels/interface_wireless.html | 72 +++ .../dcim/panels/interface_wireless_lans.html | 25 + 9 files changed, 396 insertions(+), 448 deletions(-) create mode 100644 netbox/templates/dcim/interface/attrs/mac_address.html create mode 100644 netbox/templates/dcim/interface/attrs/speed.html create mode 100644 netbox/templates/dcim/panels/interface_connection.html create mode 100644 netbox/templates/dcim/panels/interface_virtual_circuit.html create mode 100644 netbox/templates/dcim/panels/interface_wireless.html create mode 100644 netbox/templates/dcim/panels/interface_wireless_lans.html diff --git a/netbox/dcim/ui/panels.py b/netbox/dcim/ui/panels.py index 4d86d7659..72b82aad0 100644 --- a/netbox/dcim/ui/panels.py +++ b/netbox/dcim/ui/panels.py @@ -470,3 +470,106 @@ class PowerUtilizationPanel(panels.ObjectPanel): if not obj.powerports.exists() or not obj.poweroutlets.exists(): return '' return super().render(context) + + +class InterfacePanel(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.TemplatedAttr('speed', template_name='dcim/interface/attrs/speed.html', label=_('Speed')) + duplex = attrs.ChoiceAttr('duplex') + mtu = attrs.TextAttr('mtu', label=_('MTU')) + enabled = attrs.BooleanAttr('enabled') + mgmt_only = attrs.BooleanAttr('mgmt_only', label=_('Management only')) + description = attrs.TextAttr('description') + poe_mode = attrs.ChoiceAttr('poe_mode', label=_('PoE mode')) + poe_type = attrs.ChoiceAttr('poe_type', label=_('PoE type')) + mode = attrs.ChoiceAttr('mode', label=_('802.1Q mode')) + qinq_svlan = attrs.RelatedObjectAttr('qinq_svlan', linkify=True, label=_('Q-in-Q SVLAN')) + untagged_vlan = attrs.RelatedObjectAttr('untagged_vlan', linkify=True, label=_('Untagged VLAN')) + tx_power = attrs.TextAttr('tx_power', label=_('Transmit power (dBm)')) + tunnel = attrs.RelatedObjectAttr('tunnel_termination.tunnel', linkify=True, label=_('Tunnel')) + l2vpn = attrs.RelatedObjectAttr('l2vpn_termination.l2vpn', linkify=True, label=_('L2VPN')) + + +class RelatedInterfacesPanel(panels.ObjectAttributesPanel): + title = _('Related Interfaces') + + parent = attrs.RelatedObjectAttr('parent', linkify=True) + bridge = attrs.RelatedObjectAttr('bridge', linkify=True) + lag = attrs.RelatedObjectAttr('lag', linkify=True, label=_('LAG')) + + +class InterfaceAddressingPanel(panels.ObjectAttributesPanel): + title = _('Addressing') + + mac_address = attrs.TemplatedAttr( + 'primary_mac_address', + template_name='dcim/interface/attrs/mac_address.html', + label=_('MAC address'), + ) + wwn = attrs.TextAttr('wwn', style='font-monospace', label=_('WWN')) + vrf = attrs.RelatedObjectAttr('vrf', linkify=True, label=_('VRF')) + vlan_translation = attrs.RelatedObjectAttr('vlan_translation_policy', linkify=True, label=_('VLAN translation')) + + +class InterfaceConnectionPanel(panels.ObjectPanel): + """ + A connection panel for interfaces, which handles cable, wireless link, and virtual circuit cases. + """ + template_name = 'dcim/panels/interface_connection.html' + title = _('Connection') + + def render(self, context): + obj = context.get('object') + if obj and obj.is_virtual: + return '' + ctx = self.get_context(context) + return render_to_string(self.template_name, ctx, request=ctx.get('request')) + + +class VirtualCircuitPanel(panels.ObjectPanel): + """ + A panel which displays virtual circuit information for a virtual interface. + """ + template_name = 'dcim/panels/interface_virtual_circuit.html' + title = _('Virtual Circuit') + + def render(self, context): + obj = context.get('object') + if not obj or not obj.is_virtual or not obj.virtual_circuit_termination: + return '' + ctx = self.get_context(context) + return render_to_string(self.template_name, ctx, request=ctx.get('request')) + + +class InterfaceWirelessPanel(panels.ObjectPanel): + """ + A panel which displays wireless RF attributes for an interface, comparing local and peer values. + """ + template_name = 'dcim/panels/interface_wireless.html' + title = _('Wireless') + + def render(self, context): + obj = context.get('object') + if not obj or not obj.is_wireless: + return '' + ctx = self.get_context(context) + return render_to_string(self.template_name, ctx, request=ctx.get('request')) + + +class WirelessLANsPanel(panels.ObjectPanel): + """ + A panel which lists the wireless LANs associated with an interface. + """ + template_name = 'dcim/panels/interface_wireless_lans.html' + title = _('Wireless LANs') + + def render(self, context): + obj = context.get('object') + if not obj or not obj.is_wireless: + return '' + ctx = self.get_context(context) + return render_to_string(self.template_name, ctx, request=ctx.get('request')) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 4d1a2e4f2..047b3c7f3 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -17,10 +17,12 @@ from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel from extras.views import ObjectConfigContextView, ObjectRenderConfigView from ipam.models import ASN, VLAN, IPAddress, Prefix, VLANGroup from ipam.tables import VLANTranslationRuleTable +from ipam.ui.panels import FHRPGroupAssignmentsPanel from netbox.object_actions import * from netbox.ui import actions, layout from netbox.ui.panels import ( CommentsPanel, + ContextTablePanel, JSONPanel, NestedGroupObjectPanel, ObjectsTablePanel, @@ -3265,6 +3267,45 @@ class InterfaceListView(generic.ObjectListView): @register_model_view(Interface) class InterfaceView(generic.ObjectView): queryset = Interface.objects.all() + layout = layout.SimpleLayout( + left_panels=[ + panels.InterfacePanel(), + panels.RelatedInterfacesPanel(), + CustomFieldsPanel(), + TagsPanel(), + ], + right_panels=[ + ContextTablePanel('vdc_table', title=_('Virtual Device Contexts')), + panels.InterfaceAddressingPanel(), + panels.VirtualCircuitPanel(), + panels.InterfaceConnectionPanel(), + panels.InterfaceWirelessPanel(), + panels.WirelessLANsPanel(), + FHRPGroupAssignmentsPanel(), + panels.InventoryItemsPanel(), + ], + bottom_panels=[ + ObjectsTablePanel( + model='ipam.IPAddress', + filters={'interface_id': lambda ctx: ctx['object'].pk}, + title=_('IP Addresses'), + ), + ObjectsTablePanel( + model='dcim.MACAddress', + filters={'interface_id': lambda ctx: ctx['object'].pk}, + title=_('MAC Addresses'), + ), + ObjectsTablePanel( + model='ipam.VLAN', + filters={'interface_id': lambda ctx: ctx['object'].pk}, + title=_('VLANs'), + ), + ContextTablePanel('lag_interfaces_table', title=_('LAG Members')), + ContextTablePanel('vlan_translation_table', title=_('VLAN Translation')), + ContextTablePanel('bridge_interfaces_table', title=_('Bridged Interfaces')), + ContextTablePanel('child_interfaces_table', title=_('Child Interfaces')), + ], + ) def get_extra_context(self, request, instance): # Get assigned VDCs @@ -3279,30 +3320,29 @@ class InterfaceView(generic.ObjectView): vdc_table.configure(request) # Get bridge interfaces - bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance) bridge_interfaces_table = tables.InterfaceTable( - bridge_interfaces, + Interface.objects.restrict(request.user, 'view').filter(bridge=instance), exclude=('device', 'parent'), orderable=False ) bridge_interfaces_table.configure(request) # Get child interfaces - child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance) child_interfaces_table = tables.InterfaceTable( - child_interfaces, + Interface.objects.restrict(request.user, 'view').filter(parent=instance), exclude=('device', 'parent'), orderable=False ) child_interfaces_table.configure(request) - # Get LAG interfaces - lag_interfaces = Interface.objects.restrict(request.user, 'view').filter(lag=instance) - lag_interfaces_table = tables.InterfaceLAGMemberTable( - lag_interfaces, - orderable=False - ) - lag_interfaces_table.configure(request) + # Get LAG members (only for LAG interfaces) + lag_interfaces_table = None + if instance.is_lag: + lag_interfaces_table = tables.InterfaceLAGMemberTable( + Interface.objects.restrict(request.user, 'view').filter(lag=instance), + orderable=False + ) + lag_interfaces_table.configure(request) # Get VLAN translation rules vlan_translation_table = None @@ -3315,7 +3355,6 @@ class InterfaceView(generic.ObjectView): return { 'vdc_table': vdc_table, - 'bridge_interfaces': bridge_interfaces, 'bridge_interfaces_table': bridge_interfaces_table, 'child_interfaces_table': child_interfaces_table, 'lag_interfaces_table': lag_interfaces_table, diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index 34e941ec9..b8214337f 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -1,7 +1,4 @@ {% extends 'generic/object.html' %} -{% load helpers %} -{% load plugins %} -{% load render_table from django_tables2 %} {% load i18n %} {% block breadcrumbs %} @@ -19,436 +16,3 @@ {% endif %} {{ block.super }} {% endblock %} - -{% block content %} -
-
-
-

{% trans "Interface" %}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% if object.mode == 'q-in-q' %} - - - - - {% elif object.mode %} - - - - - {% endif %} - - - - - - - - - - - - -
{% 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/Duplex" %} - {{ object.speed|humanize_speed|placeholder }} / - {{ object.get_duplex_display|placeholder }} -
{% trans "MTU" %}{{ object.mtu|placeholder }}
{% trans "Enabled" %}{% checkmark object.enabled %}
{% trans "Management Only" %}{% checkmark object.mgmt_only %}
{% trans "Description" %}{{ object.description|placeholder }}
{% trans "PoE Mode" %}{{ object.get_poe_mode_display|placeholder }}
{% trans "PoE Type" %}{{ object.get_poe_type_display|placeholder }}
{% trans "802.1Q Mode" %}{{ object.get_mode_display|placeholder }}
{% trans "Q-in-Q SVLAN" %}{{ object.qinq_svlan|linkify|placeholder }}
{% trans "Untagged VLAN" %}{{ object.untagged_vlan|linkify|placeholder }}
{% trans "Transmit power (dBm)" %}{{ object.tx_power|placeholder }}
{% trans "Tunnel" %}{{ object.tunnel_termination.tunnel|linkify|placeholder }}
{% trans "L2VPN" %}{{ object.l2vpn_termination.l2vpn|linkify|placeholder }}
-
-
-

{% trans "Related Interfaces" %}

- - - - - - - - - - - - - - - - - -
{% trans "Parent" %}{{ object.parent|linkify|placeholder }}
{% trans "Bridge" %}{{ object.bridge|linkify|placeholder }}
{% trans "Bridged Interfaces" %} - {% if bridge_interfaces %} - {% for interface in bridge_interfaces %} - {{ interface|linkify }} - {% if not forloop.last %}
{% endif %} - {% endfor %} - {% else %} - {{ ''|placeholder }} - {% endif %} -
{% trans "LAG" %}{{ object.lag|linkify|placeholder }}
-
- {% include 'inc/panels/custom_fields.html' %} - {% include 'inc/panels/tags.html' %} - {% plugin_left_page object %} -
-
- {% include 'inc/panel_table.html' with table=vdc_table heading="Virtual Device Contexts" %} -
-

{% trans "Addressing" %}

- - - - - - - - - - - - - - - - - -
{% trans "MAC Address" %} - {% if object.primary_mac_address %} - {{ object.primary_mac_address|linkify }} - {% trans "Primary" %} - {% else %} - {{ ''|placeholder }} - {% endif %} -
{% trans "WWN" %} - {% if object.wwn %} - {{ object.wwn }} - {% else %} - {{ ''|placeholder }} - {% endif %} -
{% trans "VRF" %}{{ object.vrf|linkify|placeholder }}
{% trans "VLAN Translation" %}{{ object.vlan_translation_policy|linkify|placeholder }}
-
- {% if object.is_virtual and object.virtual_circuit_termination %} -
-

{% trans "Virtual Circuit" %}

- - - - - - - - - - - - - - - - - - - - - -
{% trans "Provider" %}{{ object.virtual_circuit_termination.virtual_circuit.provider|linkify }}
{% trans "Provider Network" %}{{ object.virtual_circuit_termination.virtual_circuit.provider_network|linkify }}
{% trans "Circuit ID" %}{{ object.virtual_circuit_termination.virtual_circuit|linkify }}
{% trans "Role" %}{{ object.virtual_circuit_termination.get_role_display }}
{% trans "Connections" %} - {% for termination in object.virtual_circuit_termination.peer_terminations %} - {{ termination.interface.parent_object }} - - {{ termination.interface }} - ({{ termination.get_role_display }}) - {% if not forloop.last %}
{% endif %} - {% endfor %} -
-
- {% elif not object.is_virtual %} -
-

{% trans "Connection" %}

- {% if object.mark_connected %} -
- - {% trans "Marked as Connected" %} -
- {% elif object.cable %} - {% include 'dcim/inc/connection_endpoints.html' with trace_url='dcim:interface_trace' %} - {% elif object.wireless_link %} - - - - - - {% with peer_interface=object.link_peers.0 %} - - - - - - - - - - - - - {% endwith %} -
{% trans "Wireless Link" %} - {{ object.wireless_link|linkify }} - - - -
{% trans "Device" %}{{ peer_interface.device|linkify }}
{% trans "Name" %}{{ peer_interface|linkify }}
{% trans "Type" %}{{ peer_interface.get_type_display }}
- {% else %} -
- {% trans "Not Connected" %} - {% if object.is_wired and perms.dcim.add_cable %} - - {% elif object.is_wireless and perms.wireless.add_wirelesslink %} - - {% endif %} -
- {% endif %} -
- {% endif %} - {% if object.is_wireless %} -
-

{% trans "Wireless" %}

- {% with peer=object.connected_endpoints.0 %} - - - - - - {% if peer %} - - {% endif %} - - - - - - {% if peer %} - - {% endif %} - - - - - {% if peer %} - - {{ peer.get_rf_channel_display|placeholder }} - - {% endif %} - - - - - {% if peer %} - - {% if peer.rf_channel_frequency %} - {{ peer.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %} - {% else %} - {{ ''|placeholder }} - {% endif %} - - {% endif %} - - - - - {% if peer %} - - {% if peer.rf_channel_width %} - {{ peer.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %} - {% else %} - {{ ''|placeholder }} - {% endif %} - - {% endif %} - -
{% trans "Local" %}{% trans "Peer" %}
{% trans "Role" %}{{ object.get_rf_role_display|placeholder }}{{ peer.get_rf_role_display|placeholder }}
{% trans "Channel" %}{{ object.get_rf_channel_display|placeholder }}
{% trans "Channel Frequency" %} - {% if object.rf_channel_frequency %} - {{ object.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %} - {% else %} - {{ ''|placeholder }} - {% endif %} -
{% trans "Channel Width" %} - {% if object.rf_channel_width %} - {{ object.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %} - {% else %} - {{ ''|placeholder }} - {% endif %} -
- {% endwith %} -
-
-

{% trans "Wireless LANs" %}

- - - - - - - - - {% for wlan in object.wireless_lans.all %} - - - - - {% empty %} - - - - {% endfor %} - -
{% trans "Group" %}{% trans "SSID" %}
{{ wlan.group|linkify|placeholder }}{{ wlan|linkify:"ssid" }}
{% trans "None" %}
-
- {% endif %} - {% include 'ipam/inc/panels/fhrp_groups.html' %} - {% include 'dcim/inc/panels/inventory_items.html' %} - {% plugin_right_page object %} -
-
-
-
-
-

- {% trans "IP Addresses" %} - {% if perms.ipam.add_ipaddress %} - - {% endif %} -

- {% htmx_table 'ipam:ipaddress_list' interface_id=object.pk %} -
-
-
-
-
-
-

- {% trans "MAC Addresses" %} - {% if perms.dcim.add_macaddress %} - - {% endif %} -

- {% htmx_table 'dcim:macaddress_list' interface_id=object.pk %} -
-
-
-
-
-
-

{% trans "VLANs" %}

- {% htmx_table 'ipam:vlan_list' interface_id=object.pk %} -
-
-
- {% if object.is_lag %} -
-
- {% include 'inc/panel_table.html' with table=lag_interfaces_table heading="LAG Members" %} -
-
- {% endif %} - {% if object.vlan_translation_policy %} -
-
- {% include 'inc/panel_table.html' with table=vlan_translation_table heading="VLAN Translation" %} -
-
- {% endif %} -
-
- {% include 'inc/panel_table.html' with table=bridge_interfaces_table heading="Bridged Interfaces" %} -
-
-
-
- {% include 'inc/panel_table.html' with table=child_interfaces_table heading="Child Interfaces" %} -
-
-
-
- {% plugin_full_width_page object %} -
-
-{% endblock %} diff --git a/netbox/templates/dcim/interface/attrs/mac_address.html b/netbox/templates/dcim/interface/attrs/mac_address.html new file mode 100644 index 000000000..abb3c9cb8 --- /dev/null +++ b/netbox/templates/dcim/interface/attrs/mac_address.html @@ -0,0 +1,3 @@ +{% load helpers i18n %} +{{ value|linkify }} +{% trans "Primary" %} diff --git a/netbox/templates/dcim/interface/attrs/speed.html b/netbox/templates/dcim/interface/attrs/speed.html new file mode 100644 index 000000000..fc4b15d28 --- /dev/null +++ b/netbox/templates/dcim/interface/attrs/speed.html @@ -0,0 +1,2 @@ +{% load helpers %} +{{ value|humanize_speed }} diff --git a/netbox/templates/dcim/panels/interface_connection.html b/netbox/templates/dcim/panels/interface_connection.html new file mode 100644 index 000000000..ada78f39a --- /dev/null +++ b/netbox/templates/dcim/panels/interface_connection.html @@ -0,0 +1,105 @@ +{% extends "ui/panels/_base.html" %} +{% load helpers i18n %} + +{% block panel_content %} + {% if object.mark_connected %} +
+ + {% trans "Marked as Connected" %} +
+ {% elif object.cable %} + + + + + + + + + + + + + +
{% 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 %} +
+ {% elif object.wireless_link %} + + + + + + {% with peer_interface=object.link_peers.0 %} + + + + + + + + + + + + + {% endwith %} +
{% trans "Wireless Link" %} + {{ object.wireless_link|linkify }} + + + +
{% trans "Device" %}{{ peer_interface.device|linkify }}
{% trans "Name" %}{{ peer_interface|linkify }}
{% trans "Type" %}{{ peer_interface.get_type_display }}
+ {% else %} +
+ {% trans "Not Connected" %} + {% if object.is_wired and perms.dcim.add_cable %} + + {% elif object.is_wireless and perms.wireless.add_wirelesslink %} + + {% endif %} +
+ {% endif %} +{% endblock panel_content %} diff --git a/netbox/templates/dcim/panels/interface_virtual_circuit.html b/netbox/templates/dcim/panels/interface_virtual_circuit.html new file mode 100644 index 000000000..70e0c065c --- /dev/null +++ b/netbox/templates/dcim/panels/interface_virtual_circuit.html @@ -0,0 +1,35 @@ +{% extends "ui/panels/_base.html" %} +{% load helpers i18n %} + +{% block panel_content %} + + + + + + + + + + + + + + + + + + + + + +
{% trans "Provider" %}{{ object.virtual_circuit_termination.virtual_circuit.provider|linkify }}
{% trans "Provider Network" %}{{ object.virtual_circuit_termination.virtual_circuit.provider_network|linkify }}
{% trans "Circuit ID" %}{{ object.virtual_circuit_termination.virtual_circuit|linkify }}
{% trans "Role" %}{{ object.virtual_circuit_termination.get_role_display }}
{% trans "Connections" %} + {% for termination in object.virtual_circuit_termination.peer_terminations %} + {{ termination.interface.parent_object }} + + {{ termination.interface }} + ({{ termination.get_role_display }}) + {% if not forloop.last %}
{% endif %} + {% endfor %} +
+{% endblock panel_content %} diff --git a/netbox/templates/dcim/panels/interface_wireless.html b/netbox/templates/dcim/panels/interface_wireless.html new file mode 100644 index 000000000..dfc7a3452 --- /dev/null +++ b/netbox/templates/dcim/panels/interface_wireless.html @@ -0,0 +1,72 @@ +{% extends "ui/panels/_base.html" %} +{% load helpers i18n %} + +{% block panel_content %} + {% with peer=object.connected_endpoints.0 %} + + + + + + {% if peer %} + + {% endif %} + + + + + + {% if peer %} + + {% endif %} + + + + + {% if peer %} + + {{ peer.get_rf_channel_display|placeholder }} + + {% endif %} + + + + + {% if peer %} + + {% if peer.rf_channel_frequency %} + {{ peer.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %} + {% else %} + {{ ''|placeholder }} + {% endif %} + + {% endif %} + + + + + {% if peer %} + + {% if peer.rf_channel_width %} + {{ peer.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %} + {% else %} + {{ ''|placeholder }} + {% endif %} + + {% endif %} + +
{% trans "Local" %}{% trans "Peer" %}
{% trans "Role" %}{{ object.get_rf_role_display|placeholder }}{{ peer.get_rf_role_display|placeholder }}
{% trans "Channel" %}{{ object.get_rf_channel_display|placeholder }}
{% trans "Channel frequency" %} + {% if object.rf_channel_frequency %} + {{ object.rf_channel_frequency|floatformat:"-2" }} {% trans "MHz" %} + {% else %} + {{ ''|placeholder }} + {% endif %} +
{% trans "Channel width" %} + {% if object.rf_channel_width %} + {{ object.rf_channel_width|floatformat:"-3" }} {% trans "MHz" %} + {% else %} + {{ ''|placeholder }} + {% endif %} +
+ {% endwith %} +{% endblock panel_content %} diff --git a/netbox/templates/dcim/panels/interface_wireless_lans.html b/netbox/templates/dcim/panels/interface_wireless_lans.html new file mode 100644 index 000000000..30c83101d --- /dev/null +++ b/netbox/templates/dcim/panels/interface_wireless_lans.html @@ -0,0 +1,25 @@ +{% extends "ui/panels/_base.html" %} +{% load helpers i18n %} + +{% block panel_content %} + + + + + + + + + {% for wlan in object.wireless_lans.all %} + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Group" %}{% trans "SSID" %}
{{ wlan.group|linkify|placeholder }}{{ wlan|linkify:"ssid" }}
{% trans "None" %}
+{% endblock panel_content %}