Преглед изворни кода

#6934: Account for child IP ranges when calculating prefix utilization

jeremystretch пре 4 година
родитељ
комит
5365c866ff
2 измењених фајлова са 30 додато и 18 уклоњено
  1. 11 2
      netbox/ipam/models/ip.py
  2. 19 16
      netbox/ipam/tests/test_models.py

+ 11 - 2
netbox/ipam/models/ip.py

@@ -484,11 +484,16 @@ class Prefix(PrimaryModel):
             utilization = int(float(child_prefixes.size) / self.prefix.size * 100)
         else:
             # Compile an IPSet to avoid counting duplicate IPs
-            child_count = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]).size
+            child_ips = netaddr.IPSet()
+            for iprange in self.get_child_ranges():
+                child_ips.add(iprange.range)
+            for ip in self.get_child_ips():
+                child_ips.add(ip.address.ip)
+
             prefix_size = self.prefix.size
             if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool:
                 prefix_size -= 2
-            utilization = int(float(child_count) / prefix_size * 100)
+            utilization = int(float(child_ips.size) / prefix_size * 100)
 
         return min(utilization, 100)
 
@@ -603,6 +608,10 @@ class IPRange(PrimaryModel):
     def family(self):
         return self.start_address.version if self.start_address else None
 
+    @property
+    def range(self):
+        return netaddr.IPRange(self.start_address.ip, self.end_address.ip)
+
     @property
     def mask_length(self):
         return self.start_address.prefixlen if self.start_address else None

+ 19 - 16
netbox/ipam/tests/test_models.py

@@ -185,27 +185,30 @@ class TestPrefix(TestCase):
         IPAddress.objects.create(address=IPNetwork('10.0.0.4/24'))
         self.assertEqual(parent_prefix.get_first_available_ip(), '10.0.0.5/24')
 
-    def test_get_utilization(self):
+    def test_get_utilization_container(self):
+        prefixes = (
+            Prefix(prefix=IPNetwork('10.0.0.0/24'), status=PrefixStatusChoices.STATUS_CONTAINER),
+            Prefix(prefix=IPNetwork('10.0.0.0/26')),
+            Prefix(prefix=IPNetwork('10.0.0.128/26')),
+        )
+        Prefix.objects.bulk_create(prefixes)
+        self.assertEqual(prefixes[0].get_utilization(), 50)  # 50% utilization
 
-        # Container Prefix
+    def test_get_utilization_noncontainer(self):
         prefix = Prefix.objects.create(
             prefix=IPNetwork('10.0.0.0/24'),
-            status=PrefixStatusChoices.STATUS_CONTAINER
+            status=PrefixStatusChoices.STATUS_ACTIVE
         )
-        Prefix.objects.bulk_create((
-            Prefix(prefix=IPNetwork('10.0.0.0/26')),
-            Prefix(prefix=IPNetwork('10.0.0.128/26')),
-        ))
-        self.assertEqual(prefix.get_utilization(), 50)
 
-        # Non-container Prefix
-        prefix.status = PrefixStatusChoices.STATUS_ACTIVE
-        prefix.save()
-        IPAddress.objects.bulk_create(
-            # Create 32 IPAddresses within the Prefix
-            [IPAddress(address=IPNetwork('10.0.0.{}/24'.format(i))) for i in range(1, 33)]
-        )
-        self.assertEqual(prefix.get_utilization(), 12)  # ~= 12%
+        # Create 32 child IPs
+        IPAddress.objects.bulk_create([
+            IPAddress(address=IPNetwork(f'10.0.0.{i}/24')) for i in range(1, 33)
+        ])
+        self.assertEqual(prefix.get_utilization(), 12)  # 12.5% utilization
+
+        # Create a child range with 32 additional IPs
+        IPRange.objects.create(start_address=IPNetwork('10.0.0.33/24'), end_address=IPNetwork('10.0.0.64/24'))
+        self.assertEqual(prefix.get_utilization(), 25)  # 25% utilization
 
     #
     # Uniqueness enforcement tests