| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- from django.test import TestCase
- from netaddr import IPNetwork
- from ipam.models import FHRPGroupAssignment, IPAddress, IPRange, Prefix
- from ipam.tables import *
- from ipam.utils import annotate_ip_space
- from utilities.testing import TableTestCases
- class AnnotatedIPAddressTableTest(TestCase):
- @classmethod
- def setUpTestData(cls):
- cls.prefix = Prefix.objects.create(
- prefix=IPNetwork('10.1.1.0/24'),
- status='active'
- )
- cls.ip_address = IPAddress.objects.create(
- address='10.1.1.1/24',
- status='active'
- )
- cls.ip_range = IPRange.objects.create(
- start_address=IPNetwork('10.1.1.2/24'),
- end_address=IPNetwork('10.1.1.10/24'),
- status='active',
- mark_populated=True,
- )
- def test_ipaddress_has_checkbox_iprange_does_not(self):
- data = annotate_ip_space(self.prefix)
- table = AnnotatedIPAddressTable(data, orderable=False)
- table.columns.show('pk')
- ipaddress_row = next(
- row for row in table.rows
- if isinstance(row.record, IPAddress) and row.record.pk == self.ip_address.pk
- )
- iprange_row = next(
- row for row in table.rows
- if isinstance(row.record, IPRange) and row.record.pk == self.ip_range.pk
- )
- self.assertIn('name="pk"', ipaddress_row.get_cell('pk'))
- self.assertIn(f'value="{self.ip_address.pk}"', ipaddress_row.get_cell('pk'))
- self.assertNotIn('name="pk"', iprange_row.get_cell('pk'))
- def test_annotate_ip_space_ipv4_non_pool_excludes_network_and_broadcast(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('192.0.2.0/29'), # 8 addresses total
- status='active',
- is_pool=False,
- )
- data = annotate_ip_space(prefix)
- self.assertEqual(len(data), 1)
- available = data[0]
- # /29 non-pool: exclude .0 (network) and .7 (broadcast)
- self.assertEqual(available.first_ip, '192.0.2.1/29')
- self.assertEqual(available.size, 6)
- def test_annotate_ip_space_ipv4_pool_includes_network_and_broadcast(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('192.0.2.8/29'), # 8 addresses total
- status='active',
- is_pool=True,
- )
- data = annotate_ip_space(prefix)
- self.assertEqual(len(data), 1)
- available = data[0]
- # Pool: all addresses are usable, including network/broadcast
- self.assertEqual(available.first_ip, '192.0.2.8/29')
- self.assertEqual(available.size, 8)
- def test_annotate_ip_space_ipv4_31_includes_all_ips(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('192.0.2.16/31'), # 2 addresses total
- status='active',
- is_pool=False,
- )
- data = annotate_ip_space(prefix)
- self.assertEqual(len(data), 1)
- available = data[0]
- # /31: fully usable
- self.assertEqual(available.first_ip, '192.0.2.16/31')
- self.assertEqual(available.size, 2)
- def test_annotate_ip_space_ipv4_32_includes_single_ip(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('192.0.2.100/32'), # 1 address total
- status='active',
- is_pool=False,
- )
- data = annotate_ip_space(prefix)
- self.assertEqual(len(data), 1)
- available = data[0]
- # /32: single usable address
- self.assertEqual(available.first_ip, '192.0.2.100/32')
- self.assertEqual(available.size, 1)
- def test_annotate_ip_space_ipv6_non_pool_excludes_anycast_first_ip(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('2001:db8::/126'), # 4 addresses total
- status='active',
- is_pool=False,
- )
- data = annotate_ip_space(prefix)
- # No child records -> expect one AvailableIPSpace entry
- self.assertEqual(len(data), 1)
- available = data[0]
- # For IPv6 non-pool prefixes (except /127-/128), the first address is reserved (subnet-router anycast)
- self.assertEqual(available.first_ip, '2001:db8::1/126')
- self.assertEqual(available.size, 3) # 4 total - 1 reserved anycast
- def test_annotate_ip_space_ipv6_127_includes_all_ips(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('2001:db8::/127'), # 2 addresses total
- status='active',
- is_pool=False,
- )
- data = annotate_ip_space(prefix)
- self.assertEqual(len(data), 1)
- available = data[0]
- # /127 is fully usable (no anycast exclusion)
- self.assertEqual(available.first_ip, '2001:db8::/127')
- self.assertEqual(available.size, 2)
- def test_annotate_ip_space_ipv6_128_includes_single_ip(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('2001:db8::1/128'), # 1 address total
- status='active',
- is_pool=False,
- )
- data = annotate_ip_space(prefix)
- self.assertEqual(len(data), 1)
- available = data[0]
- # /128 is fully usable (single host address)
- self.assertEqual(available.first_ip, '2001:db8::1/128')
- self.assertEqual(available.size, 1)
- def test_annotate_ip_space_ipv6_pool_includes_anycast_first_ip(self):
- prefix = Prefix.objects.create(
- prefix=IPNetwork('2001:db8:1::/126'), # 4 addresses total
- status='active',
- is_pool=True,
- )
- data = annotate_ip_space(prefix)
- self.assertEqual(len(data), 1)
- available = data[0]
- # Pools are fully usable
- self.assertEqual(available.first_ip, '2001:db8:1::/126')
- self.assertEqual(available.size, 4)
- #
- # Table ordering tests
- #
- class VRFTableTest(TableTestCases.StandardTableTestCase):
- table = VRFTable
- class RouteTargetTableTest(TableTestCases.StandardTableTestCase):
- table = RouteTargetTable
- class RIRTableTest(TableTestCases.StandardTableTestCase):
- table = RIRTable
- class AggregateTableTest(TableTestCases.StandardTableTestCase):
- table = AggregateTable
- class RoleTableTest(TableTestCases.StandardTableTestCase):
- table = RoleTable
- class PrefixTableTest(TableTestCases.StandardTableTestCase):
- table = PrefixTable
- class IPRangeTableTest(TableTestCases.StandardTableTestCase):
- table = IPRangeTable
- class IPAddressTableTest(TableTestCases.StandardTableTestCase):
- table = IPAddressTable
- class FHRPGroupTableTest(TableTestCases.StandardTableTestCase):
- table = FHRPGroupTable
- class FHRPGroupAssignmentTableTest(TableTestCases.StandardTableTestCase):
- table = FHRPGroupAssignmentTable
- # No ObjectListView exists for this table; it is only rendered inline on
- # the FHRPGroup detail view. Provide an explicit queryset source.
- queryset_sources = [
- ('FHRPGroupAssignment.objects.all()', FHRPGroupAssignment.objects.all()),
- ]
- class VLANGroupTableTest(TableTestCases.StandardTableTestCase):
- table = VLANGroupTable
- class VLANTableTest(TableTestCases.StandardTableTestCase):
- table = VLANTable
- class VLANTranslationPolicyTableTest(TableTestCases.StandardTableTestCase):
- table = VLANTranslationPolicyTable
- class VLANTranslationRuleTableTest(TableTestCases.StandardTableTestCase):
- table = VLANTranslationRuleTable
- class ASNRangeTableTest(TableTestCases.StandardTableTestCase):
- table = ASNRangeTable
- class ASNTableTest(TableTestCases.StandardTableTestCase):
- table = ASNTable
- class ServiceTemplateTableTest(TableTestCases.StandardTableTestCase):
- table = ServiceTemplateTable
- class ServiceTableTest(TableTestCases.StandardTableTestCase):
- table = ServiceTable
|