Просмотр исходного кода

Closes #11738: Annotate utilization on VLAN groups (#13108)

* Update serializers.py

* Update vlans.py

* Update vlans.py

* Update vlangroup.html

* Update vlans.py

* Update vlans.py

* Update serializers.py

* adds db annotation to calculate utilization

* optimize queries

* merge fix

* adds round function for utilization to limit decimal

* fixed object view annotation

* consolidated queryset for utilization

* lint fixes

* Renamed manager method to annotate_utilization() for consistency with other managers

---------

Co-authored-by: Abhimanyu Saharan <desk.abhimanyu@gmail.com>
Jeremy Stretch 2 лет назад
Родитель
Сommit
7419a8e112

+ 2 - 1
netbox/ipam/api/serializers.py

@@ -218,12 +218,13 @@ class VLANGroupSerializer(NetBoxModelSerializer):
     scope_id = serializers.IntegerField(allow_null=True, required=False, default=None)
     scope = serializers.SerializerMethodField(read_only=True)
     vlan_count = serializers.IntegerField(read_only=True)
+    utilization = serializers.CharField(read_only=True)
 
     class Meta:
         model = VLANGroup
         fields = [
             'id', 'url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'min_vid', 'max_vid',
-            'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count',
+            'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization'
         ]
         validators = []
 

+ 3 - 3
netbox/ipam/api/views.py

@@ -1,5 +1,7 @@
 from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
 from django.db import transaction
+from django.db.models import F
+from django.db.models.functions import Round
 from django.shortcuts import get_object_or_404
 from django_pglocks import advisory_lock
 from drf_spectacular.utils import extend_schema
@@ -145,9 +147,7 @@ class FHRPGroupAssignmentViewSet(NetBoxModelViewSet):
 
 
 class VLANGroupViewSet(NetBoxModelViewSet):
-    queryset = VLANGroup.objects.annotate(
-        vlan_count=count_related(VLAN, 'group')
-    ).prefetch_related('tags')
+    queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     serializer_class = serializers.VLANGroupSerializer
     filterset_class = filtersets.VLANGroupFilterSet
 

+ 3 - 1
netbox/ipam/models/vlans.py

@@ -9,7 +9,7 @@ from django.utils.translation import gettext as _
 from dcim.models import Interface
 from ipam.choices import *
 from ipam.constants import *
-from ipam.querysets import VLANQuerySet
+from ipam.querysets import VLANQuerySet, VLANGroupQuerySet
 from netbox.models import OrganizationalModel, PrimaryModel
 from virtualization.models import VMInterface
 
@@ -63,6 +63,8 @@ class VLANGroup(OrganizationalModel):
         help_text=_('Highest permissible ID of a child VLAN')
     )
 
+    objects = VLANGroupQuerySet.as_manager()
+
     class Meta:
         ordering = ('name', 'pk')  # Name may be non-unique
         constraints = (

+ 14 - 1
netbox/ipam/querysets.py

@@ -1,8 +1,10 @@
 from django.contrib.contenttypes.models import ContentType
-from django.db.models import Count, OuterRef, Q, Subquery, Value
+from django.db.models import Count, F, OuterRef, Q, Subquery, Value
 from django.db.models.expressions import RawSQL
+from django.db.models.functions import Round
 
 from utilities.querysets import RestrictedQuerySet
+from utilities.utils import count_related
 
 __all__ = (
     'ASNRangeQuerySet',
@@ -54,6 +56,17 @@ class PrefixQuerySet(RestrictedQuerySet):
         )
 
 
+class VLANGroupQuerySet(RestrictedQuerySet):
+
+    def annotate_utilization(self):
+        from .models import VLAN
+
+        return self.annotate(
+            vlan_count=count_related(VLAN, 'group'),
+            utilization=Round(F('vlan_count') / (F('max_vid') - F('min_vid') + 1.0) * 100, 2)
+        )
+
+
 class VLANQuerySet(RestrictedQuerySet):
 
     def get_for_device(self, device):

+ 6 - 2
netbox/ipam/tables/vlans.py

@@ -70,6 +70,10 @@ class VLANGroupTable(NetBoxTable):
         url_params={'group_id': 'pk'},
         verbose_name='VLANs'
     )
+    utilization = columns.UtilizationColumn(
+        orderable=False,
+        verbose_name='Utilization'
+    )
     tags = columns.TagColumn(
         url_name='ipam:vlangroup_list'
     )
@@ -81,9 +85,9 @@ class VLANGroupTable(NetBoxTable):
         model = VLANGroup
         fields = (
             'pk', 'id', 'name', 'scope_type', 'scope', 'min_vid', 'max_vid', 'vlan_count', 'slug', 'description',
-            'tags', 'created', 'last_updated', 'actions',
+            'tags', 'created', 'last_updated', 'actions', 'utilization',
         )
-        default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'description')
+        default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'utilization', 'description')
 
 
 #

+ 6 - 11
netbox/ipam/views.py

@@ -1,6 +1,7 @@
 from django.contrib.contenttypes.models import ContentType
-from django.db.models import Prefetch
+from django.db.models import F, Prefetch
 from django.db.models.expressions import RawSQL
+from django.db.models.functions import Round
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
 from django.utils.translation import gettext as _
@@ -882,9 +883,7 @@ class IPAddressRelatedIPsView(generic.ObjectChildrenView):
 #
 
 class VLANGroupListView(generic.ObjectListView):
-    queryset = VLANGroup.objects.annotate(
-        vlan_count=count_related(VLAN, 'group')
-    )
+    queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     filterset = filtersets.VLANGroupFilterSet
     filterset_form = forms.VLANGroupFilterForm
     table = tables.VLANGroupTable
@@ -892,7 +891,7 @@ class VLANGroupListView(generic.ObjectListView):
 
 @register_model_view(VLANGroup)
 class VLANGroupView(generic.ObjectView):
-    queryset = VLANGroup.objects.all()
+    queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
 
     def get_extra_context(self, request, instance):
         related_models = (
@@ -934,18 +933,14 @@ class VLANGroupBulkImportView(generic.BulkImportView):
 
 
 class VLANGroupBulkEditView(generic.BulkEditView):
-    queryset = VLANGroup.objects.annotate(
-        vlan_count=count_related(VLAN, 'group')
-    )
+    queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     filterset = filtersets.VLANGroupFilterSet
     table = tables.VLANGroupTable
     form = forms.VLANGroupBulkEditForm
 
 
 class VLANGroupBulkDeleteView(generic.BulkDeleteView):
-    queryset = VLANGroup.objects.annotate(
-        vlan_count=count_related(VLAN, 'group')
-    )
+    queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     filterset = filtersets.VLANGroupFilterSet
     table = tables.VLANGroupTable
 

+ 4 - 0
netbox/templates/ipam/vlangroup.html

@@ -42,6 +42,10 @@
             <th scope="row">Permitted VIDs</th>
             <td>{{ object.min_vid }} - {{ object.max_vid }}</td>
           </tr>
+          <tr>
+            <th scope="row">Utilization</th>
+            <td>{% utilization_graph object.utilization %}</td>
+          </tr>
         </table>
       </div>
     </div>