Further constrain IP to VDC

This commit is contained in:
Daniel Sheppard 2022-11-17 11:01:41 -06:00
parent a85d1b1319
commit 6a63b71e15
3 changed files with 42 additions and 21 deletions

View File

@ -1705,22 +1705,6 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
'rack_id': '$rack',
}
)
primary_ip4 = DynamicModelChoiceField(
queryset=IPAddress.objects.all(),
required=False,
query_params={
'device_id': '$device',
'family': 4,
}
)
primary_ip6 = DynamicModelChoiceField(
queryset=IPAddress.objects.all(),
required=False,
query_params={
'device_id': '$device',
'family': 6,
}
)
fieldsets = (
('Assigned Device', ('region', 'site_group', 'site', 'location', 'rack', 'device')),
@ -1736,4 +1720,35 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
]
widgets = {
'status': StaticSelect(),
'primary_ip4': StaticSelect(),
'primary_ip6': StaticSelect(),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
# Compile list of choices for primary IPv4 and IPv6 addresses
for family in [4, 6]:
ip_choices = [(None, '---------')]
# Gather PKs of all interfaces belonging to this Device or a peer VirtualChassis member
interface_ids = self.instance.interfaces.values_list('pk', flat=True)
# Collect interface IPs
interface_ips = IPAddress.objects.filter(
address__family=family,
assigned_object_type=ContentType.objects.get_for_model(Interface),
assigned_object_id__in=interface_ids
).prefetch_related('assigned_object')
if interface_ips:
ip_list = [(ip.id, f'{ip.address} ({ip.assigned_object})') for ip in interface_ips]
ip_choices.append(('Interface IPs', ip_list))
self.fields['primary_ip{}'.format(family)].choices = ip_choices
else:
# An object that doesn't exist yet can't have any IPs assigned to it
self.fields['primary_ip4'].choices = []
self.fields['primary_ip4'].widget.attrs['readonly'] = True
self.fields['primary_ip6'].choices = []
self.fields['primary_ip6'].widget.attrs['readonly'] = True

View File

@ -1178,13 +1178,12 @@ class VirtualDeviceContext(PrimaryModel):
def clean(self):
super().clean()
vc_interfaces = self.device.vc_interfaces(if_master=False)
if self.primary_ip4:
if self.primary_ip4.family != 4:
raise ValidationError({
'primary_ip4': f"{self.primary_ip4} is not an IPv4 address."
})
if self.primary_ip4.assigned_object not in vc_interfaces:
if self.primary_ip4.assigned_object not in self.interfaces.all():
raise ValidationError({
'primary_ip4': f"The specified IP address ({self.primary_ip4}) is not assigned to this device."
})
@ -1193,7 +1192,7 @@ class VirtualDeviceContext(PrimaryModel):
raise ValidationError({
'primary_ip6': f"{self.primary_ip6} is not an IPv6 address."
})
if self.primary_ip6.assigned_object not in vc_interfaces:
if self.primary_ip6.assigned_object not in self.interfaces.all():
raise ValidationError({
'primary_ip6': f"The specified IP address ({self.primary_ip6}) is not assigned to this device."
})

View File

@ -866,9 +866,16 @@ class IPAddress(PrimaryModel):
# Check for primary IP assignment that doesn't match the assigned device/VM
if self.pk:
for cls, attr in ((Device, 'device'), (VirtualMachine, 'virtual_machine'), (VirtualDeviceContext, 'device')):
for cls, attr in ((Device, 'device'), (VirtualMachine, 'virtual_machine'), (VirtualDeviceContext, 'vdcs')):
parent = cls.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
if parent and getattr(self.assigned_object, attr, None) != parent:
if parent and hasattr(self.assigned_object, attr) and \
hasattr(getattr(self.assigned_object, attr), 'all') and getattr(self.assigned_object, attr).all() \
!= parent:
raise ValidationError({
'interface': f"IP address is primary for {cls._meta.model_name} {parent} but "
f"not assigned to it!"
})
elif parent and getattr(self.assigned_object, attr, None) != parent:
# Check for a NAT relationship
if not self.nat_inside or getattr(self.nat_inside.assigned_object, attr, None) != parent:
raise ValidationError({