davidlms commited on
Commit
a6eade9
·
verified ·
1 Parent(s): fcb2e78

Update ipmentor/tools.py

Browse files
Files changed (1) hide show
  1. 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 a suitable available network
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
- # Allocate the first subnet of the required size
206
- try:
207
- allocated_subnet = list(avail_net.subnets(new_prefix=required_cidr))[0]
208
- except ValueError:
209
- # Cannot create subnet of this size
210
- continue
211
-
212
- # Calculate actual host capacity
213
- if required_cidr == 32:
214
- actual_hosts = 1
215
- first_host = str(allocated_subnet.network_address)
216
- last_host = str(allocated_subnet.network_address)
217
- elif required_cidr == 31:
218
- actual_hosts = 2
219
- first_host = str(allocated_subnet.network_address)
220
- last_host = str(allocated_subnet.broadcast_address)
221
- else:
222
- actual_hosts = 2 ** (32 - required_cidr) - 2
223
- first_host = str(allocated_subnet.network_address + 1)
224
- last_host = str(allocated_subnet.broadcast_address - 1)
225
-
226
- subnets.append({
227
- "subnet": str(allocated_subnet),
228
- "network": str(allocated_subnet.network_address),
229
- "broadcast": str(allocated_subnet.broadcast_address),
230
- "first_host": first_host,
231
- "last_host": last_host,
232
- "hosts": actual_hosts,
233
- "hosts_requested": hosts_needed,
234
- "original_order": original_idx + 1
235
- })
236
-
237
- # Remove the used network
238
- available_networks.pop(i)
239
-
240
- # Calculate the remaining space after the allocated subnet
241
- # This ensures contiguous allocation
242
- next_address = allocated_subnet.broadcast_address + 1
243
- if next_address <= avail_net.broadcast_address:
244
- # There is space remaining after the allocated subnet
245
- remaining_end = int(avail_net.broadcast_address)
246
-
247
- # Calculate the prefix length for the remaining space
248
- # We need to find blocks that fit in the remaining space
249
- current_addr = next_address
250
- while current_addr <= avail_net.broadcast_address:
251
- # Find the largest block that:
252
- # 1. Starts at current_addr
253
- # 2. Fits within the remaining space
254
- max_prefix = 32
255
- addr_int = int(current_addr)
256
- for prefix in range(avail_net.prefixlen, 33):
257
- block_size = 2 ** (32 - prefix)
258
- # Check if block is aligned and fits
259
- if addr_int % block_size == 0 and addr_int + block_size - 1 <= remaining_end:
260
- max_prefix = prefix
261
- break
262
-
263
- # Create the network block
264
- remaining_block = ipaddress.IPv4Network(f"{current_addr}/{max_prefix}", strict=False)
265
- available_networks.append(remaining_block)
266
- current_addr = remaining_block.broadcast_address + 1
267
-
268
- allocated = True
269
- break
 
 
 
 
 
 
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")