Procházet zdrojové kódy

Implemented a view for interfaces

Jeremy Stretch před 7 roky
rodič
revize
f048cf36ce

+ 5 - 1
netbox/dcim/models.py

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

+ 1 - 0
netbox/dcim/urls.py

@@ -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+)/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'^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'^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+)/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+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'),
     url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
     url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),

+ 42 - 0
netbox/dcim/views.py

@@ -21,6 +21,7 @@ from circuits.models import Circuit
 from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
 from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
 from extras.views import ObjectConfigContextView
 from extras.views import ObjectConfigContextView
 from ipam.models import Prefix, Service, VLAN
 from ipam.models import Prefix, Service, VLAN
+from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
 from utilities.forms import ConfirmationForm
 from utilities.forms import ConfirmationForm
 from utilities.paginator import EnhancedPaginator
 from utilities.paginator import EnhancedPaginator
 from utilities.views import (
 from utilities.views import (
@@ -1616,6 +1617,47 @@ class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
 # Interfaces
 # 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):
 class InterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
     permission_required = 'dcim.add_interface'
     permission_required = 'dcim.add_interface'
     parent_model = Device
     parent_model = Device

+ 35 - 0
netbox/ipam/tables.py

@@ -342,6 +342,20 @@ class IPAddressAssignTable(BaseTable):
         orderable = False
         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
 # VLAN groups
 #
 #
@@ -403,6 +417,27 @@ class VLANMemberTable(BaseTable):
         fields = ('parent', 'name', 'untagged', 'actions')
         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
 # Services
 #
 #

+ 1 - 2
netbox/templates/dcim/device.html

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

+ 5 - 8
netbox/templates/dcim/inc/interface.html

@@ -11,7 +11,7 @@
     <td>
     <td>
         <span title="{{ iface.get_form_factor_display }}">
         <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>
             <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>
         </span>
     </td>
     </td>
 
 
@@ -23,13 +23,10 @@
     </td>
     </td>
 
 
     {# Description #}
     {# 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 #}
     {# Connection or type #}
     {% if iface.is_lag %}
     {% if iface.is_lag %}
@@ -141,7 +138,7 @@
             {% endif %}
             {% endif %}
 
 
             {# IP addresses table #}
             {# IP addresses table #}
-            <td colspan="8" style="padding: 0">
+            <td colspan="7" style="padding: 0">
                 <table class="table table-condensed interface-ips">
                 <table class="table table-condensed interface-ips">
                     <thead>
                     <thead>
                         <tr class="text-muted">
                         <tr class="text-muted">

+ 275 - 0
netbox/templates/dcim/interface.html

@@ -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 %}

+ 2 - 3
netbox/templates/virtualization/virtualmachine.html

@@ -261,8 +261,7 @@
                         <th>Name</th>
                         <th>Name</th>
                         <th>LAG</th>
                         <th>LAG</th>
                         <th>Description</th>
                         <th>Description</th>
-                        <th>MTU</th>
-                        <th>MAC Address</th>
+                        <th>Mode</th>
                         <th colspan="2">Connection</th>
                         <th colspan="2">Connection</th>
                         <th></th>
                         <th></th>
                     </tr>
                     </tr>
@@ -272,7 +271,7 @@
                         {% include 'dcim/inc/interface.html' with device=virtualmachine %}
                         {% include 'dcim/inc/interface.html' with device=virtualmachine %}
                     {% empty %}
                     {% empty %}
                         <tr>
                         <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>
                         </tr>
                     {% endfor %}
                     {% endfor %}
                 </tbody>
                 </tbody>