Skip to content

Commit

Permalink
13130 dont allow reassigning ipaddress assigned object if primary ip (#…
Browse files Browse the repository at this point in the history
…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]>
  • Loading branch information
arthanson and jeremystretch authored Sep 26, 2023
1 parent f65744f commit db40119
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
27 changes: 27 additions & 0 deletions netbox/ipam/models/ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,13 @@ class Meta:
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])

Expand Down Expand Up @@ -843,6 +850,26 @@ def clean(self):
)
})

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({
Expand Down
56 changes: 56 additions & 0 deletions netbox/ipam/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,62 @@ def setUpTestData(cls):
)
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
Expand Down

0 comments on commit db40119

Please sign in to comment.