Implemented a view for interfaces

This commit is contained in:
Jeremy Stretch 2018-07-11 15:30:54 -04:00
parent 484a74defd
commit f048cf36ce
8 changed files with 366 additions and 14 deletions

View File

@ -1871,7 +1871,7 @@ class Interface(ComponentModel):
return self.name
def get_absolute_url(self):
return self.parent.get_absolute_url()
return reverse('dcim:interface', kwargs={'pk': self.pk})
def get_component_parent(self):
return self.device or self.virtual_machine
@ -1967,6 +1967,10 @@ class Interface(ComponentModel):
def parent(self):
return self.device or self.virtual_machine
@property
def is_connectable(self):
return self.form_factor not in NONCONNECTABLE_IFACE_TYPES
@property
def is_virtual(self):
return self.form_factor in VIRTUAL_IFACE_TYPES

View File

@ -199,6 +199,7 @@ urlpatterns = [
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
url(r'^devices/(?P<pk>\d+)/interface-connections/add/$', views.InterfaceConnectionAddView.as_view(), name='interfaceconnection_add'),
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.InterfaceConnectionDeleteView.as_view(), name='interfaceconnection_delete'),
url(r'^interfaces/(?P<pk>\d+)/$', views.InterfaceView.as_view(), name='interface'),
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
url(r'^interfaces/(?P<pk>\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'),
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),

View File

