Allow designating primary IPs assigned to a device's peer VC members

This commit is contained in:
Jeremy Stretch 2017-12-18 16:08:46 -05:00
parent 70d235f99e
commit 4871682dc6
2 changed files with 43 additions and 35 deletions

View File

@ -773,26 +773,24 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldForm):
# Compile list of choices for primary IPv4 and IPv6 addresses # Compile list of choices for primary IPv4 and IPv6 addresses
for family in [4, 6]: for family in [4, 6]:
ip_choices = [(None, '---------')] ip_choices = [(None, '---------')]
# Gather PKs of all interfaces belonging to this Device or a peer VirtualChassis member
interface_ids = self.instance.vc_interfaces.values('pk')
# Collect interface IPs # Collect interface IPs
interface_ips = IPAddress.objects.select_related('interface').filter( interface_ips = IPAddress.objects.select_related('interface').filter(
family=family, interface__device=self.instance family=family, interface_id__in=interface_ids
) )
if interface_ips: if interface_ips:
ip_choices.append( ip_list = [(ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips]
('Interface IPs', [ ip_choices.append(('Interface IPs', ip_list))
(ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips
])
)
# Collect NAT IPs # Collect NAT IPs
nat_ips = IPAddress.objects.select_related('nat_inside').filter( nat_ips = IPAddress.objects.select_related('nat_inside').filter(
family=family, nat_inside__interface__device=self.instance family=family, nat_inside__interface__in=interface_ids
) )
if nat_ips: if nat_ips:
ip_choices.append( ip_list = [(ip.id, '{} ({})'.format(ip.address, ip.nat_inside.address)) for ip in nat_ips]
('NAT IPs', [ ip_choices.append(('NAT IPs', ip_list))
(ip.id, '{} ({})'.format(ip.address, ip.nat_inside.address)) for ip in nat_ips
])
)
self.fields['primary_ip{}'.format(family)].choices = ip_choices self.fields['primary_ip{}'.format(family)].choices = ip_choices
# If editing an existing device, exclude it from the list of occupied rack units. This ensures that a device # If editing an existing device, exclude it from the list of occupied rack units. This ensures that a device

View File

@ -923,29 +923,28 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
except DeviceType.DoesNotExist: except DeviceType.DoesNotExist:
pass pass
# Validate primary IPv4 address # Validate primary IP addresses
if self.primary_ip4 and ( vc_interfaces = self.vc_interfaces.all()
self.primary_ip4.interface is None or if self.primary_ip4:
self.primary_ip4.interface.device != self if self.primary_ip4.interface in vc_interfaces:
) and ( pass
self.primary_ip4.nat_inside.interface is None or elif self.primary_ip4.nat_inside is not None and self.primary_ip4.nat_inside.interface in vc_interfaces:
self.primary_ip4.nat_inside.interface.device != self pass
): else:
raise ValidationError({ raise ValidationError({
'primary_ip4': "The specified IP address ({}) is not assigned to this device.".format(self.primary_ip4), 'primary_ip4': "The specified IP address ({}) is not assigned to this device.".format(
}) self.primary_ip4),
})
# Validate primary IPv6 address if self.primary_ip6:
if self.primary_ip6 and ( if self.primary_ip6.interface in vc_interfaces:
self.primary_ip6.interface is None or pass
self.primary_ip6.interface.device != self elif self.primary_ip6.nat_inside is not None and self.primary_ip6.nat_inside.interface in vc_interfaces:
) and ( pass
self.primary_ip6.nat_inside.interface is None or else:
self.primary_ip6.nat_inside.interface.device != self raise ValidationError({
): 'primary_ip6': "The specified IP address ({}) is not assigned to this device.".format(
raise ValidationError({ self.primary_ip6),
'primary_ip6': "The specified IP address ({}) is not assigned to this device.".format(self.primary_ip6), })
})
# A Device can only be assigned to a Cluster in the same Site (or no Site) # A Device can only be assigned to a Cluster in the same Site (or no Site)
if self.cluster and self.cluster.site is not None and self.cluster.site != self.site: if self.cluster and self.cluster.site is not None and self.cluster.site != self.site:
@ -1042,6 +1041,17 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
except VCMembership.DoesNotExist: except VCMembership.DoesNotExist:
return None return None
@property
def vc_interfaces(self):
"""
Return a QuerySet matching all Interfaces assigned to this Device or, if this Device is a VC master, to another
Device belonging to the same virtual chassis.
"""
if hasattr(self, 'vc_membership') and self.vc_membership.is_master:
return Interface.objects.filter(device__vc_membership__virtual_chassis=self.vc_membership.virtual_chassis)
else:
return self.interfaces.all()
def get_children(self): def get_children(self):
""" """
Return the set of child Devices installed in DeviceBays within this Device. Return the set of child Devices installed in DeviceBays within this Device.