Spaces:
Running
Running
Update ipmentor/tools.py
Browse files- ipmentor/tools.py +75 -66
ipmentor/tools.py
CHANGED
|
@@ -197,76 +197,85 @@ def calculate_subnets(network: str, number: int, method: str, hosts_list: str =
|
|
| 197 |
bits_for_hosts = math.ceil(math.log2(hosts_needed + 2))
|
| 198 |
required_cidr = 32 - bits_for_hosts
|
| 199 |
|
| 200 |
-
# Find
|
| 201 |
allocated = False
|
|
|
|
|
|
|
|
|
|
| 202 |
for i, avail_net in enumerate(available_networks):
|
| 203 |
# Check if we can fit the required subnet in this available network
|
| 204 |
if required_cidr >= avail_net.prefixlen:
|
| 205 |
-
#
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
|
| 271 |
if not allocated:
|
| 272 |
raise ValueError(f"Cannot allocate subnet for {hosts_needed} hosts")
|
|
|
|
| 197 |
bits_for_hosts = math.ceil(math.log2(hosts_needed + 2))
|
| 198 |
required_cidr = 32 - bits_for_hosts
|
| 199 |
|
| 200 |
+
# Find the smallest suitable available network (best fit)
|
| 201 |
allocated = False
|
| 202 |
+
best_fit_idx = None
|
| 203 |
+
best_fit_net = None
|
| 204 |
+
|
| 205 |
for i, avail_net in enumerate(available_networks):
|
| 206 |
# Check if we can fit the required subnet in this available network
|
| 207 |
if required_cidr >= avail_net.prefixlen:
|
| 208 |
+
# This network can fit our subnet
|
| 209 |
+
# Choose the smallest one (best fit) to minimize fragmentation
|
| 210 |
+
if best_fit_net is None or avail_net.prefixlen > best_fit_net.prefixlen:
|
| 211 |
+
best_fit_idx = i
|
| 212 |
+
best_fit_net = avail_net
|
| 213 |
+
|
| 214 |
+
if best_fit_net is not None:
|
| 215 |
+
# Allocate the first subnet of the required size from best fit network
|
| 216 |
+
try:
|
| 217 |
+
allocated_subnet = list(best_fit_net.subnets(new_prefix=required_cidr))[0]
|
| 218 |
+
except ValueError:
|
| 219 |
+
# Cannot create subnet of this size (shouldn't happen)
|
| 220 |
+
raise ValueError(f"Cannot allocate subnet for {hosts_needed} hosts")
|
| 221 |
+
|
| 222 |
+
# Calculate actual host capacity
|
| 223 |
+
if required_cidr == 32:
|
| 224 |
+
actual_hosts = 1
|
| 225 |
+
first_host = str(allocated_subnet.network_address)
|
| 226 |
+
last_host = str(allocated_subnet.network_address)
|
| 227 |
+
elif required_cidr == 31:
|
| 228 |
+
actual_hosts = 2
|
| 229 |
+
first_host = str(allocated_subnet.network_address)
|
| 230 |
+
last_host = str(allocated_subnet.broadcast_address)
|
| 231 |
+
else:
|
| 232 |
+
actual_hosts = 2 ** (32 - required_cidr) - 2
|
| 233 |
+
first_host = str(allocated_subnet.network_address + 1)
|
| 234 |
+
last_host = str(allocated_subnet.broadcast_address - 1)
|
| 235 |
+
|
| 236 |
+
subnets.append({
|
| 237 |
+
"subnet": str(allocated_subnet),
|
| 238 |
+
"network": str(allocated_subnet.network_address),
|
| 239 |
+
"broadcast": str(allocated_subnet.broadcast_address),
|
| 240 |
+
"first_host": first_host,
|
| 241 |
+
"last_host": last_host,
|
| 242 |
+
"hosts": actual_hosts,
|
| 243 |
+
"hosts_requested": hosts_needed,
|
| 244 |
+
"original_order": original_idx + 1
|
| 245 |
+
})
|
| 246 |
+
|
| 247 |
+
# Remove the used network
|
| 248 |
+
available_networks.pop(best_fit_idx)
|
| 249 |
+
|
| 250 |
+
# Calculate the remaining space after the allocated subnet
|
| 251 |
+
# This ensures contiguous allocation
|
| 252 |
+
next_address = allocated_subnet.broadcast_address + 1
|
| 253 |
+
if next_address <= best_fit_net.broadcast_address:
|
| 254 |
+
# There is space remaining after the allocated subnet
|
| 255 |
+
remaining_end = int(best_fit_net.broadcast_address)
|
| 256 |
+
|
| 257 |
+
# Calculate the prefix length for the remaining space
|
| 258 |
+
# We need to find blocks that fit in the remaining space
|
| 259 |
+
current_addr = next_address
|
| 260 |
+
while current_addr <= best_fit_net.broadcast_address:
|
| 261 |
+
# Find the largest block that:
|
| 262 |
+
# 1. Starts at current_addr
|
| 263 |
+
# 2. Fits within the remaining space
|
| 264 |
+
max_prefix = 32
|
| 265 |
+
addr_int = int(current_addr)
|
| 266 |
+
for prefix in range(best_fit_net.prefixlen, 33):
|
| 267 |
+
block_size = 2 ** (32 - prefix)
|
| 268 |
+
# Check if block is aligned and fits
|
| 269 |
+
if addr_int % block_size == 0 and addr_int + block_size - 1 <= remaining_end:
|
| 270 |
+
max_prefix = prefix
|
| 271 |
+
break
|
| 272 |
+
|
| 273 |
+
# Create the network block
|
| 274 |
+
remaining_block = ipaddress.IPv4Network(f"{current_addr}/{max_prefix}", strict=False)
|
| 275 |
+
available_networks.append(remaining_block)
|
| 276 |
+
current_addr = remaining_block.broadcast_address + 1
|
| 277 |
+
|
| 278 |
+
allocated = True
|
| 279 |
|
| 280 |
if not allocated:
|
| 281 |
raise ValueError(f"Cannot allocate subnet for {hosts_needed} hosts")
|