From 09b5b83c24d5678865db8b3641f4e966d10e7506 Mon Sep 17 00:00:00 2001 From: gyptazy Date: Sun, 14 Dec 2025 09:34:21 +0100 Subject: [PATCH] fix: HA affinity/anti-affinity rules can only be evaluated on PVE9+ nodes Fixes: #391 --- proxlb/main.py | 3 ++- proxlb/models/features.py | 34 ++++++++++++++++++++++++++++++++++ proxlb/models/ha_rules.py | 16 ++++++++++++---- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/proxlb/main.py b/proxlb/main.py index 7d90a21..994617e 100644 --- a/proxlb/main.py +++ b/proxlb/main.py @@ -74,8 +74,9 @@ def main(): # Get all required objects from the Proxmox cluster meta = {"meta": proxlb_config} nodes = Nodes.get_nodes(proxmox_api, proxlb_config) + meta = Features.validate_any_non_pve9_node(meta, nodes) pools = Pools.get_pools(proxmox_api) - ha_rules = HaRules.get_ha_rules(proxmox_api) + ha_rules = HaRules.get_ha_rules(proxmox_api, meta) guests = Guests.get_guests(proxmox_api, pools, ha_rules, nodes, meta, proxlb_config) groups = Groups.get_groups(guests, nodes) diff --git a/proxlb/models/features.py b/proxlb/models/features.py index defbfdb..3e59478 100644 --- a/proxlb/models/features.py +++ b/proxlb/models/features.py @@ -88,3 +88,37 @@ class Features: proxlb_data["meta"]["balancing"]["enable"] = False logger.debug("Finished: validate_available_features.") + + @staticmethod + def validate_any_non_pve9_node(meta: any, nodes: any) -> dict: + """ + Validate if any node in the cluster is running Proxmox VE < 9.0.0 and update meta accordingly. + + This function inspects the cluster node versions and sets a flag in meta indicating whether + any node is running a Proxmox VE version older than 9.0.0. + + Args: + meta (dict): Metadata structure that will be updated with cluster version information. + nodes (dict): Cluster nodes mapping whose values contain 'pve_version' strings. + + Returns: + dict: The updated meta dictionary with 'cluster_non_pve9' flag set to True or False. + + Side effects: + - Mutates meta["meta"]["cluster_non_pve9"] based on node versions. + - Emits debug log messages. + + Notes: + - Version comparison uses semantic version parsing; defaults to "0.0.0" if pve_version is missing. + """ + logger.debug("Starting: validate_any_non_pve9_node.") + any_non_pve9_node = any(version.parse(node.get("pve_version", "0.0.0")) < version.parse("9.0.0") for node in nodes.get("nodes", {}).values()) + + if any_non_pve9_node: + meta["meta"]["cluster_non_pve9"] = True + logger.debug("Finished: validate_any_non_pve9_node. Result: True") + else: + meta["meta"]["cluster_non_pve9"] = False + logger.debug("Finished: validate_any_non_pve9_node. Result: False") + + return meta diff --git a/proxlb/models/ha_rules.py b/proxlb/models/ha_rules.py index a0c342b..b00c64c 100644 --- a/proxlb/models/ha_rules.py +++ b/proxlb/models/ha_rules.py @@ -36,7 +36,7 @@ class HaRules: """ @staticmethod - def get_ha_rules(proxmox_api: any) -> Dict[str, Any]: + def get_ha_rules(proxmox_api: any, meta: dict) -> Dict[str, Any]: """ Retrieve all HA rules from a Proxmox cluster. @@ -46,15 +46,23 @@ class HaRules: descriptive format (affinity or anti-affinity). Args: - proxmox_api (any): Proxmox API client instance. + proxmox_api (any): Proxmox API client instance. + meta (dict): The metadata dictionary containing cluster information. Returns: - Dict[str, Any]: Dictionary with a top-level "ha_rules" key mapping rule id - to {"rule": , "type": , "members": [...]}. + Dict[str, Any]: Dictionary with a top-level "ha_rules" key mapping rule id + to {"rule": , "type": , "members": [...]}. """ logger.debug("Starting: get_ha_rules.") ha_rules = {"ha_rules": {}} + # If any node is non PVE 9, skip fetching HA rules as they are unsupported + if meta["meta"]["cluster_non_pve9"]: + logger.debug("Skipping HA rule retrieval as non Proxmox VE 9 systems detected.") + return ha_rules + else: + logger.debug("Cluster running Proxmox VE 9 or newer, proceeding with HA rule retrieval.") + for rule in proxmox_api.cluster.ha.rules.get(): # Skip disabled rules (disable key exists AND is truthy)