Przeglądaj źródła

Separate device component lists into discrete views

Jeremy Stretch 5 lat temu
rodzic
commit
9736d6ba48

+ 9 - 18
netbox/dcim/tables/devices.py

@@ -259,8 +259,7 @@ class DeviceConsolePortTable(ConsolePortTable):
     actions = ButtonsColumn(
         model=ConsolePort,
         buttons=('edit', 'delete'),
-        prepend_template=CONSOLEPORT_BUTTONS,
-        return_url_extra='%23tab_consoleports'
+        prepend_template=CONSOLEPORT_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -294,8 +293,7 @@ class DeviceConsoleServerPortTable(ConsoleServerPortTable):
     actions = ButtonsColumn(
         model=ConsoleServerPort,
         buttons=('edit', 'delete'),
-        prepend_template=CONSOLESERVERPORT_BUTTONS,
-        return_url_extra='%23tab_consoleserverports'
+        prepend_template=CONSOLESERVERPORT_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -332,8 +330,7 @@ class DevicePowerPortTable(PowerPortTable):
     actions = ButtonsColumn(
         model=PowerPort,
         buttons=('edit', 'delete'),
-        prepend_template=POWERPORT_BUTTONS,
-        return_url_extra='%23tab_powerports'
+        prepend_template=POWERPORT_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -376,8 +373,7 @@ class DevicePowerOutletTable(PowerOutletTable):
     actions = ButtonsColumn(
         model=PowerOutlet,
         buttons=('edit', 'delete'),
-        prepend_template=POWEROUTLET_BUTTONS,
-        return_url_extra='%23tab_poweroutlets'
+        prepend_template=POWEROUTLET_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -437,8 +433,7 @@ class DeviceInterfaceTable(InterfaceTable):
     actions = ButtonsColumn(
         model=Interface,
         buttons=('edit', 'delete'),
-        prepend_template=INTERFACE_BUTTONS,
-        return_url_extra='%23tab_interfaces'
+        prepend_template=INTERFACE_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -485,8 +480,7 @@ class DeviceFrontPortTable(FrontPortTable):
     actions = ButtonsColumn(
         model=FrontPort,
         buttons=('edit', 'delete'),
-        prepend_template=FRONTPORT_BUTTONS,
-        return_url_extra='%23tab_frontports'
+        prepend_template=FRONTPORT_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -524,8 +518,7 @@ class DeviceRearPortTable(RearPortTable):
     actions = ButtonsColumn(
         model=RearPort,
         buttons=('edit', 'delete'),
-        prepend_template=REARPORT_BUTTONS,
-        return_url_extra='%23tab_rearports'
+        prepend_template=REARPORT_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -567,8 +560,7 @@ class DeviceDeviceBayTable(DeviceBayTable):
     actions = ButtonsColumn(
         model=DeviceBay,
         buttons=('edit', 'delete'),
-        prepend_template=DEVICEBAY_BUTTONS,
-        return_url_extra='%23tab_devicebays'
+        prepend_template=DEVICEBAY_BUTTONS
     )
 
     class Meta(DeviceComponentTable.Meta):
@@ -608,8 +600,7 @@ class DeviceInventoryItemTable(InventoryItemTable):
     )
     actions = ButtonsColumn(
         model=InventoryItem,
-        buttons=('edit', 'delete'),
-        return_url_extra='%23tab_inventoryitems'
+        buttons=('edit', 'delete')
     )
 
     class Meta(DeviceComponentTable.Meta):

+ 26 - 26
netbox/dcim/tables/template_code.py

@@ -98,9 +98,9 @@ CONSOLEPORT_BUTTONS = """
             <span class="mdi mdi-lan-connect" aria-hidden="true"></span>
         </button>
         <ul class="dropdown-menu dropdown-menu-right">
-            <li><a href="{% url 'dcim:consoleport_connect' termination_a_id=record.pk termination_b_type='console-server-port' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports">Console Server Port</a></li>
-            <li><a href="{% url 'dcim:consoleport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports">Front Port</a></li>
-            <li><a href="{% url 'dcim:consoleport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports">Rear Port</a></li>
+            <li><a href="{% url 'dcim:consoleport_connect' termination_a_id=record.pk termination_b_type='console-server-port' %}?return_url={% url 'dcim:device_consoleports' pk=device.pk %}">Console Server Port</a></li>
+            <li><a href="{% url 'dcim:consoleport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={% url 'dcim:device_consoleports' pk=device.pk %}">Front Port</a></li>
+            <li><a href="{% url 'dcim:consoleport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={% url 'dcim:device_consoleports' pk=device.pk %}">Rear Port</a></li>
         </ul>
     </span>
 {% endif %}
@@ -116,9 +116,9 @@ CONSOLESERVERPORT_BUTTONS = """
             <span class="mdi mdi-lan-connect" aria-hidden="true"></span>
         </button>
         <ul class="dropdown-menu dropdown-menu-right">
-            <li><a href="{% url 'dcim:consoleserverport_connect' termination_a_id=record.pk termination_b_type='console-port' %}?return_url={{ device.get_absolute_url }}%23tab_consoleserverports">Console Port</a></li>
-            <li><a href="{% url 'dcim:consoleserverport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={{ device.get_absolute_url }}"%23tab_consoleserverports>Front Port</a></li>
-            <li><a href="{% url 'dcim:consoleserverport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={{ device.get_absolute_url }}"%23tab_consoleserverports>Rear Port</a></li>
+            <li><a href="{% url 'dcim:consoleserverport_connect' termination_a_id=record.pk termination_b_type='console-port' %}?return_url={% url 'dcim:device_consoleserverports' pk=device.pk %}">Console Port</a></li>
+            <li><a href="{% url 'dcim:consoleserverport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={% url 'dcim:device_consoleserverports' pk=device.pk %}">Front Port</a></li>
+            <li><a href="{% url 'dcim:consoleserverport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={% url 'dcim:device_consoleserverports' pk=device.pk %}">Rear Port</a></li>
         </ul>
     </span>
 {% endif %}
@@ -134,8 +134,8 @@ POWERPORT_BUTTONS = """
             <span class="mdi mdi-lan-connect" aria-hidden="true"></span>
         </button>
         <ul class="dropdown-menu dropdown-menu-right">
-            <li><a href="{% url 'dcim:powerport_connect' termination_a_id=record.pk termination_b_type='power-outlet' %}?return_url={{ device.get_absolute_url }}%23tab_powerports">Power Outlet</a></li>
-            <li><a href="{% url 'dcim:powerport_connect' termination_a_id=record.pk termination_b_type='power-feed' %}?return_url={{ device.get_absolute_url }}%23tab_powerports">Power Feed</a></li>
+            <li><a href="{% url 'dcim:powerport_connect' termination_a_id=record.pk termination_b_type='power-outlet' %}?return_url={% url 'dcim:device_powerports' pk=device.pk %}">Power Outlet</a></li>
+            <li><a href="{% url 'dcim:powerport_connect' termination_a_id=record.pk termination_b_type='power-feed' %}?return_url={% url 'dcim:device_powerports' pk=device.pk %}">Power Feed</a></li>
         </ul>
     </span>
 {% endif %}
@@ -146,7 +146,7 @@ POWEROUTLET_BUTTONS = """
     <a href="{% url 'dcim:poweroutlet_trace' pk=record.pk %}" class="btn btn-primary btn-xs" title="Trace"><i class="mdi mdi-transit-connection-variant"></i></a>
     {% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
 {% elif perms.dcim.add_cable %}
-    <a href="{% url 'dcim:poweroutlet_connect' termination_a_id=record.pk termination_b_type='power-port' %}?return_url={{ device.get_absolute_url }}%23tab_poweroutlets" title="Connect" class="btn btn-success btn-xs">
+    <a href="{% url 'dcim:poweroutlet_connect' termination_a_id=record.pk termination_b_type='power-port' %}?return_url={% url 'dcim:device_poweroutlets' pk=device.pk %}" title="Connect" class="btn btn-success btn-xs">
         <i class="mdi mdi-lan-connect" aria-hidden="true"></i>
     </a>
 {% endif %}
@@ -154,7 +154,7 @@ POWEROUTLET_BUTTONS = """
 
 INTERFACE_BUTTONS = """
 {% if perms.ipam.add_ipaddress %}
-    <a href="{% url 'ipam:ipaddress_add' %}?interface={{ record.pk }}&return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-xs btn-success" title="Add IP address">
+    <a href="{% url 'ipam:ipaddress_add' %}?interface={{ record.pk }}&return_url={% url 'dcim:device_interfaces' pk=device.pk %}" class="btn btn-xs btn-success" title="Add IP address">
         <i class="mdi mdi-plus-thick" aria-hidden="true"></i>
     </a>
 {% endif %}
@@ -167,10 +167,10 @@ INTERFACE_BUTTONS = """
             <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
         </button>
         <ul class="dropdown-menu dropdown-menu-right">
-            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces">Interface</a></li>
-            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces">Front Port</a></li>
-            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces">Rear Port</a></li>
-            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='circuit-termination' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces">Circuit Termination</a></li>
+            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={% url 'dcim:device_interfaces' pk=device.pk %}">Interface</a></li>
+            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={% url 'dcim:device_interfaces' pk=device.pk %}">Front Port</a></li>
+            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={% url 'dcim:device_interfaces' pk=device.pk %}">Rear Port</a></li>
+            <li><a href="{% url 'dcim:interface_connect' termination_a_id=record.pk termination_b_type='circuit-termination' %}?return_url={% url 'dcim:device_interfaces' pk=device.pk %}">Circuit Termination</a></li>
         </ul>
     </span>
 {% endif %}
@@ -186,12 +186,12 @@ FRONTPORT_BUTTONS = """
             <span class="mdi mdi-lan-connect" aria-hidden="true"></span>
         </button>
         <ul class="dropdown-menu dropdown-menu-right">
-            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={{ device.get_absolute_url }}%23tab_frontports">Interface</a></li>
-            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='console-server-port' %}?return_url={{ device.get_absolute_url }}%23tab_frontports">Console Server Port</a></li>
-            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='console-port' %}?return_url={{ device.get_absolute_url }}%23tab_frontports">Console Port</a></li>
-            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={{ device.get_absolute_url }}%23tab_frontports">Front Port</a></li>
-            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={{ device.get_absolute_url }}%23tab_frontports">Rear Port</a></li>
-            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='circuit-termination' %}?return_url={{ device.get_absolute_url }}%23tab_frontports">Circuit Termination</a></li>
+            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={% url 'dcim:device_frontports' pk=device.pk %}">Interface</a></li>
+            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='console-server-port' %}?return_url={% url 'dcim:device_frontports' pk=device.pk %}">Console Server Port</a></li>
+            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='console-port' %}?return_url={% url 'dcim:device_frontports' pk=device.pk %}">Console Port</a></li>
+            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={% url 'dcim:device_frontports' pk=device.pk %}">Front Port</a></li>
+            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={% url 'dcim:device_frontports' pk=device.pk %}">Rear Port</a></li>
+            <li><a href="{% url 'dcim:frontport_connect' termination_a_id=record.pk termination_b_type='circuit-termination' %}?return_url={% url 'dcim:device_frontports' pk=device.pk %}">Circuit Termination</a></li>
         </ul>
     </span>
 {% endif %}
@@ -207,10 +207,10 @@ REARPORT_BUTTONS = """
             <span class="mdi mdi-lan-connect" aria-hidden="true"></span>
         </button>
         <ul class="dropdown-menu dropdown-menu-right">
-            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={{ device.get_absolute_url }}%23tab_rearports">Interface</a></li>
-            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={{ device.get_absolute_url }}%23tab_rearports">Front Port</a></li>
-            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={{ device.get_absolute_url }}%23tab_rearports">Rear Port</a></li>
-            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='circuit-termination' %}?return_url={{ device.get_absolute_url }}%23tab_rearports">Circuit Termination</a></li>
+            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='interface' %}?return_url={% url 'dcim:device_rearports' pk=device.pk %}">Interface</a></li>
+            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='front-port' %}?return_url={% url 'dcim:device_rearports' pk=device.pk %}">Front Port</a></li>
+            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='rear-port' %}?return_url={% url 'dcim:device_rearports' pk=device.pk %}">Rear Port</a></li>
+            <li><a href="{% url 'dcim:rearport_connect' termination_a_id=record.pk termination_b_type='circuit-termination' %}?return_url={% url 'dcim:device_rearports' pk=device.pk %}">Circuit Termination</a></li>
         </ul>
     </span>
 {% endif %}
@@ -219,11 +219,11 @@ REARPORT_BUTTONS = """
 DEVICEBAY_BUTTONS = """
 {% if perms.dcim.change_devicebay %}
     {% if record.installed_device %}
-        <a href="{% url 'dcim:devicebay_depopulate' pk=record.pk %}?return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-danger btn-xs">
+        <a href="{% url 'dcim:devicebay_depopulate' pk=record.pk %}?return_url={% url 'dcim:device_devicebays' pk=device.pk %}" class="btn btn-danger btn-xs">
             <i class="mdi mdi-close-thick" aria-hidden="true" title="Remove device"></i>
         </a>
     {% else %}
-        <a href="{% url 'dcim:devicebay_populate' pk=record.pk %}?return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-success btn-xs">
+        <a href="{% url 'dcim:devicebay_populate' pk=record.pk %}?return_url={% url 'dcim:device_devicebays' pk=device.pk %}" class="btn btn-success btn-xs">
             <i class="mdi mdi-plus-thick" aria-hidden="true" title="Install device"></i>
         </a>
     {% endif %}

+ 9 - 0
netbox/dcim/urls.py

@@ -187,6 +187,15 @@ urlpatterns = [
     path('devices/<int:pk>/', views.DeviceView.as_view(), name='device'),
     path('devices/<int:pk>/edit/', views.DeviceEditView.as_view(), name='device_edit'),
     path('devices/<int:pk>/delete/', views.DeviceDeleteView.as_view(), name='device_delete'),
+    path('devices/<int:pk>/console-ports/', views.DeviceConsolePortsView.as_view(), name='device_consoleports'),
+    path('devices/<int:pk>/console-server-ports/', views.DeviceConsoleServerPortsView.as_view(), name='device_consoleserverports'),
+    path('devices/<int:pk>/power-ports/', views.DevicePowerPortsView.as_view(), name='device_powerports'),
+    path('devices/<int:pk>/power-outlets/', views.DevicePowerOutletsView.as_view(), name='device_poweroutlets'),
+    path('devices/<int:pk>/interfaces/', views.DeviceInterfacesView.as_view(), name='device_interfaces'),
+    path('devices/<int:pk>/front-ports/', views.DeviceFrontPortsView.as_view(), name='device_frontports'),
+    path('devices/<int:pk>/rear-ports/', views.DeviceRearPortsView.as_view(), name='device_rearports'),
+    path('devices/<int:pk>/device-bays/', views.DeviceDeviceBaysView.as_view(), name='device_devicebays'),
+    path('devices/<int:pk>/inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'),
     path('devices/<int:pk>/config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
     path('devices/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}),
     path('devices/<int:pk>/status/', views.DeviceStatusView.as_view(), name='device_status'),

+ 141 - 42
netbox/dcim/views.py

@@ -1007,7 +1007,37 @@ class DeviceView(generic.ObjectView):
         else:
             vc_members = []
 
-        # Console ports
+        # Services
+        services = Service.objects.restrict(request.user, 'view').filter(device=device)
+
+        # Secrets
+        secrets = Secret.objects.restrict(request.user, 'view').filter(device=device)
+
+        # Find up to ten devices in the same site with the same functional role for quick reference.
+        related_devices = Device.objects.restrict(request.user, 'view').filter(
+            site=device.site, device_role=device.device_role
+        ).exclude(
+            pk=device.pk
+        ).prefetch_related(
+            'rack', 'device_type__manufacturer'
+        )[:10]
+
+        return render(request, 'dcim/device/device.html', {
+            'device': device,
+            'services': services,
+            'secrets': secrets,
+            'vc_members': vc_members,
+            'related_devices': related_devices,
+            'active_tab': 'device',
+        })
+
+
+class DeviceConsolePortsView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
             'cable', '_path__destination',
         )
@@ -1019,7 +1049,19 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_consoleport') or request.user.has_perm('dcim.delete_consoleport'):
             consoleport_table.columns.show('pk')
 
-        # Console server ports
+        return render(request, 'dcim/device/consoleports.html', {
+            'device': device,
+            'consoleport_table': consoleport_table,
+            'active_tab': 'console-ports',
+        })
+
+
+class DeviceConsoleServerPortsView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         consoleserverports = ConsoleServerPort.objects.restrict(request.user, 'view').filter(
             device=device
         ).prefetch_related(
@@ -1034,7 +1076,19 @@ class DeviceView(generic.ObjectView):
                 request.user.has_perm('dcim.delete_consoleserverport'):
             consoleserverport_table.columns.show('pk')
 
-        # Power ports
+        return render(request, 'dcim/device/consoleserverports.html', {
+            'device': device,
+            'consoleserverport_table': consoleserverport_table,
+            'active_tab': 'console-server-ports',
+        })
+
+
+class DevicePowerPortsView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
             'cable', '_path__destination',
         )
@@ -1046,7 +1100,19 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_powerport') or request.user.has_perm('dcim.delete_powerport'):
             powerport_table.columns.show('pk')
 
-        # Power outlets
+        return render(request, 'dcim/device/powerports.html', {
+            'device': device,
+            'powerport_table': powerport_table,
+            'active_tab': 'power-ports',
+        })
+
+
+class DevicePowerOutletsView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         poweroutlets = PowerOutlet.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
             'cable', 'power_port', '_path__destination',
         )
@@ -1058,7 +1124,19 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_poweroutlet') or request.user.has_perm('dcim.delete_poweroutlet'):
             poweroutlet_table.columns.show('pk')
 
-        # Interfaces
+        return render(request, 'dcim/device/poweroutlets.html', {
+            'device': device,
+            'poweroutlet_table': poweroutlet_table,
+            'active_tab': 'power-outlets',
+        })
+
+
+class DeviceInterfacesView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         interfaces = device.vc_interfaces.restrict(request.user, 'view').prefetch_related(
             Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
             Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
@@ -1072,7 +1150,19 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'):
             interface_table.columns.show('pk')
 
-        # Front ports
+        return render(request, 'dcim/device/interfaces.html', {
+            'device': device,
+            'interface_table': interface_table,
+            'active_tab': 'interfaces',
+        })
+
+
+class DeviceFrontPortsView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
             'rear_port', 'cable',
         )
@@ -1084,7 +1174,19 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_frontport') or request.user.has_perm('dcim.delete_frontport'):
             frontport_table.columns.show('pk')
 
-        # Rear ports
+        return render(request, 'dcim/device/frontports.html', {
+            'device': device,
+            'frontport_table': frontport_table,
+            'active_tab': 'front-ports',
+        })
+
+
+class DeviceRearPortsView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         rearports = RearPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related('cable')
         rearport_table = tables.DeviceRearPortTable(
             data=rearports,
@@ -1094,7 +1196,19 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_rearport') or request.user.has_perm('dcim.delete_rearport'):
             rearport_table.columns.show('pk')
 
-        # Device bays
+        return render(request, 'dcim/device/rearports.html', {
+            'device': device,
+            'rearport_table': rearport_table,
+            'active_tab': 'rear-ports',
+        })
+
+
+class DeviceDeviceBaysView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
             'installed_device__device_type__manufacturer',
         )
@@ -1106,7 +1220,19 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_devicebay') or request.user.has_perm('dcim.delete_devicebay'):
             devicebay_table.columns.show('pk')
 
-        # Inventory items
+        return render(request, 'dcim/device/devicebays.html', {
+            'device': device,
+            'devicebay_table': devicebay_table,
+            'active_tab': 'device-bays',
+        })
+
+
+class DeviceInventoryView(generic.ObjectView):
+    queryset = Device.objects.all()
+
+    def get(self, request, pk):
+        device = get_object_or_404(self.queryset, pk=pk)
+
         inventoryitems = InventoryItem.objects.restrict(request.user, 'view').filter(
             device=device
         ).prefetch_related('manufacturer')
@@ -1118,36 +1244,10 @@ class DeviceView(generic.ObjectView):
         if request.user.has_perm('dcim.change_inventoryitem') or request.user.has_perm('dcim.delete_inventoryitem'):
             inventoryitem_table.columns.show('pk')
 
-        # Services
-        services = Service.objects.restrict(request.user, 'view').filter(device=device)
-
-        # Secrets
-        secrets = Secret.objects.restrict(request.user, 'view').filter(device=device)
-
-        # Find up to ten devices in the same site with the same functional role for quick reference.
-        related_devices = Device.objects.restrict(request.user, 'view').filter(
-            site=device.site, device_role=device.device_role
-        ).exclude(
-            pk=device.pk
-        ).prefetch_related(
-            'rack', 'device_type__manufacturer'
-        )[:10]
-
-        return render(request, 'dcim/device.html', {
+        return render(request, 'dcim/device/inventory.html', {
             'device': device,
-            'consoleport_table': consoleport_table,
-            'consoleserverport_table': consoleserverport_table,
-            'powerport_table': powerport_table,
-            'poweroutlet_table': poweroutlet_table,
-            'interface_table': interface_table,
-            'frontport_table': frontport_table,
-            'rearport_table': rearport_table,
-            'devicebay_table': devicebay_table,
             'inventoryitem_table': inventoryitem_table,
-            'services': services,
-            'secrets': secrets,
-            'vc_members': vc_members,
-            'related_devices': related_devices,
+            'active_tab': 'inventory',
         })
 
 
@@ -1156,10 +1256,9 @@ class DeviceStatusView(generic.ObjectView):
     queryset = Device.objects.all()
 
     def get(self, request, pk):
-
         device = get_object_or_404(self.queryset, pk=pk)
 
-        return render(request, 'dcim/device_status.html', {
+        return render(request, 'dcim/device/status.html', {
             'device': device,
             'active_tab': 'status',
         })
@@ -1176,7 +1275,7 @@ class DeviceLLDPNeighborsView(generic.ObjectView):
             type__in=NONCONNECTABLE_IFACE_TYPES
         )
 
-        return render(request, 'dcim/device_lldp_neighbors.html', {
+        return render(request, 'dcim/device/lldp_neighbors.html', {
             'device': device,
             'interfaces': interfaces,
             'active_tab': 'lldp-neighbors',
@@ -1191,7 +1290,7 @@ class DeviceConfigView(generic.ObjectView):
 
         device = get_object_or_404(self.queryset, pk=pk)
 
-        return render(request, 'dcim/device_config.html', {
+        return render(request, 'dcim/device/config.html', {
             'device': device,
             'active_tab': 'config',
         })
@@ -1199,7 +1298,7 @@ class DeviceConfigView(generic.ObjectView):
 
 class DeviceConfigContextView(ObjectConfigContextView):
     queryset = Device.objects.annotate_config_context_data()
-    base_template = 'dcim/device.html'
+    base_template = 'dcim/device/base.html'
 
 
 class DeviceEditView(generic.ObjectEditView):

+ 46 - 0
netbox/project-static/js/connection_toggles.js

@@ -0,0 +1,46 @@
+function toggleConnection(elem) {
+    var url = netbox_api_path + "dcim/cables/" + elem.attr('data') + "/";
+    if (elem.hasClass('connected')) {
+        $.ajax({
+            url: url,
+            method: 'PATCH',
+            dataType: 'json',
+            beforeSend: function(xhr, settings) {
+                xhr.setRequestHeader("X-CSRFToken", netbox_csrf_token);
+            },
+            data: {
+                'status': 'planned'
+            },
+            context: this,
+            success: function() {
+                elem.parents('tr').removeClass('success').addClass('info');
+                elem.removeClass('connected btn-warning').addClass('btn-success');
+                elem.attr('title', 'Mark installed');
+                elem.children('i').removeClass('mdi mdi-lan-disconnect').addClass('mdi mdi-lan-connect')
+            }
+        });
+    } else {
+        $.ajax({
+            url: url,
+            method: 'PATCH',
+            dataType: 'json',
+            beforeSend: function(xhr, settings) {
+                xhr.setRequestHeader("X-CSRFToken", netbox_csrf_token);
+            },
+            data: {
+                'status': 'connected'
+            },
+            context: this,
+            success: function() {
+                elem.parents('tr').removeClass('info').addClass('success');
+                elem.removeClass('btn-success').addClass('connected btn-warning');
+                elem.attr('title', 'Mark planned');
+                elem.children('i').removeClass('mdi mdi-lan-connect').addClass('mdi mdi-lan-disconnect')
+            }
+        });
+    }
+    return false;
+}
+$(".cable-toggle").click(function() {
+    return toggleConnection($(this));
+});

+ 1 - 5
netbox/project-static/js/interface_toggles.js → netbox/project-static/js/interface_filtering.js

@@ -3,20 +3,16 @@ $('input.interface-filter').on('input', function() {
     var filter = new RegExp(this.value);
     var interface;
 
-    for (interface of $('#interfaces_table > tbody > tr.interface')) {
+    for (interface of $('table > tbody > tr')) {
         // Slice off 'interface_' at the start of the ID
         if (filter.test(interface.id.slice(10))) {
             // Match the toggle in case the filter now matches the interface
             $(interface).find('input:checkbox[name=pk]').prop('checked', $('input.toggle').prop('checked'));
             $(interface).show();
-            if ($('button.toggle-ips').attr('selected')) {
-                $(interface).next('tr.ipaddresses').show();
-            }
         } else {
             // Uncheck to prevent actions from including it when it doesn't match
             $(interface).find('input:checkbox[name=pk]').prop('checked', false);
             $(interface).hide();
-            $(interface).next('tr.ipaddresses').hide();
         }
     }
 });

+ 0 - 931
netbox/templates/dcim/device.html

@@ -1,931 +0,0 @@
-{% extends 'base.html' %}
-{% load render_table from django_tables2 %}
-{% load buttons %}
-{% load static %}
-{% load helpers %}
-{% load custom_links %}
-{% load plugins %}
-
-{% block title %}{{ device }}{% endblock %}
-
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-        <ol class="breadcrumb">
-            <li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
-            <li><a href="{% url 'dcim:device_list' %}?site={{ device.site.slug }}">{{ device.site }}</a></li>
-            {% if device.parent_bay %}
-                <li><a href="{% url 'dcim:device' pk=device.parent_bay.device.pk %}">{{ device.parent_bay.device }}</a></li>
-                <li>{{ device.parent_bay }}</li>
-            {% endif %}
-            <li>{{ device }}</li>
-        </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:device_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search devices" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons device %}
-        {% if perms.dcim.change_device %}
-            <div class="btn-group">
-                <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                    <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
-                </button>
-                <ul class="dropdown-menu">
-                    {% if perms.dcim.add_consoleport %}
-                        <li><a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleports">Console Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_consoleserverport %}
-                        <li><a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleserverports">Console Server Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_powerport %}
-                        <li><a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_powerports">Power Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_poweroutlet %}
-                        <li><a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_poweroutlets">Power Outlets</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_interface %}
-                        <li><a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_interfaces">Interfaces</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_frontport %}
-                        <li><a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_frontports">Front Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_rearport %}
-                        <li><a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_rearports">Rear Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_devicebay %}
-                        <li><a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_devicebays">Device Bays</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_inventoryitem %}
-                        <li><a href="{% url 'dcim:inventoryitem_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_inventoryitems">Inventory Items</a></li>
-                    {% endif %}
-                </ul>
-            </div>
-        {% endif %}
-        {% if perms.dcim.add_device %}
-            {% clone_button device %}
-        {% endif %}
-        {% if perms.dcim.change_device %}
-            {% edit_button device %}
-        {% endif %}
-        {% if perms.dcim.delete_device %}
-            {% delete_button device %}
-        {% endif %}
-    </div>
-    <h1>{{ device }}</h1>
-    {% include 'inc/created_updated.html' with obj=device %}
-    <div class="pull-right noprint">
-        {% custom_links device %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{% url 'dcim:device' pk=device.pk %}">Device</a>
-        </li>
-        {% if perms.dcim.napalm_read_device %}
-            {% if device.status != 'active' %}
-                {% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='Device must be in active status' %}
-            {% elif not device.platform %}
-                {% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='No platform assigned to this device' %}
-            {% elif not device.platform.napalm_driver %}
-                {% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='No NAPALM driver assigned for this platform' %}
-            {% else %}
-                {% include 'dcim/inc/device_napalm_tabs.html' %}
-            {% endif %}
-        {% endif %}
-        {% if perms.extras.view_configcontext %}
-            <li role="presentation"{% if active_tab == 'config-context' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:device_configcontext' pk=device.pk %}">Config Context</a>
-            </li>
-        {% endif %}
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:device_changelog' pk=device.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
-{% endblock %}
-
-{% block content %}
-    <div class="row">
-        <div class="col-md-12">
-            <ul class="nav nav-pills" role="tablist">
-                <li role="presentation" class="active">
-                    <a href="#details" role="tab" data-toggle="tab">Details</a>
-                </li>
-                <li role="presentation">
-                    <a href="#interfaces" role="tab" data-toggle="tab">Interfaces {% badge interface_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#frontports" role="tab" data-toggle="tab">Front Ports {% badge frontport_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#rearports" role="tab" data-toggle="tab">Rear Ports {% badge rearport_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#consoleports" role="tab" data-toggle="tab">Console Ports {% badge consoleport_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#consoleserverports" role="tab" data-toggle="tab">Console Server Ports {% badge consoleserverport_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#powerports" role="tab" data-toggle="tab">Power Ports {% badge powerport_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#poweroutlets" role="tab" data-toggle="tab">Power Outlets {% badge poweroutlet_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#devicebays" role="tab" data-toggle="tab">Device Bays {% badge devicebay_table.rows|length %}</a>
-                </li>
-                <li role="presentation">
-                    <a href="#inventoryitems" role="tab" data-toggle="tab">Inventory {% badge inventoryitem_table.rows|length %}</a>
-                </li>
-            </ul>
-            <div class="tab-content">
-                <div role="tabpanel" class="tab-pane active" id="details">
-                    <div class="row">
-                        <div class="col-md-6">
-                            <div class="panel panel-default">
-                                <div class="panel-heading">
-                                    <strong>Device</strong>
-                                </div>
-                                <table class="table table-hover panel-body attr-table">
-                                    <tr>
-                                        <td>Site</td>
-                                        <td>
-                                            {% if device.site.region %}
-                                                <a href="{{ device.site.region.get_absolute_url }}">{{ device.site.region }}</a> /
-                                            {% endif %}
-                                            <a href="{% url 'dcim:site' slug=device.site.slug %}">{{ device.site }}</a>
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Rack</td>
-                                        <td>
-                                            {% if device.rack %}
-                                                {% if device.rack.group %}
-                                                    <a href="{{ device.rack.group.get_absolute_url }}">{{ device.rack.group }}</a> /
-                                                {% endif %}
-                                                <a href="{% url 'dcim:rack' pk=device.rack.pk %}">{{ device.rack }}</a>
-                                            {% else %}
-                                                <span class="text-muted">None</span>
-                                            {% endif %}
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Position</td>
-                                        <td>
-                                            {% if device.parent_bay %}
-                                                {% with device.parent_bay.device as parent %}
-                                                    <a href="{{ parent.get_absolute_url }}">{{ parent }}</a> / {{ device.parent_bay }}
-                                                    {% if parent.position %}
-                                                        (U{{ parent.position }} / {{ parent.get_face_display }})
-                                                    {% endif %}
-                                                {% endwith %}
-                                            {% elif device.rack and device.position %}
-                                                <span>U{{ device.position }} / {{ device.get_face_display }}</span>
-                                            {% elif device.rack and device.device_type.u_height %}
-                                                <span class="label label-warning">Not racked</span>
-                                            {% else %}
-                                                <span class="text-muted">&mdash;</span>
-                                            {% endif %}
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Tenant</td>
-                                        <td>
-                                            {% if device.tenant %}
-                                                {% if device.tenant.group %}
-                                                    <a href="{{ device.tenant.group.get_absolute_url }}">{{ device.tenant.group }}</a> /
-                                                {% endif %}
-                                                <a href="{{ device.tenant.get_absolute_url }}">{{ device.tenant }}</a>
-                                            {% else %}
-                                                <span class="text-muted">None</span>
-                                            {% endif %}
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Device Type</td>
-                                        <td>
-                                            <span><a href="{% url 'dcim:devicetype' pk=device.device_type.pk %}">{{ device.device_type.display_name }}</a> ({{ device.device_type.u_height }}U)</span>
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Serial Number</td>
-                                        <td><span>{{ device.serial|placeholder }}</span></td>
-                                    </tr>
-                                    <tr>
-                                        <td>Asset Tag</td>
-                                        <td><span>{{ device.asset_tag|placeholder }}</span></td>
-                                    </tr>
-                                </table>
-                            </div>
-                            {% if vc_members %}
-                                <div class="panel panel-default">
-                                    <div class="panel-heading">
-                                        <strong>Virtual Chassis</strong>
-                                    </div>
-                                    <table class="table table-hover panel-body attr-table">
-                                        <tr>
-                                            <th>Device</th>
-                                            <th>Position</th>
-                                            <th>Master</th>
-                                            <th>Priority</th>
-                                        </tr>
-                                        {% for vc_member in vc_members %}
-                                            <tr{% if vc_member == device %} class="info"{% endif %}>
-                                                <td>
-                                                    <a href="{{ vc_member.get_absolute_url }}">{{ vc_member }}</a>
-                                                </td>
-                                                <td><span class="badge badge-default">{{ vc_member.vc_position }}</span></td>
-                                                <td>{% if device.virtual_chassis.master == vc_member %}<i class="mdi mdi-check-bold"></i>{% endif %}</td>
-                                                <td>{{ vc_member.vc_priority|default:"" }}</td>
-                                            </tr>
-                                        {% endfor %}
-                                    </table>
-                                    <div class="panel-footer text-right noprint">
-                                        <a href="{{ device.virtual_chassis.get_absolute_url }}" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-arrow-right-bold" aria-hidden="true"></span> View Virtual Chassis
-                                        </a>
-                                    </div>
-                                </div>
-                            {% endif %}
-                            <div class="panel panel-default">
-                                <div class="panel-heading">
-                                    <strong>Management</strong>
-                                </div>
-                                <table class="table table-hover panel-body attr-table">
-                                    <tr>
-                                        <td>Role</td>
-                                        <td>
-                                            <a href="{% url 'dcim:device_list' %}?role={{ device.device_role.slug }}">{{ device.device_role }}</a>
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Platform</td>
-                                        <td>
-                                            {% if device.platform %}
-                                               <a href="{{ device.platform.get_absolute_url }}">{{ device.platform }}</a>
-                                            {% else %}
-                                                <span class="text-muted">None</span>
-                                            {% endif %}
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Status</td>
-                                        <td>
-                                            <span class="label label-{{ device.get_status_class }}">{{ device.get_status_display }}</span>
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Primary IPv4</td>
-                                        <td>
-                                            {% if device.primary_ip4 %}
-                                                <a href="{% url 'ipam:ipaddress' pk=device.primary_ip4.pk %}">{{ device.primary_ip4.address.ip }}</a>
-                                                {% if device.primary_ip4.nat_inside %}
-                                                    <span>(NAT for {{ device.primary_ip4.nat_inside.address.ip }})</span>
-                                                {% elif device.primary_ip4.nat_outside %}
-                                                    <span>(NAT: {{ device.primary_ip4.nat_outside.address.ip }})</span>
-                                                {% endif %}
-                                            {% else %}
-                                                <span class="text-muted">&mdash;</span>
-                                            {% endif %}
-                                        </td>
-                                    </tr>
-                                    <tr>
-                                        <td>Primary IPv6</td>
-                                        <td>
-                                            {% if device.primary_ip6 %}
-                                                <a href="{% url 'ipam:ipaddress' pk=device.primary_ip6.pk %}">{{ device.primary_ip6.address.ip }}</a>
-                                                {% if device.primary_ip6.nat_inside %}
-                                                    <span>(NAT for {{ device.primary_ip6.nat_inside.address.ip }})</span>
-                                                {% elif device.primary_ip6.nat_outside %}
-                                                    <span>(NAT: {{ device.primary_ip6.nat_outside.address.ip }})</span>
-                                                {% endif %}
-                                            {% else %}
-                                                <span class="text-muted">&mdash;</span>
-                                            {% endif %}
-                                        </td>
-                                    </tr>
-                                    {% if device.cluster %}
-                                        <tr>
-                                            <td>Cluster</td>
-                                            <td>
-                                                {% if device.cluster.group %}
-                                                    <a href="{{ device.cluster.group.get_absolute_url }}">{{ device.cluster.group }}</a> /
-                                                {% endif %}
-                                                <a href="{{ device.cluster.get_absolute_url }}">{{ device.cluster }}</a>
-                                            </td>
-                                        </tr>
-                                    {% endif %}
-                                </table>
-                            </div>
-                            {% include 'inc/custom_fields_panel.html' with obj=device %}
-                            {% include 'extras/inc/tags_panel.html' with tags=device.tags.all url='dcim:device_list' %}
-                            <div class="panel panel-default">
-                                <div class="panel-heading">
-                                    <strong>Comments</strong>
-                                </div>
-                                <div class="panel-body rendered-markdown">
-                                    {% if device.comments %}
-                                        {{ device.comments|render_markdown }}
-                                    {% else %}
-                                        <span class="text-muted">None</span>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% plugin_left_page device %}
-                        </div>
-                        <div class="col-md-6">
-                            {% if power_ports and poweroutlets %}
-                                <div class="panel panel-default">
-                                    <div class="panel-heading">
-                                        <strong>Power Utilization</strong>
-                                    </div>
-                                    <table class="table table-hover panel-body">
-                                        <tr>
-                                            <th>Input</th>
-                                            <th>Outlets</th>
-                                            <th>Allocated</th>
-                                            <th>Available</th>
-                                            <th>Utilization</th>
-                                        </tr>
-                                        {% for pp in power_ports %}
-                                            {% with utilization=pp.get_power_draw powerfeed=pp.connected_endpoint %}
-                                                <tr>
-                                                    <td>{{ pp }}</td>
-                                                    <td>{{ utilization.outlet_count }}</td>
-                                                    <td>{{ utilization.allocated }}VA</td>
-                                                    {% if powerfeed.available_power %}
-                                                        <td>{{ powerfeed.available_power }}VA</td>
-                                                        <td>{% utilization_graph utilization.allocated|percentage:powerfeed.available_power %}</td>
-                                                    {% else %}
-                                                        <td class="text-muted">&mdash;</td>
-                                                        <td class="text-muted">&mdash;</td>
-                                                    {% endif %}
-                                                </tr>
-                                                {% for leg in utilization.legs %}
-                                                    <tr>
-                                                        <td style="padding-left: 20px">Leg {{ leg.name }}</td>
-                                                        <td>{{ leg.outlet_count }}</td>
-                                                        <td>{{ leg.allocated }}</td>
-                                                        <td>{{ powerfeed.available_power|divide:3 }}VA</td>
-                                                        {% with phase_available=powerfeed.available_power|divide:3 %}
-                                                            <td>{% utilization_graph leg.allocated|percentage:phase_available %}</td>
-                                                        {% endwith %}
-                                                    </tr>
-                                                {% endfor %}
-                                            {% endwith %}
-                                        {% endfor %}
-                                    </table>
-                                </div>
-                            {% endif %}
-                            {% if perms.secrets.view_secret %}
-                                <div class="panel panel-default">
-                                    <div class="panel-heading">
-                                        <strong>Secrets</strong>
-                                    </div>
-                                    {% include 'secrets/inc/assigned_secrets.html' %}
-                                    {% if perms.secrets.add_secret %}
-                                        <div class="panel-footer text-right noprint">
-                                            <a href="{% url 'secrets:secret_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
-                                                <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add secret
-                                            </a>
-                                        </div>
-                                    {% endif %}
-                                </div>
-                            {% endif %}
-                            <div class="panel panel-default">
-                                <div class="panel-heading">
-                                    <strong>Services</strong>
-                                </div>
-                                {% if services %}
-                                    <table class="table table-hover panel-body">
-                                        {% for service in services %}
-                                            {% include 'ipam/inc/service.html' %}
-                                        {% endfor %}
-                                    </table>
-                                {% else %}
-                                    <div class="panel-body text-muted">
-                                        None
-                                    </div>
-                                {% endif %}
-                                {% if perms.ipam.add_service %}
-                                    <div class="panel-footer text-right noprint">
-                                        <a href="{% url 'dcim:device_service_assign' device=device.pk %}" class="btn btn-xs btn-primary">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Assign service
-                                        </a>
-                                    </div>
-                                {% endif %}
-                            </div>
-                            <div class="panel panel-default">
-                                <div class="panel-heading">
-                                    <strong>Images</strong>
-                                </div>
-                                {% include 'inc/image_attachments.html' with images=device.images.all %}
-                                {% if perms.extras.add_imageattachment %}
-                                    <div class="panel-footer text-right noprint">
-                                        <a href="{% url 'dcim:device_add_image' object_id=device.pk %}" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span>
-                                            Attach an image
-                                        </a>
-                                    </div>
-                                {% endif %}
-                            </div>
-                            <div class="panel panel-default noprint">
-                                <div class="panel-heading">
-                                    <strong>Related Devices</strong>
-                                </div>
-                                {% if related_devices %}
-                                    <table class="table table-hover panel-body">
-                                        {% for rd in related_devices %}
-                                            <tr>
-                                                <td>
-                                                    <a href="{% url 'dcim:device' pk=rd.pk %}">{{ rd }}</a>
-                                                </td>
-                                                <td>
-                                                    {% if rd.rack %}
-                                                        <a href="{% url 'dcim:rack' pk=rd.rack.pk %}">Rack {{ rd.rack }}</a>
-                                                    {% else %}
-                                                        <span class="text-muted">&mdash;</span>
-                                                    {% endif %}
-                                                </td>
-                                                <td>{{ rd.device_type.display_name }}</td>
-                                            </tr>
-                                        {% endfor %}
-                                    </table>
-                                {% else %}
-                                    <div class="panel-body text-muted">None found</div>
-                                {% endif %}
-                            </div>
-                            {% plugin_right_page device %}
-                        </div>
-                    </div>
-                    <div class="row">
-                        <div class="col-md-12">
-                            {% plugin_full_width_page device %}
-                        </div>
-                    </div>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="interfaces">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Interfaces</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceInterfaceTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                                <div class="pull-right col-md-2 noprint">
-                                    <input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
-                                </div>
-                            </div>
-                            {% render_table interface_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_interface %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_interface %}
-                                    <button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_interface %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add interfaces
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                             </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="frontports">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Front Ports</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceFrontPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table frontport_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_frontport %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:frontport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:frontport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_frontport %}
-                                    <button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_frontport %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add front ports
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="rearports">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Rear Ports</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceRearPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table rearport_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_rearport %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:rearport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:rearport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_rearport %}
-                                    <button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_rearport %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add rear ports
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="consoleports">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Console Ports</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceConsolePortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table consoleport_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_consoleport %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:consoleport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_consoleport %}
-                                    <button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_consoleport %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-xs btn-primary">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add console port
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="consoleserverports">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Console Server Ports</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceConsoleServerPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table consoleserverport_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_consoleserverport %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_consoleserverport %}
-                                    <button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_consoleserverport %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add console server ports
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="powerports">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Power Ports</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DevicePowerPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table powerport_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_powerport %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:powerport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:powerport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:powerport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_powerport %}
-                                    <button type="submit" name="_delete" formaction="{% url 'dcim:powerport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_powerport %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-xs btn-primary">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add power port
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="poweroutlets">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Power Outlets</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DevicePowerOutletTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table poweroutlet_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_powerport %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_poweroutlet %}
-                                    <button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_poweroutlet %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add power outlets
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="devicebays">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Device Bays</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceDeviceBayTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table devicebay_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_devicebay %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:devicebay_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_devicebay %}
-                                    <button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete selected
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_devicebay %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device bays
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                             </div>
-                        </div>
-                    </form>
-                </div>
-                <div role="tabpanel" class="tab-pane" id="inventoryitems">
-                    <form method="post">
-                        {% csrf_token %}
-                        <div class="panel panel-default">
-                            <div class="panel-heading">
-                                <strong>Inventory Items</strong>
-                                <div class="pull-right noprint">
-                                    {% if request.user.is_authenticated %}
-                                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceInventoryItemTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                                    {% endif %}
-                                </div>
-                            </div>
-                            {% render_table inventoryitem_table 'inc/table.html' %}
-                            <div class="panel-footer noprint">
-                                {% if perms.dcim.change_inventoryitem %}
-                                    <button type="submit" name="_rename" formaction="{% url 'dcim:inventoryitem_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                                    </button>
-                                    <button type="submit" name="_edit" formaction="{% url 'dcim:inventoryitem_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-warning btn-xs">
-                                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.delete_inventoryitem %}
-                                    <button type="submit" name="_delete" formaction="{% url 'dcim:inventoryitem_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-danger btn-xs">
-                                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                                    </button>
-                                {% endif %}
-                                {% if perms.dcim.add_inventoryitem %}
-                                    <div class="pull-right">
-                                        <a href="{% url 'dcim:inventoryitem_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-primary btn-xs">
-                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Inventory Item
-                                        </a>
-                                    </div>
-                                {% endif %}
-                                <div class="clearfix"></div>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-{% include 'secrets/inc/private_key_modal.html' %}
-{% table_config_form interface_table %}
-{% table_config_form frontport_table %}
-{% table_config_form rearport_table %}
-{% table_config_form consoleport_table %}
-{% table_config_form consoleserverport_table %}
-{% table_config_form powerport_table %}
-{% table_config_form poweroutlet_table %}
-{% table_config_form devicebay_table %}
-{% table_config_form inventoryitem_table %}
-{% endblock %}
-
-{% block javascript %}
-<script type="text/javascript">
-// Redirect user to appropriate components tab if specified
-var hash = document.location.hash;
-var prefix = "tab_";
-if (hash) {
-    $('.nav-pills a[href="'+hash.replace(prefix,"")+'"]').tab('show');
-}
-$('.nav-pills a').on('shown.bs.tab', function (e) {
-    window.location.hash = e.target.hash.replace("#", "#" + prefix);
-});
-
-function toggleConnection(elem) {
-    var url = netbox_api_path + "dcim/cables/" + elem.attr('data') + "/";
-    if (elem.hasClass('connected')) {
-        $.ajax({
-            url: url,
-            method: 'PATCH',
-            dataType: 'json',
-            beforeSend: function(xhr, settings) {
-                xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
-            },
-            data: {
-                'status': 'planned'
-            },
-            context: this,
-            success: function() {
-                elem.parents('tr').removeClass('success').addClass('info');
-                elem.removeClass('connected btn-warning').addClass('btn-success');
-                elem.attr('title', 'Mark installed');
-                elem.children('i').removeClass('mdi mdi-lan-disconnect').addClass('mdi mdi-lan-connect')
-            }
-        });
-    } else {
-        $.ajax({
-            url: url,
-            method: 'PATCH',
-            dataType: 'json',
-            beforeSend: function(xhr, settings) {
-                xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
-            },
-            data: {
-                'status': 'connected'
-            },
-            context: this,
-            success: function() {
-                elem.parents('tr').removeClass('info').addClass('success');
-                elem.removeClass('btn-success').addClass('connected btn-warning');
-                elem.attr('title', 'Mark planned');
-                elem.children('i').removeClass('mdi mdi-lan-connect').addClass('mdi mdi-lan-disconnect')
-            }
-        });
-    }
-    return false;
-}
-$(".cable-toggle").click(function() {
-    return toggleConnection($(this));
-});
-</script>
-<script src="{% static 'js/interface_toggles.js' %}?v{{ settings.VERSION }}"></script>
-<script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
-<script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
-{% endblock %}

+ 178 - 0
netbox/templates/dcim/device/base.html

@@ -0,0 +1,178 @@
+{% extends 'base.html' %}
+{% load buttons %}
+{% load static %}
+{% load helpers %}
+{% load custom_links %}
+{% load plugins %}
+
+{% block title %}{{ device }}{% endblock %}
+
+{% block header %}
+    <div class="row noprint">
+        <div class="col-sm-8 col-md-9">
+        <ol class="breadcrumb">
+            <li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
+            <li><a href="{% url 'dcim:device_list' %}?site={{ device.site.slug }}">{{ device.site }}</a></li>
+            {% if device.parent_bay %}
+                <li><a href="{% url 'dcim:device' pk=device.parent_bay.device.pk %}">{{ device.parent_bay.device }}</a></li>
+                <li>{{ device.parent_bay }}</li>
+            {% endif %}
+            <li>{{ device }}</li>
+        </ol>
+        </div>
+        <div class="col-sm-4 col-md-3">
+            <form action="{% url 'dcim:device_list' %}" method="get">
+                <div class="input-group">
+                    <input type="text" name="q" class="form-control" placeholder="Search devices" />
+                    <span class="input-group-btn">
+                        <button type="submit" class="btn btn-primary">
+                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
+                        </button>
+                    </span>
+                </div>
+            </form>
+        </div>
+    </div>
+    <div class="pull-right noprint">
+        {% plugin_buttons device %}
+        {% if perms.dcim.change_device %}
+            <div class="btn-group">
+                <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                    <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu">
+                    {% if perms.dcim.add_consoleport %}
+                        <li><a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleports">Console Ports</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_consoleserverport %}
+                        <li><a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleserverports">Console Server Ports</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_powerport %}
+                        <li><a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_powerports">Power Ports</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_poweroutlet %}
+                        <li><a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_poweroutlets">Power Outlets</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_interface %}
+                        <li><a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_interfaces">Interfaces</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_frontport %}
+                        <li><a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_frontports">Front Ports</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_rearport %}
+                        <li><a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_rearports">Rear Ports</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_devicebay %}
+                        <li><a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_devicebays">Device Bays</a></li>
+                    {% endif %}
+                    {% if perms.dcim.add_inventoryitem %}
+                        <li><a href="{% url 'dcim:inventoryitem_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_inventoryitems">Inventory Items</a></li>
+                    {% endif %}
+                </ul>
+            </div>
+        {% endif %}
+        {% if perms.dcim.add_device %}
+            {% clone_button device %}
+        {% endif %}
+        {% if perms.dcim.change_device %}
+            {% edit_button device %}
+        {% endif %}
+        {% if perms.dcim.delete_device %}
+            {% delete_button device %}
+        {% endif %}
+    </div>
+    <h1>{{ device }}</h1>
+    {% include 'inc/created_updated.html' with obj=device %}
+    <div class="pull-right noprint">
+        {% custom_links device %}
+    </div>
+    <ul class="nav nav-tabs">
+        <li role="presentation" {% if active_tab == 'device' %} class="active"{% endif %}>
+            <a href="{% url 'dcim:device' pk=device.pk %}">Device</a>
+        </li>
+        {% with interface_count=device.interfaces.count %}
+            {% if interface_count %}
+                <li role="presentation" {% if active_tab == 'interfaces' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_interfaces' pk=device.pk %}">Interfaces {% badge interface_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with frontport_count=device.frontports.count %}
+            {% if frontport_count %}
+                <li role="presentation" {% if active_tab == 'front-ports' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_frontports' pk=device.pk %}">Front Ports {% badge frontport_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with rearport_count=device.rearports.count %}
+            {% if rearport_count %}
+                <li role="presentation" {% if active_tab == 'rear-ports' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_rearports' pk=device.pk %}">Rear Ports {% badge rearport_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with consoleport_count=device.consoleports.count %}
+            {% if consoleport_count %}
+                <li role="presentation" {% if active_tab == 'console-ports' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_consoleports' pk=device.pk %}">Console Ports {% badge consoleport_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with consoleserverport_count=device.consoleserverports.count %}
+            {% if consoleserverport_count %}
+                <li role="presentation" {% if active_tab == 'console-server-ports' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_consoleserverports' pk=device.pk %}">Console Server Ports {% badge consoleserverport_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with powerport_count=device.powerports.count %}
+            {% if powerport_count %}
+                <li role="presentation" {% if active_tab == 'power-ports' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_powerports' pk=device.pk %}">Power Ports {% badge powerport_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with poweroutlet_count=device.poweroutlets.count %}
+            {% if poweroutlet_count %}
+                <li role="presentation" {% if active_tab == 'power-outlets' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_poweroutlets' pk=device.pk %}">Power Outlets {% badge poweroutlet_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with devicebay_count=device.devicebays.count %}
+            {% if devicebay_count %}
+                <li role="presentation" {% if active_tab == 'device-bays' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_devicebays' pk=device.pk %}">Device Bays {% badge devicebay_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% with inventoryitem_count=device.inventoryitems.count %}
+            {% if inventoryitem_count %}
+                <li role="presentation" {% if active_tab == 'inventory' %} class="active"{% endif %}>
+                    <a href="{% url 'dcim:device_inventory' pk=device.pk %}">Inventory {% badge inventoryitem_count %}</a>
+                </li>
+            {% endif %}
+        {% endwith %}
+        {% if perms.dcim.napalm_read_device %}
+            {% if device.status != 'active' %}
+                {% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='Device must be in active status' %}
+            {% elif not device.platform %}
+                {% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='No platform assigned to this device' %}
+            {% elif not device.platform.napalm_driver %}
+                {% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='No NAPALM driver assigned for this platform' %}
+            {% else %}
+                {% include 'dcim/inc/device_napalm_tabs.html' %}
+            {% endif %}
+        {% endif %}
+        {% if perms.extras.view_configcontext %}
+            <li role="presentation"{% if active_tab == 'config-context' %} class="active"{% endif %}>
+                <a href="{% url 'dcim:device_configcontext' pk=device.pk %}">Config Context</a>
+            </li>
+        {% endif %}
+        {% if perms.extras.view_objectchange %}
+            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
+                <a href="{% url 'dcim:device_changelog' pk=device.pk %}">Change Log</a>
+            </li>
+        {% endif %}
+    </ul>
+{% endblock %}

+ 1 - 1
netbox/templates/dcim/device_config.html → netbox/templates/dcim/device/config.html

@@ -1,4 +1,4 @@
-{% extends 'dcim/device.html' %}
+{% extends 'dcim/device/base.html' %}
 {% load static %}
 
 {% block title %}{{ device }} - Config{% endblock %}

+ 53 - 0
netbox/templates/dcim/device/consoleports.html

@@ -0,0 +1,53 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Console Ports</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceConsolePortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table consoleport_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_consoleport %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:consoleport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_consoleport %}
+                    <button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_consoleport %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleports" class="btn btn-xs btn-primary">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add console port
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+            </div>
+        </div>
+    </form>
+    {% table_config_form consoleport_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 53 - 0
netbox/templates/dcim/device/consoleserverports.html

@@ -0,0 +1,53 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Console Server Ports</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceConsoleServerPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table consoleserverport_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_consoleserverport %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_consoleserverport %}
+                    <button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_consoleserverport %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_consoleserverports" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add console server ports
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+            </div>
+        </div>
+    </form>
+    {% table_config_form consoleserverport_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 345 - 0
netbox/templates/dcim/device/device.html

@@ -0,0 +1,345 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load buttons %}
+{% load static %}
+{% load helpers %}
+{% load plugins %}
+
+{% block content %}
+    <div class="row">
+        <div class="col-md-12">
+            <div class="tab-content">
+                <div role="tabpanel" class="tab-pane active" id="details">
+                    <div class="row">
+                        <div class="col-md-6">
+                            <div class="panel panel-default">
+                                <div class="panel-heading">
+                                    <strong>Device</strong>
+                                </div>
+                                <table class="table table-hover panel-body attr-table">
+                                    <tr>
+                                        <td>Site</td>
+                                        <td>
+                                            {% if device.site.region %}
+                                                <a href="{{ device.site.region.get_absolute_url }}">{{ device.site.region }}</a> /
+                                            {% endif %}
+                                            <a href="{% url 'dcim:site' slug=device.site.slug %}">{{ device.site }}</a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Rack</td>
+                                        <td>
+                                            {% if device.rack %}
+                                                {% if device.rack.group %}
+                                                    <a href="{{ device.rack.group.get_absolute_url }}">{{ device.rack.group }}</a> /
+                                                {% endif %}
+                                                <a href="{% url 'dcim:rack' pk=device.rack.pk %}">{{ device.rack }}</a>
+                                            {% else %}
+                                                <span class="text-muted">None</span>
+                                            {% endif %}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Position</td>
+                                        <td>
+                                            {% if device.parent_bay %}
+                                                {% with device.parent_bay.device as parent %}
+                                                    <a href="{{ parent.get_absolute_url }}">{{ parent }}</a> / {{ device.parent_bay }}
+                                                    {% if parent.position %}
+                                                        (U{{ parent.position }} / {{ parent.get_face_display }})
+                                                    {% endif %}
+                                                {% endwith %}
+                                            {% elif device.rack and device.position %}
+                                                <span>U{{ device.position }} / {{ device.get_face_display }}</span>
+                                            {% elif device.rack and device.device_type.u_height %}
+                                                <span class="label label-warning">Not racked</span>
+                                            {% else %}
+                                                <span class="text-muted">&mdash;</span>
+                                            {% endif %}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Tenant</td>
+                                        <td>
+                                            {% if device.tenant %}
+                                                {% if device.tenant.group %}
+                                                    <a href="{{ device.tenant.group.get_absolute_url }}">{{ device.tenant.group }}</a> /
+                                                {% endif %}
+                                                <a href="{{ device.tenant.get_absolute_url }}">{{ device.tenant }}</a>
+                                            {% else %}
+                                                <span class="text-muted">None</span>
+                                            {% endif %}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Device Type</td>
+                                        <td>
+                                            <span><a href="{% url 'dcim:devicetype' pk=device.device_type.pk %}">{{ device.device_type.display_name }}</a> ({{ device.device_type.u_height }}U)</span>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Serial Number</td>
+                                        <td><span>{{ device.serial|placeholder }}</span></td>
+                                    </tr>
+                                    <tr>
+                                        <td>Asset Tag</td>
+                                        <td><span>{{ device.asset_tag|placeholder }}</span></td>
+                                    </tr>
+                                </table>
+                            </div>
+                            {% if vc_members %}
+                                <div class="panel panel-default">
+                                    <div class="panel-heading">
+                                        <strong>Virtual Chassis</strong>
+                                    </div>
+                                    <table class="table table-hover panel-body attr-table">
+                                        <tr>
+                                            <th>Device</th>
+                                            <th>Position</th>
+                                            <th>Master</th>
+                                            <th>Priority</th>
+                                        </tr>
+                                        {% for vc_member in vc_members %}
+                                            <tr{% if vc_member == device %} class="info"{% endif %}>
+                                                <td>
+                                                    <a href="{{ vc_member.get_absolute_url }}">{{ vc_member }}</a>
+                                                </td>
+                                                <td><span class="badge badge-default">{{ vc_member.vc_position }}</span></td>
+                                                <td>{% if device.virtual_chassis.master == vc_member %}<i class="mdi mdi-check-bold"></i>{% endif %}</td>
+                                                <td>{{ vc_member.vc_priority|default:"" }}</td>
+                                            </tr>
+                                        {% endfor %}
+                                    </table>
+                                    <div class="panel-footer text-right noprint">
+                                        <a href="{{ device.virtual_chassis.get_absolute_url }}" class="btn btn-primary btn-xs">
+                                            <span class="mdi mdi-arrow-right-bold" aria-hidden="true"></span> View Virtual Chassis
+                                        </a>
+                                    </div>
+                                </div>
+                            {% endif %}
+                            <div class="panel panel-default">
+                                <div class="panel-heading">
+                                    <strong>Management</strong>
+                                </div>
+                                <table class="table table-hover panel-body attr-table">
+                                    <tr>
+                                        <td>Role</td>
+                                        <td>
+                                            <a href="{% url 'dcim:device_list' %}?role={{ device.device_role.slug }}">{{ device.device_role }}</a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Platform</td>
+                                        <td>
+                                            {% if device.platform %}
+                                               <a href="{{ device.platform.get_absolute_url }}">{{ device.platform }}</a>
+                                            {% else %}
+                                                <span class="text-muted">None</span>
+                                            {% endif %}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Status</td>
+                                        <td>
+                                            <span class="label label-{{ device.get_status_class }}">{{ device.get_status_display }}</span>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Primary IPv4</td>
+                                        <td>
+                                            {% if device.primary_ip4 %}
+                                                <a href="{% url 'ipam:ipaddress' pk=device.primary_ip4.pk %}">{{ device.primary_ip4.address.ip }}</a>
+                                                {% if device.primary_ip4.nat_inside %}
+                                                    <span>(NAT for {{ device.primary_ip4.nat_inside.address.ip }})</span>
+                                                {% elif device.primary_ip4.nat_outside %}
+                                                    <span>(NAT: {{ device.primary_ip4.nat_outside.address.ip }})</span>
+                                                {% endif %}
+                                            {% else %}
+                                                <span class="text-muted">&mdash;</span>
+                                            {% endif %}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>Primary IPv6</td>
+                                        <td>
+                                            {% if device.primary_ip6 %}
+                                                <a href="{% url 'ipam:ipaddress' pk=device.primary_ip6.pk %}">{{ device.primary_ip6.address.ip }}</a>
+                                                {% if device.primary_ip6.nat_inside %}
+                                                    <span>(NAT for {{ device.primary_ip6.nat_inside.address.ip }})</span>
+                                                {% elif device.primary_ip6.nat_outside %}
+                                                    <span>(NAT: {{ device.primary_ip6.nat_outside.address.ip }})</span>
+                                                {% endif %}
+                                            {% else %}
+                                                <span class="text-muted">&mdash;</span>
+                                            {% endif %}
+                                        </td>
+                                    </tr>
+                                    {% if device.cluster %}
+                                        <tr>
+                                            <td>Cluster</td>
+                                            <td>
+                                                {% if device.cluster.group %}
+                                                    <a href="{{ device.cluster.group.get_absolute_url }}">{{ device.cluster.group }}</a> /
+                                                {% endif %}
+                                                <a href="{{ device.cluster.get_absolute_url }}">{{ device.cluster }}</a>
+                                            </td>
+                                        </tr>
+                                    {% endif %}
+                                </table>
+                            </div>
+                            {% include 'inc/custom_fields_panel.html' with obj=device %}
+                            {% include 'extras/inc/tags_panel.html' with tags=device.tags.all url='dcim:device_list' %}
+                            <div class="panel panel-default">
+                                <div class="panel-heading">
+                                    <strong>Comments</strong>
+                                </div>
+                                <div class="panel-body rendered-markdown">
+                                    {% if device.comments %}
+                                        {{ device.comments|render_markdown }}
+                                    {% else %}
+                                        <span class="text-muted">None</span>
+                                    {% endif %}
+                                </div>
+                            </div>
+                            {% plugin_left_page device %}
+                        </div>
+                        <div class="col-md-6">
+                            {% if power_ports and poweroutlets %}
+                                <div class="panel panel-default">
+                                    <div class="panel-heading">
+                                        <strong>Power Utilization</strong>
+                                    </div>
+                                    <table class="table table-hover panel-body">
+                                        <tr>
+                                            <th>Input</th>
+                                            <th>Outlets</th>
+                                            <th>Allocated</th>
+                                            <th>Available</th>
+                                            <th>Utilization</th>
+                                        </tr>
+                                        {% for pp in power_ports %}
+                                            {% with utilization=pp.get_power_draw powerfeed=pp.connected_endpoint %}
+                                                <tr>
+                                                    <td>{{ pp }}</td>
+                                                    <td>{{ utilization.outlet_count }}</td>
+                                                    <td>{{ utilization.allocated }}VA</td>
+                                                    {% if powerfeed.available_power %}
+                                                        <td>{{ powerfeed.available_power }}VA</td>
+                                                        <td>{% utilization_graph utilization.allocated|percentage:powerfeed.available_power %}</td>
+                                                    {% else %}
+                                                        <td class="text-muted">&mdash;</td>
+                                                        <td class="text-muted">&mdash;</td>
+                                                    {% endif %}
+                                                </tr>
+                                                {% for leg in utilization.legs %}
+                                                    <tr>
+                                                        <td style="padding-left: 20px">Leg {{ leg.name }}</td>
+                                                        <td>{{ leg.outlet_count }}</td>
+                                                        <td>{{ leg.allocated }}</td>
+                                                        <td>{{ powerfeed.available_power|divide:3 }}VA</td>
+                                                        {% with phase_available=powerfeed.available_power|divide:3 %}
+                                                            <td>{% utilization_graph leg.allocated|percentage:phase_available %}</td>
+                                                        {% endwith %}
+                                                    </tr>
+                                                {% endfor %}
+                                            {% endwith %}
+                                        {% endfor %}
+                                    </table>
+                                </div>
+                            {% endif %}
+                            {% if perms.secrets.view_secret %}
+                                <div class="panel panel-default">
+                                    <div class="panel-heading">
+                                        <strong>Secrets</strong>
+                                    </div>
+                                    {% include 'secrets/inc/assigned_secrets.html' %}
+                                    {% if perms.secrets.add_secret %}
+                                        <div class="panel-footer text-right noprint">
+                                            <a href="{% url 'secrets:secret_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
+                                                <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add secret
+                                            </a>
+                                        </div>
+                                    {% endif %}
+                                </div>
+                            {% endif %}
+                            <div class="panel panel-default">
+                                <div class="panel-heading">
+                                    <strong>Services</strong>
+                                </div>
+                                {% if services %}
+                                    <table class="table table-hover panel-body">
+                                        {% for service in services %}
+                                            {% include 'ipam/inc/service.html' %}
+                                        {% endfor %}
+                                    </table>
+                                {% else %}
+                                    <div class="panel-body text-muted">
+                                        None
+                                    </div>
+                                {% endif %}
+                                {% if perms.ipam.add_service %}
+                                    <div class="panel-footer text-right noprint">
+                                        <a href="{% url 'dcim:device_service_assign' device=device.pk %}" class="btn btn-xs btn-primary">
+                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Assign service
+                                        </a>
+                                    </div>
+                                {% endif %}
+                            </div>
+                            <div class="panel panel-default">
+                                <div class="panel-heading">
+                                    <strong>Images</strong>
+                                </div>
+                                {% include 'inc/image_attachments.html' with images=device.images.all %}
+                                {% if perms.extras.add_imageattachment %}
+                                    <div class="panel-footer text-right noprint">
+                                        <a href="{% url 'dcim:device_add_image' object_id=device.pk %}" class="btn btn-primary btn-xs">
+                                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span>
+                                            Attach an image
+                                        </a>
+                                    </div>
+                                {% endif %}
+                            </div>
+                            <div class="panel panel-default noprint">
+                                <div class="panel-heading">
+                                    <strong>Related Devices</strong>
+                                </div>
+                                {% if related_devices %}
+                                    <table class="table table-hover panel-body">
+                                        {% for rd in related_devices %}
+                                            <tr>
+                                                <td>
+                                                    <a href="{% url 'dcim:device' pk=rd.pk %}">{{ rd }}</a>
+                                                </td>
+                                                <td>
+                                                    {% if rd.rack %}
+                                                        <a href="{% url 'dcim:rack' pk=rd.rack.pk %}">Rack {{ rd.rack }}</a>
+                                                    {% else %}
+                                                        <span class="text-muted">&mdash;</span>
+                                                    {% endif %}
+                                                </td>
+                                                <td>{{ rd.device_type.display_name }}</td>
+                                            </tr>
+                                        {% endfor %}
+                                    </table>
+                                {% else %}
+                                    <div class="panel-body text-muted">None found</div>
+                                {% endif %}
+                            </div>
+                            {% plugin_right_page device %}
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="col-md-12">
+                            {% plugin_full_width_page device %}
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    {% include 'secrets/inc/private_key_modal.html' %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 49 - 0
netbox/templates/dcim/device/devicebays.html

@@ -0,0 +1,49 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Device Bays</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceDeviceBayTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table devicebay_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_devicebay %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:devicebay_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_devicebay %}
+                    <button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete selected
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_devicebay %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_devicebays" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device bays
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+             </div>
+        </div>
+    </form>
+    {% table_config_form devicebay_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 53 - 0
netbox/templates/dcim/device/frontports.html

@@ -0,0 +1,53 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Front Ports</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceFrontPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table frontport_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_frontport %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:frontport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:frontport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_frontport %}
+                    <button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_frontport %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_frontports" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add front ports
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+            </div>
+        </div>
+    </form>
+    {% table_config_form frontport_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 57 - 0
netbox/templates/dcim/device/interfaces.html

@@ -0,0 +1,57 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Interfaces</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceInterfaceTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+                <div class="pull-right col-md-2 noprint">
+                    <input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
+                </div>
+            </div>
+            {% render_table interface_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_interface %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_interface %}
+                    <button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_interface %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_interfaces" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add interfaces
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+             </div>
+        </div>
+    </form>
+    {% table_config_form interface_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/interface_filtering.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 49 - 0
netbox/templates/dcim/device/inventory.html

@@ -0,0 +1,49 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Inventory Items</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceInventoryItemTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table inventoryitem_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_inventoryitem %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:inventoryitem_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:inventoryitem_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_inventoryitem %}
+                    <button type="submit" name="_delete" formaction="{% url 'dcim:inventoryitem_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_inventoryitem %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:inventoryitem_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_inventoryitems" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Inventory Item
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+            </div>
+        </div>
+    </form>
+    {% table_config_form inventoryitem_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 1 - 1
netbox/templates/dcim/device_lldp_neighbors.html → netbox/templates/dcim/device/lldp_neighbors.html

@@ -1,4 +1,4 @@
-{% extends 'dcim/device.html' %}
+{% extends 'dcim/device/base.html' %}
 
 {% block title %}{{ device }} - LLDP Neighbors{% endblock %}
 

+ 53 - 0
netbox/templates/dcim/device/poweroutlets.html

@@ -0,0 +1,53 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Power Outlets</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DevicePowerOutletTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table poweroutlet_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_powerport %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_poweroutlet %}
+                    <button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_poweroutlet %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_poweroutlets" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add power outlets
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+            </div>
+        </div>
+    </form>
+    {% table_config_form poweroutlet_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 53 - 0
netbox/templates/dcim/device/powerports.html

@@ -0,0 +1,53 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Power Ports</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DevicePowerPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table powerport_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_powerport %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:powerport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:powerport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:powerport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_powerport %}
+                    <button type="submit" name="_delete" formaction="{% url 'dcim:powerport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_powerport %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_powerports" class="btn btn-xs btn-primary">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add power port
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+            </div>
+        </div>
+    </form>
+    {% table_config_form powerport_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 53 - 0
netbox/templates/dcim/device/rearports.html

@@ -0,0 +1,53 @@
+{% extends 'dcim/device/base.html' %}
+{% load render_table from django_tables2 %}
+{% load static %}
+{% load helpers %}
+
+{% block content %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Rear Ports</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#DeviceRearPortTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+            </div>
+            {% render_table rearport_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.dcim.change_rearport %}
+                    <button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'dcim:rearport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                    <button type="submit" name="_disconnect" formaction="{% url 'dcim:rearport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
+                    </button>
+                {% endif %}
+                {% if perms.dcim.delete_rearport %}
+                    <button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' %}?return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.dcim.add_rearport %}
+                    <div class="pull-right">
+                        <a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}%23tab_rearports" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add rear ports
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+            </div>
+        </div>
+    </form>
+    {% table_config_form rearport_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 1 - 1
netbox/templates/dcim/device_status.html → netbox/templates/dcim/device/status.html

@@ -1,4 +1,4 @@
-{% extends 'dcim/device.html' %}
+{% extends 'dcim/device/base.html' %}
 {% load static %}
 
 {% block title %}{{ device }} - Status{% endblock %}

+ 1 - 1
netbox/templates/virtualization/virtualmachine.html

@@ -314,6 +314,6 @@
 {% endblock %}
 
 {% block javascript %}
-<script src="{% static 'js/interface_toggles.js' %}?v{{ settings.VERSION }}"></script>
+<script src="{% static 'js/interface_filtering.js' %}?v{{ settings.VERSION }}"></script>
 <script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
 {% endblock %}