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

7537 add serial number to virtual machines (#16407)

* 7537 add serial number to virtual machines

* 7537 add migration

* 7537 add sn to search

* 7537 add to model documentation

* 8984 move serializer field

* 8984 add to detail view and search index

* 7537 serial_number -> serial

* 7537 fix migration

* Add missing serial field

* Give serial field higher precedence for search

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Arthur Hanson 1 год назад
Родитель
Сommit
c6553c45dd

+ 5 - 0
docs/models/virtualization/virtualmachine.md

@@ -51,3 +51,8 @@ The amount of running memory provisioned, in megabytes.
 ### Disk
 
 The amount of disk storage provisioned, in gigabytes.
+
+### Serial Number
+
+Optional serial number assigned to this VM.
+

+ 4 - 0
netbox/templates/virtualization/virtualmachine.html

@@ -31,6 +31,10 @@
                   <th scope="row">{% trans "Description" %}</th>
                   <td>{{ object.description|placeholder }}</td>
                 </tr>
+                <tr>
+                  <th scope="row">{% trans "Serial Number" %}</th>
+                  <td>{{ object.serial|placeholder }}</td>
+                </tr>
                 <tr>
                     <th scope="row">{% trans "Tenant" %}</th>
                     <td>

+ 6 - 6
netbox/virtualization/api/serializers_/virtualmachines.py

@@ -49,9 +49,9 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
     class Meta:
         model = VirtualMachine
         fields = [
-            'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
-            'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
-            'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role', 'tenant',
+            'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description',
+            'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
             'interface_count', 'virtual_disk_count',
         ]
         brief_fields = ('id', 'url', 'display', 'name', 'description')
@@ -62,9 +62,9 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
 
     class Meta(VirtualMachineSerializer.Meta):
         fields = [
-            'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
-            'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
-            'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created',
+            'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role', 'tenant',
+            'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description',
+            'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created',
             'last_updated', 'interface_count', 'virtual_disk_count',
         ]
 

+ 6 - 2
netbox/virtualization/filtersets.py

@@ -240,7 +240,10 @@ class VirtualMachineFilterSet(
 
     class Meta:
         model = VirtualMachine
-        fields = ('id', 'cluster', 'vcpus', 'memory', 'disk', 'description', 'interface_count', 'virtual_disk_count')
+        fields = (
+            'id', 'cluster', 'vcpus', 'memory', 'disk', 'description', 'interface_count', 'virtual_disk_count',
+            'serial'
+        )
 
     def search(self, queryset, name, value):
         if not value.strip():
@@ -250,7 +253,8 @@ class VirtualMachineFilterSet(
             Q(description__icontains=value) |
             Q(comments__icontains=value) |
             Q(primary_ip4__address__startswith=value) |
-            Q(primary_ip6__address__startswith=value)
+            Q(primary_ip6__address__startswith=value) |
+            Q(serial__icontains=value)
         )
 
     def _has_primary_ip(self, queryset, name, value):

+ 1 - 1
netbox/virtualization/forms/bulk_import.py

@@ -137,7 +137,7 @@ class VirtualMachineImportForm(NetBoxModelImportForm):
         model = VirtualMachine
         fields = (
             'name', 'status', 'role', 'site', 'cluster', 'device', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
-            'description', 'config_template', 'comments', 'tags',
+            'description', 'serial', 'config_template', 'comments', 'tags',
         )
 
 

+ 5 - 1
netbox/virtualization/forms/filtersets.py

@@ -100,7 +100,7 @@ class VirtualMachineFilterForm(
         FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
         FieldSet(
             'status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'config_template_id',
-            'local_context_data', name=_('Attributes')
+            'local_context_data', 'serial', name=_('Attributes')
         ),
         FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
         FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -178,6 +178,10 @@ class VirtualMachineFilterForm(
             choices=BOOLEAN_WITH_BLANK_CHOICES
         )
     )
+    serial = forms.CharField(
+        required=False,
+        label=_('Serial number')
+    )
     config_template_id = DynamicModelMultipleChoiceField(
         queryset=ConfigTemplate.objects.all(),
         required=False,

+ 3 - 3
netbox/virtualization/forms/model_forms.py

@@ -217,7 +217,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
     comments = CommentField()
 
     fieldsets = (
-        FieldSet('name', 'role', 'status', 'description', 'tags', name=_('Virtual Machine')),
+        FieldSet('name', 'role', 'status', 'description', 'serial', 'tags', name=_('Virtual Machine')),
         FieldSet('site', 'cluster', 'device', name=_('Site/Cluster')),
         FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
         FieldSet('platform', 'primary_ip4', 'primary_ip6', 'config_template', name=_('Management')),
@@ -229,8 +229,8 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
         model = VirtualMachine
         fields = [
             'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4',
-            'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments', 'tags', 'local_context_data',
-            'config_template',
+            'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'serial', 'comments', 'tags',
+            'local_context_data', 'config_template',
         ]
 
     def __init__(self, *args, **kwargs):

+ 18 - 0
netbox/virtualization/migrations/0040_virtualmachine_serial_number.py

@@ -0,0 +1,18 @@
+# Generated by Django 5.0.6 on 2024-06-04 17:09
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('virtualization', '0039_convert_disk_size'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='virtualmachine',
+            name='serial',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+    ]

+ 5 - 0
netbox/virtualization/models/virtualmachines.py

@@ -127,6 +127,11 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
         null=True,
         verbose_name=_('disk (MB)')
     )
+    serial = models.CharField(
+        verbose_name=_('serial number'),
+        blank=True,
+        max_length=50
+    )
 
     # Counter fields
     interface_count = CounterCacheField(

+ 2 - 1
netbox/virtualization/search.py

@@ -39,11 +39,12 @@ class ClusterTypeIndex(SearchIndex):
 class VirtualMachineIndex(SearchIndex):
     model = models.VirtualMachine
     fields = (
+        ('serial', 60),
         ('name', 100),
         ('description', 500),
         ('comments', 5000),
     )
-    display_attrs = ('site', 'cluster', 'device', 'tenant', 'platform', 'status', 'role', 'description')
+    display_attrs = ('site', 'cluster', 'device', 'tenant', 'platform', 'status', 'serial', 'role', 'description')
 
 
 @register_search

+ 1 - 1
netbox/virtualization/tables/virtualmachines.py

@@ -116,7 +116,7 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
         fields = (
             'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'vcpus',
             'memory', 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'description', 'comments', 'config_template',
-            'contacts', 'tags', 'created', 'last_updated',
+            'serial', 'contacts', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',

+ 8 - 2
netbox/virtualization/tests/test_filtersets.py

@@ -327,7 +327,8 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests):
                 memory=1,
                 disk=1,
                 description='foobar1',
-                local_context_data={"foo": 123}
+                local_context_data={"foo": 123},
+                serial='111-aaa'
             ),
             VirtualMachine(
                 name='Virtual Machine 2',
@@ -341,7 +342,8 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests):
                 vcpus=2,
                 memory=2,
                 disk=2,
-                description='foobar2'
+                description='foobar2',
+                serial='222-bbb'
             ),
             VirtualMachine(
                 name='Virtual Machine 3',
@@ -518,6 +520,10 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests):
         params = {'primary_ip6_id': [addresses[2].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
 
+    def test_serial_number(self):
+        params = {'serial': ['111-aaa', '222-bbb']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
 
 class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
     queryset = VMInterface.objects.all()

+ 1 - 0
netbox/virtualization/tests/test_views.py

@@ -234,6 +234,7 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
             'vcpus': 4,
             'memory': 32768,
             'disk': 4000,
+            'serial': 'aaa-111',
             'comments': 'Some comments',
             'tags': [t.pk for t in tags],
             'local_context_data': None,