mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-17 20:46:30 -06:00
Convert device interfaces list to table
This commit is contained in:
parent
e3f98a011c
commit
2146c38748
@ -11,7 +11,7 @@ from utilities.tables import (
|
|||||||
TagColumn, ToggleColumn,
|
TagColumn, ToggleColumn,
|
||||||
)
|
)
|
||||||
from .template_code import (
|
from .template_code import (
|
||||||
CABLETERMINATION, CONSOLEPORT_BUTTONS, CONSOLESERVERPORT_BUTTONS, DEVICE_LINK, FRONTPORT_BUTTONS,
|
CABLETERMINATION, CONSOLEPORT_BUTTONS, CONSOLESERVERPORT_BUTTONS, DEVICE_LINK, FRONTPORT_BUTTONS, INTERFACE_BUTTONS,
|
||||||
INTERFACE_IPADDRESSES, INTERFACE_TAGGED_VLANS, POWEROUTLET_BUTTONS, POWERPORT_BUTTONS, REARPORT_BUTTONS,
|
INTERFACE_IPADDRESSES, INTERFACE_TAGGED_VLANS, POWEROUTLET_BUTTONS, POWERPORT_BUTTONS, REARPORT_BUTTONS,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ __all__ = (
|
|||||||
'DeviceConsoleServerPortTable',
|
'DeviceConsoleServerPortTable',
|
||||||
'DeviceFrontPortTable',
|
'DeviceFrontPortTable',
|
||||||
'DeviceImportTable',
|
'DeviceImportTable',
|
||||||
|
'DeviceInterfaceTable',
|
||||||
'DevicePowerPortTable',
|
'DevicePowerPortTable',
|
||||||
'DevicePowerOutletTable',
|
'DevicePowerOutletTable',
|
||||||
'DeviceRearPortTable',
|
'DeviceRearPortTable',
|
||||||
@ -405,6 +406,32 @@ class InterfaceTable(DeviceComponentTable, BaseInterfaceTable, PathEndpointTable
|
|||||||
default_columns = ('pk', 'device', 'name', 'label', 'enabled', 'type', 'description')
|
default_columns = ('pk', 'device', 'name', 'label', 'enabled', 'type', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceInterfaceTable(InterfaceTable):
|
||||||
|
name = tables.TemplateColumn(
|
||||||
|
template_code='<i class="fa fa-{% if iface.mgmt_only %}wrench{% elif iface.is_lag %}align-justify'
|
||||||
|
'{% elif iface.is_virtual %}circle{% elif iface.is_wireless %}wifi{% else %}exchange'
|
||||||
|
'{% endif %}"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>'
|
||||||
|
)
|
||||||
|
actions = ButtonsColumn(
|
||||||
|
model=Interface,
|
||||||
|
buttons=('edit', 'delete'),
|
||||||
|
prepend_template=INTERFACE_BUTTONS
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(DeviceComponentTable.Meta):
|
||||||
|
model = Interface
|
||||||
|
fields = (
|
||||||
|
'pk', 'name', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', 'mode', 'mac_address', 'description', 'cable',
|
||||||
|
'cable_peer', 'connection', 'tags', 'ip_addresses', 'untagged_vlan', 'tagged_vlans', 'actions',
|
||||||
|
)
|
||||||
|
default_columns = (
|
||||||
|
'pk', 'name', 'label', 'type', 'enabled', 'description', 'cable', 'cable_peer', 'actions',
|
||||||
|
)
|
||||||
|
row_attrs = {
|
||||||
|
'class': lambda record: record.cable.get_status_class() if record.cable else ''
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTable(DeviceComponentTable, CableTerminationTable):
|
class FrontPortTable(DeviceComponentTable, CableTerminationTable):
|
||||||
rear_port_position = tables.Column(
|
rear_port_position = tables.Column(
|
||||||
verbose_name='Position'
|
verbose_name='Position'
|
||||||
|
@ -138,6 +138,31 @@ POWEROUTLET_BUTTONS = """
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
INTERFACE_BUTTONS = """
|
||||||
|
{% if perms.ipam.add_ipaddress %}
|
||||||
|
<a href="{% url 'ipam:ipaddress_add' %}?interface={{ record.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-success" title="Add IP address">
|
||||||
|
<i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.change_interface %}
|
||||||
|
{% if record.cable %}
|
||||||
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
||||||
|
{% elif record.is_connectable and perms.dcim.add_cable %}
|
||||||
|
<span class="dropdown">
|
||||||
|
<button type="button" class="btn btn-success btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="glyphicon glyphicon-resize-small" 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 }}">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 }}">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 }}">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 }}">Circuit Termination</a></li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
"""
|
||||||
|
|
||||||
FRONTPORT_BUTTONS = """
|
FRONTPORT_BUTTONS = """
|
||||||
{% if frontport.cable %}
|
{% if frontport.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=record.cable %}
|
||||||
|
@ -1056,6 +1056,9 @@ class DeviceView(ObjectView):
|
|||||||
Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
|
Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
|
||||||
'lag', 'cable', '_path__destination', 'tags',
|
'lag', 'cable', '_path__destination', 'tags',
|
||||||
)
|
)
|
||||||
|
interface_table = tables.DeviceInterfaceTable(interfaces, orderable=False)
|
||||||
|
if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'):
|
||||||
|
interface_table.columns.show('pk')
|
||||||
|
|
||||||
# Front ports
|
# Front ports
|
||||||
frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
||||||
@ -1102,7 +1105,7 @@ class DeviceView(ObjectView):
|
|||||||
'consoleserverport_table': consoleserverport_table,
|
'consoleserverport_table': consoleserverport_table,
|
||||||
'powerport_table': powerport_table,
|
'powerport_table': powerport_table,
|
||||||
'poweroutlet_table': poweroutlet_table,
|
'poweroutlet_table': poweroutlet_table,
|
||||||
'interfaces': interfaces,
|
'interface_table': interface_table,
|
||||||
'frontport_table': frontport_table,
|
'frontport_table': frontport_table,
|
||||||
'rearport_table': rearport_table,
|
'rearport_table': rearport_table,
|
||||||
'devicebays': devicebays,
|
'devicebays': devicebays,
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
<a href="#details" role="tab" data-toggle="tab">Details</a>
|
<a href="#details" role="tab" data-toggle="tab">Details</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation">
|
<li role="presentation">
|
||||||
<a href="#interfaces" role="tab" data-toggle="tab">Interfaces {% badge interfaces|length %}</a>
|
<a href="#interfaces" role="tab" data-toggle="tab">Interfaces {% badge interface_table.rows|length %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation">
|
<li role="presentation">
|
||||||
<a href="#frontports" role="tab" data-toggle="tab">Front Ports {% badge frontport_table.rows|length %}</a>
|
<a href="#frontports" role="tab" data-toggle="tab">Front Ports {% badge frontport_table.rows|length %}</a>
|
||||||
@ -494,29 +494,7 @@
|
|||||||
<input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
|
<input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table id="interfaces_table" class="table table-hover table-headings panel-body component-list">
|
{% include 'responsive_table.html' with table=interface_table %}
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{% if perms.dcim.change_interface or perms.dcim.delete_interface %}
|
|
||||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
|
||||||
{% endif %}
|
|
||||||
<th>Name</th>
|
|
||||||
<th>LAG</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>MTU</th>
|
|
||||||
<th>Mode</th>
|
|
||||||
<th>Cable</th>
|
|
||||||
<th colspan="2">Cable Termination</th>
|
|
||||||
<th colspan="2">Connection</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for iface in interfaces %}
|
|
||||||
{% include 'dcim/inc/interface.html' %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="panel-footer noprint">
|
<div class="panel-footer noprint">
|
||||||
{% if interfaces and perms.dcim.change_interface %}
|
{% if interfaces and perms.dcim.change_interface %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
@ -1,200 +0,0 @@
|
|||||||
{% load helpers %}
|
|
||||||
<tr class="interface{% if not iface.enabled %} danger{% elif iface.cable %} {{ iface.cable.get_status_class }}{% elif iface.is_virtual %} warning{% endif %}" id="interface_{{ iface.name }}">
|
|
||||||
|
|
||||||
{# Checkbox #}
|
|
||||||
{% if perms.dcim.change_interface or perms.dcim.delete_interface %}
|
|
||||||
<td class="pk">
|
|
||||||
<input name="pk" type="checkbox" value="{{ iface.pk }}" />
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Icon and name #}
|
|
||||||
<td class="text-nowrap">
|
|
||||||
<span title="{{ iface.get_type_display }}">
|
|
||||||
<i class="fa fa-fw fa-{% if iface.mgmt_only %}wrench{% elif iface.is_lag %}align-justify{% elif iface.is_virtual %}circle{% elif iface.is_wireless %}wifi{% else %}exchange{% endif %}"></i>
|
|
||||||
<a href="{{ iface.get_absolute_url }}">{{ iface }}</a>
|
|
||||||
</span>
|
|
||||||
{% if iface.mac_address %}
|
|
||||||
<br/><small class="text-muted">{{ iface.mac_address }}</small>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{# LAG #}
|
|
||||||
<td>
|
|
||||||
{% if iface.lag %}
|
|
||||||
<a href="{{ iface.lag.device.get_absolute_url }}#interface_{{ iface.lag }}" class="label label-primary" title="{{ iface.lag.description }}">{{ iface.lag }}</a>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{# Description/tags #}
|
|
||||||
<td>
|
|
||||||
{% if iface.description %}
|
|
||||||
{{ iface.description }}<br/>
|
|
||||||
{% endif %}
|
|
||||||
{% for tag in iface.tags.all %}
|
|
||||||
{% tag tag %}
|
|
||||||
{% empty %}
|
|
||||||
{% if not iface.description %}—{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{# MTU #}
|
|
||||||
<td>{{ iface.mtu|default:"—" }}</td>
|
|
||||||
|
|
||||||
{# 802.1Q mode #}
|
|
||||||
<td>{{ iface.get_mode_display|default:"—" }}</td>
|
|
||||||
|
|
||||||
{# Cable #}
|
|
||||||
{% if iface.cable %}
|
|
||||||
<td>
|
|
||||||
<a href="{{ iface.cable.get_absolute_url }}">{{ iface.cable }}</a>
|
|
||||||
<a href="{% url 'dcim:interface_trace' pk=iface.pk %}" class="btn btn-primary btn-xs" title="Trace">
|
|
||||||
<i class="fa fa-share-alt" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% include 'dcim/inc/cabletermination.html' with termination=iface.get_cable_peer %}
|
|
||||||
{% else %}
|
|
||||||
<td colspan="3">
|
|
||||||
<span class="text-muted">Not connected</span>
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Connection or type #}
|
|
||||||
{% if iface.is_lag %}
|
|
||||||
<td colspan="2" class="text-muted">
|
|
||||||
LAG interface<br />
|
|
||||||
<small class="text-muted">
|
|
||||||
{% for member in iface.member_interfaces.all %}
|
|
||||||
<a href="{{ member.get_absolute_url }}">{{ member }}</a>{% if not forloop.last %}, {% endif %}
|
|
||||||
{% empty %}
|
|
||||||
No members
|
|
||||||
{% endfor %}
|
|
||||||
</small>
|
|
||||||
</td>
|
|
||||||
{% elif iface.is_virtual %}
|
|
||||||
<td colspan="2" class="text-muted">Virtual interface</td>
|
|
||||||
{% elif iface.is_wireless %}
|
|
||||||
<td colspan="2" class="text-muted">Wireless interface</td>
|
|
||||||
{% else %}
|
|
||||||
{% include 'dcim/inc/endpoint_connection.html' with path=iface.path %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Buttons #}
|
|
||||||
<td class="text-right text-nowrap noprint">
|
|
||||||
{% if perms.ipam.add_ipaddress %}
|
|
||||||
<a href="{% url 'ipam:ipaddress_add' %}?interface={{ iface.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-success" title="Add IP address">
|
|
||||||
<i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.dcim.change_interface %}
|
|
||||||
{% if iface.cable %}
|
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=iface.cable %}
|
|
||||||
{% elif iface.is_connectable and perms.dcim.add_cable %}
|
|
||||||
<span class="dropdown">
|
|
||||||
<button type="button" class="btn btn-success btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-resize-small" aria-hidden="true"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-right">
|
|
||||||
<li><a href="{% url 'dcim:interface_connect' termination_a_id=iface.pk termination_b_type='interface' %}?return_url={{ device.get_absolute_url }}">Interface</a></li>
|
|
||||||
<li><a href="{% url 'dcim:interface_connect' termination_a_id=iface.pk termination_b_type='front-port' %}?return_url={{ device.get_absolute_url }}">Front Port</a></li>
|
|
||||||
<li><a href="{% url 'dcim:interface_connect' termination_a_id=iface.pk termination_b_type='rear-port' %}?return_url={{ device.get_absolute_url }}">Rear Port</a></li>
|
|
||||||
<li><a href="{% url 'dcim:interface_connect' termination_a_id=iface.pk termination_b_type='circuit-termination' %}?return_url={{ device.get_absolute_url }}">Circuit Termination</a></li>
|
|
||||||
</ul>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
<a href="{% url 'dcim:interface_edit' pk=iface.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-info btn-xs" title="Edit interface">
|
|
||||||
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.dcim.delete_interface %}
|
|
||||||
{% if iface.connection or iface.circuit_termination %}
|
|
||||||
<button class="btn btn-danger btn-xs" disabled="disabled">
|
|
||||||
<i class="glyphicon glyphicon-trash" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
{% else %}
|
|
||||||
<a href="{% url 'dcim:interface_delete' pk=iface.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs" title="Delete interface">
|
|
||||||
<i class="glyphicon glyphicon-trash" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% with ipaddresses=iface.ip_addresses.all %}
|
|
||||||
{% if ipaddresses %}
|
|
||||||
<tr class="ipaddresses">
|
|
||||||
{# Placeholder #}
|
|
||||||
{% if perms.dcim.change_interface or perms.dcim.delete_interface %}
|
|
||||||
<td></td>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# IP addresses table #}
|
|
||||||
<td colspan="9" style="padding: 0">
|
|
||||||
<table class="table table-condensed interface-ips">
|
|
||||||
<thead>
|
|
||||||
<tr class="text-muted">
|
|
||||||
<th class="col-md-3">IP Address</th>
|
|
||||||
<th class="col-md-2">Status/Role</th>
|
|
||||||
<th class="col-md-3">VRF</th>
|
|
||||||
<th class="col-md-3">Description</th>
|
|
||||||
<th class="col-md-1"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{% for ip in iface.ip_addresses.all %}
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
{# IP address #}
|
|
||||||
<td>
|
|
||||||
<a href="{% url 'ipam:ipaddress' pk=ip.pk %}">{{ ip }}</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{# Primary/status/role #}
|
|
||||||
<td>
|
|
||||||
{% if device.primary_ip4 == ip or device.primary_ip6 == ip %}
|
|
||||||
<span class="label label-success">Primary</span>
|
|
||||||
{% endif %}
|
|
||||||
<span class="label label-{{ ip.get_status_class }}">{{ ip.get_status_display }}</span>
|
|
||||||
{% if ip.role %}
|
|
||||||
<span class="label label-{{ ip.get_role_class }}">{{ ip.get_role_display }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{# VRF #}
|
|
||||||
<td>
|
|
||||||
{% if ip.vrf %}
|
|
||||||
<a href="{% url 'ipam:vrf' pk=ip.vrf.pk %}" title="{{ ip.vrf.rd }}">{{ ip.vrf.name }}</a>
|
|
||||||
{% else %}
|
|
||||||
<span class="text-muted">Global</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{# Description #}
|
|
||||||
<td>
|
|
||||||
{% if ip.description %}
|
|
||||||
{{ ip.description }}
|
|
||||||
{% else %}
|
|
||||||
<span class="text-muted">—</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{# Buttons #}
|
|
||||||
<td class="text-right text-nowrap noprint">
|
|
||||||
{% if perms.ipam.change_ipaddress %}
|
|
||||||
<a href="{% url 'ipam:ipaddress_edit' pk=ip.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-info btn-xs">
|
|
||||||
<i class="glyphicon glyphicon-pencil" aria-hidden="true" title="Edit IP address"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.ipam.delete_ipaddress %}
|
|
||||||
<a href="{% url 'ipam:ipaddress_delete' pk=ip.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<i class="glyphicon glyphicon-trash" aria-hidden="true" title="Delete IP address"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
Loading…
Reference in New Issue
Block a user