Explorar o código

Fixes #5105: Validation should fail when reassigning a primary IP from device to VM

Jeremy Stretch %!s(int64=5) %!d(string=hai) anos
pai
achega
03b207d154
Modificáronse 3 ficheiros con 10 adicións e 26 borrados
  1. 1 0
      docs/release-notes/version-2.9.md
  2. 4 9
      netbox/ipam/forms.py
  3. 5 17
      netbox/ipam/models.py

+ 1 - 0
docs/release-notes/version-2.9.md

@@ -9,6 +9,7 @@
 ### Bug Fixes
 ### Bug Fixes
 
 
 * [#5050](https://github.com/netbox-community/netbox/issues/5050) - Fix potential failure on `0016_replicate_interfaces` schema migration from old release
 * [#5050](https://github.com/netbox-community/netbox/issues/5050) - Fix potential failure on `0016_replicate_interfaces` schema migration from old release
+* [#5105](https://github.com/netbox-community/netbox/issues/5105) - Validation should fail when reassigning a primary IP from device to VM
 * [#5109](https://github.com/netbox-community/netbox/issues/5109) - Fix representation of custom choice field values for webhook data
 * [#5109](https://github.com/netbox-community/netbox/issues/5109) - Fix representation of custom choice field values for webhook data
 * [#5108](https://github.com/netbox-community/netbox/issues/5108) - Fix execution of reports via CLI
 * [#5108](https://github.com/netbox-community/netbox/issues/5108) - Fix execution of reports via CLI
 * [#5111](https://github.com/netbox-community/netbox/issues/5111) - Allow use of tuples when specifying ObjectVar `query_params`
 * [#5111](https://github.com/netbox-community/netbox/issues/5111) - Allow use of tuples when specifying ObjectVar `query_params`

+ 4 - 9
netbox/ipam/forms.py

@@ -641,11 +641,11 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
                 self.initial['primary_for_parent'] = True
                 self.initial['primary_for_parent'] = True
 
 
     def clean(self):
     def clean(self):
-        super().clean()
 
 
         # Cannot select both a device interface and a VM interface
         # Cannot select both a device interface and a VM interface
         if self.cleaned_data.get('interface') and self.cleaned_data.get('vminterface'):
         if self.cleaned_data.get('interface') and self.cleaned_data.get('vminterface'):
             raise forms.ValidationError("Cannot select both a device interface and a virtual machine interface")
             raise forms.ValidationError("Cannot select both a device interface and a virtual machine interface")
+        self.instance.assigned_object = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
 
 
         # Primary IP assignment is only available if an interface has been assigned.
         # Primary IP assignment is only available if an interface has been assigned.
         interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
         interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
@@ -655,26 +655,21 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
             )
             )
 
 
     def save(self, *args, **kwargs):
     def save(self, *args, **kwargs):
-
-        # Set assigned object
-        interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
-        if interface:
-            self.instance.assigned_object = interface
-
         ipaddress = super().save(*args, **kwargs)
         ipaddress = super().save(*args, **kwargs)
 
 
         # Assign/clear this IPAddress as the primary for the associated Device/VirtualMachine.
         # Assign/clear this IPAddress as the primary for the associated Device/VirtualMachine.
+        interface = self.instance.assigned_object
         if interface and self.cleaned_data['primary_for_parent']:
         if interface and self.cleaned_data['primary_for_parent']:
             if ipaddress.address.version == 4:
             if ipaddress.address.version == 4:
                 interface.parent.primary_ip4 = ipaddress
                 interface.parent.primary_ip4 = ipaddress
             else:
             else:
-                interface.primary_ip6 = ipaddress
+                interface.parent.primary_ip6 = ipaddress
             interface.parent.save()
             interface.parent.save()
         elif interface and ipaddress.address.version == 4 and interface.parent.primary_ip4 == ipaddress:
         elif interface and ipaddress.address.version == 4 and interface.parent.primary_ip4 == ipaddress:
             interface.parent.primary_ip4 = None
             interface.parent.primary_ip4 = None
             interface.parent.save()
             interface.parent.save()
         elif interface and ipaddress.address.version == 6 and interface.parent.primary_ip6 == ipaddress:
         elif interface and ipaddress.address.version == 6 and interface.parent.primary_ip6 == ipaddress:
-            interface.parent.primary_ip4 = None
+            interface.parent.primary_ip6 = None
             interface.parent.save()
             interface.parent.save()
 
 
         return ipaddress
         return ipaddress

+ 5 - 17
netbox/ipam/models.py

@@ -726,30 +726,18 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
                     })
                     })
 
 
         # Check for primary IP assignment that doesn't match the assigned device/VM
         # Check for primary IP assignment that doesn't match the assigned device/VM
-        if self.pk and type(self.assigned_object) is Interface:
+        if self.pk:
             device = Device.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
             device = Device.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
             if device:
             if device:
-                if self.assigned_object is None:
+                if getattr(self.assigned_object, 'device', None) != device:
                     raise ValidationError({
                     raise ValidationError({
-                        'interface': f"IP address is primary for device {device} but not assigned to an interface"
+                        'interface': f"IP address is primary for device {device} but not assigned to it!"
                     })
                     })
-                elif self.assigned_object.device != device:
-                    raise ValidationError({
-                        'interface': f"IP address is primary for device {device} but assigned to "
-                                     f"{self.assigned_object.device} ({self.assigned_object})"
-                    })
-        elif self.pk and type(self.assigned_object) is VMInterface:
             vm = VirtualMachine.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
             vm = VirtualMachine.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
             if vm:
             if vm:
-                if self.assigned_object is None:
-                    raise ValidationError({
-                        'vminterface': f"IP address is primary for virtual machine {vm} but not assigned to an "
-                                       f"interface"
-                    })
-                elif self.assigned_object.virtual_machine != vm:
+                if getattr(self.assigned_object, 'virtual_machine', None) != vm:
                     raise ValidationError({
                     raise ValidationError({
-                        'vminterface': f"IP address is primary for virtual machine {vm} but assigned to "
-                                       f"{self.assigned_object.virtual_machine} ({self.assigned_object})"
+                        'vminterface': f"IP address is primary for virtual machine {vm} but not assigned to it!"
                     })
                     })
 
 
         # Validate IP status selection
         # Validate IP status selection