Skip to content

Commit

Permalink
Closes #11738: Annotate utilization on VLAN groups (#13108)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
jeremystretch and abhi1693 authored Jul 6, 2023
1 parent 62bdb90 commit 7419a8e
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 19 deletions.
3 changes: 2 additions & 1 deletion netbox/ipam/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand Down
6 changes: 3 additions & 3 deletions netbox/ipam/api/views.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion netbox/ipam/models/vlans.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
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

Expand Down Expand Up @@ -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 = (
Expand Down
15 changes: 14 additions & 1 deletion netbox/ipam/querysets.py
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -54,6 +56,17 @@ def annotate_hierarchy(self):
)


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):
Expand Down
8 changes: 6 additions & 2 deletions netbox/ipam/tables/vlans.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
)
Expand All @@ -81,9 +85,9 @@ class Meta(NetBoxTable.Meta):
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')


#
Expand Down
17 changes: 6 additions & 11 deletions netbox/ipam/views.py
Original file line number Diff line number Diff line change
@@ -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 _
Expand Down Expand Up @@ -882,17 +883,15 @@ def get_children(self, request, parent):
#

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


@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 = (
Expand Down Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions netbox/templates/ipam/vlangroup.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ <h5 class="card-header">VLAN Group</h5>
<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>
Expand Down

0 comments on commit 7419a8e

Please sign in to comment.