diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index d9c2e30c3..3c47c27df 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -344,10 +344,11 @@ class IPAddressForm(BootstrapMixin, ReturnURLForm, CustomFieldForm): query_key='q', query_url='ipam-api:ipaddress_list', field_to_update='nat_inside', obj_label='address' ) ) + primary_for_device = forms.BooleanField(required=False, label='Make this the primary IP for the device') class Meta: model = IPAddress - fields = ['address', 'vrf', 'tenant', 'status', 'interface', 'nat_inside', 'description'] + fields = ['address', 'vrf', 'tenant', 'status', 'description', 'interface', 'primary_for_device', 'nat_inside'] widgets = { 'interface': APISelect(api_url='/api/dcim/devices/{{interface_device}}/interfaces/'), 'nat_inside': APISelect(api_url='/api/ipam/ip-addresses/?device_id={{nat_device}}', display_field='address') @@ -388,6 +389,15 @@ class IPAddressForm(BootstrapMixin, ReturnURLForm, CustomFieldForm): else: self.fields['interface'].choices = [] + # Initialize primary_for_device if IP address is already assigned + if self.instance.interface is not None: + device = self.instance.interface.device + if ( + self.instance.address.version == 4 and device.primary_ip4 == self.instance or + self.instance.address.version == 6 and device.primary_ip6 == self.instance + ): + self.initial['primary_for_device'] = True + if self.instance.nat_inside: nat_inside = self.instance.nat_inside # If the IP is assigned to an interface, populate site/device fields accordingly @@ -420,6 +430,43 @@ class IPAddressForm(BootstrapMixin, ReturnURLForm, CustomFieldForm): else: self.fields['nat_inside'].choices = [] + def clean(self): + super(IPAddressForm, self).clean() + + # Primary IP assignment is only available if an interface has been assigned. + if self.cleaned_data.get('primary_for_device') and not self.cleaned_data.get('interface'): + self.add_error( + 'primary_for_device', "Only IP addresses assigned to an interface can be designated as primary IPs." + ) + + def save(self, *args, **kwargs): + + ipaddress = super(IPAddressForm, self).save(*args, **kwargs) + + # Assign this IPAddress as the primary for the associated Device. + if self.cleaned_data['primary_for_device']: + device = self.cleaned_data['interface'].device + if ipaddress.address.version == 4: + device.primary_ip4 = ipaddress + else: + device.primary_ip6 = ipaddress + device.save() + + # Clear assignment as primary for device if set. + else: + try: + if ipaddress.address.version == 4: + device = ipaddress.primary_ip4_for + device.primary_ip4 = None + else: + device = ipaddress.primary_ip6_for + device.primary_ip6 = None + device.save() + except Device.DoesNotExist: + pass + + return ipaddress + class IPAddressBulkAddForm(BootstrapMixin, CustomFieldForm): address_pattern = ExpandableIPAddressField(label='Address Pattern') diff --git a/netbox/templates/ipam/ipaddress_edit.html b/netbox/templates/ipam/ipaddress_edit.html index 82523eba0..531a91990 100644 --- a/netbox/templates/ipam/ipaddress_edit.html +++ b/netbox/templates/ipam/ipaddress_edit.html @@ -28,6 +28,7 @@ {% render_field form.interface_rack %} {% render_field form.interface_device %} {% render_field form.interface %} + {% render_field form.primary_for_device %}