diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 440c12623..a0fa14764 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -642,13 +642,28 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldForm): # Compile list of choices for primary IPv4 and IPv6 addresses for family in [4, 6]: - ip_choices = [] - interface_ips = IPAddress.objects.filter(family=family, interface__device=self.instance) - ip_choices += [(ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips] - nat_ips = IPAddress.objects.filter(family=family, nat_inside__interface__device=self.instance)\ - .select_related('nat_inside__interface') - ip_choices += [(ip.id, '{} ({} NAT)'.format(ip.address, ip.nat_inside.interface)) for ip in nat_ips] - self.fields['primary_ip{}'.format(family)].choices = [(None, '---------')] + ip_choices + ip_choices = [(None, '---------')] + # Collect interface IPs + interface_ips = IPAddress.objects.select_related('interface').filter( + family=family, interface__device=self.instance + ) + if interface_ips: + ip_choices.append( + ('Interface IPs', [ + (ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips + ]) + ) + # Collect NAT IPs + nat_ips = IPAddress.objects.select_related('nat_inside').filter( + family=family, nat_inside__interface__device=self.instance + ) + if nat_ips: + ip_choices.append( + ('NAT IPs', [ + (ip.id, '{} ({})'.format(ip.address, ip.nat_inside.address)) for ip in nat_ips + ]) + ) + 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 # can be flipped from one face to another. diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index a44097f51..a189d9f29 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -891,13 +891,25 @@ class Device(CreatedUpdatedModel, CustomFieldModel): pass # Validate primary IPv4 address - if self.primary_ip4 and (self.primary_ip4.interface is None or self.primary_ip4.interface.device != self): + if self.primary_ip4 and ( + self.primary_ip4.interface is None or + self.primary_ip4.interface.device != self + ) and ( + self.primary_ip4.nat_inside.interface is None or + self.primary_ip4.nat_inside.interface.device != self + ): raise ValidationError({ 'primary_ip4': "The specified IP address ({}) is not assigned to this device.".format(self.primary_ip4), }) # Validate primary IPv6 address - if self.primary_ip6 and (self.primary_ip6.interface is None or self.primary_ip6.interface.device != self): + if self.primary_ip6 and ( + self.primary_ip6.interface is None or + self.primary_ip6.interface.device != self + ) and ( + self.primary_ip6.nat_inside.interface is None or + self.primary_ip6.nat_inside.interface.device != self + ): raise ValidationError({ 'primary_ip6': "The specified IP address ({}) is not assigned to this device.".format(self.primary_ip6), })