Skip to content

Commit db40119

Browse files
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 <[email protected]>
1 parent f65744f commit db40119

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

netbox/ipam/models/ip.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,13 @@ class Meta:
782782
def __str__(self):
783783
return str(self.address)
784784

785+
def __init__(self, *args, **kwargs):
786+
super().__init__(*args, **kwargs)
787+
788+
# Denote the original assigned object (if any) for validation in clean()
789+
self._original_assigned_object_id = self.__dict__.get('assigned_object_id')
790+
self._original_assigned_object_type_id = self.__dict__.get('assigned_object_type_id')
791+
785792
def get_absolute_url(self):
786793
return reverse('ipam:ipaddress', args=[self.pk])
787794

@@ -843,6 +850,26 @@ def clean(self):
843850
)
844851
})
845852

853+
if self._original_assigned_object_id and self._original_assigned_object_type_id:
854+
parent = getattr(self.assigned_object, 'parent_object', None)
855+
ct = ContentType.objects.get_for_id(self._original_assigned_object_type_id)
856+
original_assigned_object = ct.get_object_for_this_type(pk=self._original_assigned_object_id)
857+
original_parent = getattr(original_assigned_object, 'parent_object', None)
858+
859+
# can't use is_primary_ip as self.assigned_object might be changed
860+
is_primary = False
861+
if self.family == 4 and hasattr(original_parent, 'primary_ip4') and original_parent.primary_ip4_id == self.pk:
862+
is_primary = True
863+
if self.family == 6 and hasattr(original_parent, 'primary_ip6') and original_parent.primary_ip6_id == self.pk:
864+
is_primary = True
865+
866+
if is_primary and (parent != original_parent):
867+
raise ValidationError({
868+
'assigned_object': _(
869+
"Cannot reassign IP address while it is designated as the primary IP for the parent object"
870+
)
871+
})
872+
846873
# Validate IP status selection
847874
if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
848875
raise ValidationError({

netbox/ipam/tests/test_api.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,62 @@ def setUpTestData(cls):
659659
)
660660
IPAddress.objects.bulk_create(ip_addresses)
661661

662+
def test_assign_object(self):
663+
"""
664+
Test the creation of available IP addresses within a parent IP range.
665+
"""
666+
site = Site.objects.create(name='Site 1')
667+
manufacturer = Manufacturer.objects.create(name='Manufacturer 1')
668+
device_type = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer)
669+
role = DeviceRole.objects.create(name='Switch')
670+
device1 = Device.objects.create(
671+
name='Device 1',
672+
site=site,
673+
device_type=device_type,
674+
role=role,
675+
status='active'
676+
)
677+
interface1 = Interface.objects.create(name='Interface 1', device=device1, type='1000baset')
678+
interface2 = Interface.objects.create(name='Interface 2', device=device1, type='1000baset')
679+
device2 = Device.objects.create(
680+
name='Device 2',
681+
site=site,
682+
device_type=device_type,
683+
role=role,
684+
status='active'
685+
)
686+
interface3 = Interface.objects.create(name='Interface 3', device=device2, type='1000baset')
687+
688+
ip_addresses = (
689+
IPAddress(address=IPNetwork('192.168.0.4/24'), assigned_object=interface1),
690+
IPAddress(address=IPNetwork('192.168.1.4/24')),
691+
)
692+
IPAddress.objects.bulk_create(ip_addresses)
693+
694+
ip1 = ip_addresses[0]
695+
ip1.assigned_object = interface1
696+
device1.primary_ip4 = ip_addresses[0]
697+
device1.save()
698+
699+
ip2 = ip_addresses[1]
700+
701+
url = reverse('ipam-api:ipaddress-detail', kwargs={'pk': ip1.pk})
702+
self.add_permissions('ipam.change_ipaddress')
703+
704+
# assign to same parent
705+
data = {
706+
'assigned_object_id': interface2.pk
707+
}
708+
response = self.client.patch(url, data, format='json', **self.header)
709+
self.assertHttpStatus(response, status.HTTP_200_OK)
710+
711+
# assign to same different parent - should error
712+
data = {
713+
'assigned_object_id': interface3.pk
714+
}
715+
response = self.client.patch(url, data, format='json', **self.header)
716+
self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
717+
662718

663719
class FHRPGroupTest(APIViewTestCases.APIViewTestCase):
664720
model = FHRPGroup

0 commit comments

Comments
 (0)