From 35c2c8e8de9994f637cca5352a5173a3b72fbc9d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 9 Oct 2017 15:01:57 -0400 Subject: [PATCH] #1444: Added a serial number field to the rack model --- netbox/dcim/api/serializers.py | 8 +++--- netbox/dcim/filters.py | 3 ++- netbox/dcim/forms.py | 12 +++++---- netbox/dcim/migrations/0048_rack_serial.py | 20 +++++++++++++++ netbox/dcim/models.py | 4 ++- netbox/templates/dcim/rack.html | 29 +++++++++++++++++----- netbox/templates/dcim/rack_edit.html | 1 + 7 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 netbox/dcim/migrations/0048_rack_serial.py diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 46dfd7682..fa0f6dc14 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -142,8 +142,8 @@ class RackSerializer(CustomFieldModelSerializer): class Meta: model = Rack fields = [ - 'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height', - 'desc_units', 'comments', 'custom_fields', + 'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width', + 'u_height', 'desc_units', 'comments', 'custom_fields', ] @@ -160,8 +160,8 @@ class WritableRackSerializer(CustomFieldModelSerializer): class Meta: model = Rack fields = [ - 'id', 'name', 'facility_id', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height', 'desc_units', - 'comments', 'custom_fields', + 'id', 'name', 'facility_id', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width', 'u_height', + 'desc_units', 'comments', 'custom_fields', ] # Omit the UniqueTogetherValidator that would be automatically added to validate (site, facility_id). This # prevents facility_id from being interpreted as a required field. diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 50a4e0f98..6e0c6fa3d 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -159,7 +159,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet): class Meta: model = Rack - fields = ['type', 'width', 'u_height', 'desc_units'] + fields = ['serial', 'type', 'width', 'u_height', 'desc_units'] def search(self, queryset, name, value): if not value.strip(): @@ -167,6 +167,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet): return queryset.filter( Q(name__icontains=value) | Q(facility_id__icontains=value) | + Q(serial__icontains=value.strip()) | Q(comments__icontains=value) ) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 3c1c2e383..1fb156d2f 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -232,8 +232,8 @@ class RackForm(BootstrapMixin, TenancyForm, CustomFieldForm): class Meta: model = Rack fields = [ - 'site', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'role', 'type', 'width', 'u_height', - 'desc_units', 'comments', + 'site', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'role', 'serial', 'type', 'width', + 'u_height', 'desc_units', 'comments', ] help_texts = { 'site': "The site at which the rack exists", @@ -293,7 +293,8 @@ class RackCSVForm(forms.ModelForm): class Meta: model = Rack fields = [ - 'site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'width', 'u_height', 'desc_units', + 'site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'serial', 'type', 'width', 'u_height', + 'desc_units', ] help_texts = { 'name': 'Rack name', @@ -321,6 +322,7 @@ class RackBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): group = forms.ModelChoiceField(queryset=RackGroup.objects.all(), required=False, label='Group') tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False) role = forms.ModelChoiceField(queryset=RackRole.objects.all(), required=False) + serial = forms.CharField(max_length=50, required=False, label='Serial Number') type = forms.ChoiceField(choices=add_blank_choice(RACK_TYPE_CHOICES), required=False, label='Type') width = forms.ChoiceField(choices=add_blank_choice(RACK_WIDTH_CHOICES), required=False, label='Width') u_height = forms.IntegerField(required=False, label='Height (U)') @@ -328,7 +330,7 @@ class RackBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): comments = CommentField(widget=SmallTextarea) class Meta: - nullable_fields = ['group', 'tenant', 'role', 'comments'] + nullable_fields = ['group', 'tenant', 'role', 'serial', 'comments'] class RackFilterForm(BootstrapMixin, CustomFieldFilterForm): @@ -938,7 +940,7 @@ class DeviceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): serial = forms.CharField(max_length=50, required=False, label='Serial Number') class Meta: - nullable_fields = ['tenant', 'platform'] + nullable_fields = ['tenant', 'platform', 'serial'] def device_status_choices(): diff --git a/netbox/dcim/migrations/0048_rack_serial.py b/netbox/dcim/migrations/0048_rack_serial.py new file mode 100644 index 000000000..8e060c865 --- /dev/null +++ b/netbox/dcim/migrations/0048_rack_serial.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-10-09 18:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0047_more_100ge_form_factors'), + ] + + operations = [ + migrations.AddField( + model_name='rack', + name='serial', + field=models.CharField(blank=True, max_length=50, verbose_name='Serial number'), + ), + ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 860475ad5..9120f94e3 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -222,6 +222,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel): group = models.ForeignKey('RackGroup', related_name='racks', blank=True, null=True, on_delete=models.SET_NULL) tenant = models.ForeignKey(Tenant, blank=True, null=True, related_name='racks', on_delete=models.PROTECT) role = models.ForeignKey('RackRole', related_name='racks', blank=True, null=True, on_delete=models.PROTECT) + serial = models.CharField(max_length=50, blank=True, verbose_name='Serial number') type = models.PositiveSmallIntegerField(choices=RACK_TYPE_CHOICES, blank=True, null=True, verbose_name='Type') width = models.PositiveSmallIntegerField(choices=RACK_WIDTH_CHOICES, default=RACK_WIDTH_19IN, verbose_name='Width', help_text='Rail-to-rail width') @@ -236,7 +237,8 @@ class Rack(CreatedUpdatedModel, CustomFieldModel): objects = RackManager() csv_headers = [ - 'site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'width', 'u_height', 'desc_units', + 'site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'serial', 'width', 'u_height', + 'desc_units', ] class Meta: diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 2adb78e92..4463ee21b 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -112,6 +112,29 @@ {% endif %} + + Serial Number + + {% if rack.serial %} + {{ rack.serial }} + {% else %} + N/A + {% endif %} + + + + Devices + + {{ rack.devices.count }} + + + + +
+
+ Dimensions +
+ - - - -
Type @@ -130,12 +153,6 @@ Height {{ rack.u_height }}U ({% if rack.desc_units %}descending{% else %}ascending{% endif %})
Devices - {{ rack.devices.count }} -
{% with rack.get_custom_fields as custom_fields %} diff --git a/netbox/templates/dcim/rack_edit.html b/netbox/templates/dcim/rack_edit.html index d7a6f0dfb..4ab129a1d 100644 --- a/netbox/templates/dcim/rack_edit.html +++ b/netbox/templates/dcim/rack_edit.html @@ -10,6 +10,7 @@ {% render_field form.facility_id %} {% render_field form.group %} {% render_field form.role %} + {% render_field form.serial %}