@ -21,6 +21,7 @@ from circuits.models import Circuit
from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
from extras.views import ObjectConfigContextView
from ipam.models import Prefix, Service, VLAN
from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator
from utilities.views import (
@ -1616,6 +1617,47 @@ class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Interfaces
#
class InterfaceView(View):
def get(self, request, pk):
interface = get_object_or_404(Interface, pk=pk)
# Get connected interface
connected_interface = interface.connected_interface
if connected_interface is None and hasattr(interface, 'circuit_termination'):
peer_termination = interface.circuit_termination.get_peer_termination()
if peer_termination is not None:
connected_interface = peer_termination.interface
# Get assigned IP addresses
ipaddress_table = InterfaceIPAddressTable(
data=interface.ip_addresses.select_related('vrf', 'tenant'),
orderable=False
)
# Get assigned VLANs and annotate whether each is tagged or untagged
vlans = []
if interface.untagged_vlan is not None:
vlans.append(interface.untagged_vlan)
vlans[0].tagged = False
for vlan in interface.tagged_vlans.select_related('site', 'group', 'tenant', 'role'):
vlan.tagged = True
vlans.append(vlan)
vlan_table = InterfaceVLANTable(
interface=interface,
data=vlans,
orderable=False
)
return render(request, 'dcim/interface.html', {
'interface': interface,
'connected_interface': connected_interface,
'ipaddress_table': ipaddress_table,
'vlan_table': vlan_table,
})
class InterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_interface'
parent_model = Device

View File

@ -342,6 +342,20 @@ class IPAddressAssignTable(BaseTable):
orderable = False
class InterfaceIPAddressTable(BaseTable):
"""
List IP addresses assigned to a specific Interface.
"""
address = tables.TemplateColumn(IPADDRESS_ASSIGN_LINK, verbose_name='IP Address')
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
status = tables.TemplateColumn(STATUS_LABEL)
tenant = tables.TemplateColumn(template_code=TENANT_LINK)
class Meta(BaseTable.Meta):
model = IPAddress
fields = ('address', 'vrf', 'status', 'role', 'tenant', 'description')
#
# VLAN groups
#
@ -403,6 +417,27 @@ class VLANMemberTable(BaseTable):
fields = ('parent', 'name', 'untagged', 'actions')
class InterfaceVLANTable(BaseTable):
"""
List VLANs assigned to a specific Interface.
"""
vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
tagged = BooleanColumn()
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
tenant = tables.TemplateColumn(template_code=COL_TENANT)
status = tables.TemplateColumn(STATUS_LABEL)
role = tables.TemplateColumn(VLAN_ROLE_LINK)
class Meta(BaseTable.Meta):
model = VLAN
fields = ('vid', 'tagged', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description')
def __init__(self, interface, *args, **kwargs):
self.interface = interface
super(InterfaceVLANTable, self).__init__(*args, **kwargs)
#
# Services
#

View File

@ -522,8 +522,7 @@
<th>Name</th>
<th>LAG</th>
<th>Description</th>
<th>MTU</th>
<th>MAC Address</th>
<th>Mode</th>
<th colspan="2">Connection</th>
<th></th>
</tr>

View File

@ -11,7 +11,7 @@
<td>
<span title="{{ iface.get_form_factor_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>
{{ iface }}
<a href="{{ iface.get_absolute_url }}">{{ iface }}</a>
</span>
</td>
@ -23,13 +23,10 @@
</td>
{# Description #}
<td>{{ iface.description }}</td>
<td>{{ iface.description|default:"&mdash;" }}</td>
{# MTU #}
<td>{{ iface.mtu|default:"" }}</td>
{# MAC address #}
<td>{{ iface.mac_address|default:"" }}</td>
{# 802.1Q mode #}
<td>{{ iface.get_mode_display }}</td>
{# Connection or type #}
{% if iface.is_lag %}
@ -141,7 +138,7 @@
{% endif %}
{# IP addresses table #}
<td colspan="8" style="padding: 0">
<td colspan="7" style="padding: 0">
<table class="table table-condensed interface-ips">
<thead>
<tr class="text-muted">

View File

@ -0,0 +1,275 @@
{% extends '_base.html' %}
{% load helpers %}
{% block header %}
<div class="row">
<div class="col-md-12">
<ol class="breadcrumb">
{% if interface.device %}
<li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
{% else %}
<li><a href="{% url 'virtualization:virtualmachine_list' %}">Virtual Machines</a></li>
{% endif %}
<li><a href="{{ interface.parent.get_absolute_url }}">{{ interface.parent }}</a></li>
<li>{{ interface }}</li>
</ol>
</div>
</div>
<div class="pull-right">
{% if perms.dcim.change_interface %}
<a href="{% url 'dcim:interface_edit' pk=interface.pk %}" class="btn btn-warning">
<span class="fa fa-pencil" aria-hidden="true"></span> Edit this interface
</a>
{% endif %}
{% if perms.dcim.delete_interface %}
<a href="{% url 'dcim:interface_delete' pk=interface.pk %}" class="btn btn-danger">
<span class="fa fa-trash" aria-hidden="true"></span> Delete this interface
</a>
{% endif %}
</div>
<h1>{% block title %}{{ interface.parent }} / {{ interface.name }}{% endblock %}</h1>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Interface</strong>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>{% if interface.device %}Device{% else %}Virtual Machine{% endif %}</td>
<td>
<a href="{{ interface.parent.get_absolute_url }}">{{ interface.parent }}</a>
</td>
</tr>
<tr>
<td>Name</td>
<td>{{ interface.name }}</td>
</tr>
<tr>
<td>Type</td>
<td>{{ interface.get_form_factor_display }}</td>
</tr>
<tr>
<td>Enabled</td>
<td>
{% if interface.enabled %}
<span class="text-success"><i class="fa fa-check"></i></span>
{% else %}
<span class="text-danger"><i class="fa fa-close"></i></span>
{% endif %}
</td>
</tr>
<tr>
<td>LAG</td>
<td>
{% if interface.lag%}
<a href="{{ interface.lag.get_absolute_url }}">{{ interface.lag }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<td>Description</td>
<td>
{% if interface.description %}
<span>{{ interface.description }}</span>
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>MTU</td>
<td>
{% if interface.mtu %}
<span>{{ interface.mtu }}</span>
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>MAC Address</td>
<td>
{% if interface.mac_address %}
<span>{{ interface.mac_address }}</span>
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>802.1Q Mode</td>
<td>{{ interface.get_mode_display }}</td>
</tr>
</table>
</div>
{% include 'extras/inc/tags_panel.html' with tags=interface.tags.all %}
</div>
<div class="col-md-6">
{% if interface.is_connectable %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Connected Interface</strong>
</div>
{% if connected_interface %}
<table class="table table-hover panel-body attr-table">
<tr>
<td>{% if connected_interface.device %}Device{% else %}Virtual Machine{% endif %}</td>
<td>
<a href="{{ connected_interface.parent.get_absolute_url }}">{{ connected_interface.parent }}</a>
</td>
</tr>
<tr>
<td>Name</td>
<td>{{ connected_interface.name }}</td>
</tr>
<tr>
<td>Type</td>
<td>{{ connected_interface.get_form_factor_display }}</td>
</tr>
<tr>
<td>Enabled</td>
<td>
{% if connected_interface.enabled %}
<span class="text-success"><i class="fa fa-check"></i></span>
{% else %}
<span class="text-danger"><i class="fa fa-close"></i></span>
{% endif %}
</td>
</tr>
<tr>
<td>LAG</td>
<td>
{% if connected_interface.lag%}
<a href="{{ connected_interface.lag.get_absolute_url }}">{{ connected_interface.lag }}</a>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</td>
</tr>
<tr>
<td>Description</td>
<td>
{% if connected_interface.description %}
<span>{{ connected_interface.description }}</span>
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>MTU</td>
<td>
{% if connected_interface.mtu %}
<span>{{ connected_interface.mtu }}</span>
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>MAC Address</td>
<td>
{% if connected_interface.mac_address %}
<span>{{ connected_interface.mac_address }}</span>
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>802.1Q Mode</td>
<td>{{ connected_interface.get_mode_display }}</td>
</tr>
{% if interface.connection %}
<tr>
<td>Connection Status</td>
<td>
{% if interface.connection.connection_status %}
<span class="label label-success">{{ interface.connection.get_connection_status_display }}</span>
{% else %}
<span class="label label-info">{{ interface.connection.get_connection_status_display }}</span>
{% endif %}
</td>
</tr>
{% endif %}
</table>
{% else %}
<div class="panel-body text-muted">
No connected interface
</div>
{% endif %}
</div>
<div class="panel panel-default">
<div class="panel-heading">
<strong>Circuit Termination</strong>
</div>
<table class="table table-hover panel-body">
{% if interface.circuit_termination %}
<tr>
<td>Circuit</td>
<td><a href="{{ interface.circuit_termination.circuit.get_absolute_url }}">{{ interface.circuit_termination.circuit }}</a></td>
</tr>
<tr>
<td>Side</td>
<td>{{ interface.circuit_termination.term_side }}</td>
</tr>
{% else %}
<tr>
<td colspan="2" class="text-muted">None</td>
</tr>
{% endif %}
</table>
</div>
{% endif %}
{% if interface.is_lag %}
<div class="panel panel-default">
<div class="panel-heading"><strong>LAG Members</strong></div>
<table class="table table-hover table-headings panel-body">
<thead>
<tr>
<th>Parent</th>
<th>Interface</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{% for member in interface.member_interfaces.all %}
<tr>
<td>
<a href="{{ member.parent.get_absolute_url }}">{{ member.parent }}</a>
</td>
<td>
<a href="{{ member.get_absolute_url }}">{{ member }}</a>
</td>
<td>
{{ member.get_form_factor_display }}
</td>
</tr>
{% empty %}
<tr>
<td colspan="3" class="text-muted">No member interfaces</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-12">
{% include 'panel_table.html' with table=ipaddress_table heading="IP Addresses" %}
</div>
</div>
<div class="row">
<div class="col-md-12">
{% include 'panel_table.html' with table=vlan_table heading="VLANs" %}
</div>
</div>
{% endblock %}

View File

@ -261,8 +261,7 @@
<th>Name</th>
<th>LAG</th>
<th>Description</th>
<th>MTU</th>
<th>MAC Address</th>
<th>Mode</th>
<th colspan="2">Connection</th>
<th></th>
</tr>
@ -272,7 +271,7 @@
{% include 'dcim/inc/interface.html' with device=virtualmachine %}
{% empty %}
<tr>
<td colspan="9" class="text-center text-muted">&mdash; No interfaces defined &mdash;</td>
<td colspan="8" class="text-center text-muted">&mdash; No interfaces defined &mdash;</td>
</tr>
{% endfor %}
</tbody>