Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closes #10197: Add a cached counter field for virtual chassis members #13270

Merged
merged 1 commit into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion netbox/dcim/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,13 +1156,15 @@ def get_path(self, obj):
class VirtualChassisSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
master = NestedDeviceSerializer(required=False, allow_null=True, default=None)

# Counter fields
member_count = serializers.IntegerField(read_only=True)

class Meta:
model = VirtualChassis
fields = [
'id', 'url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags', 'custom_fields',
'member_count', 'created', 'last_updated',
'created', 'last_updated', 'member_count',
]


Expand Down
4 changes: 1 addition & 3 deletions netbox/dcim/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,9 +579,7 @@ class CableTerminationViewSet(NetBoxModelViewSet):
#

class VirtualChassisViewSet(NetBoxModelViewSet):
queryset = VirtualChassis.objects.prefetch_related('tags').annotate(
member_count=count_related(Device, 'virtual_chassis')
)
queryset = VirtualChassis.objects.prefetch_related('tags')
serializer_class = serializers.VirtualChassisSerializer
filterset_class = filtersets.VirtualChassisFilterSet
brief_prefetch_fields = ['master']
Expand Down
4 changes: 2 additions & 2 deletions netbox/dcim/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class DCIMConfig(AppConfig):

def ready(self):
from . import signals, search
from .models import CableTermination, Device
from .models import CableTermination, Device, VirtualChassis
from utilities.counters import connect_counters

# Register denormalized fields
Expand All @@ -27,4 +27,4 @@ def ready(self):
})

# Register counters
connect_counters(Device)
connect_counters(Device, VirtualChassis)
35 changes: 35 additions & 0 deletions netbox/dcim/migrations/0177_virtual_chassis_member_counter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.db import migrations
from django.db.models import Count

import utilities.fields


def populate_virtualchassis_members(apps, schema_editor):
VirtualChassis = apps.get_model('dcim', 'VirtualChassis')

vcs = list(VirtualChassis.objects.annotate(_member_count=Count('members', distinct=True)))

for vc in vcs:
vc.member_count = vc._member_count

VirtualChassis.objects.bulk_update(vcs, ['member_count'])


class Migration(migrations.Migration):
dependencies = [
('dcim', '0176_device_component_counters'),
]

operations = [
migrations.AddField(
model_name='virtualchassis',
name='member_count',
field=utilities.fields.CounterCacheField(
default=0, to_field='virtual_chassis', to_model='dcim.Device'
),
),
migrations.RunPython(
code=populate_virtualchassis_members,
reverse_code=migrations.RunPython.noop
),
]
9 changes: 8 additions & 1 deletion netbox/dcim/models/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from netbox.models import OrganizationalModel, PrimaryModel
from utilities.choices import ColorChoices
from utilities.fields import ColorField, CounterCacheField, NaturalOrderingField
from utilities.tracking import TrackingModelMixin
from .device_components import *
from .mixins import WeightMixin

Expand Down Expand Up @@ -469,7 +470,7 @@ def update_interface_bridges(device, interface_templates, module=None):
interface.save()


class Device(PrimaryModel, ConfigContextModel):
class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin):
"""
A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType,
DeviceRole, and (optionally) a Platform. Device names are not required, however if one is set it must be unique.
Expand Down Expand Up @@ -1206,6 +1207,12 @@ class VirtualChassis(PrimaryModel):
blank=True
)

# Counter fields
member_count = CounterCacheField(
to_model='dcim.Device',
to_field='virtual_chassis'
)

class Meta:
ordering = ['name']
verbose_name_plural = 'virtual chassis'
Expand Down
4 changes: 1 addition & 3 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3227,9 +3227,7 @@ def get_extra_context(self, request):
#

class VirtualChassisListView(generic.ObjectListView):
queryset = VirtualChassis.objects.annotate(
member_count=count_related(Device, 'virtual_chassis')
)
queryset = VirtualChassis.objects.all()
table = tables.VirtualChassisTable
filterset = filtersets.VirtualChassisFilterSet
filterset_form = forms.VirtualChassisFilterForm
Expand Down
10 changes: 10 additions & 0 deletions netbox/templates/dcim/virtualchassis.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ <h5 class="card-header">Virtual Chassis</h5>
<th scope="row">Description</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">Members</th>
<td>
{% if object.member_count %}
<a href="{% url 'dcim:device_list' %}?virtual_chassis_id={{ object.pk }}">{{ object.member_count }}</a>
{% else %}
{{ object.member_count }}
{% endif %}
</td>
</tr>
</table>
</div>
</div>
Expand Down