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

13130 dont allow reassigning ipaddress assigned object if primary ip (#13893)

* 13130 dont allow reassigning ipaddress assigned object if primary ip

* 13130 add tests fix parent check

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Arthur Hanson 2 лет назад
Родитель
Сommit
db40119faa
2 измененных файлов с 83 добавлено и 0 удалено
  1. 27 0
      netbox/ipam/models/ip.py
  2. 56 0
      netbox/ipam/tests/test_api.py

+ 27 - 0
netbox/ipam/models/ip.py

@@ -782,6 +782,13 @@ class IPAddress(PrimaryModel):
     def __str__(self):
         return str(self.address)
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Denote the original assigned object (if any) for validation in clean()
+        self._original_assigned_object_id = self.__dict__.get('assigned_object_id')
+        self._original_assigned_object_type_id = self.__dict__.get('assigned_object_type_id')
+
     def get_absolute_url(self):
         return reverse('ipam:ipaddress', args=[self.pk])
 
@@ -843,6 +850,26 @@ class IPAddress(PrimaryModel):
                         )
                     })
 
+        if self._original_assigned_object_id and self._original_assigned_object_type_id:
+            parent = getattr(self.assigned_object, 'parent_object', None)
+            ct = ContentType.objects.get_for_id(self._original_assigned_object_type_id)
+            original_assigned_object = ct.get_object_for_this_type(pk=self._original_assigned_object_id)
+            original_parent = getattr(original_assigned_object, 'parent_object', None)
+
+            # can't use is_primary_ip as self.assigned_object might be changed
+            is_primary = False
+            if self.family == 4 and hasattr(original_parent, 'primary_ip4') and original_parent.primary_ip4_id == self.pk:
+                is_primary = True
+            if self.family == 6 and hasattr(original_parent, 'primary_ip6') and original_parent.primary_ip6_id == self.pk:
+                is_primary = True
+
+            if is_primary and (parent != original_parent):
+                raise ValidationError({
+                    'assigned_object': _(
+                        "Cannot reassign IP address while it is designated as the primary IP for the parent object"
+                    )
+                })
+
         # Validate IP status selection
         if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
             raise ValidationError({

+ 56 - 0
netbox/ipam/tests/test_api.py

@@ -659,6 +659,62 @@ class IPAddressTest(APIViewTestCases.APIViewTestCase):
         )
         IPAddress.objects.bulk_create(ip_addresses)
 
+    def test_assign_object(self):
+        """
+        Test the creation of available IP addresses within a parent IP range.
+        """
+        site = Site.objects.create(name='Site 1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1')
+        device_type = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer)
+        role = DeviceRole.objects.create(name='Switch')
+        device1 = Device.objects.create(
+            name='Device 1',
+            site=site,
+            device_type=device_type,
+            role=role,
+            status='active'
+        )
+        interface1 = Interface.objects.create(name='Interface 1', device=device1, type='1000baset')
+        interface2 = Interface.objects.create(name='Interface 2', device=device1, type='1000baset')
+        device2 = Device.objects.create(
+            name='Device 2',
+            site=site,
+            device_type=device_type,
+            role=role,
+            status='active'
+        )
+        interface3 = Interface.objects.create(name='Interface 3', device=device2, type='1000baset')
+
+        ip_addresses = (
+            IPAddress(address=IPNetwork('192.168.0.4/24'), assigned_object=interface1),
+            IPAddress(address=IPNetwork('192.168.1.4/24')),
+        )
+        IPAddress.objects.bulk_create(ip_addresses)
+
+        ip1 = ip_addresses[0]
+        ip1.assigned_object = interface1
+        device1.primary_ip4 = ip_addresses[0]
+        device1.save()
+
+        ip2 = ip_addresses[1]
+
+        url = reverse('ipam-api:ipaddress-detail', kwargs={'pk': ip1.pk})
+        self.add_permissions('ipam.change_ipaddress')
+
+        # assign to same parent
+        data = {
+            'assigned_object_id': interface2.pk
+        }
+        response = self.client.patch(url, data, format='json', **self.header)
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+
+        # assign to same different parent - should error
+        data = {
+            'assigned_object_id': interface3.pk
+        }
+        response = self.client.patch(url, data, format='json', **self.header)
+        self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
+
 
 class FHRPGroupTest(APIViewTestCases.APIViewTestCase):
     model = FHRPGroup