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

#2269: Allow non-unique VirtualMachine names

Jeremy Stretch 6 лет назад
Родитель
Сommit
2e37b19e9f

+ 23 - 0
netbox/virtualization/migrations/0012_vm_name_nonunique.py

@@ -0,0 +1,23 @@
+# Generated by Django 2.2.6 on 2019-12-09 16:57
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tenancy', '0006_custom_tag_models'),
+        ('virtualization', '0011_3569_virtualmachine_fields'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='virtualmachine',
+            name='name',
+            field=models.CharField(max_length=64),
+        ),
+        migrations.AlterUniqueTogether(
+            name='virtualmachine',
+            unique_together={('cluster', 'tenant', 'name')},
+        ),
+    ]

+ 18 - 2
netbox/virtualization/models.py

@@ -193,8 +193,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
         null=True
     )
     name = models.CharField(
-        max_length=64,
-        unique=True
+        max_length=64
     )
     status = models.CharField(
         max_length=50,
@@ -267,6 +266,9 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
 
     class Meta:
         ordering = ['name']
+        unique_together = [
+            ['cluster', 'tenant', 'name']
+        ]
 
     def __str__(self):
         return self.name
@@ -274,6 +276,20 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
     def get_absolute_url(self):
         return reverse('virtualization:virtualmachine', args=[self.pk])
 
+    def validate_unique(self, exclude=None):
+
+        # Check for a duplicate name on a VM assigned to the same Cluster and no Tenant. This is necessary
+        # because Django does not consider two NULL fields to be equal, and thus will not trigger a violation
+        # of the uniqueness constraint without manual intervention.
+        if self.tenant is None and VirtualMachine.objects.exclude(pk=self.pk).filter(
+                name=self.name, tenant__isnull=True
+        ):
+            raise ValidationError({
+                'name': 'A virtual machine with this name already exists.'
+            })
+
+        super().validate_unique(exclude)
+
     def clean(self):
 
         super().clean()

+ 44 - 0
netbox/virtualization/tests/test_models.py

@@ -0,0 +1,44 @@
+from django.test import TestCase
+
+from virtualization.models import *
+from tenancy.models import Tenant
+
+
+class VirtualMachineTestCase(TestCase):
+
+    def setUp(self):
+
+        cluster_type = ClusterType.objects.create(name='Test Cluster Type 1', slug='Test Cluster Type 1')
+        self.cluster = Cluster.objects.create(name='Test Cluster 1', type=cluster_type)
+
+    def test_vm_duplicate_name_per_cluster(self):
+
+        vm1 = VirtualMachine(
+            cluster=self.cluster,
+            name='Test VM 1'
+        )
+        vm1.save()
+
+        vm2 = VirtualMachine(
+            cluster=vm1.cluster,
+            name=vm1.name
+        )
+
+        # Two VMs assigned to the same Cluster and no Tenant should fail validation
+        with self.assertRaises(ValidationError):
+            vm2.full_clean()
+
+        tenant = Tenant.objects.create(name='Test Tenant 1', slug='test-tenant-1')
+        vm1.tenant = tenant
+        vm1.save()
+        vm2.tenant = tenant
+
+        # Two VMs assigned to the same Cluster and the same Tenant should fail validation
+        with self.assertRaises(ValidationError):
+            vm2.full_clean()
+
+        vm2.tenant = None
+
+        # Two VMs assigned to the same Cluster and different Tenants should pass validation
+        vm2.full_clean()
+        vm2.save()