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 "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,
|