mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Restore ability to assign interface when editing an IPAddress
This commit is contained in:
@ -522,11 +522,33 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
||||
#
|
||||
|
||||
class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModelForm):
|
||||
# TODO: Restore ability to select assigned object when editing IPAddress
|
||||
# interface = forms.ModelChoiceField(
|
||||
# queryset=Interface.objects.all(),
|
||||
# required=False
|
||||
# )
|
||||
device = DynamicModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
filter_for={
|
||||
'interface': 'device_id'
|
||||
}
|
||||
)
|
||||
)
|
||||
interface = DynamicModelChoiceField(
|
||||
queryset=Interface.objects.all(),
|
||||
required=False
|
||||
)
|
||||
virtual_machine = DynamicModelChoiceField(
|
||||
queryset=VirtualMachine.objects.all(),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
filter_for={
|
||||
'vminterface': 'virtual_machine_id'
|
||||
}
|
||||
)
|
||||
)
|
||||
vminterface = DynamicModelChoiceField(
|
||||
queryset=VMInterface.objects.all(),
|
||||
required=False,
|
||||
label='Interface'
|
||||
)
|
||||
vrf = DynamicModelChoiceField(
|
||||
queryset=VRF.objects.all(),
|
||||
required=False,
|
||||
@ -611,67 +633,68 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
||||
# Initialize helper selectors
|
||||
instance = kwargs.get('instance')
|
||||
initial = kwargs.get('initial', {}).copy()
|
||||
if instance and instance.nat_inside and instance.nat_inside.device is not None:
|
||||
initial['nat_site'] = instance.nat_inside.device.site
|
||||
initial['nat_rack'] = instance.nat_inside.device.rack
|
||||
initial['nat_device'] = instance.nat_inside.device
|
||||
if instance:
|
||||
if type(instance.assigned_object) is Interface:
|
||||
initial['device'] = instance.assigned_object.device
|
||||
initial['interface'] = instance.assigned_object
|
||||
elif type(instance.assigned_object) is VMInterface:
|
||||
initial['virtual_machine'] = instance.assigned_object.virtual_machine
|
||||
initial['vminterface'] = instance.assigned_object
|
||||
if instance.nat_inside and instance.nat_inside.device is not None:
|
||||
initial['nat_site'] = instance.nat_inside.device.site
|
||||
initial['nat_rack'] = instance.nat_inside.device.rack
|
||||
initial['nat_device'] = instance.nat_inside.device
|
||||
kwargs['initial'] = initial
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['vrf'].empty_label = 'Global'
|
||||
|
||||
# # Limit interface selections to those belonging to the parent device/VM
|
||||
# if self.instance and self.instance.interface:
|
||||
# self.fields['interface'].queryset = Interface.objects.filter(
|
||||
# device=self.instance.interface.device, virtual_machine=self.instance.interface.virtual_machine
|
||||
# ).prefetch_related(
|
||||
# 'device__primary_ip4',
|
||||
# 'device__primary_ip6',
|
||||
# 'virtual_machine__primary_ip4',
|
||||
# 'virtual_machine__primary_ip6',
|
||||
# ) # We prefetch the primary address fields to ensure cache invalidation does not balk on the save()
|
||||
# else:
|
||||
# self.fields['interface'].choices = []
|
||||
#
|
||||
# # Initialize primary_for_parent if IP address is already assigned
|
||||
# if self.instance.pk and self.instance.interface is not None:
|
||||
# parent = self.instance.interface.parent
|
||||
# if (
|
||||
# self.instance.address.version == 4 and parent.primary_ip4_id == self.instance.pk or
|
||||
# self.instance.address.version == 6 and parent.primary_ip6_id == self.instance.pk
|
||||
# ):
|
||||
# self.initial['primary_for_parent'] = True
|
||||
# Initialize primary_for_parent if IP address is already assigned
|
||||
if self.instance.pk and self.instance.assigned_object:
|
||||
parent = self.instance.assigned_object.parent
|
||||
if (
|
||||
self.instance.address.version == 4 and parent.primary_ip4_id == self.instance.pk or
|
||||
self.instance.address.version == 6 and parent.primary_ip6_id == self.instance.pk
|
||||
):
|
||||
self.initial['primary_for_parent'] = True
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Cannot select both a device interface and a VM interface
|
||||
if self.cleaned_data.get('interface') and self.cleaned_data.get('vminterface'):
|
||||
raise forms.ValidationError("Cannot select both a device interface and a virtual machine interface")
|
||||
|
||||
# Primary IP assignment is only available if an interface has been assigned.
|
||||
if self.cleaned_data.get('primary_for_parent') and not self.cleaned_data.get('interface'):
|
||||
interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
|
||||
if self.cleaned_data.get('primary_for_parent') and not interface:
|
||||
self.add_error(
|
||||
'primary_for_parent', "Only IP addresses assigned to an interface can be designated as primary IPs."
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Set assigned object
|
||||
interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
|
||||
if interface:
|
||||
self.instance.assigned_object = interface
|
||||
|
||||
ipaddress = super().save(*args, **kwargs)
|
||||
|
||||
# Assign/clear this IPAddress as the primary for the associated Device/VirtualMachine.
|
||||
if self.cleaned_data['primary_for_parent']:
|
||||
parent = self.cleaned_data['interface'].parent
|
||||
if interface and self.cleaned_data['primary_for_parent']:
|
||||
if ipaddress.address.version == 4:
|
||||
parent.primary_ip4 = ipaddress
|
||||
interface.parent.primary_ip4 = ipaddress
|
||||
else:
|
||||
parent.primary_ip6 = ipaddress
|
||||
parent.save()
|
||||
# elif self.cleaned_data['interface']:
|
||||
# parent = self.cleaned_data['interface'].parent
|
||||
# if ipaddress.address.version == 4 and parent.primary_ip4 == ipaddress:
|
||||
# parent.primary_ip4 = None
|
||||
# parent.save()
|
||||
# elif ipaddress.address.version == 6 and parent.primary_ip6 == ipaddress:
|
||||
# parent.primary_ip6 = None
|
||||
# parent.save()
|
||||
interface.primary_ip6 = ipaddress
|
||||
interface.parent.save()
|
||||
elif interface and ipaddress.address.version == 4 and interface.parent.primary_ip4 == ipaddress:
|
||||
interface.parent.primary_ip4 = None
|
||||
interface.parent.save()
|
||||
elif interface and ipaddress.address.version == 6 and interface.parent.primary_ip6 == ipaddress:
|
||||
interface.parent.primary_ip4 = None
|
||||
interface.parent.save()
|
||||
|
||||
return ipaddress
|
||||
|
||||
|
@ -15,7 +15,7 @@ from extras.utils import extras_features
|
||||
from utilities.models import ChangeLoggedModel
|
||||
from utilities.querysets import RestrictedQuerySet
|
||||
from utilities.utils import serialize_object
|
||||
from virtualization.models import VirtualMachine
|
||||
from virtualization.models import VirtualMachine, VMInterface
|
||||
from .choices import *
|
||||
from .constants import *
|
||||
from .fields import IPNetworkField, IPAddressField
|
||||
@ -717,32 +717,31 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
||||
)
|
||||
})
|
||||
|
||||
if self.pk:
|
||||
|
||||
# Check for primary IP assignment that doesn't match the assigned device/VM
|
||||
# Check for primary IP assignment that doesn't match the assigned device/VM
|
||||
if self.pk and type(self.assigned_object) is Interface:
|
||||
device = Device.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
|
||||
if device:
|
||||
if self.interface is None:
|
||||
if self.assigned_object is None:
|
||||
raise ValidationError({
|
||||
'interface': "IP address is primary for device {} but not assigned".format(device)
|
||||
'interface': f"IP address is primary for device {device} but not assigned to an interface"
|
||||
})
|
||||
elif (device.primary_ip4 == self or device.primary_ip6 == self) and self.interface.device != device:
|
||||
elif self.assigned_object.device != device:
|
||||
raise ValidationError({
|
||||
'interface': "IP address is primary for device {} but assigned to {} ({})".format(
|
||||
device, self.interface.device, self.interface
|
||||
)
|
||||
'interface': f"IP address is primary for device {device} but assigned to "
|
||||
f"{self.assigned_object.device} ({self.assigned_object})"
|
||||
})
|
||||
elif self.pk and type(self.assigned_object) is VMInterface:
|
||||
vm = VirtualMachine.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
|
||||
if vm:
|
||||
if self.interface is None:
|
||||
if self.assigned_object is None:
|
||||
raise ValidationError({
|
||||
'interface': "IP address is primary for virtual machine {} but not assigned".format(vm)
|
||||
'vminterface': f"IP address is primary for virtual machine {vm} but not assigned to an "
|
||||
f"interface"
|
||||
})
|
||||
elif (vm.primary_ip4 == self or vm.primary_ip6 == self) and self.interface.virtual_machine != vm:
|
||||
elif self.interface.virtual_machine != vm:
|
||||
raise ValidationError({
|
||||
'interface': "IP address is primary for virtual machine {} but assigned to {} ({})".format(
|
||||
vm, self.interface.virtual_machine, self.interface
|
||||
)
|
||||
'vminterface': f"IP address is primary for virtual machine {vm} but assigned to "
|
||||
f"{self.assigned_object.virtual_machine} ({self.assigned_object})"
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
@ -28,32 +28,30 @@
|
||||
{% render_field form.tenant %}
|
||||
</div>
|
||||
</div>
|
||||
{% if obj.assigned_object %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Interface Assignment</strong>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{{ obj.assigned_object.parent|meta:"verbose_name"|bettertitle }}</label>
|
||||
<div class="col-md-9">
|
||||
<p class="form-control-static">
|
||||
<a href="{{ obj.assigned_object.parent.get_absolute_url }}">{{ obj.assigned_object.parent }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Interface</label>
|
||||
<div class="col-md-9">
|
||||
<p class="form-control-static">
|
||||
<a href="{{ obj.assigned_object.get_absolute_url }}">{{ obj.assigned_object }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% render_field form.primary_for_parent %}
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Interface Assignment</strong>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="panel-body">
|
||||
{% with vm_tab_active=obj.vminterface.exists %}
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation"{% if not vm_tab_active %} class="active"{% endif %}><a href="#device" role="tab" data-toggle="tab">Device</a></li>
|
||||
<li role="presentation"{% if vm_tab_active %} class="active"{% endif %}><a href="#virtualmachine" role="tab" data-toggle="tab">Virtual Machine</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane{% if not vm_tab_active %} active{% endif %}" id="device">
|
||||
{% render_field form.device %}
|
||||
{% render_field form.interface %}
|
||||
</div>
|
||||
<div class="tab-pane{% if vm_tab_active %} active{% endif %}" id="virtualmachine">
|
||||
{% render_field form.virtual_machine %}
|
||||
{% render_field form.vminterface %}
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% render_field form.primary_for_parent %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>NAT IP (Inside)</strong></div>
|
||||
<div class="panel-body">
|
||||
|
Reference in New Issue
Block a user