mirror of
https://github.com/gyptazy/ProxLB.git
synced 2026-04-05 20:31:57 +02:00
Merge pull request #396 from gyptazy/fix/395_fix_pool_based_node_pinning
fix: Fixed pool and ha-rules based node pinning of guests.
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
feature:
|
feature:
|
||||||
- Add support for Proxmox's native HA (affinity/anti-affinity) rules (@gyptazy). [#391]
|
- 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]
|
||||||
|
|||||||
2
.changelogs/1.1.11/395_fix_pool_based_node_pinning.yml
Normal file
2
.changelogs/1.1.11/395_fix_pool_based_node_pinning.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fixed:
|
||||||
|
- Fixed pool based node pinning (@gyptazy). [#395]
|
||||||
@@ -102,7 +102,7 @@ class Guests:
|
|||||||
guests['guests'][guest['name']]['affinity_groups'] = Tags.get_affinity_groups(guests['guests'][guest['name']]['tags'], guests['guests'][guest['name']]['pools'], guests['guests'][guest['name']]['ha_rules'], proxlb_config)
|
guests['guests'][guest['name']]['affinity_groups'] = Tags.get_affinity_groups(guests['guests'][guest['name']]['tags'], guests['guests'][guest['name']]['pools'], guests['guests'][guest['name']]['ha_rules'], proxlb_config)
|
||||||
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']]['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']]['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'], proxlb_config)
|
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']]['type'] = 'vm'
|
guests['guests'][guest['name']]['type'] = 'vm'
|
||||||
|
|
||||||
logger.debug(f"Resources of Guest {guest['name']} (type VM) added: {guests['guests'][guest['name']]}")
|
logger.debug(f"Resources of Guest {guest['name']} (type VM) added: {guests['guests'][guest['name']]}")
|
||||||
@@ -144,10 +144,11 @@ class Guests:
|
|||||||
guests['guests'][guest['name']]['pressure_hot'] = False
|
guests['guests'][guest['name']]['pressure_hot'] = False
|
||||||
guests['guests'][guest['name']]['tags'] = Tags.get_tags_from_guests(proxmox_api, node, guest['vmid'], 'ct')
|
guests['guests'][guest['name']]['tags'] = Tags.get_tags_from_guests(proxmox_api, node, guest['vmid'], 'ct')
|
||||||
guests['guests'][guest['name']]['pools'] = Pools.get_pools_for_guest(guest['name'], pools)
|
guests['guests'][guest['name']]['pools'] = Pools.get_pools_for_guest(guest['name'], pools)
|
||||||
guests['guests'][guest['name']]['affinity_groups'] = Tags.get_affinity_groups(guests['guests'][guest['name']]['tags'], guests['guests'][guest['name']]['pools'], proxlb_config)
|
guests['guests'][guest['name']]['ha_rules'] = HaRules.get_ha_rules_for_guest(guest['name'], ha_rules, guest['vmid'])
|
||||||
guests['guests'][guest['name']]['anti_affinity_groups'] = Tags.get_anti_affinity_groups(guests['guests'][guest['name']]['tags'], guests['guests'][guest['name']]['pools'], proxlb_config)
|
guests['guests'][guest['name']]['affinity_groups'] = Tags.get_affinity_groups(guests['guests'][guest['name']]['tags'], guests['guests'][guest['name']]['pools'], guests['guests'][guest['name']]['ha_rules'], proxlb_config)
|
||||||
|
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']]['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'], proxlb_config)
|
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']]['type'] = 'ct'
|
guests['guests'][guest['name']]['type'] = 'ct'
|
||||||
|
|
||||||
logger.debug(f"Resources of Guest {guest['name']} (type CT) added: {guests['guests'][guest['name']]}")
|
logger.debug(f"Resources of Guest {guest['name']} (type CT) added: {guests['guests'][guest['name']]}")
|
||||||
|
|||||||
@@ -56,9 +56,15 @@ class HaRules:
|
|||||||
ha_rules = {"ha_rules": {}}
|
ha_rules = {"ha_rules": {}}
|
||||||
|
|
||||||
for rule in proxmox_api.cluster.ha.rules.get():
|
for rule in proxmox_api.cluster.ha.rules.get():
|
||||||
|
|
||||||
|
# Skip disabled rules (disable key exists AND is truthy)
|
||||||
|
if rule.get("disable", 0):
|
||||||
|
logger.debug(f"Skipping ha-rule: {rule['rule']} of type {rule['type']} affecting guests: {rule['resources']}. Rule is disabled.")
|
||||||
|
continue
|
||||||
|
|
||||||
# Create a resource list by splitting on commas and stripping whitespace containing
|
# Create a resource list by splitting on commas and stripping whitespace containing
|
||||||
# the VM and CT IDs that are part of this HA rule
|
# the VM and CT IDs that are part of this HA rule
|
||||||
resources_list = [int(r.split(":")[1]) for r in rule["resources"].split(",") if r.strip()]
|
resources_list_guests = [int(r.split(":")[1]) for r in rule["resources"].split(",") if r.strip()]
|
||||||
|
|
||||||
# Convert the affinity field to a more descriptive type
|
# Convert the affinity field to a more descriptive type
|
||||||
if rule.get("affinity", None) == "negative":
|
if rule.get("affinity", None) == "negative":
|
||||||
@@ -66,11 +72,17 @@ class HaRules:
|
|||||||
else:
|
else:
|
||||||
affinity_type = "affinity"
|
affinity_type = "affinity"
|
||||||
|
|
||||||
|
# Create affected nodes list
|
||||||
|
resources_list_nodes = []
|
||||||
|
if rule.get("nodes", None):
|
||||||
|
resources_list_nodes = [n for n in rule["nodes"].split(",") if n]
|
||||||
|
|
||||||
# Create the ha_rule element
|
# Create the ha_rule element
|
||||||
ha_rules['ha_rules'][rule['rule']] = {}
|
ha_rules['ha_rules'][rule['rule']] = {}
|
||||||
ha_rules['ha_rules'][rule['rule']]['rule'] = rule['rule']
|
ha_rules['ha_rules'][rule['rule']]['rule'] = rule['rule']
|
||||||
ha_rules['ha_rules'][rule['rule']]['type'] = affinity_type
|
ha_rules['ha_rules'][rule['rule']]['type'] = affinity_type
|
||||||
ha_rules['ha_rules'][rule['rule']]['members'] = resources_list
|
ha_rules['ha_rules'][rule['rule']]['nodes'] = resources_list_nodes
|
||||||
|
ha_rules['ha_rules'][rule['rule']]['members'] = resources_list_guests
|
||||||
|
|
||||||
logger.debug(f"Got ha-rule: {rule['rule']} as type {affinity_type} affecting guests: {rule['resources']}")
|
logger.debug(f"Got ha-rule: {rule['rule']} as type {affinity_type} affecting guests: {rule['resources']}")
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ class Tags:
|
|||||||
Args:
|
Args:
|
||||||
tags (List): A list holding all defined tags for a given guest.
|
tags (List): A list holding all defined tags for a given guest.
|
||||||
pools (List): A list holding all defined pools for a given guest.
|
pools (List): A list holding all defined pools for a given guest.
|
||||||
|
ha_rules (List): A list holding all defined ha_rules for a given guest.
|
||||||
proxlb_config (Dict): A dict holding the ProxLB configuration.
|
proxlb_config (Dict): A dict holding the ProxLB configuration.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -140,6 +141,7 @@ class Tags:
|
|||||||
Args:
|
Args:
|
||||||
tags (List): A list holding all defined tags for a given guest.
|
tags (List): A list holding all defined tags for a given guest.
|
||||||
pools (List): A list holding all defined pools for a given guest.
|
pools (List): A list holding all defined pools for a given guest.
|
||||||
|
ha_rules (List): A list holding all defined ha_rules for a given guest.
|
||||||
proxlb_config (Dict): A dict holding the ProxLB configuration.
|
proxlb_config (Dict): A dict holding the ProxLB configuration.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -203,7 +205,7 @@ class Tags:
|
|||||||
return ignore_tag
|
return ignore_tag
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_node_relationships(tags: List[str], nodes: Dict[str, Any], pools: List[str], proxlb_config: Dict[str, Any]) -> str:
|
def get_node_relationships(tags: List[str], nodes: Dict[str, Any], pools: List[str], ha_rules: List[str], proxlb_config: Dict[str, Any]) -> str:
|
||||||
"""
|
"""
|
||||||
Get a node relationship tag for a guest from the Proxmox cluster by the API to pin
|
Get a node relationship tag for a guest from the Proxmox cluster by the API to pin
|
||||||
a guest to a node or by defined pools from ProxLB configuration.
|
a guest to a node or by defined pools from ProxLB configuration.
|
||||||
@@ -215,6 +217,7 @@ class Tags:
|
|||||||
tags (List): A list holding all defined tags for a given guest.
|
tags (List): A list holding all defined tags for a given guest.
|
||||||
nodes (Dict): A dictionary holding all available nodes in the cluster.
|
nodes (Dict): A dictionary holding all available nodes in the cluster.
|
||||||
pools (List): A list holding all defined pools for a given guest.
|
pools (List): A list holding all defined pools for a given guest.
|
||||||
|
ha_rules (List): A list holding all defined ha_rules for a given guest.
|
||||||
proxlb_config (Dict): A dict holding the ProxLB configuration.
|
proxlb_config (Dict): A dict holding the ProxLB configuration.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -223,6 +226,7 @@ class Tags:
|
|||||||
logger.debug("Starting: get_node_relationships.")
|
logger.debug("Starting: get_node_relationships.")
|
||||||
node_relationship_tags = []
|
node_relationship_tags = []
|
||||||
|
|
||||||
|
# Tag based node relationship
|
||||||
if len(tags) > 0:
|
if len(tags) > 0:
|
||||||
logger.debug("Validating node pinning by tags.")
|
logger.debug("Validating node pinning by tags.")
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
@@ -237,21 +241,38 @@ class Tags:
|
|||||||
else:
|
else:
|
||||||
logger.warning(f"Tag {node_relationship_tag} is invalid! Defined node does not exist in the cluster. Not applying pinning.")
|
logger.warning(f"Tag {node_relationship_tag} is invalid! Defined node does not exist in the cluster. Not applying pinning.")
|
||||||
|
|
||||||
|
# Pool based node relationship
|
||||||
if len(pools) > 0:
|
if len(pools) > 0:
|
||||||
logger.debug("Validating node pinning by pools.")
|
logger.debug("Validating node pinning by pools.")
|
||||||
for pool in pools:
|
for pool in pools:
|
||||||
if pool in (proxlb_config['balancing'].get('pools') or {}):
|
if pool in (proxlb_config['balancing'].get('pools') or {}):
|
||||||
|
|
||||||
node = proxlb_config['balancing']['pools'][pool].get('pin', None)
|
pool_nodes = proxlb_config['balancing']['pools'][pool].get('pin', None)
|
||||||
# Validate if the node to pin is present in the cluster
|
for node in pool_nodes:
|
||||||
if Helper.validate_node_presence(node, nodes):
|
|
||||||
logger.debug(f"Pool pinning tag {node} is valid! Defined node exists in the cluster.")
|
# Validate if the node to pin is present in the cluster
|
||||||
logger.debug(f"Setting node relationship because of pool {pool} to {node}.")
|
if Helper.validate_node_presence(node, nodes):
|
||||||
node_relationship_tags.append(node)
|
logger.debug(f"Pool pinning tag {node} is valid! Defined node exists in the cluster.")
|
||||||
else:
|
logger.debug(f"Setting node relationship because of pool {pool} to {node}.")
|
||||||
logger.warning(f"Pool pinning tag {node} is invalid! Defined node does not exist in the cluster. Not applying pinning.")
|
node_relationship_tags.append(node)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Pool pinning tag {node} is invalid! Defined node does not exist in the cluster. Not applying pinning.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Skipping pinning for pool {pool}. Pool is not defined in ProxLB configuration.")
|
logger.debug(f"Skipping pinning for pool {pool}. Pool is not defined in ProxLB configuration.")
|
||||||
|
|
||||||
|
# HA rule based node relationship
|
||||||
|
if len(ha_rules) > 0:
|
||||||
|
logger.debug("Validating node pinning by ha-rules.")
|
||||||
|
for ha_rule in ha_rules:
|
||||||
|
if len(ha_rule.get("nodes", 0)) > 0:
|
||||||
|
if ha_rule.get("type", None) == "affinity":
|
||||||
|
logger.debug(f"ha-rule {ha_rule['rule']} is of type affinity.")
|
||||||
|
for node in ha_rule["nodes"]:
|
||||||
|
logger.debug(f"Adding {node} as node relationship because of ha-rule {ha_rule['rule']}.")
|
||||||
|
node_relationship_tags.append(node)
|
||||||
|
else:
|
||||||
|
logger.debug(f"ha-rule {ha_rule['rule']} is of type anti-affinity. Skipping node relationship addition.")
|
||||||
|
|
||||||
logger.debug("Finished: get_node_relationships.")
|
logger.debug("Finished: get_node_relationships.")
|
||||||
return node_relationship_tags
|
return node_relationship_tags
|
||||||
|
|||||||
Reference in New Issue
Block a user