@@ -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