Merge pull request #412 from gyptazy/feature/406-strict-nonstrict-node-pinning

Add support for configuring node-pinning strictness (default: true) within pools
This commit is contained in:
gyptazy
2026-01-04 12:28:40 +01:00
committed by GitHub
7 changed files with 46 additions and 2 deletions

View File

@@ -1,3 +1,3 @@
feature:
added:
- Add support for Proxmox's native HA (affinity/anti-affinity) rules (@gyptazy). [#391]
- Add support for Proxmox's native HA (node-affinity) rules for pinning guests to nodes (@gyptazy). [#391]

View File

@@ -0,0 +1,2 @@
added:
- Add support for configuring node-pinning strictness (default: true) within pools (@gyptazy). [#406]

View File

@@ -393,6 +393,7 @@ balancing:
pin:
- virt66
- virt77
strict: False
service:
daemon: True

View File

@@ -74,6 +74,7 @@ balancing:
pin: # Define a pinning og guests to specific node(s)
- virt66
- virt77
strict: False # Disable strict mode of node pinning for this pool
service:
daemon: True

View File

@@ -519,8 +519,20 @@ class Calculations:
if len(proxlb_data["guests"][guest_name]["node_relationships"]) > 0:
logger.debug(f"Guest '{guest_name}' has relationships defined to node(s): {','.join(proxlb_data['guests'][guest_name]['node_relationships'])}. Pinning to node.")
# Get the node with the most free resources of the group
# Get the list of nodes that are defined as relationship for the guest
guest_node_relation_list = proxlb_data["guests"][guest_name]["node_relationships"]
# Validate if strict relationships are defined. If not, we prefer
# the most free node in addition to the relationship list.
if proxlb_data["guests"][guest_name]["node_relationships_strict"]:
logger.debug(f"Guest '{guest_name}' has strict node relationships defined. Only nodes in the relationship list will be considered for pinning.")
else:
logger.debug(f"Guest '{guest_name}' has non-strict node relationships defined. Prefering nodes in the relationship list for pinning.")
Calculations.get_most_free_node(proxlb_data)
most_free_node = proxlb_data["meta"]["balancing"]["balance_next_node"]
guest_node_relation_list.append(most_free_node)
# Get the most free node from the relationship list, or the most free node overall
Calculations.get_most_free_node(proxlb_data, False, guest_node_relation_list)
# Validate if the specified node name is really part of the cluster

View File

@@ -103,6 +103,7 @@ class Guests:
guests['guests'][guest['name']]['anti_affinity_groups'] = Tags.get_anti_affinity_groups(guests['guests'][guest['name']]['tags'], guests['guests'][guest['name']]['pools'], guests['guests'][guest['name']]['ha_rules'], proxlb_config)
guests['guests'][guest['name']]['ignore'] = Tags.get_ignore(guests['guests'][guest['name']]['tags'])
guests['guests'][guest['name']]['node_relationships'] = Tags.get_node_relationships(guests['guests'][guest['name']]['tags'], nodes, guests['guests'][guest['name']]['pools'], guests['guests'][guest['name']]['ha_rules'], proxlb_config)
guests['guests'][guest['name']]['node_relationships_strict'] = Pools.get_pool_node_affinity_strictness(proxlb_config, guests['guests'][guest['name']]['pools'])
guests['guests'][guest['name']]['type'] = 'vm'
logger.debug(f"Resources of Guest {guest['name']} (type VM) added: {guests['guests'][guest['name']]}")
@@ -149,6 +150,7 @@ class Guests:
guests['guests'][guest['name']]['anti_affinity_groups'] = Tags.get_anti_affinity_groups(guests['guests'][guest['name']]['tags'], guests['guests'][guest['name']]['pools'], guests['guests'][guest['name']]['ha_rules'], proxlb_config)
guests['guests'][guest['name']]['ignore'] = Tags.get_ignore(guests['guests'][guest['name']]['tags'])
guests['guests'][guest['name']]['node_relationships'] = Tags.get_node_relationships(guests['guests'][guest['name']]['tags'], nodes, guests['guests'][guest['name']]['pools'], guests['guests'][guest['name']]['ha_rules'], proxlb_config)
guests['guests'][guest['name']]['node_relationships_strict'] = Pools.get_pool_node_affinity_strictness(proxlb_config, guests['guests'][guest['name']]['pools'])
guests['guests'][guest['name']]['type'] = 'ct'
logger.debug(f"Resources of Guest {guest['name']} (type CT) added: {guests['guests'][guest['name']]}")

View File

@@ -115,3 +115,29 @@ class Pools:
logger.debug("Finished: get_pools_for_guests.")
return guest_pools
@staticmethod
def get_pool_node_affinity_strictness(proxlb_config: Dict[str, Any], guest_pools: list) -> bool:
"""
Retrieve the node affinity strictness setting for a guest across its pools.
Queries the ProxLB configuration to determine the node affinity strictness
level for the specified guest based on its pool memberships. Returns the
strictness setting from the first matching pool configuration.
Args:
proxlb_config (Dict[str, Any]): ProxLB configuration dictionary.
guest_pools (list): List of pool names the guest belongs to.
Returns:
bool: Node affinity strictness setting (default True if not specified).
"""
logger.debug("Starting: get_pool_node_affinity_strictness.")
node_strictness = True
for pool in guest_pools:
pool_settings = proxlb_config.get("balancing", {}).get("pools", {}).get(pool, {})
node_strictness = pool_settings.get("strict", True)
logger.debug("Finished: get_pool_node_affinity_strictness.")
return node_strictness