|
24 | 24 | import time |
25 | 25 | from typing import Dict, Any |
26 | 26 | from utils.logger import SystemdLogger |
| 27 | +from utils.helper import Helper |
27 | 28 |
|
28 | 29 | logger = SystemdLogger() |
29 | 30 |
|
@@ -102,6 +103,7 @@ def get_nodes(proxmox_api: any, proxlb_config: Dict[str, Any]) -> Dict[str, Any] |
102 | 103 | nodes["nodes"][node["node"]]["disk_pressure_full_spikes_percent"] = Nodes.get_node_rrd_data(proxmox_api, node["node"], "disk", "full", spikes=True) |
103 | 104 | nodes["nodes"][node["node"]]["disk_pressure_hot"] = False |
104 | 105 |
|
| 106 | + Nodes.apply_resource_reservation(nodes["nodes"][node["node"]]["name"], proxlb_config, nodes["nodes"][node["node"]]) |
105 | 107 | # Evaluate if node should be set to maintenance mode |
106 | 108 | if Nodes.set_node_maintenance(proxmox_api, proxlb_config, node["node"]): |
107 | 109 | nodes["nodes"][node["node"]]["maintenance"] = True |
@@ -253,3 +255,62 @@ def get_node_pve_version(proxmox_api, node_name: str) -> float: |
253 | 255 | logger.debug(f"Got version {version['version']} for node {node_name}.") |
254 | 256 | logger.debug("Finished: get_node_pve_version.") |
255 | 257 | return version["version"] |
| 258 | + |
| 259 | + |
| 260 | + @staticmethod |
| 261 | + def apply_resource_reservation(node_name, proxlb_config: Dict[str, Any], node_data: Dict[str, Any]) -> None: |
| 262 | + """ |
| 263 | + Check if there is a a configured resource reservation for the current nodes and apply it as needed. |
| 264 | + Checks for a node specific config first, then if there is any configured default and if neither then nothing is reserved. |
| 265 | + Reservations are applied by directly modifying the data gathered from the nodes. |
| 266 | +
|
| 267 | + Args: |
| 268 | + node_name: (str) the name of the node |
| 269 | + proxlb_config (Dict[str, Any]): A dictionary containing the ProxLB configuration. |
| 270 | + node_data: (Dict[str, Any]): Dict containing the current nodes data |
| 271 | + Returns: none |
| 272 | + """ |
| 273 | + logger.debug(f"Starting: Resource reservation") |
| 274 | + logger.debug(f"Processing resource reservation for node {node_name}") |
| 275 | + # load the balancing section from the config dict, will create an empty dict if there isnt anything |
| 276 | + balancing_cfg = proxlb_config.get("balancing", {}) |
| 277 | + # get the reservation dict from the previously loaded dict , will create an empty dict if there isnt anything |
| 278 | + reserve_cfg = balancing_cfg.get("node_resource_reserve", {}) |
| 279 | + # try to get the reserved memory for the node |
| 280 | + reserved_memory_gb_node = reserve_cfg.get(node_name, {}).get("memory") |
| 281 | + # try to load the default reserved memory - will set to 0 if there isn't anything |
| 282 | + reserved_memory_gb_default = reserve_cfg.get("defaults", {}).get("memory", 0) |
| 283 | + |
| 284 | + # make sure the reservation is a numeric value - check for both, default and node specific |
| 285 | + if not isinstance(reserved_memory_gb_default, (int, float)): |
| 286 | + if reserved_memory_gb_default is not None: |
| 287 | + logger.info("Invalid default memory reservation: Found a string while expecting a numeric value - skipping default reservation") |
| 288 | + reserved_memory_gb_default = 0 |
| 289 | + |
| 290 | + if not isinstance(reserved_memory_gb_node, (int, float)): |
| 291 | + if reserved_memory_gb_node is not None: |
| 292 | + logger.info(f"Invalid memory reservation: Found a string while expecting a numeric value - applying current default of {reserved_memory_gb_default} Bytes") |
| 293 | + reserved_memory_gb_node = reserved_memory_gb_default |
| 294 | + |
| 295 | + # make sure the reservation is positive |
| 296 | + if reserved_memory_gb_node < 0: |
| 297 | + logger.info(f"{nodes['nodes'][node['node']]['name']}: Invalid assigned memory reservation, applying defaults") |
| 298 | + reserved_memory_gb_node = reserved_memory_gb_default |
| 299 | + |
| 300 | + # convert the reservation from GB to Bytes, get the current nodes physical memory |
| 301 | + reserved_memory_node = int(round(reserved_memory_gb_node * 1024 ** 3)) |
| 302 | + total_mem = node_data.get("memory_total") |
| 303 | + |
| 304 | + # check if the reservation doesnt exceed the nodes total memory |
| 305 | + if reserved_memory_node > total_mem: |
| 306 | + logger.debug(f"Reservation of {reserved_memory_node} Bytes exceeds available memory of {total_mem} Bytes- skipping reservation") |
| 307 | + reserved_memory_node = 0 |
| 308 | + reserved_memory_gb_node = 0 |
| 309 | + |
| 310 | + logger.debug(f"Reserved Memory: {reserved_memory_gb_node} GB ({reserved_memory_node} Bytes)") |
| 311 | + node_data["memory_total"] -= reserved_memory_node |
| 312 | + Helper.update_node_resource_percentages(node_data) |
| 313 | + |
| 314 | + logger.debug(f"End: Resource reservation") |
| 315 | + return |
| 316 | + |
0 commit comments