Просмотр исходного кода

Merge pull request #4178 from netbox-community/4081-drop-ip-family

Closes #4081: Drop the family column from IP objects
Jeremy Stretch 6 лет назад
Родитель
Сommit
f6cbce65fa

+ 1 - 0
docs/release-notes/version-2.7.md

@@ -9,6 +9,7 @@
 
 * [#2519](https://github.com/netbox-community/netbox/issues/2519) - Avoid race condition when provisioning "next available" IPs/prefixes via the API
 * [#4168](https://github.com/netbox-community/netbox/issues/4168) - Role is not required when creating a virtual machine
+* [#4175](https://github.com/netbox-community/netbox/issues/4175) - Fix potential exception when bulk editing objects from a filtered list
 
 ---
 

+ 2 - 2
netbox/dcim/forms.py

@@ -1857,14 +1857,14 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
 
                 # Collect interface IPs
                 interface_ips = IPAddress.objects.prefetch_related('interface').filter(
-                    family=family, interface_id__in=interface_ids
+                    address__family=family, interface_id__in=interface_ids
                 )
                 if interface_ips:
                     ip_list = [(ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips]
                     ip_choices.append(('Interface IPs', ip_list))
                 # Collect NAT IPs
                 nat_ips = IPAddress.objects.prefetch_related('nat_inside').filter(
-                    family=family, nat_inside__interface__in=interface_ids
+                    address__family=family, nat_inside__interface__in=interface_ids
                 )
                 if nat_ips:
                     ip_list = [(ip.id, '{} ({})'.format(ip.address, ip.nat_inside.address)) for ip in nat_ips]

+ 2 - 2
netbox/dcim/tests/test_filters.py

@@ -1144,8 +1144,8 @@ class DeviceTestCase(TestCase):
 
         # Assign primary IPs for filtering
         ipaddresses = (
-            IPAddress(family=4, address='192.0.2.1/24', interface=interfaces[0]),
-            IPAddress(family=4, address='192.0.2.2/24', interface=interfaces[1]),
+            IPAddress(address='192.0.2.1/24', interface=interfaces[0]),
+            IPAddress(address='192.0.2.2/24', interface=interfaces[1]),
         )
         IPAddress.objects.bulk_create(ipaddresses)
         Device.objects.filter(pk=devices[0].pk).update(primary_ip4=ipaddresses[0])

+ 2 - 0
netbox/ipam/fields.py

@@ -63,6 +63,7 @@ IPNetworkField.register_lookup(lookups.NetContained)
 IPNetworkField.register_lookup(lookups.NetContainedOrEqual)
 IPNetworkField.register_lookup(lookups.NetContains)
 IPNetworkField.register_lookup(lookups.NetContainsOrEquals)
+IPNetworkField.register_lookup(lookups.NetFamily)
 IPNetworkField.register_lookup(lookups.NetMaskLength)
 
 
@@ -90,4 +91,5 @@ IPAddressField.register_lookup(lookups.NetContainsOrEquals)
 IPAddressField.register_lookup(lookups.NetHost)
 IPAddressField.register_lookup(lookups.NetIn)
 IPAddressField.register_lookup(lookups.NetHostContained)
+IPAddressField.register_lookup(lookups.NetFamily)
 IPAddressField.register_lookup(lookups.NetMaskLength)

+ 15 - 3
netbox/ipam/filters.py

@@ -73,6 +73,10 @@ class AggregateFilterSet(CustomFieldFilterSet, CreatedUpdatedFilterSet):
         method='search',
         label='Search',
     )
+    family = django_filters.NumberFilter(
+        field_name='prefix',
+        lookup_expr='family'
+    )
     prefix = django_filters.CharFilter(
         method='filter_prefix',
         label='Prefix',
@@ -91,7 +95,7 @@ class AggregateFilterSet(CustomFieldFilterSet, CreatedUpdatedFilterSet):
 
     class Meta:
         model = Aggregate
-        fields = ['family', 'date_added']
+        fields = ('date_added',)
 
     def search(self, queryset, name, value):
         if not value.strip():
@@ -134,6 +138,10 @@ class PrefixFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
         method='search',
         label='Search',
     )
+    family = django_filters.NumberFilter(
+        field_name='prefix',
+        lookup_expr='family'
+    )
     prefix = django_filters.CharFilter(
         method='filter_prefix',
         label='Prefix',
@@ -211,7 +219,7 @@ class PrefixFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
 
     class Meta:
         model = Prefix
-        fields = ['family', 'is_pool']
+        fields = ('is_pool',)
 
     def search(self, queryset, name, value):
         if not value.strip():
@@ -282,6 +290,10 @@ class IPAddressFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedF
         method='search',
         label='Search',
     )
+    family = django_filters.NumberFilter(
+        field_name='address',
+        lookup_expr='family'
+    )
     parent = django_filters.CharFilter(
         method='search_by_parent',
         label='Parent prefix',
@@ -350,7 +362,7 @@ class IPAddressFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedF
 
     class Meta:
         model = IPAddress
-        fields = ['family', 'dns_name']
+        fields = ('dns_name',)
 
     def search(self, queryset, name, value):
         if not value.strip():

+ 9 - 0
netbox/ipam/lookups.py

@@ -154,6 +154,15 @@ class NetHostContained(Lookup):
         return 'CAST(HOST(%s) AS INET) << %s' % (lhs, rhs), params
 
 
+class NetFamily(Transform):
+    lookup_name = 'family'
+    function = 'FAMILY'
+
+    @property
+    def output_field(self):
+        return IntegerField()
+
+
 class NetMaskLength(Transform):
     lookup_name = 'net_mask_length'
     function = 'MASKLEN'

+ 1 - 1
netbox/ipam/managers.py

@@ -13,4 +13,4 @@ class IPAddressManager(models.Manager):
         IP address as a /32 or /128.
         """
         qs = super().get_queryset()
-        return qs.annotate(host=RawSQL('INET(HOST(ipam_ipaddress.address))', [])).order_by('family', 'host')
+        return qs.annotate(host=RawSQL('INET(HOST(ipam_ipaddress.address))', [])).order_by('host')

+ 38 - 0
netbox/ipam/migrations/0035_drop_ip_family.py

@@ -0,0 +1,38 @@
+# Generated by Django 2.2.9 on 2020-02-14 19:36
+
+from django.db import migrations
+import django.db.models.expressions
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0034_fix_ipaddress_status_dhcp'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='aggregate',
+            options={'ordering': ('prefix', 'pk')},
+        ),
+        migrations.AlterModelOptions(
+            name='ipaddress',
+            options={'ordering': ('address', 'pk'), 'verbose_name': 'IP address', 'verbose_name_plural': 'IP addresses'},
+        ),
+        migrations.AlterModelOptions(
+            name='prefix',
+            options={'ordering': (django.db.models.expressions.OrderBy(django.db.models.expressions.F('vrf'), nulls_first=True), 'prefix', 'pk'), 'verbose_name_plural': 'prefixes'},
+        ),
+        migrations.RemoveField(
+            model_name='aggregate',
+            name='family',
+        ),
+        migrations.RemoveField(
+            model_name='ipaddress',
+            name='family',
+        ),
+        migrations.RemoveField(
+            model_name='prefix',
+            name='family',
+        ),
+    ]

+ 26 - 32
netbox/ipam/models.py

@@ -150,9 +150,6 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
     An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize
     the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR.
     """
-    family = models.PositiveSmallIntegerField(
-        choices=IPAddressFamilyChoices
-    )
     prefix = IPNetworkField()
     rir = models.ForeignKey(
         to='ipam.RIR',
@@ -182,7 +179,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
     ]
 
     class Meta:
-        ordering = ('family', 'prefix', 'pk')  # (family, prefix) may be non-unique
+        ordering = ('prefix', 'pk')  # prefix may be non-unique
 
     def __str__(self):
         return str(self.prefix)
@@ -225,12 +222,6 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
                     )
                 })
 
-    def save(self, *args, **kwargs):
-        if self.prefix:
-            # Infer address family from IPNetwork object
-            self.family = self.prefix.version
-        super().save(*args, **kwargs)
-
     def to_csv(self):
         return (
             self.prefix,
@@ -239,6 +230,12 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
             self.description,
         )
 
+    @property
+    def family(self):
+        if self.prefix:
+            return self.prefix.version
+        return None
+
     def get_utilization(self):
         """
         Determine the prefix utilization of the aggregate and return it as a percentage.
@@ -291,10 +288,6 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
     VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be
     assigned to a VLAN where appropriate.
     """
-    family = models.PositiveSmallIntegerField(
-        choices=IPAddressFamilyChoices,
-        editable=False
-    )
     prefix = IPNetworkField(
         help_text='IPv4 or IPv6 network with mask'
     )
@@ -376,7 +369,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
     }
 
     class Meta:
