mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Initial work on #93: Primary IPv4/IPv6 support
This commit is contained in:
@ -221,12 +221,14 @@ class DeviceSerializer(serializers.ModelSerializer):
|
|||||||
platform = PlatformNestedSerializer()
|
platform = PlatformNestedSerializer()
|
||||||
rack = RackNestedSerializer()
|
rack = RackNestedSerializer()
|
||||||
primary_ip = DeviceIPAddressNestedSerializer()
|
primary_ip = DeviceIPAddressNestedSerializer()
|
||||||
|
primary_ip4 = DeviceIPAddressNestedSerializer()
|
||||||
|
primary_ip6 = DeviceIPAddressNestedSerializer()
|
||||||
parent_device = serializers.SerializerMethodField()
|
parent_device = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = ['id', 'name', 'display_name', 'device_type', 'device_role', 'platform', 'serial', 'rack', 'position',
|
fields = ['id', 'name', 'display_name', 'device_type', 'device_role', 'platform', 'serial', 'rack', 'position',
|
||||||
'face', 'parent_device', 'status', 'primary_ip', 'comments']
|
'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6', 'comments']
|
||||||
|
|
||||||
def get_parent_device(self, obj):
|
def get_parent_device(self, obj):
|
||||||
try:
|
try:
|
||||||
|
@ -194,7 +194,7 @@ class DeviceListView(generics.ListAPIView):
|
|||||||
List devices (filterable)
|
List devices (filterable)
|
||||||
"""
|
"""
|
||||||
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'platform', 'rack__site')\
|
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'platform', 'rack__site')\
|
||||||
.prefetch_related('primary_ip__nat_outside')
|
.prefetch_related('primary_ip4__nat_outside', 'primary_ip6__nat_outside')
|
||||||
serializer_class = serializers.DeviceSerializer
|
serializer_class = serializers.DeviceSerializer
|
||||||
filter_class = filters.DeviceFilter
|
filter_class = filters.DeviceFilter
|
||||||
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
|
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
|
||||||
|
@ -1919,7 +1919,8 @@
|
|||||||
"position": 1,
|
"position": 1,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": 1,
|
"primary_ip4": 1,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1938,7 +1939,8 @@
|
|||||||
"position": 17,
|
"position": 17,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": 5,
|
"primary_ip4": 5,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1957,7 +1959,8 @@
|
|||||||
"position": 33,
|
"position": 33,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": null,
|
"primary_ip4": null,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1976,7 +1979,8 @@
|
|||||||
"position": 34,
|
"position": 34,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": null,
|
"primary_ip4": null,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1995,7 +1999,8 @@
|
|||||||
"position": 34,
|
"position": 34,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": null,
|
"primary_ip4": null,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2014,7 +2019,8 @@
|
|||||||
"position": 33,
|
"position": 33,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": null,
|
"primary_ip4": null,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2033,7 +2039,8 @@
|
|||||||
"position": 1,
|
"position": 1,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": 3,
|
"primary_ip4": 3,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2052,7 +2059,8 @@
|
|||||||
"position": 17,
|
"position": 17,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": 19,
|
"primary_ip4": 19,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2071,7 +2079,8 @@
|
|||||||
"position": 42,
|
"position": 42,
|
||||||
"face": 0,
|
"face": 0,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": null,
|
"primary_ip4": null,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2090,7 +2099,8 @@
|
|||||||
"position": null,
|
"position": null,
|
||||||
"face": null,
|
"face": null,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": null,
|
"primary_ip4": null,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2109,7 +2119,8 @@
|
|||||||
"position": null,
|
"position": null,
|
||||||
"face": null,
|
"face": null,
|
||||||
"status": true,
|
"status": true,
|
||||||
"primary_ip": null,
|
"primary_ip4": null,
|
||||||
|
"primary_ip6": null,
|
||||||
"comments": ""
|
"comments": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -349,7 +349,7 @@ class DeviceForm(forms.ModelForm, BootstrapMixin):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = ['name', 'device_role', 'device_type', 'serial', 'site', 'rack', 'position', 'face', 'status',
|
fields = ['name', 'device_role', 'device_type', 'serial', 'site', 'rack', 'position', 'face', 'status',
|
||||||
'platform', 'primary_ip', 'comments']
|
'platform', 'primary_ip4', 'primary_ip6', 'comments']
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'device_role': "The function this device serves",
|
'device_role': "The function this device serves",
|
||||||
'serial': "Chassis serial number",
|
'serial': "Chassis serial number",
|
||||||
@ -369,20 +369,23 @@ class DeviceForm(forms.ModelForm, BootstrapMixin):
|
|||||||
self.initial['site'] = self.instance.rack.site
|
self.initial['site'] = self.instance.rack.site
|
||||||
self.initial['manufacturer'] = self.instance.device_type.manufacturer
|
self.initial['manufacturer'] = self.instance.device_type.manufacturer
|
||||||
|
|
||||||
# Compile list of IPs assigned to this device
|
# Compile list of choices for primary IPv4 and IPv6 addresses
|
||||||
primary_ip_choices = []
|
for family in [4, 6]:
|
||||||
interface_ips = IPAddress.objects.filter(interface__device=self.instance)
|
ip_choices = []
|
||||||
primary_ip_choices += [(ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips]
|
interface_ips = IPAddress.objects.filter(family=family, interface__device=self.instance)
|
||||||
nat_ips = IPAddress.objects.filter(nat_inside__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')
|
.select_related('nat_inside__interface')
|
||||||
primary_ip_choices += [(ip.id, '{} ({} NAT)'.format(ip.address, ip.nat_inside.interface)) for ip in nat_ips]
|
ip_choices += [(ip.id, '{} ({} NAT)'.format(ip.address, ip.nat_inside.interface)) for ip in nat_ips]
|
||||||
self.fields['primary_ip'].choices = [(None, '---------')] + primary_ip_choices
|
self.fields['primary_ip{}'.format(family)].choices = [(None, '---------')] + ip_choices
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# An object that doesn't exist yet can't have any IPs assigned to it
|
# An object that doesn't exist yet can't have any IPs assigned to it
|
||||||
self.fields['primary_ip'].choices = []
|
self.fields['primary_ip4'].choices = []
|
||||||
self.fields['primary_ip'].widget.attrs['readonly'] = True
|
self.fields['primary_ip4'].widget.attrs['readonly'] = True
|
||||||
|
self.fields['primary_ip6'].choices = []
|
||||||
|
self.fields['primary_ip6'].widget.attrs['readonly'] = True
|
||||||
|
|
||||||
# Limit rack choices
|
# Limit rack choices
|
||||||
if self.is_bound:
|
if self.is_bound:
|
||||||
|
27
netbox/dcim/migrations/0006_add_device_primary_ip4_ip6.py
Normal file
27
netbox/dcim/migrations/0006_add_device_primary_ip4_ip6.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.7 on 2016-07-11 18:40
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('ipam', '0001_initial'),
|
||||||
|
('dcim', '0005_auto_20160706_1722'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='device',
|
||||||
|
name='primary_ip4',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip4_for', to='ipam.IPAddress', verbose_name=b'Primary IPv4'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='device',
|
||||||
|
name='primary_ip6',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip6_for', to='ipam.IPAddress', verbose_name=b'Primary IPv6'),
|
||||||
|
),
|
||||||
|
]
|
41
netbox/dcim/migrations/0007_device_copy_primary_ip.py
Normal file
41
netbox/dcim/migrations/0007_device_copy_primary_ip.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.7 on 2016-07-11 18:40
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def copy_primary_ip(apps, schema_editor):
|
||||||
|
Device = apps.get_model('dcim', 'Device')
|
||||||
|
for d in Device.objects.select_related('primary_ip'):
|
||||||
|
if not d.primary_ip:
|
||||||
|
continue
|
||||||
|
if d.primary_ip.family == 4:
|
||||||
|
d.primary_ip4 = d.primary_ip
|
||||||
|
elif d.primary_ip.family == 6:
|
||||||
|
d.primary_ip6 = d.primary_ip
|
||||||
|
d.save()
|
||||||
|
|
||||||
|
|
||||||
|
def restore_primary_ip(apps, schema_editor):
|
||||||
|
Device = apps.get_model('dcim', 'Device')
|
||||||
|
for d in Device.objects.select_related('primary_ip4', 'primary_ip6'):
|
||||||
|
if d.primary_ip:
|
||||||
|
continue
|
||||||
|
# Prefer IPv6 over IPv4
|
||||||
|
if d.primary_ip6:
|
||||||
|
d.primary_ip = d.primary_ip6
|
||||||
|
elif d.primary_ip4:
|
||||||
|
d.primary_ip = d.primary_ip4
|
||||||
|
d.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0006_add_device_primary_ip4_ip6'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(copy_primary_ip, restore_primary_ip),
|
||||||
|
]
|
19
netbox/dcim/migrations/0008_device_remove_primary_ip.py
Normal file
19
netbox/dcim/migrations/0008_device_remove_primary_ip.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.7 on 2016-07-11 19:01
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0007_device_copy_primary_ip'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='device',
|
||||||
|
name='primary_ip',
|
||||||
|
),
|
||||||
|
]
|
@ -605,8 +605,10 @@ class Device(CreatedUpdatedModel):
|
|||||||
help_text='Number of the lowest U position occupied by the device')
|
help_text='Number of the lowest U position occupied by the device')
|
||||||
face = models.PositiveSmallIntegerField(blank=True, null=True, choices=RACK_FACE_CHOICES, verbose_name='Rack face')
|
face = models.PositiveSmallIntegerField(blank=True, null=True, choices=RACK_FACE_CHOICES, verbose_name='Rack face')
|
||||||
status = models.BooleanField(choices=STATUS_CHOICES, default=STATUS_ACTIVE, verbose_name='Status')
|
status = models.BooleanField(choices=STATUS_CHOICES, default=STATUS_ACTIVE, verbose_name='Status')
|
||||||
primary_ip = models.OneToOneField('ipam.IPAddress', related_name='primary_for', on_delete=models.SET_NULL,
|
primary_ip4 = models.OneToOneField('ipam.IPAddress', related_name='primary_ip4_for', on_delete=models.SET_NULL,
|
||||||
blank=True, null=True, verbose_name='Primary IP')
|
blank=True, null=True, verbose_name='Primary IPv4')
|
||||||
|
primary_ip6 = models.OneToOneField('ipam.IPAddress', related_name='primary_ip6_for', on_delete=models.SET_NULL,
|
||||||
|
blank=True, null=True, verbose_name='Primary IPv6')
|
||||||
comments = models.TextField(blank=True)
|
comments = models.TextField(blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -709,6 +711,15 @@ class Device(CreatedUpdatedModel):
|
|||||||
return self.name
|
return self.name
|
||||||
return '{{{}}}'.format(self.pk)
|
return '{{{}}}'.format(self.pk)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def primary_ip(self):
|
||||||
|
if self.primary_ip6:
|
||||||
|
return self.primary_ip6
|
||||||
|
elif self.primary_ip4:
|
||||||
|
return self.primary_ip4
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
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.
|
||||||
|
@ -318,6 +318,8 @@ class DeviceTest(APITestCase):
|
|||||||
'parent_device',
|
'parent_device',
|
||||||
'status',
|
'status',
|
||||||
'primary_ip',
|
'primary_ip',
|
||||||
|
'primary_ip4',
|
||||||
|
'primary_ip6',
|
||||||
'comments',
|
'comments',
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -375,6 +377,10 @@ class DeviceTest(APITestCase):
|
|||||||
'primary_ip_address',
|
'primary_ip_address',
|
||||||
'primary_ip_family',
|
'primary_ip_family',
|
||||||
'primary_ip_id',
|
'primary_ip_id',
|
||||||
|
'primary_ip4_address',
|
||||||
|
'primary_ip4_family',
|
||||||
|
'primary_ip4_id',
|
||||||
|
'primary_ip6',
|
||||||
'rack_display_name',
|
'rack_display_name',
|
||||||
'rack_facility_id',
|
'rack_facility_id',
|
||||||
'rack_id',
|
'rack_id',
|
||||||
|
@ -501,7 +501,8 @@ class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class DeviceListView(ObjectListView):
|
class DeviceListView(ObjectListView):
|
||||||
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'rack__site', 'primary_ip')
|
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'rack__site', 'primary_ip4',
|
||||||
|
'primary_ip6')
|
||||||
filter = filters.DeviceFilter
|
filter = filters.DeviceFilter
|
||||||
filter_form = forms.DeviceFilterForm
|
filter_form = forms.DeviceFilterForm
|
||||||
table = tables.DeviceTable
|
table = tables.DeviceTable
|
||||||
@ -1634,7 +1635,10 @@ def ipaddress_assign(request, pk):
|
|||||||
ipaddress.interface))
|
ipaddress.interface))
|
||||||
|
|
||||||
if form.cleaned_data['set_as_primary']:
|
if form.cleaned_data['set_as_primary']:
|
||||||
device.primary_ip = ipaddress
|
if ipaddress.family == 4:
|
||||||
|
device.primary_ip4 = ipaddress
|
||||||
|
elif ipaddress.family == 6:
|
||||||
|
device.primary_ip6 = ipaddress
|
||||||
device.save()
|
device.save()
|
||||||
|
|
||||||
if '_addanother' in request.POST:
|
if '_addanother' in request.POST:
|
||||||
|
@ -329,7 +329,7 @@ class IPAddressForm(forms.ModelForm, BootstrapMixin):
|
|||||||
|
|
||||||
class IPAddressFromCSVForm(forms.ModelForm):
|
class IPAddressFromCSVForm(forms.ModelForm):
|
||||||
vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, to_field_name='rd',
|
vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, to_field_name='rd',
|
||||||
error_messages={'invalid_choice': 'Site not found.'})
|
error_messages={'invalid_choice': 'VRF not found.'})
|
||||||
device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name',
|
device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name',
|
||||||
error_messages={'invalid_choice': 'Device not found.'})
|
error_messages={'invalid_choice': 'Device not found.'})
|
||||||
interface_name = forms.CharField(required=False)
|
interface_name = forms.CharField(required=False)
|
||||||
@ -368,7 +368,10 @@ class IPAddressFromCSVForm(forms.ModelForm):
|
|||||||
name=self.cleaned_data['interface_name'])
|
name=self.cleaned_data['interface_name'])
|
||||||
# Set as primary for device
|
# Set as primary for device
|
||||||
if self.cleaned_data['is_primary']:
|
if self.cleaned_data['is_primary']:
|
||||||
self.instance.primary_for = self.cleaned_data['device']
|
if self.instance.family == 4:
|
||||||
|
self.instance.primary_ip4_for = self.cleaned_data['device']
|
||||||
|
elif self.instance.family == 6:
|
||||||
|
self.instance.primary_ip6_for = self.cleaned_data['device']
|
||||||
|
|
||||||
return super(IPAddressFromCSVForm, self).save(commit=commit)
|
return super(IPAddressFromCSVForm, self).save(commit=commit)
|
||||||
|
|
||||||
|
@ -314,12 +314,20 @@ class IPAddress(CreatedUpdatedModel):
|
|||||||
super(IPAddress, self).save(*args, **kwargs)
|
super(IPAddress, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
|
|
||||||
|
# Determine if this IP is primary for a Device
|
||||||
|
is_primary = False
|
||||||
|
if self.family == 4 and getattr(self, 'primary_ip4_for', False):
|
||||||
|
is_primary = True
|
||||||
|
elif self.family == 6 and getattr(self, 'primary_ip6_for', False):
|
||||||
|
is_primary = True
|
||||||
|
|
||||||
return ','.join([
|
return ','.join([
|
||||||
str(self.address),
|
str(self.address),
|
||||||
self.vrf.rd if self.vrf else '',
|
self.vrf.rd if self.vrf else '',
|
||||||
self.device.identifier if self.device else '',
|
self.device.identifier if self.device else '',
|
||||||
self.interface.name if self.interface else '',
|
self.interface.name if self.interface else '',
|
||||||
'True' if getattr(self, 'primary_for', False) else '',
|
'True' if is_primary else '',
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -364,7 +364,7 @@ def prefix_ipaddresses(request, pk):
|
|||||||
|
|
||||||
# Find all IPAddresses belonging to this Prefix
|
# Find all IPAddresses belonging to this Prefix
|
||||||
ipaddresses = IPAddress.objects.filter(address__net_contained_or_equal=str(prefix.prefix))\
|
ipaddresses = IPAddress.objects.filter(address__net_contained_or_equal=str(prefix.prefix))\
|
||||||
.select_related('vrf', 'interface__device', 'primary_for')
|
.select_related('vrf', 'interface__device', 'primary_ip4_for', 'primary_ip6_for')
|
||||||
|
|
||||||
ip_table = tables.IPAddressTable(ipaddresses)
|
ip_table = tables.IPAddressTable(ipaddresses)
|
||||||
ip_table.model = IPAddress
|
ip_table.model = IPAddress
|
||||||
@ -383,7 +383,7 @@ def prefix_ipaddresses(request, pk):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class IPAddressListView(ObjectListView):
|
class IPAddressListView(ObjectListView):
|
||||||
queryset = IPAddress.objects.select_related('vrf', 'interface__device', 'primary_for')
|
queryset = IPAddress.objects.select_related('vrf', 'interface__device', 'primary_ip4_for', 'primary_ip6_for')
|
||||||
filter = filters.IPAddressFilter
|
filter = filters.IPAddressFilter
|
||||||
filter_form = forms.IPAddressFilterForm
|
filter_form = forms.IPAddressFilterForm
|
||||||
table = tables.IPAddressTable
|
table = tables.IPAddressTable
|
||||||
@ -443,8 +443,13 @@ class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
obj.save()
|
obj.save()
|
||||||
# Update primary IP for device if needed
|
# Update primary IP for device if needed
|
||||||
try:
|
try:
|
||||||
device = obj.primary_for
|
if obj.family == 4 and obj.primary_ip4_for:
|
||||||
device.primary_ip = obj
|
device = obj.primary_ip4_for
|
||||||
|
device.primary_ip4 = obj
|
||||||
|
device.save()
|
||||||
|
elif obj.family == 6 and obj.primary_ip6_for:
|
||||||
|
device = obj.primary_ip6_for
|
||||||
|
device.primary_ip6 = obj
|
||||||
device.save()
|
device.save()
|
||||||
except Device.DoesNotExist:
|
except Device.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
@ -101,14 +101,29 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Primary IP</td>
|
<td>Primary IPv4</td>
|
||||||
<td>
|
<td>
|
||||||
{% if device.primary_ip %}
|
{% if device.primary_ip4 %}
|
||||||
<a href="{% url 'ipam:ipaddress' pk=device.primary_ip.pk %}">{{ device.primary_ip.address.ip }}</a>
|
<a href="{% url 'ipam:ipaddress' pk=device.primary_ip4.pk %}">{{ device.primary_ip4.address.ip }}</a>
|
||||||
{% if device.primary_ip.nat_inside %}
|
{% if device.primary_ip4.nat_inside %}
|
||||||
<span>(NAT for {{ device.primary_ip.nat_inside.address.ip }})</span>
|
<span>(NAT for {{ device.primary_ip4.nat_inside.address.ip }})</span>
|
||||||
{% elif device.primary_ip.nat_outside %}
|
{% elif device.primary_ip4.nat_outside %}
|
||||||
<span>(NAT: {{ device.primary_ip.nat_outside.address.ip }})</span>
|
<span>(NAT: {{ device.primary_ip4.nat_outside.address.ip }})</span>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">Not defined</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Primary IPv6</td>
|
||||||
|
<td>
|
||||||
|
{% if device.primary_ip6 %}
|
||||||
|
<a href="{% url 'ipam:ipaddress' pk=device.primary_ip6.pk %}">{{ device.primary_ip6.address.ip }}</a>
|
||||||
|
{% if device.primary_ip6.nat_inside %}
|
||||||
|
<span>(NAT for {{ device.primary_ip6.nat_inside.address.ip }})</span>
|
||||||
|
{% elif device.primary_ip6.nat_outside %}
|
||||||
|
<span>(NAT: {{ device.primary_ip6.nat_outside.address.ip }})</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-muted">Not defined</span>
|
<span class="text-muted">Not defined</span>
|
||||||
|
@ -31,7 +31,10 @@
|
|||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% render_field form.platform %}
|
{% render_field form.platform %}
|
||||||
{% render_field form.status %}
|
{% render_field form.status %}
|
||||||
{% if obj %}{% render_field form.primary_ip %}{% endif %}
|
{% if obj %}
|
||||||
|
{% render_field form.primary_ip4 %}
|
||||||
|
{% render_field form.primary_ip6 %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ ip.interface }}</td>
|
<td>{{ ip.interface }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if device.primary_ip == ip %}
|
{% if device.primary_ip4 == ip or device.primary_ip6 == ip %}
|
||||||
<span class="label label-success">Primary</span>
|
<span class="label label-success">Primary</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
Reference in New Issue
Block a user