Parcourir la source

Closes #1945: Implemented a VLAN members view

Jeremy Stretch il y a 7 ans
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.exceptions import ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
 from django.db import models
+from django.db.models import Q
 from django.db.models.expressions import RawSQL
 from django.db.models.expressions import RawSQL
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.encoding import python_2_unicode_compatible
 from django.utils.encoding import python_2_unicode_compatible
@@ -616,6 +617,13 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
     def get_status_class(self):
     def get_status_class(self):
         return STATUS_CHOICE_CLASSES[self.status]
         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
 @python_2_unicode_compatible
 class Service(CreatedUpdatedModel):
 class Service(CreatedUpdatedModel):

+ 31 - 0
netbox/ipam/tables.py

@@ -3,6 +3,7 @@ from __future__ import unicode_literals
 import django_tables2 as tables
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 from django_tables2.utils import Accessor
 
 
+from dcim.models import Interface
 from tenancy.tables import COL_TENANT
 from tenancy.tables import COL_TENANT
 from utilities.tables import BaseTable, ToggleColumn
 from utilities.tables import BaseTable, ToggleColumn
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF
@@ -138,6 +139,18 @@ VLANGROUP_ACTIONS = """
 {% endif %}
 {% 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 = """
 TENANT_LINK = """
 {% if record.tenant %}
 {% if record.tenant %}
     <a href="{% url 'tenancy:tenant' slug=record.tenant.slug %}" title="{{ record.tenant.description }}">{{ record.tenant }}</a>
     <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):
     class Meta(VLANTable.Meta):
         fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
         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/edit/$', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
     url(r'^vlans/delete/$', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
     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+)/$', 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+)/edit/$', views.VLANEditView.as_view(), name='vlan_edit'),
     url(r'^vlans/(?P<pk>\d+)/delete/$', views.VLANDeleteView.as_view(), name='vlan_delete'),
     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):
 class VLANCreateView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.add_vlan'
     permission_required = 'ipam.add_vlan'
     model = 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' %}
 {% extends '_base.html' %}
 
 
 {% block content %}
 {% 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="row">
 	<div class="col-md-6">
 	<div class="col-md-6">
         <div class="panel panel-default">
         <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 %}