-        ordering = (F('vrf').asc(nulls_first=True), 'family', 'prefix', 'pk')  # (vrf, family, prefix) may be non-unique
+        ordering = (F('vrf').asc(nulls_first=True), 'prefix', 'pk')  # (vrf, prefix) may be non-unique
         verbose_name_plural = 'prefixes'
 
     def __str__(self):
@@ -423,9 +416,6 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
             # Clear host bits from prefix
             self.prefix = self.prefix.cidr
 
-            # Record address family
-            self.family = self.prefix.version
-
         super().save(*args, **kwargs)
 
     def to_csv(self):
@@ -442,6 +432,12 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
             self.description,
         )
 
+    @property
+    def family(self):
+        if self.prefix:
+            return self.prefix.version
+        return None
+
     def _set_prefix_length(self, value):
         """
         Expose the IPNetwork object's prefixlen attribute on the parent model so that it can be manipulated directly,
@@ -501,9 +497,9 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
 
         # All IP addresses within a point-to-point prefix (IPv4 /31 or IPv6 /127) are considered usable
         if (
-            self.family == 4 and self.prefix.prefixlen == 31  # RFC 3021
+            self.prefix.version == 4 and self.prefix.prefixlen == 31  # RFC 3021
         ) or (
-            self.family == 6 and self.prefix.prefixlen == 127  # RFC 6164
+            self.prefix.version == 6 and self.prefix.prefixlen == 127  # RFC 6164
         ):
             return available_ips
 
@@ -546,7 +542,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
             # Compile an IPSet to avoid counting duplicate IPs
             child_count = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]).size
             prefix_size = self.prefix.size
-            if self.family == 4 and self.prefix.prefixlen < 31 and not self.is_pool:
+            if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool:
                 prefix_size -= 2
             return int(float(child_count) / prefix_size * 100)
 
@@ -562,10 +558,6 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
     for example, when mapping public addresses to private addresses. When an Interface has been assigned an IPAddress
     which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP.
     """
