diff --git a/docs/models/virtualization/virtualmachine.md b/docs/models/virtualization/virtualmachine.md index 769d49154e..7a801ca656 100644 --- a/docs/models/virtualization/virtualmachine.md +++ b/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. + diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html index d2603ddd2f..37cd11836c 100644 --- a/netbox/templates/virtualization/virtualmachine.html +++ b/netbox/templates/virtualization/virtualmachine.html @@ -31,6 +31,10 @@
{% trans "Virtual Machine" %}
{% trans "Description" %} {{ object.description|placeholder }} + + {% trans "Serial Number" %} + {{ object.serial|placeholder }} + {% trans "Tenant" %} diff --git a/netbox/virtualization/api/serializers_/virtualmachines.py b/netbox/virtualization/api/serializers_/virtualmachines.py index 53146e44ab..ea1021a251 100644 --- a/netbox/virtualization/api/serializers_/virtualmachines.py +++ b/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', ] diff --git a/netbox/virtualization/filtersets.py b/netbox/virtualization/filtersets.py index 55fadd1af4..ec0831f9fc 100644 --- a/netbox/virtualization/filtersets.py +++ b/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 @@ def search(self, queryset, name, value): 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): diff --git a/netbox/virtualization/forms/bulk_import.py b/netbox/virtualization/forms/bulk_import.py index 5d44ddceba..17efc567ab 100644 --- a/netbox/virtualization/forms/bulk_import.py +++ b/netbox/virtualization/forms/bulk_import.py @@ -137,7 +137,7 @@ class Meta: 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', ) diff --git a/netbox/virtualization/forms/filtersets.py b/netbox/virtualization/forms/filtersets.py index 1cb652a1bf..7c040d9485 100644 --- a/netbox/virtualization/forms/filtersets.py +++ b/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, diff --git a/netbox/virtualization/forms/model_forms.py b/netbox/virtualization/forms/model_forms.py index bfdfc9ada0..2c60cb46fd 100644 --- a/netbox/virtualization/forms/model_forms.py +++ b/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 Meta: 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): diff --git a/netbox/virtualization/migrations/0040_virtualmachine_serial_number.py b/netbox/virtualization/migrations/0040_virtualmachine_serial_number.py new file mode 100644 index 0000000000..5ca72d66db --- /dev/null +++ b/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), + ), + ] diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py index fc446922bb..0c3fc5f4a2 100644 --- a/netbox/virtualization/models/virtualmachines.py +++ b/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( diff --git a/netbox/virtualization/search.py b/netbox/virtualization/search.py index c72b3345ba..cdaf0c074e 100644 --- a/netbox/virtualization/search.py +++ b/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 diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py index f37a1bebc9..50be18ac5a 100644 --- a/netbox/virtualization/tables/virtualmachines.py +++ b/netbox/virtualization/tables/virtualmachines.py @@ -116,7 +116,7 @@ class Meta(NetBoxTable.Meta): 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', diff --git a/netbox/virtualization/tests/test_filtersets.py b/netbox/virtualization/tests/test_filtersets.py index ff55aba10b..d2e6cc05f8 100644 --- a/netbox/virtualization/tests/test_filtersets.py +++ b/netbox/virtualization/tests/test_filtersets.py @@ -327,7 +327,8 @@ def setUpTestData(cls): 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 @@ def setUpTestData(cls): vcpus=2, memory=2, disk=2, - description='foobar2' + description='foobar2', + serial='222-bbb' ), VirtualMachine( name='Virtual Machine 3', @@ -518,6 +520,10 @@ def test_primary_ip6(self): 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() diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py index ed6bef1e4b..0daa55a5cd 100644 --- a/netbox/virtualization/tests/test_views.py +++ b/netbox/virtualization/tests/test_views.py @@ -234,6 +234,7 @@ def setUpTestData(cls): 'vcpus': 4, 'memory': 32768, 'disk': 4000, + 'serial': 'aaa-111', 'comments': 'Some comments', 'tags': [t.pk for t in tags], 'local_context_data': None,