Adjust the integration of node resource reservation

- Renamed to set_node_resource_reservation
  - Immediately apply the values as maxmem for a node
    - Avoid recalculating percentage values
  - Simplified code
  - Adjusted debug logging
This commit is contained in:
gyptazy
2025-12-24 08:37:15 +01:00
parent 89ad425243
commit e0331e83e1
3 changed files with 45 additions and 67 deletions

View File

@@ -753,10 +753,10 @@ class Calculations:
guest_disk_required = proxlb_data["guests"][guest_name]["disk_used"]
if guest_memory_required < node_memory_free:
logger.debug(f"Node '{node_target}' has sufficient resources for guest '{guest_name}'.")
logger.debug(f"Node '{node_target}' has sufficient resources ({node_memory_free / (1024 ** 3):.2f} GB free) for guest '{guest_name}'.")
logger.debug("Finished: validate_node_resources.")
return True
else:
logger.debug(f"Node '{node_target}' lacks sufficient resources for guest '{guest_name}'.")
logger.debug(f"Node '{node_target}' lacks sufficient resources ({node_memory_free / (1024 ** 3):.2f} GB free) for guest '{guest_name}'.")
logger.debug("Finished: validate_node_resources.")
return False

View File

@@ -78,7 +78,7 @@ class Nodes:
nodes["nodes"][node["node"]]["cpu_pressure_some_spikes_percent"] = Nodes.get_node_rrd_data(proxmox_api, node["node"], "cpu", "some", spikes=True)
nodes["nodes"][node["node"]]["cpu_pressure_full_spikes_percent"] = Nodes.get_node_rrd_data(proxmox_api, node["node"], "cpu", "full", spikes=True)
nodes["nodes"][node["node"]]["cpu_pressure_hot"] = False
nodes["nodes"][node["node"]]["memory_total"] = node["maxmem"]
nodes["nodes"][node["node"]]["memory_total"] = Nodes.set_node_resource_reservation(node["node"], node["maxmem"], proxlb_config, "memory")
nodes["nodes"][node["node"]]["memory_assigned"] = 0
nodes["nodes"][node["node"]]["memory_used"] = node["mem"]
nodes["nodes"][node["node"]]["memory_free"] = node["maxmem"] - node["mem"]
@@ -103,7 +103,6 @@ class Nodes:
nodes["nodes"][node["node"]]["disk_pressure_full_spikes_percent"] = Nodes.get_node_rrd_data(proxmox_api, node["node"], "disk", "full", spikes=True)
nodes["nodes"][node["node"]]["disk_pressure_hot"] = False
Nodes.apply_resource_reservation(nodes["nodes"][node["node"]]["name"], proxlb_config, nodes["nodes"][node["node"]])
# Evaluate if node should be set to maintenance mode
if Nodes.set_node_maintenance(proxmox_api, proxlb_config, node["node"]):
nodes["nodes"][node["node"]]["maintenance"] = True
@@ -257,51 +256,57 @@ class Nodes:
return version["version"]
@staticmethod
def apply_resource_reservation(node_name, proxlb_config: Dict[str, Any], node_data: Dict[str, Any]) -> None:
def set_node_resource_reservation(node_name, resource_value, proxlb_config, resource_type) -> int:
"""
Check if there is a a configured resource reservation for the current nodes and apply it as needed.
Check if there is a configured resource reservation for the current node and apply it as needed.
Checks for a node specific config first, then if there is any configured default and if neither then nothing is reserved.
Reservations are applied by directly modifying the data gathered from the nodes.
Reservations are applied by directly modifying the resource value.
Args:
node_name: (str) the name of the node
proxlb_config (Dict[str, Any]): A dictionary containing the ProxLB configuration.
node_data: (Dict[str, Any]): Dict containing the current nodes data
Returns: none
node_name (str): The name of the node.
resource_value (int): The total resource value in bytes.
proxlb_config (Dict[str, Any]): A dictionary containing the ProxLB configuration.
resource_type (str): The type of resource ('memory', 'disk', etc.).
Returns:
int: The resource value after applying any configured reservations.
"""
logger.debug(f"Starting: apply_resource_reservation")
balancing_cfg = proxlb_config.get("balancing", {})
reserve_cfg = balancing_cfg.get("node_resource_reserve", {})
reserved_memory_gb_node = reserve_cfg.get(node_name, {}).get("memory")
reserved_memory_gb_default = reserve_cfg.get("defaults", {}).get("memory", 0)
node_resource_reservation = reserve_cfg.get(node_name, {}).get(resource_type, 0)
default_resource_reservation = reserve_cfg.get("defaults", {}).get(resource_type, 0)
# Make sure the reservation is a numeric value - check for both, default and node specific
if not isinstance(reserved_memory_gb_default, (int, float)):
if reserved_memory_gb_default is not None:
logger.info("Invalid default memory reservation: Found a string while expecting a numeric value - skipping default reservation")
reserved_memory_gb_default = 0
# Ensure reservations are numeric values
node_resource_reservation = node_resource_reservation if isinstance(node_resource_reservation, (int, float)) else 0
default_resource_reservation = default_resource_reservation if isinstance(default_resource_reservation, (int, float)) else 0
if not isinstance(reserved_memory_gb_node, (int, float)):
if reserved_memory_gb_node is not None:
logger.info(f"Invalid memory reservation: Found a string while expecting a numeric value - applying current default of {reserved_memory_gb_default} Bytes")
reserved_memory_gb_node = reserved_memory_gb_default
# Apply node specific reservation if set
if node_resource_reservation > 0:
if resource_value < (node_resource_reservation * 1024 ** 3):
logger.critical(f"Configured resource reservation for node {node_name} of type {resource_type} with {node_resource_reservation} GB is higher than available resource value {resource_value / (1024 ** 3):.2f} GB. Not applying...")
return resource_value
else:
logger.debug(f"Applying node specific reservation for {node_name} of type {resource_type} with {node_resource_reservation} GB.")
resource_value_new = resource_value - (node_resource_reservation * 1024 ** 3)
logger.debug(f'Switched resource value for node {node_name} of type {resource_type} from {resource_value / (1024 ** 3):.2f} GB to {resource_value_new / (1024 ** 3):.2f} GB after applying reservation.')
logger.debug(f"Before: {resource_value} | After: {resource_value_new}")
return resource_value_new
# Make sure the reservation is positive
if reserved_memory_gb_node < 0:
logger.info(f"{nodes['nodes'][node['node']]['name']}: Invalid assigned memory reservation, applying defaults")
reserved_memory_gb_node = reserved_memory_gb_default
# Apply default reservation if set and no node specific reservation has been performed
elif default_resource_reservation > 0:
if resource_value < (default_resource_reservation * 1024 ** 3):
logger.critical(f"Configured default reservation for node {node_name} of type {resource_type} with {default_resource_reservation} GB is higher than available resource value {resource_value / (1024 ** 3):.2f} GB. Not applying...")
return resource_value
else:
logger.debug(f"Applying default reservation for {node_name} of type {resource_type} with {default_resource_reservation} GB.")
resource_value_new = resource_value - (default_resource_reservation * 1024 ** 3)
logger.debug(f'Switched resource value for node {node_name} of type {resource_type} from {resource_value / (1024 ** 3):.2f} GB to {resource_value_new / (1024 ** 3):.2f} GB after applying reservation.')
logger.debug(f"Before: {resource_value} | After: {resource_value_new}")
return resource_value_new
# Convert the reservation from GB to Bytes, get the current nodes physical memory
reserved_memory_node = int(round(reserved_memory_gb_node * 1024 ** 3))
total_mem = node_data.get("memory_total")
# Check if the reservation doesnt exceed the nodes total memory
if reserved_memory_node > total_mem:
logger.debug(f"Reservation of {reserved_memory_node} Bytes exceeds available memory of {total_mem} Bytes- skipping reservation")
reserved_memory_node = 0
reserved_memory_gb_node = 0
logger.debug(f"Reserved Memory: {reserved_memory_gb_node} GB ({reserved_memory_node} Bytes)")
node_data["memory_total"] -= reserved_memory_node
Helper.update_node_resource_percentages(node_data)
logger.debug(f"Finished: apply_resource_reservation")
else:
logger.debug(f"No default or node specific resource reservation for node {node_name} found. Skipping...")
logger.debug(f"Finished: apply_resource_reservation")
return resource_value