-    family = models.PositiveSmallIntegerField(
-        choices=IPAddressFamilyChoices,
-        editable=False
-    )
     address = IPAddressField(
         help_text='IPv4 or IPv6 address (with mask)'
     )
@@ -659,7 +651,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
     }
 
     class Meta:
-        ordering = ('family', 'address', 'pk')  # (family, address) may be non-unique
+        ordering = ('address', 'pk')  # address may be non-unique
         verbose_name = 'IP address'
         verbose_name_plural = 'IP addresses'
 
@@ -727,10 +719,6 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
 
     def save(self, *args, **kwargs):
 
-        # Record address family
-        if isinstance(self.address, netaddr.IPNetwork):
-            self.family = self.address.version
-
         # Force dns_name to lowercase
         self.dns_name = self.dns_name.lower()
 
@@ -754,9 +742,9 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
     def to_csv(self):
 
         # Determine if this IP is primary for a Device
-        if self.family == 4 and getattr(self, 'primary_ip4_for', False):
+        if self.address.version == 4 and getattr(self, 'primary_ip4_for', False):
             is_primary = True
-        elif self.family == 6 and getattr(self, 'primary_ip6_for', False):
+        elif self.address.version == 6 and getattr(self, 'primary_ip6_for', False):
             is_primary = True
         else:
             is_primary = False
@@ -775,6 +763,12 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
             self.description,
         )
 
