Browse Source

Closes #1945: Implemented a VLAN members view

Jeremy Stretch 7 years ago
parent
commit
38a208242b

+ 8 - 0
netbox/ipam/models.py

@@ -6,6 +6,7 @@ from django.contrib.contenttypes.fields import GenericRelation
 from django.core.exceptions import ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
+from django.db.models import Q
 from django.db.models.expressions import RawSQL
 from django.urls import reverse
 from django.utils.encoding import python_2_unicode_compatible
@@ -616,6 +617,13 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
     def get_status_class(self):
         return STATUS_CHOICE_CLASSES[self.status]
 
+    def get_members(self):
+        # Return all interfaces assigned to this VLAN
+        return Interface.objects.filter(
+            Q(untagged_vlan_id=self.pk) |
+            Q(tagged_vlans=self.pk)
+        )
+
 
 @python_2_unicode_compatible
 class Service(CreatedUpdatedModel):

+ 31 - 0
netbox/ipam/tables.py

@@ -3,6 +3,7 @@ from __future__ import unicode_literals
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 
+from dcim.models import Interface
 from tenancy.tables import COL_TENANT
 from utilities.tables import BaseTable, ToggleColumn
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF
@@ -138,6 +139,18 @@ VLANGROUP_ACTIONS = """
 {% endif %}
 """
 
