From b475a575e43003143ae80f858995934a89319860 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 14 Feb 2020 15:04:33 -0500 Subject: [PATCH] Drop family column from Aggregate, Prefix, and IPAddress models --- netbox/ipam/fields.py | 2 + netbox/ipam/filters.py | 6 +- netbox/ipam/lookups.py | 9 +++ netbox/ipam/managers.py | 2 +- netbox/ipam/migrations/0035_drop_ip_family.py | 38 ++++++++++++ netbox/ipam/models.py | 58 +++++++++---------- netbox/ipam/views.py | 2 +- netbox/templates/ipam/aggregate.html | 2 +- netbox/templates/ipam/ipaddress.html | 2 +- netbox/templates/ipam/prefix.html | 2 +- 10 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 netbox/ipam/migrations/0035_drop_ip_family.py diff --git a/netbox/ipam/fields.py b/netbox/ipam/fields.py index 456a7debc..7d28127a4 100644 --- a/netbox/ipam/fields.py +++ b/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) diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py index 5f8bcabff..e773fed27 100644 --- a/netbox/ipam/filters.py +++ b/netbox/ipam/filters.py @@ -91,7 +91,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(): @@ -211,7 +211,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(): @@ -350,7 +350,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(): diff --git a/netbox/ipam/lookups.py b/netbox/ipam/lookups.py index 62b356c5d..306e1e925 100644 --- a/netbox/ipam/lookups.py +++ b/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' diff --git a/netbox/ipam/managers.py b/netbox/ipam/managers.py index 8aebc60ce..47dd08251 100644 --- a/netbox/ipam/managers.py +++ b/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') diff --git a/netbox/ipam/migrations/0035_drop_ip_family.py b/netbox/ipam/migrations/0035_drop_ip_family.py new file mode 100644 index 000000000..e0142973f --- /dev/null +++ b/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', + ), + ] diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index b4ba92fb5..025c4c8af 100644 --- a/netbox/ipam/models.py +++ b/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, diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 053098f0b..faf349222 100644 --- a/netbox/ipam/views.py +++ b/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)) diff --git a/netbox/templates/ipam/aggregate.html b/netbox/templates/ipam/aggregate.html index 66281aace..c34380722 100644 --- a/netbox/templates/ipam/aggregate.html +++ b/netbox/templates/ipam/aggregate.html @@ -64,7 +64,7 @@ - + diff --git a/netbox/templates/ipam/ipaddress.html b/netbox/templates/ipam/ipaddress.html index 50bd90610..167d3fddf 100644 --- a/netbox/templates/ipam/ipaddress.html +++ b/netbox/templates/ipam/ipaddress.html @@ -65,7 +65,7 @@
Family{{ aggregate.get_family_display }}IPv{{ aggregate.family }}
RIR
- + diff --git a/netbox/templates/ipam/prefix.html b/netbox/templates/ipam/prefix.html index 324bd927d..f14cab259 100644 --- a/netbox/templates/ipam/prefix.html +++ b/netbox/templates/ipam/prefix.html @@ -85,7 +85,7 @@
Family{{ ipaddress.get_family_display }}IPv{{ ipaddress.family }}
VRF
- +
Family{{ prefix.get_family_display }}IPv{{ prefix.family }}
VRF