test_tables.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. from django.test import TestCase
  2. from netaddr import IPNetwork
  3. from ipam.models import FHRPGroupAssignment, IPAddress, IPRange, Prefix
  4. from ipam.tables import *
  5. from ipam.utils import annotate_ip_space
  6. from utilities.testing import TableTestCases
  7. class AnnotatedIPAddressTableTest(TestCase):
  8. @classmethod
  9. def setUpTestData(cls):
  10. cls.prefix = Prefix.objects.create(
  11. prefix=IPNetwork('10.1.1.0/24'),
  12. status='active'
  13. )
  14. cls.ip_address = IPAddress.objects.create(
  15. address='10.1.1.1/24',
  16. status='active'
  17. )
  18. cls.ip_range = IPRange.objects.create(
  19. start_address=IPNetwork('10.1.1.2/24'),
  20. end_address=IPNetwork('10.1.1.10/24'),
  21. status='active',
  22. mark_populated=True,
  23. )
  24. def test_ipaddress_has_checkbox_iprange_does_not(self):
  25. data = annotate_ip_space(self.prefix)
  26. table = AnnotatedIPAddressTable(data, orderable=False)
  27. table.columns.show('pk')
  28. ipaddress_row = next(
  29. row for row in table.rows
  30. if isinstance(row.record, IPAddress) and row.record.pk == self.ip_address.pk
  31. )
  32. iprange_row = next(
  33. row for row in table.rows
  34. if isinstance(row.record, IPRange) and row.record.pk == self.ip_range.pk
  35. )
  36. self.assertIn('name="pk"', ipaddress_row.get_cell('pk'))
  37. self.assertIn(f'value="{self.ip_address.pk}"', ipaddress_row.get_cell('pk'))
  38. self.assertNotIn('name="pk"', iprange_row.get_cell('pk'))
  39. def test_annotate_ip_space_ipv4_non_pool_excludes_network_and_broadcast(self):
  40. prefix = Prefix.objects.create(
  41. prefix=IPNetwork('192.0.2.0/29'), # 8 addresses total
  42. status='active',
  43. is_pool=False,
  44. )
  45. data = annotate_ip_space(prefix)
  46. self.assertEqual(len(data), 1)
  47. available = data[0]
  48. # /29 non-pool: exclude .0 (network) and .7 (broadcast)
  49. self.assertEqual(available.first_ip, '192.0.2.1/29')
  50. self.assertEqual(available.size, 6)
  51. def test_annotate_ip_space_ipv4_pool_includes_network_and_broadcast(self):
  52. prefix = Prefix.objects.create(
  53. prefix=IPNetwork('192.0.2.8/29'), # 8 addresses total
  54. status='active',
  55. is_pool=True,
  56. )
  57. data = annotate_ip_space(prefix)
  58. self.assertEqual(len(data), 1)
  59. available = data[0]
  60. # Pool: all addresses are usable, including network/broadcast
  61. self.assertEqual(available.first_ip, '192.0.2.8/29')
  62. self.assertEqual(available.size, 8)
  63. def test_annotate_ip_space_ipv4_31_includes_all_ips(self):
  64. prefix = Prefix.objects.create(
  65. prefix=IPNetwork('192.0.2.16/31'), # 2 addresses total
  66. status='active',
  67. is_pool=False,
  68. )
  69. data = annotate_ip_space(prefix)
  70. self.assertEqual(len(data), 1)
  71. available = data[0]
  72. # /31: fully usable
  73. self.assertEqual(available.first_ip, '192.0.2.16/31')
  74. self.assertEqual(available.size, 2)
  75. def test_annotate_ip_space_ipv4_32_includes_single_ip(self):
  76. prefix = Prefix.objects.create(
  77. prefix=IPNetwork('192.0.2.100/32'), # 1 address total
  78. status='active',
  79. is_pool=False,
  80. )
  81. data = annotate_ip_space(prefix)
  82. self.assertEqual(len(data), 1)
  83. available = data[0]
  84. # /32: single usable address
  85. self.assertEqual(available.first_ip, '192.0.2.100/32')
  86. self.assertEqual(available.size, 1)
  87. def test_annotate_ip_space_ipv6_non_pool_excludes_anycast_first_ip(self):
  88. prefix = Prefix.objects.create(
  89. prefix=IPNetwork('2001:db8::/126'), # 4 addresses total
  90. status='active',
  91. is_pool=False,
  92. )
  93. data = annotate_ip_space(prefix)
  94. # No child records -> expect one AvailableIPSpace entry
  95. self.assertEqual(len(data), 1)
  96. available = data[0]
  97. # For IPv6 non-pool prefixes (except /127-/128), the first address is reserved (subnet-router anycast)
  98. self.assertEqual(available.first_ip, '2001:db8::1/126')
  99. self.assertEqual(available.size, 3) # 4 total - 1 reserved anycast
  100. def test_annotate_ip_space_ipv6_127_includes_all_ips(self):
  101. prefix = Prefix.objects.create(
  102. prefix=IPNetwork('2001:db8::/127'), # 2 addresses total
  103. status='active',
  104. is_pool=False,
  105. )
  106. data = annotate_ip_space(prefix)
  107. self.assertEqual(len(data), 1)
  108. available = data[0]
  109. # /127 is fully usable (no anycast exclusion)
  110. self.assertEqual(available.first_ip, '2001:db8::/127')
  111. self.assertEqual(available.size, 2)
  112. def test_annotate_ip_space_ipv6_128_includes_single_ip(self):
  113. prefix = Prefix.objects.create(
  114. prefix=IPNetwork('2001:db8::1/128'), # 1 address total
  115. status='active',
  116. is_pool=False,
  117. )
  118. data = annotate_ip_space(prefix)
  119. self.assertEqual(len(data), 1)
  120. available = data[0]
  121. # /128 is fully usable (single host address)
  122. self.assertEqual(available.first_ip, '2001:db8::1/128')
  123. self.assertEqual(available.size, 1)
  124. def test_annotate_ip_space_ipv6_pool_includes_anycast_first_ip(self):
  125. prefix = Prefix.objects.create(
  126. prefix=IPNetwork('2001:db8:1::/126'), # 4 addresses total
  127. status='active',
  128. is_pool=True,
  129. )
  130. data = annotate_ip_space(prefix)
  131. self.assertEqual(len(data), 1)
  132. available = data[0]
  133. # Pools are fully usable
  134. self.assertEqual(available.first_ip, '2001:db8:1::/126')
  135. self.assertEqual(available.size, 4)
  136. #
  137. # Table ordering tests
  138. #
  139. class VRFTableTest(TableTestCases.StandardTableTestCase):
  140. table = VRFTable
  141. class RouteTargetTableTest(TableTestCases.StandardTableTestCase):
  142. table = RouteTargetTable
  143. class RIRTableTest(TableTestCases.StandardTableTestCase):
  144. table = RIRTable
  145. class AggregateTableTest(TableTestCases.StandardTableTestCase):
  146. table = AggregateTable
  147. class RoleTableTest(TableTestCases.StandardTableTestCase):
  148. table = RoleTable
  149. class PrefixTableTest(TableTestCases.StandardTableTestCase):
  150. table = PrefixTable
  151. class IPRangeTableTest(TableTestCases.StandardTableTestCase):
  152. table = IPRangeTable
  153. class IPAddressTableTest(TableTestCases.StandardTableTestCase):
  154. table = IPAddressTable
  155. class FHRPGroupTableTest(TableTestCases.StandardTableTestCase):
  156. table = FHRPGroupTable
  157. class FHRPGroupAssignmentTableTest(TableTestCases.StandardTableTestCase):
  158. table = FHRPGroupAssignmentTable
  159. # No ObjectListView exists for this table; it is only rendered inline on
  160. # the FHRPGroup detail view. Provide an explicit queryset source.
  161. queryset_sources = [
  162. ('FHRPGroupAssignment.objects.all()', FHRPGroupAssignment.objects.all()),
  163. ]
  164. class VLANGroupTableTest(TableTestCases.StandardTableTestCase):
  165. table = VLANGroupTable
  166. class VLANTableTest(TableTestCases.StandardTableTestCase):
  167. table = VLANTable
  168. class VLANTranslationPolicyTableTest(TableTestCases.StandardTableTestCase):
  169. table = VLANTranslationPolicyTable
  170. class VLANTranslationRuleTableTest(TableTestCases.StandardTableTestCase):
  171. table = VLANTranslationRuleTable
  172. class ASNRangeTableTest(TableTestCases.StandardTableTestCase):
  173. table = ASNRangeTable
  174. class ASNTableTest(TableTestCases.StandardTableTestCase):
  175. table = ASNTable
  176. class ServiceTemplateTableTest(TableTestCases.StandardTableTestCase):
  177. table = ServiceTemplateTable
  178. class ServiceTableTest(TableTestCases.StandardTableTestCase):
  179. table = ServiceTable