diff --git a/.changelogs/1.1.11/275_add_overprovisioning_safety_guard.yml b/.changelogs/1.1.11/275_add_overprovisioning_safety_guard.yml new file mode 100644 index 0000000..0a6869b --- /dev/null +++ b/.changelogs/1.1.11/275_add_overprovisioning_safety_guard.yml @@ -0,0 +1,2 @@ +fixed: + - Fixed missing overprovisioning safety guard to avoid node overprovisioning (@gyptazy). [#275] diff --git a/proxlb/models/calculations.py b/proxlb/models/calculations.py index 3165e71..3186bb4 100644 --- a/proxlb/models/calculations.py +++ b/proxlb/models/calculations.py @@ -387,6 +387,10 @@ class Calculations: for guest_name in proxlb_data["groups"]["affinity"][group_name]["guests"]: mode = proxlb_data["meta"]["balancing"].get("mode", "used") + if not Calculations.validate_node_resources(proxlb_data, guest_name): + 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}.") + continue + if mode == 'psi': logger.debug(f"Evaluating guest relocation based on {mode} mode.") method = proxlb_data["meta"]["balancing"].get("method", "memory") @@ -707,3 +711,40 @@ class Calculations: return False return True + + @staticmethod + def validate_node_resources(proxlb_data: Dict[str, Any], guest_name: str) -> bool: + """ + Validate that the target node has sufficient resources to host the specified guest. + + This function checks if the target node, determined by the balancing logic, + has enough CPU, memory, and disk resources available to accommodate the guest. + + Args: + proxlb_data (Dict[str, Any]): A dictionary containing the complete ProxLB state including: + - "nodes": Dictionary with node resource information + - "guests": Dictionary with guest resource requirements + - "meta": Dictionary with balancing information including target node + guest_name (str): The name of the guest to validate resources for + Returns: + bool: True if the target node has sufficient resources, False otherwise + """ + logger.debug("Starting: validate_node_resources.") + node_target = proxlb_data["meta"]["balancing"]["balance_next_node"] + + node_memory_free = proxlb_data["nodes"][node_target]["memory_free"] + node_cpu_free = proxlb_data["nodes"][node_target]["cpu_free"] + node_disk_free = proxlb_data["nodes"][node_target]["disk_free"] + + guest_memory_required = proxlb_data["guests"][guest_name]["memory_used"] + guest_cpu_required = proxlb_data["guests"][guest_name]["cpu_used"] + 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("Finished: validate_node_resources.") + return True + else: + logger.debug(f"Node '{node_target}' lacks sufficient resources for guest '{guest_name}'.") + logger.debug("Finished: validate_node_resources.") + return False