mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Allowed assigning an IP address to either a device or a VM
This commit is contained in:
@@ -377,50 +377,9 @@ class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
#
|
||||
|
||||
class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm):
|
||||
interface_site = forms.ModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
required=False,
|
||||
label='Site',
|
||||
widget=forms.Select(
|
||||
attrs={'filter-for': 'interface_rack'}
|
||||
)
|
||||
)
|
||||
interface_rack = ChainedModelChoiceField(
|
||||
queryset=Rack.objects.all(),
|
||||
chains=(
|
||||
('site', 'interface_site'),
|
||||
),
|
||||
required=False,
|
||||
label='Rack',
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/racks/?site_id={{interface_site}}',
|
||||
display_field='display_name',
|
||||
attrs={'filter-for': 'interface_device', 'nullable': 'true'}
|
||||
)
|
||||
)
|
||||
interface_device = ChainedModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
chains=(
|
||||
('site', 'interface_site'),
|
||||
('rack', 'interface_rack'),
|
||||
),
|
||||
required=False,
|
||||
label='Device',
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/devices/?site_id={{interface_site}}&rack_id={{interface_rack}}',
|
||||
display_field='display_name',
|
||||
attrs={'filter-for': 'interface'}
|
||||
)
|
||||
)
|
||||
interface = ChainedModelChoiceField(
|
||||
interface = forms.ModelChoiceField(
|
||||
queryset=Interface.objects.all(),
|
||||
chains=(
|
||||
('device', 'interface_device'),
|
||||
),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/interfaces/?device_id={{interface_device}}'
|
||||
)
|
||||
required=False
|
||||
)
|
||||
nat_site = forms.ModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
@@ -479,13 +438,13 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
|
||||
obj_label='address'
|
||||
)
|
||||
)
|
||||
primary_for_device = forms.BooleanField(required=False, label='Make this the primary IP for the device')
|
||||
primary_for_parent = forms.BooleanField(required=False, label='Make this the primary IP for the device/VM')
|
||||
|
||||
class Meta:
|
||||
model = IPAddress
|
||||
fields = [
|
||||
'address', 'vrf', 'status', 'role', 'description', 'interface', 'primary_for_device', 'nat_site', 'nat_rack',
|
||||
'nat_inside', 'tenant_group', 'tenant',
|
||||
'address', 'vrf', 'status', 'role', 'description', 'interface', 'primary_for_parent', 'nat_site',
|
||||
'nat_rack', 'nat_inside', 'tenant_group', 'tenant',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -493,10 +452,6 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
|
||||
# Initialize helper selectors
|
||||
instance = kwargs.get('instance')
|
||||
initial = kwargs.get('initial', {}).copy()
|
||||
if instance and instance.interface is not None:
|
||||
initial['interface_site'] = instance.interface.device.site
|
||||
initial['interface_rack'] = instance.interface.device.rack
|
||||
initial['interface_device'] = instance.interface.device
|
||||
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
|
||||
@@ -507,22 +462,30 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
|
||||
|
||||
self.fields['vrf'].empty_label = 'Global'
|
||||
|
||||
# Initialize primary_for_device if IP address is already assigned
|
||||
if self.instance.interface is not None:
|
||||
device = self.instance.interface.device
|
||||
# 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
|
||||
)
|
||||
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 device.primary_ip4 == self.instance or
|
||||
self.instance.address.version == 6 and device.primary_ip6 == self.instance
|
||||
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_device'] = True
|
||||
self.initial['primary_for_parent'] = True
|
||||
|
||||
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'):
|
||||
if self.cleaned_data.get('primary_for_parent') 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."
|
||||
'primary_for_parent', "Only IP addresses assigned to an interface can be designated as primary IPs."
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
@@ -530,13 +493,13 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
|
||||
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 self.cleaned_data['primary_for_parent']:
|
||||
parent = self.cleaned_data['interface'].parent
|
||||
if ipaddress.address.version == 4:
|
||||
device.primary_ip4 = ipaddress
|
||||
parent.primary_ip4 = ipaddress
|
||||
else:
|
||||
device.primary_ip6 = ipaddress
|
||||
device.save()
|
||||
parent.primary_ip6 = ipaddress
|
||||
parent.save()
|
||||
|
||||
# Clear assignment as primary for device if set.
|
||||
else:
|
||||
|
||||
@@ -77,9 +77,9 @@ IPADDRESS_LINK = """
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
IPADDRESS_DEVICE = """
|
||||
IPADDRESS_PARENT = """
|
||||
{% if record.interface %}
|
||||
<a href="{{ record.interface.device.get_absolute_url }}">{{ record.interface.device }}</a>
|
||||
<a href="{{ record.interface.parent.get_absolute_url }}">{{ record.interface.parent }}</a>
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
@@ -265,12 +265,12 @@ class IPAddressTable(BaseTable):
|
||||
status = tables.TemplateColumn(STATUS_LABEL)
|
||||
vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
|
||||
tenant = tables.TemplateColumn(TENANT_LINK)
|
||||
device = tables.TemplateColumn(IPADDRESS_DEVICE, orderable=False)
|
||||
parent = tables.TemplateColumn(IPADDRESS_PARENT, orderable=False)
|
||||
interface = tables.Column(orderable=False)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = IPAddress
|
||||
fields = ('pk', 'address', 'vrf', 'status', 'role', 'tenant', 'device', 'interface', 'description')
|
||||
fields = ('pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description')
|
||||
row_attrs = {
|
||||
'class': lambda record: 'success' if not isinstance(record, IPAddress) else '',
|
||||
}
|
||||
@@ -283,7 +283,7 @@ class IPAddressDetailTable(IPAddressTable):
|
||||
|
||||
class Meta(IPAddressTable.Meta):
|
||||
fields = (
|
||||
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'device', 'interface', 'description',
|
||||
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'description',
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from django.shortcuts import get_object_or_404, render
|
||||
from django.urls import reverse
|
||||
from django.views.generic import View
|
||||
|
||||
from dcim.models import Device
|
||||
from dcim.models import Device, Interface
|
||||
from utilities.paginator import EnhancedPaginator
|
||||
from utilities.views import (
|
||||
BulkCreateView, BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||
@@ -597,7 +597,7 @@ class IPAddressListView(ObjectListView):
|
||||
queryset = IPAddress.objects.select_related(
|
||||
'vrf__tenant', 'tenant', 'nat_inside'
|
||||
).prefetch_related(
|
||||
'interface__device'
|
||||
'interface__device', 'interface__virtual_machine'
|
||||
)
|
||||
filter = filters.IPAddressFilter
|
||||
filter_form = forms.IPAddressFilterForm
|
||||
@@ -657,6 +657,17 @@ class IPAddressCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||
template_name = 'ipam/ipaddress_edit.html'
|
||||
default_return_url = 'ipam:ipaddress_list'
|
||||
|
||||
def alter_obj(self, obj, request, url_args, url_kwargs):
|
||||
|
||||
interface_id = request.GET.get('interface')
|
||||
if interface_id:
|
||||
try:
|
||||
obj.interface = Interface.objects.get(pk=interface_id)
|
||||
except (ValueError, Interface.DoesNotExist):
|
||||
pass
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class IPAddressEditView(IPAddressCreateView):
|
||||
permission_required = 'ipam.change_ipaddress'
|
||||
|
||||
Reference in New Issue
Block a user