diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index 2ab691c1b..107a51943 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -396,6 +396,16 @@ class Prefix(PrimaryModel): else: 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): """ Return all IPAddresses within this Prefix and VRF. If this Prefix is a container in the global table, return diff --git a/netbox/ipam/tests/test_models.py b/netbox/ipam/tests/test_models.py index 4fefdec54..bc9507277 100644 --- a/netbox/ipam/tests/test_models.py +++ b/netbox/ipam/tests/test_models.py @@ -3,7 +3,7 @@ from django.core.exceptions import ValidationError from django.test import TestCase, override_settings 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): @@ -72,6 +72,23 @@ class TestPrefix(TestCase): # VRF container is limited to its own VRF 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): vrfs = VRF.objects.bulk_create(( VRF(name='VRF 1'), diff --git a/netbox/ipam/urls.py b/netbox/ipam/urls.py index 5a5cf21ac..9d9a846bf 100644 --- a/netbox/ipam/urls.py +++ b/netbox/ipam/urls.py @@ -77,6 +77,7 @@ urlpatterns = [ path('prefixes//changelog/', ObjectChangeLogView.as_view(), name='prefix_changelog', kwargs={'model': Prefix}), path('prefixes//journal/', ObjectJournalView.as_view(), name='prefix_journal', kwargs={'model': Prefix}), path('prefixes//prefixes/', views.PrefixPrefixesView.as_view(), name='prefix_prefixes'), + path('prefixes//ip-ranges/', views.PrefixIPRangesView.as_view(), name='prefix_ipranges'), path('prefixes//ip-addresses/', views.PrefixIPAddressesView.as_view(), name='prefix_ipaddresses'), # IP ranges diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 636ebdd29..92908d8b4 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -412,30 +412,44 @@ class PrefixPrefixesView(generic.ObjectView): if child_prefixes and request.GET.get('show_available', 'true') == 'true': 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'): - prefix_table.columns.show('pk') - paginate_table(prefix_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'), - } + table.columns.show('pk') + paginate_table(table, request) bulk_querystring = 'vrf_id={}&within={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix) return { 'first_available_prefix': instance.get_first_available_prefix(), - 'prefix_table': prefix_table, - 'permissions': permissions, + 'table': table, 'bulk_querystring': bulk_querystring, 'active_tab': 'prefixes', '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): queryset = Prefix.objects.all() template_name = 'ipam/prefix/ip_addresses.html' @@ -450,24 +464,16 @@ class PrefixIPAddressesView(generic.ObjectView): if request.GET.get('show_available', 'true') == 'true': 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'): - ip_table.columns.show('pk') - paginate_table(ip_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'), - } + 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 { 'first_available_ip': instance.get_first_available_ip(), - 'ip_table': ip_table, - 'permissions': permissions, + 'table': table, 'bulk_querystring': bulk_querystring, 'active_tab': 'ip-addresses', 'show_available': request.GET.get('show_available', 'true') == 'true', diff --git a/netbox/templates/ipam/prefix/base.html b/netbox/templates/ipam/prefix/base.html index eca2250a6..35f36e1ec 100644 --- a/netbox/templates/ipam/prefix/base.html +++ b/netbox/templates/ipam/prefix/base.html @@ -17,13 +17,18 @@ + {% if perms.ipam.view_ipaddress and object.status != 'container' %} {% endif %} diff --git a/netbox/templates/ipam/prefix/ip_addresses.html b/netbox/templates/ipam/prefix/ip_addresses.html index 6291befcf..e8c9704bb 100644 --- a/netbox/templates/ipam/prefix/ip_addresses.html +++ b/netbox/templates/ipam/prefix/ip_addresses.html @@ -11,7 +11,7 @@ {% block content %}
- {% 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' %}
{% endblock %} diff --git a/netbox/templates/ipam/prefix/ip_ranges.html b/netbox/templates/ipam/prefix/ip_ranges.html new file mode 100644 index 000000000..334cc90b0 --- /dev/null +++ b/netbox/templates/ipam/prefix/ip_ranges.html @@ -0,0 +1,10 @@ +{% extends 'ipam/prefix/base.html' %} + + +{% block content %} +
+
+ {% 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 %} +
+
+{% endblock %} diff --git a/netbox/templates/ipam/prefix/prefixes.html b/netbox/templates/ipam/prefix/prefixes.html index 4f18c476c..baf58b673 100644 --- a/netbox/templates/ipam/prefix/prefixes.html +++ b/netbox/templates/ipam/prefix/prefixes.html @@ -19,7 +19,7 @@ {% block content %}
- {% 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 %}
{% endblock %}