View File

@@ -335,30 +335,3 @@ class Helper:
return (rc == 0, rc if rc != 0 else None)
finally:
test_socket.close()
@staticmethod
def update_node_resource_percentages(node_data: Dict[str, any]) -> None:
"""
This function updates the percentages of the node's resources in its respective dict
Args:
node_data: (Dict[str, any]) dict containing the nodes data to be updated
Returns: none
"""
logger.debug(f"Starting: Update resource percentages")
logger.debug(f"node data: {node_data}")
# memory
node_data["memory_assigned_percent"] = node_data["memory_assigned"] / node_data["memory_total"] * 100
node_data["memory_free_percent"] = node_data["memory_free"] / node_data["memory_total"] * 100
node_data["memory_used_percent"] = node_data["memory_used"] / node_data["memory_total"] * 100
# cpu
node_data["cpu_assigned_percent"] = node_data["cpu_assigned"] / node_data["cpu_total"] * 100
node_data["cpu_free_percent"] = node_data["cpu_free"] / node_data["cpu_total"] * 100
node_data["cpu_used_percent"] = node_data["cpu_used"] / node_data["cpu_total"] * 100
# disk
node_data["disk_assigned_percent"] = node_data["disk_assigned"] / node_data["disk_total"] * 100
node_data["disk_free_percent"] = node_data["disk_free"] / node_data["disk_total"] * 100
node_data["disk_used_percent"] = node_data["disk_used"] / node_data["disk_total"] * 100
logger.debug(f"node data: {node_data}")
logger.debug(f"End: Update resource percentages")
return