+VLAN_MEMBER_UNTAGGED = """
+{% if record.untagged_vlan_id == vlan.pk %}
+    <i class="glyphicon glyphicon-ok">
+{% endif %}
+"""
+
+VLAN_MEMBER_ACTIONS = """
+{% if perms.dcim.change_interface %}
+    <a href="{% if record.device %}{% url 'dcim:interface_edit' pk=record.pk %}{% else %}{% url 'virtualization:interface_edit' pk=record.pk %}{% endif %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil"></i></a>
+{% endif %}
+"""
+
 TENANT_LINK = """
 {% if record.tenant %}
     <a href="{% url 'tenancy:tenant' slug=record.tenant.slug %}" title="{{ record.tenant.description }}">{{ record.tenant }}</a>
@@ -361,3 +374,21 @@ class VLANDetailTable(VLANTable):
 
     class Meta(VLANTable.Meta):
         fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
+
+
+class VLANMemberTable(BaseTable):
+    parent = tables.LinkColumn(order_by=['device', 'virtual_machine'])
+    name = tables.Column(verbose_name='Interface')
+    untagged = tables.TemplateColumn(
+        template_code=VLAN_MEMBER_UNTAGGED,
+        orderable=False
+    )
+    actions = tables.TemplateColumn(
+        template_code=VLAN_MEMBER_ACTIONS,
+        attrs={'td': {'class': 'text-right'}},
+        verbose_name=''
+    )
+
+    class Meta(BaseTable.Meta):
+        model = Interface
+        fields = ('parent', 'name', 'untagged', 'actions')

+ 1 - 0
netbox/ipam/urls.py

@@ -80,6 +80,7 @@ urlpatterns = [
     url(r'^vlans/edit/$', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
     url(r'^vlans/delete/$', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
     url(r'^vlans/(?P<pk>\d+)/$', views.VLANView.as_view(), name='vlan'),
+    url(r'^vlans/(?P<pk>\d+)/members/$', views.VLANMembersView.as_view(), name='vlan_members'),
     url(r'^vlans/(?P<pk>\d+)/edit/$', views.VLANEditView.as_view(), name='vlan_edit'),
     url(r'^vlans/(?P<pk>\d+)/delete/$', views.VLANDeleteView.as_view(), name='vlan_delete'),
 

+ 32 - 0
netbox/ipam/views.py

@@ -851,6 +851,38 @@ class VLANView(View):
         })
 
 
+class VLANMembersView(View):
+
+    def get(self, request, pk):
+
+        vlan = get_object_or_404(VLAN.objects.all(), pk=pk)
+        members = vlan.get_members().select_related('device', 'virtual_machine')
+
+        members_table = tables.VLANMemberTable(members)
+        # if request.user.has_perm('dcim.change_interface'):
+        #     members_table.columns.show('pk')
+
+        paginate = {
+            'klass': EnhancedPaginator,
+            'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
+        }
+        RequestConfig(request, paginate).configure(members_table)
+
+        # Compile permissions list for rendering the object table
+        # permissions = {
+        #     'add': request.user.has_perm('ipam.add_ipaddress'),
+        #     'change': request.user.has_perm('ipam.change_ipaddress'),
+        #     'delete': request.user.has_perm('ipam.delete_ipaddress'),
+        # }
+
+        return render(request, 'ipam/vlan_members.html', {
+            'vlan': vlan,
+            'members_table': members_table,
+            # 'permissions': permissions,
+            # 'bulk_querystring': 'vrf_id={}&parent={}'.format(prefix.vrf.pk if prefix.vrf else '0', prefix.prefix),
+        })
+
+
 class VLANCreateView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.add_vlan'
     model = VLAN

+ 46 - 0
netbox/templates/ipam/inc/vlan_header.html

@@ -0,0 +1,46 @@
+<div class="row">
+    <div class="col-sm-8 col-md-9">
+        <ol class="breadcrumb">
+            <li><a href="{% url 'ipam:vlan_list' %}">VLANs</a></li>
+            {% if vlan.site %}
+                <li><a href="{% url 'ipam:vlan_list' %}?site={{ vlan.site.slug }}">{{ vlan.site }}</a></li>
+            {% endif %}
+            {% if vlan.group %}
+                <li><a href="{% url 'ipam:vlan_list' %}?group={{ vlan.group.slug }}">{{ vlan.group }}</a></li>
+            {% endif %}
+            <li>{{ vlan }}</li>
+        </ol>
+    </div>
+    <div class="col-sm-4 col-md-3">
+    <form action="{% url 'ipam:vlan_list' %}" method="get">
+        <div class="input-group">
+            <input type="text" name="q" class="form-control" placeholder="Search VLANs" />
+            <span class="input-group-btn">
+                <button type="submit" class="btn btn-primary">
+                    <span class="fa fa-search" aria-hidden="true"></span>
+                </button>
+            </span>
+        </div>
+    </form>
+    </div>
+</div>
+<div class="pull-right">
+    {% if perms.ipam.change_vlan %}
+        <a href="{% url 'ipam:vlan_edit' pk=vlan.pk %}" class="btn btn-warning">
+            <span class="fa fa-pencil" aria-hidden="true"></span>
+            Edit this VLAN
+        </a>
+    {% endif %}
+    {% if perms.ipam.delete_vlan %}
+        <a href="{% url 'ipam:vlan_delete' pk=vlan.pk %}" class="btn btn-danger">
+            <span class="fa fa-trash" aria-hidden="true"></span>
+            Delete this VLAN
+        </a>
+    {% endif %}
+</div>
+<h1>{% block title %}VLAN {{ vlan.display_name }}{% endblock %}</h1>
+{% include 'inc/created_updated.html' with obj=vlan %}
+<ul class="nav nav-tabs" style="margin-bottom: 20px">
+    <li role="presentation"{% if active_tab == 'vlan' %} class="active"{% endif %}><a href="{% url 'ipam:vlan' pk=vlan.pk %}">VLAN</a></li>
+    <li role="presentation"{% if active_tab == 'members' %} class="active"{% endif %}><a href="{% url 'ipam:vlan_members' pk=vlan.pk %}">Members <span class="badge">{{ vlan.get_members.count }}</span></a></li>
+</ul>

+ 1 - 42
netbox/templates/ipam/vlan.html

@@ -1,48 +1,7 @@
 {% extends '_base.html' %}
 
 {% block content %}
-<div class="row">
-    <div class="col-sm-8 col-md-9">
-        <ol class="breadcrumb">
-            <li><a href="{% url 'ipam:vlan_list' %}">VLANs</a></li>
-            {% if vlan.site %}
-                <li><a href="{% url 'ipam:vlan_list' %}?site={{ vlan.site.slug }}">{{ vlan.site }}</a></li>
-            {% endif %}
-            {% if vlan.group %}
-                <li><a href="{% url 'ipam:vlan_list' %}?group={{ vlan.group.slug }}">{{ vlan.group }}</a></li>
-            {% endif %}
-            <li>{{ vlan }}</li>
-        </ol>
-    </div>
-    <div class="col-sm-4 col-md-3">
-    <form action="{% url 'ipam:vlan_list' %}" method="get">
-        <div class="input-group">
-            <input type="text" name="q" class="form-control" placeholder="Search VLANs" />
-            <span class="input-group-btn">
-                <button type="submit" class="btn btn-primary">
-                    <span class="fa fa-search" aria-hidden="true"></span>
-                </button>
-            </span>
-        </div>
-    </form>
-    </div>
-</div>
-<div class="pull-right">
-    {% if perms.ipam.change_vlan %}
-        <a href="{% url 'ipam:vlan_edit' pk=vlan.pk %}" class="btn btn-warning">
-            <span class="fa fa-pencil" aria-hidden="true"></span>
-            Edit this VLAN
-        </a>
-    {% endif %}
-    {% if perms.ipam.delete_vlan %}
-        <a href="{% url 'ipam:vlan_delete' pk=vlan.pk %}" class="btn btn-danger">
-            <span class="fa fa-trash" aria-hidden="true"></span>
-            Delete this VLAN
-        </a>
-    {% endif %}
-</div>
-<h1>{% block title %}VLAN {{ vlan.display_name }}{% endblock %}</h1>
-{% include 'inc/created_updated.html' with obj=vlan %}
+{% include 'ipam/inc/vlan_header.html' with active_tab='vlan' %}
 <div class="row">
 	<div class="col-md-6">
         <div class="panel panel-default">

+ 12 - 0
netbox/templates/ipam/vlan_members.html

@@ -0,0 +1,12 @@
+{% extends '_base.html' %}
+
+{% block title %}{{ vlan }} - Members{% endblock %}
+
+{% block content %}
+    {% include 'ipam/inc/vlan_header.html' with active_tab='members' %}
+    <div class="row">
+        <div class="col-md-12">
+            {% include 'utilities/obj_table.html' with table=members_table table_template='panel_table.html' heading='VLAN Members' parent=vlan %}
+        </div>
+    </div>
+{% endblock %}