Fixes #658: Added is_pool field to Prefix model

This commit is contained in:
Jeremy Stretch 2016-12-27 15:07:52 -05:00
parent d9d7068c5f
commit 8edaff860c
8 changed files with 71 additions and 25 deletions

View File

@ -138,7 +138,7 @@ class PrefixSerializer(CustomFieldSerializer, serializers.ModelSerializer):
class Meta:
model = Prefix
fields = ['id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'description',
fields = ['id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
'custom_fields']

View File

@ -158,15 +158,7 @@ class PrefixForm(BootstrapMixin, CustomFieldForm):
class Meta:
model = Prefix
fields = ['prefix', 'vrf', 'tenant', 'site', 'vlan', 'status', 'role', 'description']
help_texts = {
'prefix': "IPv4 or IPv6 network",
'vrf': "VRF (if applicable)",
'site': "The site to which this prefix is assigned (if applicable)",
'vlan': "The VLAN to which this prefix is assigned (if applicable)",
'status': "Operational status of this prefix",
'role': "The primary function of this prefix",
}
fields = ['prefix', 'vrf', 'tenant', 'site', 'vlan', 'status', 'role', 'is_pool', 'description']
def __init__(self, *args, **kwargs):
super(PrefixForm, self).__init__(*args, **kwargs)
@ -197,7 +189,7 @@ class PrefixFromCSVForm(forms.ModelForm):
class Meta:
model = Prefix
fields = ['prefix', 'vrf', 'tenant', 'site', 'vlan_group_name', 'vlan_vid', 'status_name', 'role',
fields = ['prefix', 'vrf', 'tenant', 'site', 'vlan_group_name', 'vlan_vid', 'status_name', 'role', 'is_pool',
'description']
def clean(self):

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-27 19:34
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import ipam.fields
class Migration(migrations.Migration):
dependencies = [
('ipam', '0012_services'),
]
operations = [
migrations.AddField(
model_name='prefix',
name='is_pool',
field=models.BooleanField(default=False, help_text=b'All IP addresses within this prefix are considered usable', verbose_name=b'Is a pool'),
),
migrations.AlterField(
model_name='prefix',
name='prefix',
field=ipam.fields.IPNetworkField(help_text=b'IPv4 or IPv6 network with mask'),
),
migrations.AlterField(
model_name='prefix',
name='role',
field=models.ForeignKey(blank=True, help_text=b'The primary function of this prefix', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='prefixes', to='ipam.Role'),
),
migrations.AlterField(
model_name='prefix',
name='status',
field=models.PositiveSmallIntegerField(choices=[(0, b'Container'), (1, b'Active'), (2, b'Reserved'), (3, b'Deprecated')], default=1, help_text=b'Operational status of this prefix', verbose_name=b'Status'),
),
]

View File

@ -269,15 +269,19 @@ class Prefix(CreatedUpdatedModel, CustomFieldModel):
assigned to a VLAN where appropriate.
"""
family = models.PositiveSmallIntegerField(choices=AF_CHOICES, editable=False)
prefix = IPNetworkField()
prefix = IPNetworkField(help_text="IPv4 or IPv6 network with mask")
site = models.ForeignKey('dcim.Site', related_name='prefixes', on_delete=models.PROTECT, blank=True, null=True)
vrf = models.ForeignKey('VRF', related_name='prefixes', on_delete=models.PROTECT, blank=True, null=True,
verbose_name='VRF')
tenant = models.ForeignKey(Tenant, related_name='prefixes', blank=True, null=True, on_delete=models.PROTECT)
vlan = models.ForeignKey('VLAN', related_name='prefixes', on_delete=models.PROTECT, blank=True, null=True,
verbose_name='VLAN')
status = models.PositiveSmallIntegerField('Status', choices=PREFIX_STATUS_CHOICES, default=1)
role = models.ForeignKey('Role', related_name='prefixes', on_delete=models.SET_NULL, blank=True, null=True)
status = models.PositiveSmallIntegerField('Status', choices=PREFIX_STATUS_CHOICES, default=PREFIX_STATUS_ACTIVE,
help_text="Operational status of this prefix")
role = models.ForeignKey('Role', related_name='prefixes', on_delete=models.SET_NULL, blank=True, null=True,
help_text="The primary function of this prefix")
is_pool = models.BooleanField(verbose_name='Is a pool', default=False,
help_text="All IP addresses within this prefix are considered usable")
description = models.CharField(max_length=100, blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')

View File

@ -38,24 +38,21 @@ def add_available_prefixes(parent, prefix_list):
return prefix_list
def add_available_ipaddresses(prefix, ipaddress_list):
def add_available_ipaddresses(prefix, ipaddress_list, is_pool=False):
"""
Annotate ranges of available IP addresses within a given prefix.
Annotate ranges of available IP addresses within a given prefix. If is_pool is True, the first and last IP will be
considered usable (regardless of mask length).
"""
output = []
prev_ip = None
# Ignore the "network address" for IPv4 prefixes larger than /31
if prefix.version == 4 and prefix.prefixlen < 31:
# Ignore the network and broadcast addresses for non-pool IPv4 prefixes larger than /31.
if prefix.version == 4 and prefix.prefixlen < 31 and not is_pool:
first_ip_in_prefix = netaddr.IPAddress(prefix.first + 1)
else:
first_ip_in_prefix = netaddr.IPAddress(prefix.first)
# Ignore the broadcast address for IPv4 prefixes larger than /31
if prefix.version == 4 and prefix.prefixlen < 31:
last_ip_in_prefix = netaddr.IPAddress(prefix.last - 1)
else:
first_ip_in_prefix = netaddr.IPAddress(prefix.first)
last_ip_in_prefix = netaddr.IPAddress(prefix.last)
if not ipaddress_list:
@ -476,7 +473,7 @@ def prefix_ipaddresses(request, pk):
# Find all IPAddresses belonging to this Prefix
ipaddresses = IPAddress.objects.filter(vrf=prefix.vrf, address__net_contained_or_equal=str(prefix.prefix))\
.select_related('vrf', 'interface__device', 'primary_ip4_for', 'primary_ip6_for')
ipaddresses = add_available_ipaddresses(prefix.prefix, ipaddresses)
ipaddresses = add_available_ipaddresses(prefix.prefix, ipaddresses, prefix.is_pool)
ip_table = tables.IPAddressTable(ipaddresses)
if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):

View File

@ -85,6 +85,16 @@
{% endif %}
</td>
</tr>
<tr>
<td>Is a pool</td>
<td>
{% if prefix.is_pool %}
<i class="glyphicon glyphicon-ok text-success" title="Yes"></i>
{% else %}
<i class="glyphicon glyphicon-remove text-danger" title="No"></i>
{% endif %}
</td>
</tr>
<tr>
<td>Description</td>
<td>

View File

@ -12,6 +12,7 @@
{% render_field form.vlan %}
{% render_field form.status %}
{% render_field form.role %}
{% render_field form.is_pool %}
{% render_field form.description %}
</div>
</div>

View File

@ -68,6 +68,11 @@
<td>Functional role (optional)</td>
<td>Customer</td>
</tr>
<tr>
<td>Is a pool</td>
<td>True if all IPs are considered usable</td>
<td>False</td>
</tr>
<tr>
<td>Description</td>
<td>Short description (optional)</td>
@ -76,7 +81,7 @@
</tbody>
</table>
<h4>Example</h4>
<pre>192.168.42.0/24,65000:123,ABC01,HQ,Customers,801,Active,Customer,7th floor WiFi</pre>
<pre>192.168.42.0/24,65000:123,ABC01,HQ,Customers,801,Active,Customer,False,7th floor WiFi</pre>
</div>
</div>
{% endblock %}