+    @property
+    def family(self):
+        if self.address:
+            return self.address.version
+        return None
+
     def _set_mask_length(self, value):
         """
         Expose the IPNetwork object's prefixlen attribute on the parent model so that it can be manipulated directly,

+ 26 - 26
netbox/ipam/tests/test_filters.py

@@ -96,12 +96,12 @@ class AggregateTestCase(TestCase):
         RIR.objects.bulk_create(rirs)
 
         aggregates = (
-            Aggregate(family=4, prefix='10.1.0.0/16', rir=rirs[0], date_added='2020-01-01'),
-            Aggregate(family=4, prefix='10.2.0.0/16', rir=rirs[0], date_added='2020-01-02'),
-            Aggregate(family=4, prefix='10.3.0.0/16', rir=rirs[1], date_added='2020-01-03'),
-            Aggregate(family=6, prefix='2001:db8:1::/48', rir=rirs[1], date_added='2020-01-04'),
-            Aggregate(family=6, prefix='2001:db8:2::/48', rir=rirs[2], date_added='2020-01-05'),
-            Aggregate(family=6, prefix='2001:db8:3::/48', rir=rirs[2], date_added='2020-01-06'),
+            Aggregate(prefix='10.1.0.0/16', rir=rirs[0], date_added='2020-01-01'),
+            Aggregate(prefix='10.2.0.0/16', rir=rirs[0], date_added='2020-01-02'),
+            Aggregate(prefix='10.3.0.0/16', rir=rirs[1], date_added='2020-01-03'),
+            Aggregate(prefix='2001:db8:1::/48', rir=rirs[1], date_added='2020-01-04'),
+            Aggregate(prefix='2001:db8:2::/48', rir=rirs[2], date_added='2020-01-05'),
+            Aggregate(prefix='2001:db8:3::/48', rir=rirs[2], date_added='2020-01-06'),
         )
         Aggregate.objects.bulk_create(aggregates)
 
@@ -199,16 +199,16 @@ class PrefixTestCase(TestCase):
         Role.objects.bulk_create(roles)
 
         prefixes = (
-            Prefix(family=4, prefix='10.0.0.0/24', site=None, vrf=None, vlan=None, role=None, is_pool=True),
-            Prefix(family=4, prefix='10.0.1.0/24', site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
-            Prefix(family=4, prefix='10.0.2.0/24', site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
-            Prefix(family=4, prefix='10.0.3.0/24', site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
-            Prefix(family=6, prefix='2001:db8::/64', site=None, vrf=None, vlan=None, role=None, is_pool=True),
-            Prefix(family=6, prefix='2001:db8:0:1::/64', site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
-            Prefix(family=6, prefix='2001:db8:0:2::/64', site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
-            Prefix(family=6, prefix='2001:db8:0:3::/64', site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
-            Prefix(family=4, prefix='10.0.0.0/16'),
-            Prefix(family=6, prefix='2001:db8::/32'),
+            Prefix(prefix='10.0.0.0/24', site=None, vrf=None, vlan=None, role=None, is_pool=True),
+            Prefix(prefix='10.0.1.0/24', site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
+            Prefix(prefix='10.0.2.0/24', site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
+            Prefix(prefix='10.0.3.0/24', site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
+            Prefix(prefix='2001:db8::/64', site=None, vrf=None, vlan=None, role=None, is_pool=True),
+            Prefix(prefix='2001:db8:0:1::/64', site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
+            Prefix(prefix='2001:db8:0:2::/64', site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
+            Prefix(prefix='2001:db8:0:3::/64', site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
+            Prefix(prefix='10.0.0.0/16'),
+            Prefix(prefix='2001:db8::/32'),
         )
         Prefix.objects.bulk_create(prefixes)
 
@@ -333,16 +333,16 @@ class IPAddressTestCase(TestCase):
         Interface.objects.bulk_create(interfaces)
 
         ipaddresses = (
-            IPAddress(family=4, address='10.0.0.1/24', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
-            IPAddress(family=4, address='10.0.0.2/24', vrf=vrfs[0], interface=interfaces[0], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
-            IPAddress(family=4, address='10.0.0.3/24', vrf=vrfs[1], interface=interfaces[1], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
-            IPAddress(family=4, address='10.0.0.4/24', vrf=vrfs[2], interface=interfaces[2], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
-            IPAddress(family=4, address='10.0.0.1/25', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
-            IPAddress(family=6, address='2001:db8::1/64', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
-            IPAddress(family=6, address='2001:db8::2/64', vrf=vrfs[0], interface=interfaces[3], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
-            IPAddress(family=6, address='2001:db8::3/64', vrf=vrfs[1], interface=interfaces[4], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
-            IPAddress(family=6, address='2001:db8::4/64', vrf=vrfs[2], interface=interfaces[5], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
-            IPAddress(family=6, address='2001:db8::1/65', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
+            IPAddress(address='10.0.0.1/24', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
+            IPAddress(address='10.0.0.2/24', vrf=vrfs[0], interface=interfaces[0], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
+            IPAddress(address='10.0.0.3/24', vrf=vrfs[1], interface=interfaces[1], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
+            IPAddress(address='10.0.0.4/24', vrf=vrfs[2], interface=interfaces[2], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
+            IPAddress(address='10.0.0.1/25', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
+            IPAddress(address='2001:db8::1/64', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
+            IPAddress(address='2001:db8::2/64', vrf=vrfs[0], interface=interfaces[3], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
+            IPAddress(address='2001:db8::3/64', vrf=vrfs[1], interface=interfaces[4], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
+            IPAddress(address='2001:db8::4/64', vrf=vrfs[2], interface=interfaces[5], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
+            IPAddress(address='2001:db8::1/65', vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
 
         )
         IPAddress.objects.bulk_create(ipaddresses)

+ 47 - 48
netbox/ipam/tests/test_models.py

@@ -15,22 +15,22 @@ class TestAggregate(TestCase):
 
         # 25% utilization
         Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/12')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.16.0.0/12')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.32.0.0/12')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.48.0.0/12')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/12')),
+            Prefix(prefix=netaddr.IPNetwork('10.16.0.0/12')),
+            Prefix(prefix=netaddr.IPNetwork('10.32.0.0/12')),
+            Prefix(prefix=netaddr.IPNetwork('10.48.0.0/12')),
         ))
         self.assertEqual(aggregate.get_utilization(), 25)
 
         # 50% utilization
         Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.64.0.0/10')),
+            Prefix(prefix=netaddr.IPNetwork('10.64.0.0/10')),
         ))
         self.assertEqual(aggregate.get_utilization(), 50)
 
         # 100% utilization
         Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.128.0.0/9')),
+            Prefix(prefix=netaddr.IPNetwork('10.128.0.0/9')),
         ))
         self.assertEqual(aggregate.get_utilization(), 100)
 
@@ -39,9 +39,9 @@ class TestPrefix(TestCase):
 
     def test_get_duplicates(self):
         prefixes = Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('192.0.2.0/24')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('192.0.2.0/24')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('192.0.2.0/24')),
+            Prefix(prefix=netaddr.IPNetwork('192.0.2.0/24')),
+            Prefix(prefix=netaddr.IPNetwork('192.0.2.0/24')),
+            Prefix(prefix=netaddr.IPNetwork('192.0.2.0/24')),
         ))
         duplicate_prefix_pks = [p.pk for p in prefixes[0].get_duplicates()]
 
@@ -54,11 +54,11 @@ class TestPrefix(TestCase):
             VRF(name='VRF 3'),
         ))
         prefixes = Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/16'), status=PrefixStatusChoices.STATUS_CONTAINER),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/24'), vrf=None),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.1.0/24'), vrf=vrfs[0]),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.2.0/24'), vrf=vrfs[1]),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.3.0/24'), vrf=vrfs[2]),
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/16'), status=PrefixStatusChoices.STATUS_CONTAINER),
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/24'), vrf=None),
+            Prefix(prefix=netaddr.IPNetwork('10.0.1.0/24'), vrf=vrfs[0]),
+            Prefix(prefix=netaddr.IPNetwork('10.0.2.0/24'), vrf=vrfs[1]),
+            Prefix(prefix=netaddr.IPNetwork('10.0.3.0/24'), vrf=vrfs[2]),
         ))
         child_prefix_pks = {p.pk for p in prefixes[0].get_child_prefixes()}
 
@@ -79,13 +79,13 @@ class TestPrefix(TestCase):
             VRF(name='VRF 3'),
         ))
         parent_prefix = Prefix.objects.create(
-            family=4, prefix=netaddr.IPNetwork('10.0.0.0/16'), status=PrefixStatusChoices.STATUS_CONTAINER
+            prefix=netaddr.IPNetwork('10.0.0.0/16'), status=PrefixStatusChoices.STATUS_CONTAINER
         )
         ips = IPAddress.objects.bulk_create((
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.1/24'), vrf=None),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.1.1/24'), vrf=vrfs[0]),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.2.1/24'), vrf=vrfs[1]),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.3.1/24'), vrf=vrfs[2]),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.1/24'), vrf=None),
+            IPAddress(address=netaddr.IPNetwork('10.0.1.1/24'), vrf=vrfs[0]),
+            IPAddress(address=netaddr.IPNetwork('10.0.2.1/24'), vrf=vrfs[1]),
+            IPAddress(address=netaddr.IPNetwork('10.0.3.1/24'), vrf=vrfs[2]),
         ))
         child_ip_pks = {p.pk for p in parent_prefix.get_child_ips()}
 
@@ -102,10 +102,10 @@ class TestPrefix(TestCase):
     def test_get_available_prefixes(self):
 
         prefixes = Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/16')),  # Parent prefix
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/20')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.32.0/20')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.128.0/18')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/16')),  # Parent prefix
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/20')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.32.0/20')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.128.0/18')),
         ))
         missing_prefixes = netaddr.IPSet([
             netaddr.IPNetwork('10.0.16.0/20'),
@@ -119,15 +119,15 @@ class TestPrefix(TestCase):
 
     def test_get_available_ips(self):
 
-        parent_prefix = Prefix.objects.create(family=4, prefix=netaddr.IPNetwork('10.0.0.0/28'))
+        parent_prefix = Prefix.objects.create(prefix=netaddr.IPNetwork('10.0.0.0/28'))
         IPAddress.objects.bulk_create((
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.1/26')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.3/26')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.5/26')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.7/26')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.9/26')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.11/26')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.13/26')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.1/26')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.3/26')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.5/26')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.7/26')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.9/26')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.11/26')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.13/26')),
         ))
         missing_ips = netaddr.IPSet([
             '10.0.0.2/32',
@@ -145,40 +145,39 @@ class TestPrefix(TestCase):
     def test_get_first_available_prefix(self):
 
         prefixes = Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/16')),  # Parent prefix
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/24')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.1.0/24')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.2.0/24')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/16')),  # Parent prefix
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/24')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.1.0/24')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.2.0/24')),
         ))
         self.assertEqual(prefixes[0].get_first_available_prefix(), netaddr.IPNetwork('10.0.3.0/24'))
 
-        Prefix.objects.create(family=4, prefix=netaddr.IPNetwork('10.0.3.0/24'))
+        Prefix.objects.create(prefix=netaddr.IPNetwork('10.0.3.0/24'))
         self.assertEqual(prefixes[0].get_first_available_prefix(), netaddr.IPNetwork('10.0.4.0/22'))
 
     def test_get_first_available_ip(self):
 
-        parent_prefix = Prefix.objects.create(family=4, prefix=netaddr.IPNetwork('10.0.0.0/24'))
+        parent_prefix = Prefix.objects.create(prefix=netaddr.IPNetwork('10.0.0.0/24'))
         IPAddress.objects.bulk_create((
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.1/24')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.2/24')),
-            IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.3/24')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.1/24')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.2/24')),
+            IPAddress(address=netaddr.IPNetwork('10.0.0.3/24')),
         ))
         self.assertEqual(parent_prefix.get_first_available_ip(), '10.0.0.4/24')
 
-        IPAddress.objects.create(family=4, address=netaddr.IPNetwork('10.0.0.4/24'))
+        IPAddress.objects.create(address=netaddr.IPNetwork('10.0.0.4/24'))
         self.assertEqual(parent_prefix.get_first_available_ip(), '10.0.0.5/24')
 
     def test_get_utilization(self):
 
         # Container Prefix
         prefix = Prefix.objects.create(
-            family=4,
             prefix=netaddr.IPNetwork('10.0.0.0/24'),
             status=PrefixStatusChoices.STATUS_CONTAINER
         )
         Prefix.objects.bulk_create((
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.0/26')),
-            Prefix(family=4, prefix=netaddr.IPNetwork('10.0.0.128/26')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.0/26')),
+            Prefix(prefix=netaddr.IPNetwork('10.0.0.128/26')),
         ))
         self.assertEqual(prefix.get_utilization(), 50)
 
@@ -187,7 +186,7 @@ class TestPrefix(TestCase):
         prefix.save()
         IPAddress.objects.bulk_create(
             # Create 32 IPAddresses within the Prefix
-            [IPAddress(family=4, address=netaddr.IPNetwork('10.0.0.{}/24'.format(i))) for i in range(1, 33)]
+            [IPAddress(address=netaddr.IPNetwork('10.0.0.{}/24'.format(i))) for i in range(1, 33)]
         )
         self.assertEqual(prefix.get_utilization(), 12)  # ~= 12%
 
@@ -224,9 +223,9 @@ class TestIPAddress(TestCase):
 
     def test_get_duplicates(self):
         ips = IPAddress.objects.bulk_create((
-            IPAddress(family=4, address=netaddr.IPNetwork('192.0.2.1/24')),
-            IPAddress(family=4, address=netaddr.IPNetwork('192.0.2.1/24')),
-            IPAddress(family=4, address=netaddr.IPNetwork('192.0.2.1/24')),
+            IPAddress(address=netaddr.IPNetwork('192.0.2.1/24')),
+            IPAddress(address=netaddr.IPNetwork('192.0.2.1/24')),
+            IPAddress(address=netaddr.IPNetwork('192.0.2.1/24')),
         ))
         duplicate_ip_pks = [p.pk for p in ips[0].get_duplicates()]
 

+ 81 - 81
netbox/ipam/tests/test_ordering.py

@@ -42,45 +42,45 @@ class PrefixOrderingTestCase(OrderingTestBase):
 
         # Setup Prefixes
         prefixes = (
-            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.0.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.1.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.2.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.3.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.4.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.5.0/24')),
-
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/8')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.1.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.2.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.3.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.4.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.1.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.2.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.3.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.4.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.1.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.2.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.3.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.4.0/24')),
-
-            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.0.0/12')),
-            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.0.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.1.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.2.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.3.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.4.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.0.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.1.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.2.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.3.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.4.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, prefix=netaddr.IPNetwork('192.168.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.0.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.2.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.3.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.4.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.5.0/24')),
+
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.0.0/8')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.0.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.2.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.3.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.4.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.1.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.1.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.1.2.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.1.3.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.1.4.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.2.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.2.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.2.2.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.2.3.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.2.4.0/24')),
+
+            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, prefix=netaddr.IPNetwork('172.16.0.0/12')),
+            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, prefix=netaddr.IPNetwork('172.16.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.16.0.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.16.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.16.2.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.16.3.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.16.4.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, prefix=netaddr.IPNetwork('172.17.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.17.0.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.17.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.17.2.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.17.3.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, prefix=netaddr.IPNetwork('172.17.4.0/24')),
         )
 
         Prefix.objects.bulk_create(prefixes)
@@ -109,15 +109,15 @@ class PrefixOrderingTestCase(OrderingTestBase):
 
         # Setup Prefixes
         prefixes = [
-            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, family=4, prefix=netaddr.IPNetwork('10.0.0.0/8')),
-            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, family=4, prefix=netaddr.IPNetwork('10.0.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('10.1.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.0.0/16')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.1.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.1.0/25')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.0.0/24')),
-            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, prefix=netaddr.IPNetwork('10.0.0.0/8')),
+            Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, prefix=netaddr.IPNetwork('10.0.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('10.1.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.0.0/16')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.0.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.1.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.0.1.0/25')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.1.0.0/24')),
+            Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, prefix=netaddr.IPNetwork('10.1.1.0/24')),
         ]
         Prefix.objects.bulk_create(prefixes)
 
@@ -136,39 +136,39 @@ class IPAddressOrderingTestCase(OrderingTestBase):
 
         # Setup Addresses
         addresses = (
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.0.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.1.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.2.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.3.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.4.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.0.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.1.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.2.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.3.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.4.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.0.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.1.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.2.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.3.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.4.1/24')),
-
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.0.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.1.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.2.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.3.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.4.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.0.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.1.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.2.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.3.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.4.1/24')),
-
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.0.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.1.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.2.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.3.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.4.1/24')),
-            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.5.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.0.0.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.0.1.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.0.2.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.0.3.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.0.4.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.1.0.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.1.1.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.1.2.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.1.3.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.1.4.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.2.0.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.2.1.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.2.2.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.2.3.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, address=netaddr.IPNetwork('10.2.4.1/24')),
+
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.16.0.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.16.1.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.16.2.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.16.3.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.16.4.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.17.0.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.17.1.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.17.2.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.17.3.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, address=netaddr.IPNetwork('172.17.4.1/24')),
+
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.0.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.1.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.2.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.3.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.4.1/24')),
+            IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.5.1/24')),
         )
         IPAddress.objects.bulk_create(addresses)
 

+ 9 - 10
netbox/ipam/tests/test_views.py

@@ -82,13 +82,12 @@ class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         RIR.objects.bulk_create(rirs)
 
         Aggregate.objects.bulk_create([
-            Aggregate(family=4, prefix=IPNetwork('10.1.0.0/16'), rir=rirs[0]),
-            Aggregate(family=4, prefix=IPNetwork('10.2.0.0/16'), rir=rirs[0]),
-            Aggregate(family=4, prefix=IPNetwork('10.3.0.0/16'), rir=rirs[0]),
+            Aggregate(prefix=IPNetwork('10.1.0.0/16'), rir=rirs[0]),
+            Aggregate(prefix=IPNetwork('10.2.0.0/16'), rir=rirs[0]),
+            Aggregate(prefix=IPNetwork('10.3.0.0/16'), rir=rirs[0]),
         ])
 
         cls.form_data = {
-            'family': 4,
             'prefix': IPNetwork('10.99.0.0/16'),
             'rir': rirs[1].pk,
             'date_added': datetime.date(2020, 1, 1),
@@ -161,9 +160,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         )
 
         Prefix.objects.bulk_create([
-            Prefix(family=4, prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
-            Prefix(family=4, prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
-            Prefix(family=4, prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
+            Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
+            Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
+            Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
         ])
 
         cls.form_data = {
@@ -209,9 +208,9 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         )
 
         IPAddress.objects.bulk_create([
-            IPAddress(family=4, address=IPNetwork('192.0.2.1/24'), vrf=vrfs[0]),
-            IPAddress(family=4, address=IPNetwork('192.0.2.2/24'), vrf=vrfs[0]),
-            IPAddress(family=4, address=IPNetwork('192.0.2.3/24'), vrf=vrfs[0]),
+            IPAddress(address=IPNetwork('192.0.2.1/24'), vrf=vrfs[0]),
+            IPAddress(address=IPNetwork('192.0.2.2/24'), vrf=vrfs[0]),
+            IPAddress(address=IPNetwork('192.0.2.3/24'), vrf=vrfs[0]),
         ])
 
         cls.form_data = {

+ 1 - 1
netbox/ipam/views.py

@@ -207,7 +207,7 @@ class RIRListView(PermissionRequiredMixin, ObjectListView):
                 'deprecated': 0,
                 'available': 0,
             }
-            aggregate_list = Aggregate.objects.filter(family=family, rir=rir)
+            aggregate_list = Aggregate.objects.filter(prefix__family=family, rir=rir)
             for aggregate in aggregate_list:
 
                 queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(aggregate.prefix))

+ 1 - 1
netbox/templates/ipam/aggregate.html

@@ -64,7 +64,7 @@
             <table class="table table-hover panel-body attr-table">
                 <tr>
                     <td>Family</td>
-                    <td>{{ aggregate.get_family_display }}</td>
+                    <td>IPv{{ aggregate.family }}</td>
                 </tr>
                 <tr>
                     <td>RIR</td>

+ 1 - 1
netbox/templates/ipam/ipaddress.html

@@ -65,7 +65,7 @@
             <table class="table table-hover panel-body attr-table">
                 <tr>
                     <td>Family</td>
-                    <td>{{ ipaddress.get_family_display }}</td>
+                    <td>IPv{{ ipaddress.family }}</td>
                 </tr>
                 <tr>
                     <td>VRF</td>

+ 1 - 1
netbox/templates/ipam/prefix.html

@@ -85,7 +85,7 @@
                 <table class="table table-hover panel-body attr-table">
                     <tr>
                         <td>Family</td>
-                        <td>{{ prefix.get_family_display }}</td>
+                        <td>IPv{{ prefix.family }}</td>
                     </tr>
                     <tr>
                         <td>VRF</td>

+ 2 - 2
netbox/templates/utilities/obj_list.html

@@ -51,12 +51,12 @@
                 <div class="pull-left noprint">
                     {% block bulk_buttons %}{% endblock %}
                     {% if bulk_edit_url and permissions.change %}
-                        <button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm">
+                        <button type="submit" name="_edit" formaction="{% url bulk_edit_url %}" class="btn btn-warning btn-sm">
                             <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Selected
                         </button>
                     {% endif %}
                     {% if bulk_delete_url and permissions.delete %}
-                        <button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm">
+                        <button type="submit" name="_delete" formaction="{% url bulk_delete_url %}" class="btn btn-danger btn-sm">
                             <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete Selected
                         </button>
                     {% endif %}

+ 1 - 5
netbox/utilities/views.py

@@ -634,7 +634,7 @@ class BulkEditView(GetReturnURLMixin, View):
             post_data['pk'] = [obj.pk for obj in self.filterset(request.GET, model.objects.only('pk')).qs]
 
         if '_apply' in request.POST:
-            form = self.form(model, request.POST, initial=request.GET)
+            form = self.form(model, request.POST)
             if form.is_valid():
 
                 custom_fields = form.custom_fields if hasattr(form, 'custom_fields') else []
@@ -718,10 +718,6 @@ class BulkEditView(GetReturnURLMixin, View):
         else:
             # Pass the PK list as initial data to avoid binding the form
             initial_data = querydict_to_dict(post_data)
-
-            # Append any normal initial data (passed as GET parameters)
-            initial_data.update(request.GET)
-
             form = self.form(model, initial=initial_data)
 
         # Retrieve objects being edited

+ 2 - 2
netbox/virtualization/forms.py

@@ -408,7 +408,7 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
                 ip_choices = [(None, '---------')]
                 # Collect interface IPs
                 interface_ips = IPAddress.objects.prefetch_related('interface').filter(
-                    family=family, interface__virtual_machine=self.instance
+                    address__family=family, interface__virtual_machine=self.instance
                 )
                 if interface_ips:
                     ip_choices.append(
@@ -418,7 +418,7 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
                     )
                 # Collect NAT IPs
                 nat_ips = IPAddress.objects.prefetch_related('nat_inside').filter(
-                    family=family, nat_inside__interface__virtual_machine=self.instance
+                    address__family=family, nat_inside__interface__virtual_machine=self.instance
                 )
                 if nat_ips:
                     ip_choices.append(