Skip to content

Commit cfce2c6

Browse files
committed
Change balancing and sorting behaviour of guests
- Sort to be balanced guests first by size of affinity group (ASC) - Sort to be balanced guests afterwards by used memory size (ASC/DESC) - Validate if lowest used node is still the lowest one Fixes: #378 Fixes: #390
1 parent ab7ee0d commit cfce2c6

2 files changed

Lines changed: 49 additions & 5 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
changed:
2+
- Changed balancing and sorting behaviour (@gyptazy). [#378]
3+
- Balancing objects will be ordered by: count of objects in affinity-rules, followed by memory size

‎proxlb/models/calculations.py‎

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -386,18 +386,38 @@ def relocate_guests(proxlb_data: Dict[str, Any]):
386386
else:
387387
logger.debug("Smaller guests will be processed first. (Sorting ascending by memory used)")
388388

389-
sorted_guest_usage_groups = sorted(proxlb_data["groups"]["affinity"], key=lambda g: proxlb_data["groups"]["affinity"][g]["memory_used"], reverse=larger_first)
389+
# Sort affinity groups by number of guests to avoid creating more migrations than needed
390+
# because of affinity-groups and use afterwards memory for defining smaller/larger guests
391+
sorted_guest_usage_groups = sorted(
392+
proxlb_data["groups"]["affinity"],
393+
key=lambda g: (
394+
proxlb_data["groups"]["affinity"][g]["counter"],
395+
-proxlb_data["groups"]["affinity"][g]["memory_used"]
396+
if larger_first
397+
else proxlb_data["groups"]["affinity"][g]["memory_used"],
398+
)
399+
)
390400

391401
# Iterate over all affinity groups
392402
for group_name in sorted_guest_usage_groups:
393403

394-
# We get initially the node with the most free resources and then
395-
# migrate all guests within the group to that node to ensure the
396-
# affinity.
397-
Calculations.get_most_free_node(proxlb_data)
404+
# Validate balanciness again before processing each group
405+
Calculations.get_balanciness(proxlb_data)
406+
logger.debug(proxlb_data["meta"]["balancing"]["balance"])
407+
if (not proxlb_data["meta"]["balancing"]["balance"]) and (not proxlb_data["meta"]["balancing"].get("enforce_affinity", False)):
408+
logger.debug("Skipping further guest relocations as balanciness is now ok.")
409+
break
398410

399411
for guest_name in proxlb_data["groups"]["affinity"][group_name]["guests"]:
412+
# Stop moving guests if the source node is no longer the most loaded
413+
source_node = proxlb_data["guests"][guest_name]["node_current"]
414+
method = proxlb_data["meta"]["balancing"].get("method", "memory")
400415
mode = proxlb_data["meta"]["balancing"].get("mode", "used")
416+
highest_node = max(proxlb_data["nodes"].values(), key=lambda n: n[f"{method}_{mode}_percent"])
417+
418+
if highest_node["name"] != source_node:
419+
logger.debug(f"Stopping relocation for guest {guest_name}: source node {source_node} is no longer the most loaded node.")
420+
break
401421

402422
if not Calculations.validate_node_resources(proxlb_data, guest_name):
403423
logger.warning(f"Skipping relocation of guest {guest_name} due to insufficient resources on target node {proxlb_data['meta']['balancing']['balance_next_node']}. This might affect affinity group {group_name}.")
@@ -586,6 +606,9 @@ def update_node_resources(proxlb_data: Dict[str, Any]):
586606
else:
587607
logger.debug(f"Guest {guest_name} is marked as ignored. Skipping target node assignment.")
588608

609+
Calculations.recalc_node_statistics(proxlb_data, node_target)
610+
Calculations.recalc_node_statistics(proxlb_data, node_current)
611+
589612
logger.debug("Finished: update_node_resources.")
590613

591614
def validate_affinity_map(proxlb_data: Dict[str, Any]):
@@ -763,3 +786,21 @@ def validate_node_resources(proxlb_data: Dict[str, Any], guest_name: str) -> boo
763786
logger.debug(f"Node '{node_target}' lacks sufficient resources ({node_memory_free / (1024 ** 3):.2f} GB free) for guest '{guest_name}'.")
764787
logger.debug("Finished: validate_node_resources.")
765788
return False
789+
790+
@staticmethod
791+
def recalc_node_statistics(proxlb_data: Dict[str, Any], node_name: str) -> None:
792+
n = proxlb_data["nodes"][node_name]
793+
794+
# free derived from totals and used (keeps your data consistent after virtual moves)
795+
n["cpu_free"] = max(0, n["cpu_total"] - n["cpu_used"])
796+
n["memory_free"] = max(0, n["memory_total"] - n["memory_used"])
797+
n["disk_free"] = max(0, n["disk_total"] - n["disk_used"])
798+
799+
# percent recompute (avoid drift)
800+
n["cpu_used_percent"] = (n["cpu_used"] / n["cpu_total"] * 100) if n["cpu_total"] else 0
801+
n["memory_used_percent"] = (n["memory_used"] / n["memory_total"] * 100) if n["memory_total"] else 0
802+
n["disk_used_percent"] = (n["disk_used"] / n["disk_total"] * 100) if n["disk_total"] else 0
803+
804+
n["cpu_assigned_percent"] = (n["cpu_assigned"] / n["cpu_total"] * 100) if n["cpu_total"] else 0
805+
n["memory_assigned_percent"] = (n["memory_assigned"] / n["memory_total"] * 100) if n["memory_total"] else 0
806+
n["disk_assigned_percent"] = (n["disk_assigned"] / n["disk_total"] * 100) if n["disk_total"] else 0

0 commit comments

Comments
 (0)