Files
ProxLB/proxlb/models/groups.py
gyptazy 31498da25a refactor: Code refactor of ProxLB preparing release 1.1.0
Fixes: #114
Fixes: #132
Fixes: #130
Fixes: #129
Fixes: #128
Fixes: #127
Fixes: #123
Fixes: #102
2025-03-02 17:03:49 +01:00

111 lines
6.2 KiB
Python

"""
The groups class is responsible for handling the correlations between the guests
and their groups like affinity and anti-affinity groups. To ensure a proper balancing
guests will ge grouped and then evaluated for further balancing.
"""
from typing import Dict, Any
from utils.logger import SystemdLogger
from utils.helper import Helper
logger = SystemdLogger()
class Groups:
"""
The groups class is responsible for handling the correlations between the guests
and their groups like affinity and anti-affinity groups. To ensure a proper balancing
guests will ge grouped and then evaluated for further balancing.
"""
def __init__(self, proxlb_data: Dict[str, Any]):
"""
Initializes the Groups class with the provided ProxLB data.
Args:
proxlb_data (Dict[str, Any]): The data required for balancing VMs and CTs.
"""
@staticmethod
def get_groups(guests: Dict[str, Any], nodes: Dict[str, Any]) -> Dict[str, Any]:
"""
Generates and returns a dictionary of affinity and anti-affinity groups based on the provided data.
Args:
guests (Dict[str, Any]): A dictionary containing the guest data.
nodes (Dict[str, Any]): A dictionary containing the nodes data.
Returns:
Dict[str, Any]: A dictionary containing the created groups that includes:
* Affinity groups (or a randon and uniq group)
* Anti-affinity groups
* A list of guests that are currently placed on a node which
is defined to be in maintenance.
"""
logger.debug("Starting: get_groups.")
groups = {'groups': {'affinity': {}, 'anti_affinity': {}, 'maintenance': []}}
for guest_name, guest_meta in guests["guests"].items():
# Create affinity grouping
# Use an affinity group if available for the guest
if len(guest_meta["affinity_groups"]) > 0:
for affinity_group in guest_meta["affinity_groups"]:
group_name = affinity_group
logger.debug(f'Affinity group {affinity_group} for {guest_name} will be used.')
else:
# Generate a random uniq group name for the guest if
# the guest does not belong to any affinity group
random_group = Helper.get_uuid_string()
group_name = random_group
logger.debug(f'Random uniq group {random_group} for {guest_name} will be used.')
if not groups["groups"]["affinity"].get(group_name, False):
# Create group template with initial guest meta information
groups["groups"]["affinity"][group_name] = {}
groups["groups"]["affinity"][group_name]["guests"] = []
groups["groups"]["affinity"][group_name]["guests"].append(guest_name)
groups["groups"]["affinity"][group_name]["counter"] = 1
# Create groups resource template by the guests resources
groups["groups"]["affinity"][group_name]["cpu_total"] = guest_meta["cpu_total"]
groups["groups"]["affinity"][group_name]["cpu_used"] = guest_meta["cpu_used"]
groups["groups"]["affinity"][group_name]["memory_total"] = guest_meta["memory_total"]
groups["groups"]["affinity"][group_name]["memory_used"] = guest_meta["cpu_used"]
groups["groups"]["affinity"][group_name]["disk_total"] = guest_meta["disk_total"]
groups["groups"]["affinity"][group_name]["disk_used"] = guest_meta["cpu_used"]
else:
# Update group templates by guest meta information
groups["groups"]["affinity"][group_name]["guests"].append(guest_name)
groups["groups"]["affinity"][group_name]["counter"] += 1
# Update group resources by guest resources
groups["groups"]["affinity"][group_name]["cpu_total"] += guest_meta["cpu_total"]
groups["groups"]["affinity"][group_name]["cpu_used"] += guest_meta["cpu_used"]
groups["groups"]["affinity"][group_name]["memory_total"] += guest_meta["memory_total"]
groups["groups"]["affinity"][group_name]["memory_used"] += guest_meta["cpu_used"]
groups["groups"]["affinity"][group_name]["disk_total"] += guest_meta["disk_total"]
groups["groups"]["affinity"][group_name]["disk_used"] += guest_meta["cpu_used"]
# Create anti-affinity grouping
if len(guest_meta["anti_affinity_groups"]) > 0:
for anti_affinity_group in guest_meta["anti_affinity_groups"]:
anti_affinity_group_name = anti_affinity_group
logger.debug(f'Anti-affinity group {anti_affinity_group_name} for {guest_name} will be used.')
if not groups["groups"]["anti_affinity"].get(anti_affinity_group_name, False):
groups["groups"]["anti_affinity"][anti_affinity_group_name] = {}
groups["groups"]["anti_affinity"][anti_affinity_group_name]["guests"] = []
groups["groups"]["anti_affinity"][anti_affinity_group_name]["guests"].append(guest_name)
groups["groups"]["anti_affinity"][anti_affinity_group_name]["counter"] = 1
groups["groups"]["anti_affinity"][anti_affinity_group_name]["used_nodes"] = []
else:
groups["groups"]["anti_affinity"][anti_affinity_group_name]["guests"].append(guest_name)
groups["groups"]["anti_affinity"][anti_affinity_group_name]["counter"] += 1
# Create grouping of guests that are currently located on nodes that are
# marked as in maintenance and must be migrated
if nodes["nodes"][guest_meta["node_current"]]["maintenance"]:
logger.debug(f'{guest_name} will be migrated to another node because the underlying node {guest_meta["node_current"]} is defined to be in maintenance.')
groups["groups"]["maintenance"].append(guest_name)
logger.debug("Finished: get_groups.")
return groups