#6934: Include child IP ranges under prefix view

This commit is contained in:
jeremystretch 2021-08-10 16:26:14 -04:00
parent c7ebad0fbb
commit 6d27e11043
8 changed files with 78 additions and 29 deletions

View File

@ -396,6 +396,16 @@ class Prefix(PrimaryModel):
else: else:
return Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf) return Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf)
def get_child_ranges(self):
"""
Return all IPRanges within this Prefix and VRF.
"""
return IPRange.objects.filter(
vrf=self.vrf,
start_address__net_host_contained=str(self.prefix),
end_address__net_host_contained=str(self.prefix)
)
def get_child_ips(self): def get_child_ips(self):
""" """
Return all IPAddresses within this Prefix and VRF. If this Prefix is a container in the global table, return Return all IPAddresses within this Prefix and VRF. If this Prefix is a container in the global table, return

View File

@ -3,7 +3,7 @@ from django.core.exceptions import ValidationError
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from ipam.choices import IPAddressRoleChoices, PrefixStatusChoices from ipam.choices import IPAddressRoleChoices, PrefixStatusChoices
from ipam.models import Aggregate, IPAddress, Prefix, RIR, VLAN, VLANGroup, VRF from ipam.models import Aggregate, IPAddress, IPRange, Prefix, RIR, VLAN, VLANGroup, VRF
class TestAggregate(TestCase): class TestAggregate(TestCase):
@ -72,6 +72,23 @@ class TestPrefix(TestCase):
# VRF container is limited to its own VRF # VRF container is limited to its own VRF
self.assertSetEqual(child_prefix_pks, {prefixes[2].pk}) self.assertSetEqual(child_prefix_pks, {prefixes[2].pk})
def test_get_child_ranges(self):
prefix = Prefix(prefix='192.168.0.16/28')
prefix.save()
ranges = IPRange.objects.bulk_create((
IPRange(start_address=IPNetwork('192.168.0.1/24'), end_address=IPNetwork('192.168.0.10/24'), size=10), # No overlap
IPRange(start_address=IPNetwork('192.168.0.11/24'), end_address=IPNetwork('192.168.0.17/24'), size=7), # Partial overlap
IPRange(start_address=IPNetwork('192.168.0.18/24'), end_address=IPNetwork('192.168.0.23/24'), size=6), # Full overlap
IPRange(start_address=IPNetwork('192.168.0.24/24'), end_address=IPNetwork('192.168.0.30/24'), size=7), # Full overlap
IPRange(start_address=IPNetwork('192.168.0.31/24'), end_address=IPNetwork('192.168.0.40/24'), size=10), # Partial overlap
))
child_ranges = prefix.get_child_ranges()
self.assertEqual(len(child_ranges), 2)
self.assertEqual(child_ranges[0], ranges[2])
self.assertEqual(child_ranges[1], ranges[3])
def test_get_child_ips(self): def test_get_child_ips(self):
vrfs = VRF.objects.bulk_create(( vrfs = VRF.objects.bulk_create((
VRF(name='VRF 1'), VRF(name='VRF 1'),

View File

@ -77,6 +77,7 @@ urlpatterns = [
path('prefixes/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='prefix_changelog', kwargs={'model': Prefix}), path('prefixes/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='prefix_changelog', kwargs={'model': Prefix}),
path('prefixes/<int:pk>/journal/', ObjectJournalView.as_view(), name='prefix_journal', kwargs={'model': Prefix}), path('prefixes/<int:pk>/journal/', ObjectJournalView.as_view(), name='prefix_journal', kwargs={'model': Prefix}),
path('prefixes/<int:pk>/prefixes/', views.PrefixPrefixesView.as_view(), name='prefix_prefixes'), path('prefixes/<int:pk>/prefixes/', views.PrefixPrefixesView.as_view(), name='prefix_prefixes'),
path('prefixes/<int:pk>/ip-ranges/', views.PrefixIPRangesView.as_view(), name='prefix_ipranges'),
path('prefixes/<int:pk>/ip-addresses/', views.PrefixIPAddressesView.as_view(), name='prefix_ipaddresses'), path('prefixes/<int:pk>/ip-addresses/', views.PrefixIPAddressesView.as_view(), name='prefix_ipaddresses'),
# IP ranges # IP ranges

View File

@ -412,30 +412,44 @@ class PrefixPrefixesView(generic.ObjectView):
if child_prefixes and request.GET.get('show_available', 'true') == 'true': if child_prefixes and request.GET.get('show_available', 'true') == 'true':
child_prefixes = add_available_prefixes(instance.prefix, child_prefixes) child_prefixes = add_available_prefixes(instance.prefix, child_prefixes)
prefix_table = tables.PrefixDetailTable(child_prefixes) table = tables.PrefixDetailTable(child_prefixes)
if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'): if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
prefix_table.columns.show('pk') table.columns.show('pk')
paginate_table(prefix_table, request) paginate_table(table, request)
# Compile permissions list for rendering the object table
permissions = {
'add': request.user.has_perm('ipam.add_prefix'),
'change': request.user.has_perm('ipam.change_prefix'),
'delete': request.user.has_perm('ipam.delete_prefix'),
}
bulk_querystring = 'vrf_id={}&within={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix) bulk_querystring = 'vrf_id={}&within={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix)
return { return {
'first_available_prefix': instance.get_first_available_prefix(), 'first_available_prefix': instance.get_first_available_prefix(),
'prefix_table': prefix_table, 'table': table,
'permissions': permissions,
'bulk_querystring': bulk_querystring, 'bulk_querystring': bulk_querystring,
'active_tab': 'prefixes', 'active_tab': 'prefixes',
'show_available': request.GET.get('show_available', 'true') == 'true', 'show_available': request.GET.get('show_available', 'true') == 'true',
} }
class PrefixIPRangesView(generic.ObjectView):
queryset = Prefix.objects.all()
template_name = 'ipam/prefix/ip_ranges.html'
def get_extra_context(self, request, instance):
# Find all IPRanges belonging to this Prefix
ip_ranges = instance.get_child_ranges().restrict(request.user, 'view').prefetch_related('vrf')
table = tables.IPRangeTable(ip_ranges)
if request.user.has_perm('ipam.change_iprange') or request.user.has_perm('ipam.delete_iprange'):
table.columns.show('pk')
paginate_table(table, request)
bulk_querystring = 'vrf_id={}&parent={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix)
return {
'table': table,
'bulk_querystring': bulk_querystring,
'active_tab': 'ip-ranges',
}
class PrefixIPAddressesView(generic.ObjectView): class PrefixIPAddressesView(generic.ObjectView):
queryset = Prefix.objects.all() queryset = Prefix.objects.all()
template_name = 'ipam/prefix/ip_addresses.html' template_name = 'ipam/prefix/ip_addresses.html'
@ -450,24 +464,16 @@ class PrefixIPAddressesView(generic.ObjectView):
if request.GET.get('show_available', 'true') == 'true': if request.GET.get('show_available', 'true') == 'true':
ipaddresses = add_available_ipaddresses(instance.prefix, ipaddresses, instance.is_pool) ipaddresses = add_available_ipaddresses(instance.prefix, ipaddresses, instance.is_pool)
ip_table = tables.IPAddressTable(ipaddresses) table = tables.IPAddressTable(ipaddresses)
if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'): if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):
ip_table.columns.show('pk') table.columns.show('pk')
paginate_table(ip_table, request) paginate_table(table, request)
# Compile permissions list for rendering the object table
permissions = {
'add': request.user.has_perm('ipam.add_ipaddress'),
'change': request.user.has_perm('ipam.change_ipaddress'),
'delete': request.user.has_perm('ipam.delete_ipaddress'),
}
bulk_querystring = 'vrf_id={}&parent={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix) bulk_querystring = 'vrf_id={}&parent={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix)
return { return {
'first_available_ip': instance.get_first_available_ip(), 'first_available_ip': instance.get_first_available_ip(),
'ip_table': ip_table, 'table': table,
'permissions': permissions,
'bulk_querystring': bulk_querystring, 'bulk_querystring': bulk_querystring,
'active_tab': 'ip-addresses', 'active_tab': 'ip-addresses',
'show_available': request.GET.get('show_available', 'true') == 'true', 'show_available': request.GET.get('show_available', 'true') == 'true',

View File

@ -17,13 +17,18 @@
</li> </li>
<li role="presentation" class="nav-item"> <li role="presentation" class="nav-item">
<a class="nav-link{% if active_tab == 'prefixes' %} active{% endif %}" href="{% url 'ipam:prefix_prefixes' pk=object.pk %}"> <a class="nav-link{% if active_tab == 'prefixes' %} active{% endif %}" href="{% url 'ipam:prefix_prefixes' pk=object.pk %}">
Child Prefixes <span class="badge bg-primary">{{ object.get_child_prefixes.count }}</span> Child Prefixes {% badge object.get_child_prefixes.count %}
</a>
</li>
<li role="presentation" class="nav-item">
<a class="nav-link{% if active_tab == 'ip-ranges' %} active{% endif %}" href="{% url 'ipam:prefix_ipranges' pk=object.pk %}">
Child Ranges {% badge object.get_child_ranges.count %}
</a> </a>
</li> </li>
{% if perms.ipam.view_ipaddress and object.status != 'container' %} {% if perms.ipam.view_ipaddress and object.status != 'container' %}
<li role="presentation" class="nav-item"> <li role="presentation" class="nav-item">
<a class="nav-link{% if active_tab == 'ip-addresses' %} active{% endif %}" href="{% url 'ipam:prefix_ipaddresses' pk=object.pk %}"> <a class="nav-link{% if active_tab == 'ip-addresses' %} active{% endif %}" href="{% url 'ipam:prefix_ipaddresses' pk=object.pk %}">
IP Addresses <span class="badge bg-primary">{{ object.get_child_ips.count }}</span> IP Addresses {% badge object.get_child_ips.count %}
</a> </a>
</li> </li>
{% endif %} {% endif %}

View File

@ -11,7 +11,7 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">
{% include 'utilities/obj_table.html' with table=ip_table heading='IP Addresses' bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %} {% include 'utilities/obj_table.html' with heading='IP Addresses' bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,10 @@
{% extends 'ipam/prefix/base.html' %}
{% block content %}
<div class="row">
<div class="col col-md-12">
{% include 'utilities/obj_table.html' with heading='Child IP Ranges' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' parent=prefix %}
</div>
</div>
{% endblock %}

View File

@ -19,7 +19,7 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">
{% include 'utilities/obj_table.html' with table=prefix_table heading='Child Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' parent=prefix %} {% include 'utilities/obj_table.html' with heading='Child Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' parent=prefix %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}