* [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup
@ 2025-11-03 10:19 Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 01/21] config: do not add ignored resources to dependent resources Daniel Kral
` (20 more replies)
0 siblings, 21 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
v2: https://lore.proxmox.com/pve-devel/20250909083539.39675-1-d.kral@proxmox.com/
v1: https://lore.proxmox.com/pve-devel/20250821143705.256562-1-d.kral@proxmox.com/
This series is based on top of the granular accounting series [0]
[0] https://lore.proxmox.com/pve-devel/20251027164513.542678-1-d.kral@proxmox.com/
Changelog from v2 -> v3:
- rebased on top of master + granular accounting series [0], because
[0] is already closer to being merged and as both change some rules'
interfaces I had to choose one above the other
- fix and add clarifying notes to inter-rules feasibility checks
- remove usage of prototypes introduced by the original HA rules
series, which I had wrong assumptions about
- some minor code changes (see per-patch notes):
- use 5.36 for newly introduced module PVE::HA::Rules::Helpers
- use my sub name {} over my $name = sub {} for new private
subroutines / helpers
- change POD signature of get_node_affinity(...) too
Ran a `git rebase master --exec 'make clean && make deb'` on the series
and tested the changes to the Status API manually.
I put the patches in decreasing priority:
PATCH 1 fix output of get_resource_info() about dependent resources
PATCH 2 fix retranslating ha rules on nodelist changes
PATCH 3-5 fix wrong assumption about positive resource affinity checks
PATCH 6-10 ha rules performance improvements + preparations
PATCH 11-12 make test cases use to_json(...) and add compiled configs
PATCH 13-21 various smaller cleanups related to HA rules
Ad PATCH 1, 2, 3-5:
The first few patches fix some wrong assumptions and bugs.
Ad PATCH 3-5:
During the initial HA rules implementation there were quite a few
changes done to how rules were checked and translated, which moved the
merging of positive resource affinity rules to the end of the pipeline.
Not taking notice of this clearly enough, this causes two checks to have
wrong assumptions about positive positive resource affinity rules: they
assume that the resource sets of positive resource affinity rules are
already disjoint from each other, even though that is only done at a
later stage.
As it would be rather cumbersome to interleave checks/pruning infeasible
rules and rule transforms [0] (i.e. move the merging transform inbetween
the previous resource affinity checks and the inter-consistency check; I
tried that but it looked rather ugly), the best method IMO was to use
the already existing helper to find these disjoint sets. This also
provides these checks with the correct positive resource affinity rule
ids to blame instead of building any extra logic for handling that if
checks/transforms would've been interleaved.
Ad PATCH 6-10:
These patches prepare and implement the compilation of HA rules when
these are used in the HA Manager, which improves the performance of
checking for HA rules (significant for overall performance) and when
applying these (significant only for resources with rules).
Ad PATCH 11-21:
These patches are various smaller improvements related to the core HA
rules feature, which are dependent on the changes above and/or are
easier to apply in a single patch series (e.g. due to larger changes in
a single source file):
- make rules tests use to_json(...) instead of Data::Dumper
- make rules tests also output compiled configs to document changes
- synchronize how active LRMs are determined, which included how it is
done for checking health for the HA groups migration
- add notes about the why's and how's for feasibility checks
Daniel Kral (21):
config: do not add ignored resources to dependent resources
manager: retranslate rules if nodes are added or removed
rules: factor out disjoint rules' resource set helper
rules: resource affinity: inter-consistency check with merged positive
rules
rules: add merged positive resource affinity info in global checks
rules: make rules sorting optional in foreach_rule helper
rename rule's canonicalize stage to transform stage
rules: make plugins register transformers instead of plugin_transform
rules: node affinity: decouple get_node_affinity helper from Usage
class
compile ha rules to a more compact representation
test: rules: use to_json instead of Data::Dumper for config output
test: rules: add compiled config output to rules config test cases
rules: node affinity: define node priority outside hash access
move minimum version check helper to ha tools
manager: move group migration cooldown variable into helper
api: status: sync active service counting with lrm's helper
manager: group migration: sync active service counting with lrm's
helper
factor out counting of active services into helper
tree-wide: remove misused function prototype declaractions
rules: fix documentation for inter-rules checker subroutines
rules: add documentation about current feasibility check
implementations
debian/pve-ha-manager.install | 1 +
src/PVE/API2/HA/Rules.pm | 1 +
src/PVE/API2/HA/Status.pm | 17 +-
src/PVE/HA/Config.pm | 18 +-
src/PVE/HA/HashTools.pm | 6 +-
src/PVE/HA/LRM.pm | 20 +-
src/PVE/HA/Manager.pm | 89 ++--
src/PVE/HA/NodeStatus.pm | 14 +
src/PVE/HA/Rules.pm | 215 +++++++---
src/PVE/HA/Rules/Helpers.pm | 77 ++++
src/PVE/HA/Rules/Makefile | 2 +-
src/PVE/HA/Rules/NodeAffinity.pm | 90 ++--
src/PVE/HA/Rules/ResourceAffinity.pm | 220 +++++-----
src/PVE/HA/Tools.pm | 52 +++
...efaults-for-node-affinity-rules.cfg.expect | 145 ++++---
...lts-for-resource-affinity-rules.cfg.expect | 87 ++--
...onsistent-node-resource-affinity-rules.cfg | 19 +
...nt-node-resource-affinity-rules.cfg.expect | 185 ++++++---
.../inconsistent-resource-affinity-rules.cfg | 15 +
...sistent-resource-affinity-rules.cfg.expect | 21 +-
...egative-resource-affinity-rules.cfg.expect | 73 ++--
...fective-resource-affinity-rules.cfg.expect | 18 +-
...egative-resource-affinity-rules.cfg.expect | 342 +++++++++------
...ositive-resource-affinity-rules.cfg.expect | 391 +++++++++++++-----
...egative-resource-affinity-rules.cfg.expect | 186 +++++----
...ositive-resource-affinity-rules.cfg.expect | 264 +++++++++---
...ty-with-resource-affinity-rules.cfg.expect | 114 +++--
...rce-refs-in-node-affinity-rules.cfg.expect | 200 ++++++---
src/test/test_failover1.pl | 15 +-
src/test/test_rules_config.pl | 14 +-
30 files changed, 1904 insertions(+), 1007 deletions(-)
create mode 100644 src/PVE/HA/Rules/Helpers.pm
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 01/21] config: do not add ignored resources to dependent resources
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 02/21] manager: retranslate rules if nodes are added or removed Daniel Kral
` (19 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Otherwise HA resources that are in positive resource affinity rules with
ignored HA resources will be shown as dependent resources for callers of
get_resource_motion_info(...), even though the ignored HA resource(s)
won't be affected.
These are already ignored in the HA Manager for the migrate command
since 1c9c35d4 ("config, manager: do not check ignored resources with
affinity when migrating").
Fixes: 47340b13 ("api: resources: add check for resource affinity in resource migrations")
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Config.pm | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/PVE/HA/Config.pm b/src/PVE/HA/Config.pm
index 301c62fe..b2ef26aa 100644
--- a/src/PVE/HA/Config.pm
+++ b/src/PVE/HA/Config.pm
@@ -394,7 +394,12 @@ sub get_resource_motion_info {
my $rules = read_and_check_effective_rules_config();
my ($together, $separate) = get_affinitive_resources($rules, $sid);
- push @$dependent_resources, $_ for sort keys %$together;
+ for my $csid (sort keys %$together) {
+ next if !defined($ss->{$csid});
+ next if $ss->{$csid}->{state} eq 'ignored';
+
+ push @$dependent_resources, $csid;
+ }
for my $node (keys %$ns) {
next if $ns->{$node} ne 'online';
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 02/21] manager: retranslate rules if nodes are added or removed
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 01/21] config: do not add ignored resources to dependent resources Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 03/21] rules: factor out disjoint rules' resource set helper Daniel Kral
` (18 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Some rule checks depend on the list of cluster nodes, e.g., to check
whether a negative resource affinity rule doesn't specify more HA
resources than cluster nodes.
The HA Manager retranslates rules only in certain conditions to reduce
unnecessary computations, but lacks a check whether cluster nodes have
been added or removed, which is different from what users are reported
through the rules API endpoints and web interface.
Fixes: 6c4c0458 ("rules: add haenv node list to the rules' canonicalization stage")
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Manager.pm | 2 ++
src/PVE/HA/NodeStatus.pm | 14 ++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index 83167075..9c0a8e09 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -664,6 +664,7 @@ sub manage {
my ($haenv, $ms, $ns, $ss) = ($self->{haenv}, $self->{ms}, $self->{ns}, $self->{ss});
my ($node_info) = $haenv->get_node_info();
+ my $has_changed_nodelist = $ns->check_for_changed_nodelist($node_info);
my ($lrm_results, $lrm_modes) = $self->read_lrm_status();
$ns->update($node_info, $lrm_modes);
@@ -719,6 +720,7 @@ sub manage {
if (
!$self->{rules}
+ || $has_changed_nodelist
|| $new_rules->{digest} ne $self->{last_rules_digest}
|| $self->{groups}->{digest} ne $self->{last_groups_digest}
|| $services_digest && $services_digest ne $self->{last_services_digest}
diff --git a/src/PVE/HA/NodeStatus.pm b/src/PVE/HA/NodeStatus.pm
index 1512ae2b..e5dddf3b 100644
--- a/src/PVE/HA/NodeStatus.pm
+++ b/src/PVE/HA/NodeStatus.pm
@@ -90,6 +90,20 @@ sub list_online_nodes {
return $res;
}
+sub check_for_changed_nodelist {
+ my ($self, $node_info) = @_;
+
+ for my $node (keys %$node_info) {
+ return 1 if !$self->{status}->{$node};
+ }
+
+ for my $node (keys $self->{status}->%*) {
+ return 1 if !$node_info->{$node};
+ }
+
+ return 0;
+}
+
my $delete_node = sub {
my ($self, $node) = @_;
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 03/21] rules: factor out disjoint rules' resource set helper
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 01/21] config: do not add ignored resources to dependent resources Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 02/21] manager: retranslate rules if nodes are added or removed Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 04/21] rules: resource affinity: inter-consistency check with merged positive rules Daniel Kral
` (17 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
This is done in preparation to the next patches, where the helper will
be used in different packages.
As the helper is not dependent on resource affinity rules at all, rename
it to find_disjoint_rules_resource_sets(...) and adapt the documentation
accordingly.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- fix minor merge conflict for Makefile changes while rebasing
- make new HA rules helper use perl 5.36 + signatures
- change `my $name = sub { ... }` to `my sub name { ... }` as the
latter has a name in perl debugging utilities
debian/pve-ha-manager.install | 1 +
src/PVE/HA/Rules/Helpers.pm | 77 ++++++++++++++++++++++++++++
src/PVE/HA/Rules/Makefile | 2 +-
src/PVE/HA/Rules/ResourceAffinity.pm | 58 ++-------------------
4 files changed, 83 insertions(+), 55 deletions(-)
create mode 100644 src/PVE/HA/Rules/Helpers.pm
diff --git a/debian/pve-ha-manager.install b/debian/pve-ha-manager.install
index 2e6b7d55..38d5d60b 100644
--- a/debian/pve-ha-manager.install
+++ b/debian/pve-ha-manager.install
@@ -35,6 +35,7 @@
/usr/share/perl5/PVE/HA/Resources/PVECT.pm
/usr/share/perl5/PVE/HA/Resources/PVEVM.pm
/usr/share/perl5/PVE/HA/Rules.pm
+/usr/share/perl5/PVE/HA/Rules/Helpers.pm
/usr/share/perl5/PVE/HA/Rules/NodeAffinity.pm
/usr/share/perl5/PVE/HA/Rules/ResourceAffinity.pm
/usr/share/perl5/PVE/HA/Tools.pm
diff --git a/src/PVE/HA/Rules/Helpers.pm b/src/PVE/HA/Rules/Helpers.pm
new file mode 100644
index 00000000..7861332b
--- /dev/null
+++ b/src/PVE/HA/Rules/Helpers.pm
@@ -0,0 +1,77 @@
+package PVE::HA::Rules::Helpers;
+
+use v5.36;
+
+use PVE::HA::HashTools qw(sets_are_disjoint);
+
+my sub sort_by_lowest_resource_id($rules) {
+ my $lowest_rule_resource_id = {};
+ for my $ruleid (keys %$rules) {
+ my @rule_resources = sort keys $rules->{$ruleid}->{resources}->%*;
+ $lowest_rule_resource_id->{$ruleid} = $rule_resources[0];
+ }
+
+ # sort rules such that rules with the lowest numbered resource come first
+ my @sorted_ruleids = sort {
+ $lowest_rule_resource_id->{$a} cmp $lowest_rule_resource_id->{$b}
+ } sort keys %$rules;
+
+ return @sorted_ruleids;
+}
+
+=head3 find_disjoint_rules_resource_sets($rules)
+
+Finds the rule subsets in C<$rules>, which reference a disjoint set of resources
+with respect to the other rules. The disjoint rule resource sets are returned as
+a list of hashes, where each item contains the disjoint resource set and the
+ruleids from C<$rules> that were used to create the resource subset.
+
+For example, if one rule references the resources C<'vm:101'> and C<'vm:102'>,
+and another rule references the resources C<'vm:102'> and C<'vm:103'>, these
+will result in one disjoint rule resource set, thus the return value:
+
+ (
+ {
+ ruleids = [ 'rule1', 'rule2' ],
+ resources => {
+ 'vm:101' => 1,
+ 'vm:102' => 1,
+ 'vm:103' => 1
+ }
+ }
+ )
+
+=cut
+
+sub find_disjoint_rules_resource_sets($rules) {
+ my @disjoint_rules = ();
+
+ # order needed so that it is easier to check whether there is an overlap
+ my @sorted_ruleids = sort_by_lowest_resource_id($rules);
+
+ for my $ruleid (@sorted_ruleids) {
+ my $rule = $rules->{$ruleid};
+
+ my $found = 0;
+ for my $entry (@disjoint_rules) {
+ next if sets_are_disjoint($rule->{resources}, $entry->{resources});
+
+ $found = 1;
+ push @{ $entry->{ruleids} }, $ruleid;
+ $entry->{resources}->{$_} = 1 for keys $rule->{resources}->%*;
+
+ last;
+ }
+ if (!$found) {
+ push @disjoint_rules,
+ {
+ ruleids => [$ruleid],
+ resources => { $rule->{resources}->%* },
+ };
+ }
+ }
+
+ return @disjoint_rules;
+}
+
+1;
diff --git a/src/PVE/HA/Rules/Makefile b/src/PVE/HA/Rules/Makefile
index 1cbde5ba..977c882a 100644
--- a/src/PVE/HA/Rules/Makefile
+++ b/src/PVE/HA/Rules/Makefile
@@ -1,4 +1,4 @@
-SIM_SOURCES=NodeAffinity.pm ResourceAffinity.pm
+SIM_SOURCES=Helpers.pm NodeAffinity.pm ResourceAffinity.pm
SOURCES=${SIM_SOURCES}
.PHONY: install
diff --git a/src/PVE/HA/Rules/ResourceAffinity.pm b/src/PVE/HA/Rules/ResourceAffinity.pm
index 9a928196..86b94d60 100644
--- a/src/PVE/HA/Rules/ResourceAffinity.pm
+++ b/src/PVE/HA/Rules/ResourceAffinity.pm
@@ -3,8 +3,9 @@ package PVE::HA::Rules::ResourceAffinity;
use strict;
use warnings;
-use PVE::HA::HashTools qw(set_intersect sets_are_disjoint);
+use PVE::HA::HashTools qw(set_intersect);
use PVE::HA::Rules;
+use PVE::HA::Rules::Helpers;
use PVE::HA::Usage;
use base qw(Exporter);
@@ -248,58 +249,6 @@ __PACKAGE__->register_check(
=cut
-my $sort_by_lowest_resource_id = sub {
- my ($rules) = @_;
-
- my $lowest_rule_resource_id = {};
- for my $ruleid (keys %$rules) {
- my @rule_resources = sort keys $rules->{$ruleid}->{resources}->%*;
- $lowest_rule_resource_id->{$ruleid} = $rule_resources[0];
- }
-
- # sort rules such that rules with the lowest numbered resource come first
- my @sorted_ruleids = sort {
- $lowest_rule_resource_id->{$a} cmp $lowest_rule_resource_id->{$b}
- } sort keys %$rules;
-
- return @sorted_ruleids;
-};
-
-# returns a list of hashes, which contain disjoint resource affinity rules, i.e.,
-# put resource affinity constraints on disjoint sets of resources
-my $find_disjoint_resource_affinity_rules = sub {
- my ($rules) = @_;
-
- my @disjoint_rules = ();
-
- # order needed so that it is easier to check whether there is an overlap
- my @sorted_ruleids = $sort_by_lowest_resource_id->($rules);
-
- for my $ruleid (@sorted_ruleids) {
- my $rule = $rules->{$ruleid};
-
- my $found = 0;
- for my $entry (@disjoint_rules) {
- next if sets_are_disjoint($rule->{resources}, $entry->{resources});
-
- $found = 1;
- push @{ $entry->{ruleids} }, $ruleid;
- $entry->{resources}->{$_} = 1 for keys $rule->{resources}->%*;
-
- last;
- }
- if (!$found) {
- push @disjoint_rules,
- {
- ruleids => [$ruleid],
- resources => { $rule->{resources}->%* },
- };
- }
- }
-
- return @disjoint_rules;
-};
-
=head3 merge_connected_positive_resource_affinity_rules($rules, $positive_rules)
Modifies C<$rules> to contain only disjoint positive resource affinity rules
@@ -320,7 +269,8 @@ a resource, in C<$rules> at a later point in time.
sub merge_connected_positive_resource_affinity_rules {
my ($rules, $positive_rules) = @_;
- my @disjoint_positive_rules = $find_disjoint_resource_affinity_rules->($positive_rules);
+ my @disjoint_positive_rules =
+ PVE::HA::Rules::Helpers::find_disjoint_rules_resource_sets($positive_rules);
for my $entry (@disjoint_positive_rules) {
next if @{ $entry->{ruleids} } < 2;
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 04/21] rules: resource affinity: inter-consistency check with merged positive rules
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (2 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 03/21] rules: factor out disjoint rules' resource set helper Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 05/21] rules: add merged positive resource affinity info in global checks Daniel Kral
` (16 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
The resource affinity rule set is checked whether it contains pairs of
positive and negative resource affinity rules, which specify two or more
HA resources in them, and prunes them as those are infeasible.
This check has the assumption that each positive resource affinity
rule's resource set is disjoint from each other, but this is only done
in the later transformation stage when positive resource affinity with
overlapping HA resources in them are merged to one rule.
For example, the following inconsistent rules are not pruned:
- positive resource affinity rule between vm:101 and vm:102
- positive resource affinity rule between vm:102 and vm:103
- negative resource affinity rule between vm:101 and vm:103
Therefore build the same disjoint positive resource affinity resource
sets as the merge_connected_positive_resource_affinity_rules(...)
subroutine, so that the inconsistency check has the necessary
information in advance.
Fixes: 367cdbfa ("rules: introduce resource affinity rule plugin")
Reported-by: Hannes Dürr <h.duerr@proxmox.com>
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Rules/ResourceAffinity.pm | 23 ++++++++++++-------
.../inconsistent-resource-affinity-rules.cfg | 15 ++++++++++++
...sistent-resource-affinity-rules.cfg.expect | 5 +++-
3 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/src/PVE/HA/Rules/ResourceAffinity.pm b/src/PVE/HA/Rules/ResourceAffinity.pm
index 86b94d60..22b7b5fe 100644
--- a/src/PVE/HA/Rules/ResourceAffinity.pm
+++ b/src/PVE/HA/Rules/ResourceAffinity.pm
@@ -207,14 +207,18 @@ sub check_inter_resource_affinity_rules_consistency {
my @conflicts = ();
- while (my ($positiveid, $positive) = each %$positive_rules) {
- my $positive_resources = $positive->{resources};
+ my @disjoint_positive_rules =
+ PVE::HA::Rules::Helpers::find_disjoint_rules_resource_sets($positive_rules);
+
+ for my $entry (@disjoint_positive_rules) {
+ my $positive_resources = $entry->{resources};
while (my ($negativeid, $negative) = each %$negative_rules) {
my $common_resources = set_intersect($positive_resources, $negative->{resources});
next if %$common_resources < 2;
- push @conflicts, [$positiveid, $negativeid];
+ push @conflicts, ['negative', $negativeid];
+ push @conflicts, ['positive', $_] for $entry->{ruleids}->@*;
}
}
@@ -235,12 +239,15 @@ __PACKAGE__->register_check(
my ($conflicts, $errors) = @_;
for my $conflict (@$conflicts) {
- my ($positiveid, $negativeid) = @$conflict;
+ my ($type, $ruleid) = @$conflict;
- push $errors->{$positiveid}->{resources}->@*,
- "rule shares two or more resources with a negative resource affinity rule";
- push $errors->{$negativeid}->{resources}->@*,
- "rule shares two or more resources with a positive resource affinity rule";
+ if ($type eq 'positive') {
+ push $errors->{$ruleid}->{resources}->@*,
+ "rule shares two or more resources with a negative resource affinity rule";
+ } elsif ($type eq 'negative') {
+ push $errors->{$ruleid}->{resources}->@*,
+ "rule shares two or more resources with a positive resource affinity rule";
+ }
}
},
);
diff --git a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg
index a620e293..6bfc2dad 100644
--- a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg
+++ b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg
@@ -1,3 +1,4 @@
+# Case 1: Remove positive and negative resource affinity rules, which share two or more ha resources.
resource-affinity: keep-apart1
resources vm:102,vm:103
affinity negative
@@ -9,3 +10,17 @@ resource-affinity: keep-apart2
resource-affinity: stick-together1
resources vm:101,vm:102,vm:103,vm:104,vm:106
affinity positive
+
+# Case 2: Remove positive and negative resource affinity rules, which share two or more HA resources with the positive
+# resource affinity being split in two.
+resource-affinity: split-stick-together1
+ resources vm:201,vm:202
+ affinity positive
+
+resource-affinity: split-stick-together2
+ resources vm:202,vm:203
+ affinity positive
+
+resource-affinity: split-keep-apart1
+ resources vm:201,vm:203
+ affinity negative
diff --git a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
index d4a2d7b2..8f2338d9 100644
--- a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
@@ -1,11 +1,14 @@
--- Log ---
Drop rule 'keep-apart1', because rule shares two or more resources with a positive resource affinity rule.
Drop rule 'keep-apart2', because rule shares two or more resources with a positive resource affinity rule.
+Drop rule 'split-keep-apart1', because rule shares two or more resources with a positive resource affinity rule.
+Drop rule 'split-stick-together1', because rule shares two or more resources with a negative resource affinity rule.
+Drop rule 'split-stick-together2', because rule shares two or more resources with a negative resource affinity rule.
Drop rule 'stick-together1', because rule shares two or more resources with a negative resource affinity rule.
Drop rule 'stick-together1', because rule shares two or more resources with a negative resource affinity rule.
--- Config ---
$VAR1 = {
- 'digest' => '50875b320034d8ac7dded185e590f5f87c4e2bb6',
+ 'digest' => '80cdc11a1d5bf2d1d500665af1210cd59aabede6',
'ids' => {},
'order' => {}
};
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 05/21] rules: add merged positive resource affinity info in global checks
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (3 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 04/21] rules: resource affinity: inter-consistency check with merged positive rules Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 06/21] rules: make rules sorting optional in foreach_rule helper Daniel Kral
` (15 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
The node affinity and positive resource affinity rule subsets are
checked whether HA resources in a positive resource affinity rule are in
more than one node affinity rule in total, and if so prunes them as
those are not feasible [0].
This check has the assumption that each positive resource affinity
rule's resource set is disjoint from each other, but this is only done
in the later transformation stage when positive resource affinity with
overlapping HA resources in them are merged to one rule.
For example, the following inconsistent rules are not pruned:
- positive resource affinity rule between vm:101 and vm:102
- positive resource affinity rule between vm:102 and vm:103
- node affinity rule for vm:101 on node1
- node affinity rule for vm:103 on node3
Therefore build the same disjoint positive resource affinity resource
sets as the merge_connected_positive_resource_affinity_rules(...)
subroutine, so that the inconsistency check has the necessary
information in advance.
Fixes: c48d9e66 ("rules: restrict inter-plugin resource references to simple cases")
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Rules.pm | 10 +++++++---
...onsistent-node-resource-affinity-rules.cfg | 19 +++++++++++++++++++
...nt-node-resource-affinity-rules.cfg.expect | 6 +++++-
3 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index 323ad038..e13769a1 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -8,6 +8,7 @@ use PVE::Tools;
use PVE::HA::HashTools qw(set_intersect set_union sets_are_disjoint);
use PVE::HA::Tools;
+use PVE::HA::Rules::Helpers;
use base qw(PVE::SectionConfig);
@@ -580,8 +581,11 @@ sub check_single_node_affinity_per_positive_resource_affinity_rule {
my @conflicts = ();
- while (my ($positiveid, $positive_rule) = each %$positive_rules) {
- my $positive_resources = $positive_rule->{resources};
+ my @disjoint_positive_rules =
+ PVE::HA::Rules::Helpers::find_disjoint_rules_resource_sets($positive_rules);
+
+ for my $entry (@disjoint_positive_rules) {
+ my $positive_resources = $entry->{resources};
my @paired_node_affinity_rules = ();
while (my ($node_affinity_id, $node_affinity_rule) = each %$node_affinity_rules) {
@@ -590,7 +594,7 @@ sub check_single_node_affinity_per_positive_resource_affinity_rule {
push @paired_node_affinity_rules, $node_affinity_id;
}
if (@paired_node_affinity_rules > 1) {
- push @conflicts, ['positive', $positiveid];
+ push @conflicts, ['positive', $_] for $entry->{ruleids}->@*;
push @conflicts, ['node-affinity', $_] for @paired_node_affinity_rules;
}
}
diff --git a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg
index 88e6dd0e..52402cd9 100644
--- a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg
+++ b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg
@@ -52,3 +52,22 @@ node-affinity: vm402-must-be-on-node1
resource-affinity: vm401-vm402-must-be-kept-separate
resources vm:401,vm:402
affinity negative
+
+# Case 5: Remove positive resource affinity rules, which have overlapping HA resources, and are restricted with two different node affinity rules.
+node-affinity: vm501-must-be-on-node1
+ resources vm:501
+ nodes node1
+ strict 1
+
+node-affinity: vm503-must-be-on-node2
+ resources vm:503
+ nodes node2
+ strict 1
+
+resource-affinity: vm501-vm502-must-be-kept-together
+ resources vm:501,vm:502
+ affinity positive
+
+resource-affinity: vm502-vm503-must-be-kept-together
+ resources vm:502,vm:503
+ affinity positive
diff --git a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
index e12242ab..80222c86 100644
--- a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
@@ -5,9 +5,13 @@ Drop rule 'vm301-vm302-must-be-kept-together', because resources are in multiple
Drop rule 'vm401-must-be-on-node1', because at least one resource is in a negative resource affinity rule and this rule would restrict these to less nodes than available to the resources.
Drop rule 'vm401-vm402-must-be-kept-separate', because two or more resources are restricted to less nodes than available to the resources.
Drop rule 'vm402-must-be-on-node1', because at least one resource is in a negative resource affinity rule and this rule would restrict these to less nodes than available to the resources.
+Drop rule 'vm501-must-be-on-node1', because at least one resource is in a positive resource affinity rule and there are other resources in at least one other node affinity rule already.
+Drop rule 'vm501-vm502-must-be-kept-together', because resources are in multiple node affinity rules.
+Drop rule 'vm502-vm503-must-be-kept-together', because resources are in multiple node affinity rules.
+Drop rule 'vm503-must-be-on-node2', because at least one resource is in a positive resource affinity rule and there are other resources in at least one other node affinity rule already.
--- Config ---
$VAR1 = {
- 'digest' => 'a5d782a442bbe3bf3a4d088db82a575b382a53fe',
+ 'digest' => '00a70f4c2bdd41aed16b6e5b9860cd9fb7f0f098',
'ids' => {
'vm101-vm102-must-be-kept-together' => {
'affinity' => 'positive',
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 06/21] rules: make rules sorting optional in foreach_rule helper
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (4 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 05/21] rules: add merged positive resource affinity info in global checks Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 07/21] rename rule's canonicalize stage to transform stage Daniel Kral
` (14 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Most users of the foreach_rule helper are agnostic to the iteration
order of the rules and as sorting can become a non-neglible overhead for
larger rule sets, make sorting rules optional.
The only caller dependent on a sorted rule set is the rules' index API
endpoint.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/API2/HA/Rules.pm | 1 +
src/PVE/HA/Rules.pm | 10 ++++++----
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/PVE/API2/HA/Rules.pm b/src/PVE/API2/HA/Rules.pm
index ab431019..fb8b465b 100644
--- a/src/PVE/API2/HA/Rules.pm
+++ b/src/PVE/API2/HA/Rules.pm
@@ -194,6 +194,7 @@ __PACKAGE__->register_method({
},
type => $type,
sid => $resource,
+ sorted => 1,
);
return $res;
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index e13769a1..784f2d36 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -428,7 +428,7 @@ Filters the given C<$rules> according to the C<$opts> and loops over the
resulting rules in the order as defined in the section config and executes
C<$func> with the parameters C<L<< ($rule, $ruleid) >>>.
-The following key-value pairs for C<$opts> as filter properties are:
+The following key-value pairs for C<$opts> are:
=over
@@ -438,6 +438,8 @@ The following key-value pairs for C<$opts> as filter properties are:
=item C<$exclude_disabled_rules>: Limits C<$rules> to those which are enabled.
+=item C<$sorted>: Sorts C<$rules> according to C<< $rules->{order} >>.
+
=back
=cut
@@ -447,9 +449,9 @@ sub foreach_rule : prototype($$;%) {
my $sid = $opts{sid};
- my @ruleids = sort {
- $rules->{order}->{$a} <=> $rules->{order}->{$b}
- } keys %{ $rules->{ids} };
+ my @ruleids = keys $rules->{ids}->%*;
+ @ruleids = sort { $rules->{order}->{$a} <=> $rules->{order}->{$b} } @ruleids
+ if defined($opts{sorted});
for my $ruleid (@ruleids) {
my $rule = $rules->{ids}->{$ruleid};
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 07/21] rename rule's canonicalize stage to transform stage
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (5 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 06/21] rules: make rules sorting optional in foreach_rule helper Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 08/21] rules: make plugins register transformers instead of plugin_transform Daniel Kral
` (13 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
The name 'canonicalize' was chosen when the actual goal was to transform
the rules in a canonical form for the scheduler, but as development went
on they are used to transform $rules and induce new rules from $rules,
so 'canonicalize' is not the right term anymore.
'transform' is chosen because it's broad enough for future use but still
conveys what is done with the $rules.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Config.pm | 2 +-
src/PVE/HA/Manager.pm | 2 +-
src/PVE/HA/Rules.pm | 18 +++++++++---------
src/PVE/HA/Rules/ResourceAffinity.pm | 4 ++--
src/test/test_rules_config.pl | 2 +-
5 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/PVE/HA/Config.pm b/src/PVE/HA/Config.pm
index b2ef26aa..bd87a0ab 100644
--- a/src/PVE/HA/Config.pm
+++ b/src/PVE/HA/Config.pm
@@ -237,7 +237,7 @@ sub read_and_check_effective_rules_config {
PVE::HA::Groups::migrate_groups_to_rules($rules, $groups, $resources);
- PVE::HA::Rules->canonicalize($rules, $nodes);
+ PVE::HA::Rules->transform($rules, $nodes);
return $rules;
}
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index 9c0a8e09..f42373dd 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -728,7 +728,7 @@ sub manage {
PVE::HA::Groups::migrate_groups_to_rules($new_rules, $self->{groups}, $sc);
my $nodes = $self->{ns}->list_nodes();
- my $messages = PVE::HA::Rules->canonicalize($new_rules, $nodes);
+ my $messages = PVE::HA::Rules->transform($new_rules, $nodes);
$haenv->log('info', $_) for @$messages;
$self->{rules} = $new_rules;
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index 784f2d36..c81b1525 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -360,7 +360,7 @@ sub check_feasibility : prototype($$$) {
return $global_errors;
}
-=head3 $class->plugin_canonicalize($rules)
+=head3 $class->plugin_transform($rules)
B<OPTIONAL:> Can be implemented in the I<rule plugin>.
@@ -368,11 +368,11 @@ Modifies the C<$rules> to a plugin-specific canonical form.
=cut
-sub plugin_canonicalize : prototype($$) {
+sub plugin_transform : prototype($$) {
my ($class, $rules) = @_;
}
-=head3 $class->canonicalize($rules, $nodes)
+=head3 $class->transform($rules, $nodes)
Modifies C<$rules> to contain only feasible rules.
@@ -386,7 +386,7 @@ Returns a list of messages with the reasons why rules were removed.
=cut
-sub canonicalize : prototype($$$) {
+sub transform : prototype($$$) {
my ($class, $rules, $nodes) = @_;
my $messages = [];
@@ -407,11 +407,11 @@ sub canonicalize : prototype($$$) {
for my $type (@$types) {
my $plugin = $class->lookup($type);
- eval { $plugin->plugin_canonicalize($rules) };
- next if $@; # plugin doesn't implement plugin_canonicalize(...)
+ eval { $plugin->plugin_transform($rules) };
+ next if $@; # plugin doesn't implement plugin_transform(...)
}
- $class->global_canonicalize($rules);
+ $class->global_transform($rules);
return $messages;
}
@@ -709,7 +709,7 @@ __PACKAGE__->register_check(
},
);
-=head1 INTER-PLUGIN RULE CANONICALIZATION HELPERS
+=head1 INTER-PLUGIN RULE TRANSFORMATION HELPERS
=cut
@@ -750,7 +750,7 @@ sub create_implicit_positive_resource_affinity_node_affinity_rules {
}
}
-sub global_canonicalize {
+sub global_transform {
my ($class, $rules) = @_;
my $args = $class->get_check_arguments($rules);
diff --git a/src/PVE/HA/Rules/ResourceAffinity.pm b/src/PVE/HA/Rules/ResourceAffinity.pm
index 22b7b5fe..52e3c47b 100644
--- a/src/PVE/HA/Rules/ResourceAffinity.pm
+++ b/src/PVE/HA/Rules/ResourceAffinity.pm
@@ -252,7 +252,7 @@ __PACKAGE__->register_check(
},
);
-=head1 RESOURCE AFFINITY RULE CANONICALIZATION HELPERS
+=head1 RESOURCE AFFINITY RULE TRANSFORMATION HELPERS
=cut
@@ -382,7 +382,7 @@ sub create_implicit_negative_resource_affinity_rules {
}
}
-sub plugin_canonicalize {
+sub plugin_transform {
my ($class, $rules) = @_;
my $args = $class->get_plugin_check_arguments($rules);
diff --git a/src/test/test_rules_config.pl b/src/test/test_rules_config.pl
index c2a7af43..3c2dac4b 100755
--- a/src/test/test_rules_config.pl
+++ b/src/test/test_rules_config.pl
@@ -53,7 +53,7 @@ sub check_cfg {
print "--- Log ---\n";
my $cfg = PVE::HA::Rules->parse_config($cfg_fn, $raw);
PVE::HA::Rules->set_rule_defaults($_) for values %{ $cfg->{ids} };
- my $messages = PVE::HA::Rules->canonicalize($cfg, $nodes);
+ my $messages = PVE::HA::Rules->transform($cfg, $nodes);
print $_ for @$messages;
print "--- Config ---\n";
{
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 08/21] rules: make plugins register transformers instead of plugin_transform
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (6 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 07/21] rename rule's canonicalize stage to transform stage Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 09/21] rules: node affinity: decouple get_node_affinity helper from Usage class Daniel Kral
` (12 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Replaces the rule plugins' plugin_transform(...) method by a
register_transform(...) call for each declared rule transformer to make
their interface more similar to the one for rule plugin checks.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Rules.pm | 85 ++++++++++++++++++++--------
src/PVE/HA/Rules/ResourceAffinity.pm | 22 +++----
2 files changed, 71 insertions(+), 36 deletions(-)
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index c81b1525..a075feac 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -32,6 +32,12 @@ the feasibility between rules of the same type and and between rules of
different types, and prune the rule set in such a way, that it becomes feasible
again, while minimizing the amount of rules that need to be pruned.
+More so, the rules given by the config file might not be in the best format to
+be used internally or does not contain the implicitly stated rules, which are
+induced by the relationship between different rules. Therefore, this package
+also provides the capability to C<L<register transforms|/REGISTERING TRANSFORMS>>
+to implement these internal rule transformations.
+
This packages inherits its config-related methods from C<L<PVE::SectionConfig>>
and therefore rule plugins need to implement methods from there as well.
@@ -90,6 +96,28 @@ and blames these errors on the I<comment> property:
}
);
+=head2 REGISTERING TRANSFORMS
+
+Rule transforms are used for transforming the rule set in such a way that
+the rules provided by the rules config are easier to work with (for example,
+transforming rules into equivalent forms) or make the rule set more complete
+(e.g. explicitly create semantically implicit rules).
+
+C<L<< Registering transforms|/$class->register_transform(...) >>> is the same
+as for registering checks. Following up on the example from that section, the
+following example shows a possible rule plugin's transform, which removes the
+I<comment> property from each rule:
+
+ __PACKAGE__->register_transformer(
+ sub {
+ my ($rules, $args) = @_;
+
+ for my $ruleid (keys $args->{custom_rules}->%*) {
+ delete $rules->{ids}->{$ruleid}->{comment};
+ }
+ }
+ );
+
=head1 METHODS
=cut
@@ -246,10 +274,11 @@ sub set_rule_defaults : prototype($$) {
}
}
-# Rule checks definition and methods
+# Rule checks and transforms definition and methods
my $types = [];
my $checkdef;
+my $transformdef;
sub register {
my ($class) = @_;
@@ -279,6 +308,23 @@ sub register_check : prototype($$$) {
];
}
+=head3 $class->register_transform(...)
+
+=head3 $class->register_transform($transform_func)
+
+Used to register rule transformers for a rule plugin.
+
+=cut
+
+sub register_transform : prototype($$) {
+ my ($class, $transform_func) = @_;
+
+ my $type = eval { $class->type() };
+ $type = 'global' if $@;
+
+ push $transformdef->{$type}->@*, $transform_func;
+}
+
=head3 $class->get_plugin_check_arguments(...)
=head3 $class->get_plugin_check_arguments($rules)
@@ -287,6 +333,7 @@ B<OPTIONAL:> Can be implemented in the I<rule plugin>.
Returns a hash, usually subsets of rules relevant to the plugin, which are
passed to the plugin's C<L<< registered checks|/$class->register_check(...) >>>
+and C<L<< registered transforms|/$class->register_transform(...) >>>
so that the creation of these can be shared inbetween rule check
implementations.
@@ -360,18 +407,6 @@ sub check_feasibility : prototype($$$) {
return $global_errors;
}
-=head3 $class->plugin_transform($rules)
-
-B<OPTIONAL:> Can be implemented in the I<rule plugin>.
-
-Modifies the C<$rules> to a plugin-specific canonical form.
-
-=cut
-
-sub plugin_transform : prototype($$) {
- my ($class, $rules) = @_;
-}
-
=head3 $class->transform($rules, $nodes)
Modifies C<$rules> to contain only feasible rules.
@@ -380,7 +415,9 @@ C<$nodes> is a list of the configured cluster nodes.
This is done by running all checks, which were registered with
C<L<< register_check()|/$class->register_check(...) >>> and removing any
-rule, which makes the rule set infeasible.
+rule, which makes the rule set infeasible, and afterwards running all
+transforms on the feasible rule set, which were registered with
+C<L<< register_transform()|/$class->register_transform(...) >>>.
Returns a list of messages with the reasons why rules were removed.
@@ -405,13 +442,13 @@ sub transform : prototype($$$) {
}
}
- for my $type (@$types) {
- my $plugin = $class->lookup($type);
- eval { $plugin->plugin_transform($rules) };
- next if $@; # plugin doesn't implement plugin_transform(...)
- }
+ for my $type (@$types, 'global') {
+ for my $transform ($transformdef->{$type}->@*) {
+ my $global_args = $class->get_check_arguments($rules);
- $class->global_transform($rules);
+ $transform->($rules, $global_args);
+ }
+ }
return $messages;
}
@@ -750,16 +787,14 @@ sub create_implicit_positive_resource_affinity_node_affinity_rules {
}
}
-sub global_transform {
- my ($class, $rules) = @_;
-
- my $args = $class->get_check_arguments($rules);
+__PACKAGE__->register_transform(sub {
+ my ($rules, $args) = @_;
create_implicit_positive_resource_affinity_node_affinity_rules(
$rules,
$args->{positive_rules},
$args->{node_affinity_rules},
);
-}
+});
1;
diff --git a/src/PVE/HA/Rules/ResourceAffinity.pm b/src/PVE/HA/Rules/ResourceAffinity.pm
index 52e3c47b..4a6e8627 100644
--- a/src/PVE/HA/Rules/ResourceAffinity.pm
+++ b/src/PVE/HA/Rules/ResourceAffinity.pm
@@ -299,6 +299,12 @@ sub merge_connected_positive_resource_affinity_rules {
}
}
+__PACKAGE__->register_transform(sub {
+ my ($rules, $args) = @_;
+
+ merge_connected_positive_resource_affinity_rules($rules, $args->{positive_rules});
+});
+
# retrieve the existing negative resource affinity relationships for any of the
# $resources in the $negative_rules; returns a hash map, where the keys are the
# resources to be separated from and the values are subsets of the $resources
@@ -382,23 +388,17 @@ sub create_implicit_negative_resource_affinity_rules {
}
}
-sub plugin_transform {
- my ($class, $rules) = @_;
+# must come after merging connected positive rules, because of this helpers
+# assumptions about resource sets and inter-resource affinity consistency
+__PACKAGE__->register_transform(sub {
+ my ($rules, $args) = @_;
- my $args = $class->get_plugin_check_arguments($rules);
-
- merge_connected_positive_resource_affinity_rules($rules, $args->{positive_rules});
-
- $args = $class->get_plugin_check_arguments($rules);
-
- # must come after merging connected positive rules, because of this helpers
- # assumptions about resource sets and inter-resource affinity consistency
create_implicit_negative_resource_affinity_rules(
$rules,
$args->{positive_rules},
$args->{negative_rules},
);
-}
+});
=head1 RESOURCE AFFINITY RULE HELPERS
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 09/21] rules: node affinity: decouple get_node_affinity helper from Usage class
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (7 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 08/21] rules: make plugins register transformers instead of plugin_transform Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 10/21] compile ha rules to a more compact representation Daniel Kral
` (11 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
This is done in preparation of the next patch, which moves some of the
logic of get_node_affinity(...) in the HA rule translaton stage.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- minor change due to rebase on accounting series where the
$online_nodes in select_service_node(...) is now shared by both
affinity rule helpers
- change the get_node_affinity(...) POD signature too
src/PVE/HA/Manager.pm | 4 ++--
src/PVE/HA/Rules/NodeAffinity.pm | 12 ++++++------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index f42373dd..a26bde76 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -157,11 +157,11 @@ sub select_service_node {
my ($current_node, $tried_nodes, $maintenance_fallback) =
$sd->@{qw(node failed_nodes maintenance_node)};
- my ($allowed_nodes, $pri_nodes) = get_node_affinity($rules, $sid, $online_node_usage);
+ my $online_nodes = { map { $_ => 1 } $online_node_usage->list_nodes() };
+ my ($allowed_nodes, $pri_nodes) = get_node_affinity($rules, $sid, $online_nodes);
return undef if !%$pri_nodes;
- my $online_nodes = { map { $_ => 1 } $online_node_usage->list_nodes() };
my ($together, $separate) = get_resource_affinity($rules, $sid, $ss, $online_nodes);
# stay on current node if possible (avoids random migrations)
diff --git a/src/PVE/HA/Rules/NodeAffinity.pm b/src/PVE/HA/Rules/NodeAffinity.pm
index 5394832e..111ff6fe 100644
--- a/src/PVE/HA/Rules/NodeAffinity.pm
+++ b/src/PVE/HA/Rules/NodeAffinity.pm
@@ -237,11 +237,11 @@ my $get_resource_node_affinity_rule = sub {
return $node_affinity_rule;
};
-=head3 get_node_affinity($rules, $sid, $online_node_usage)
+=head3 get_node_affinity($rules, $sid, $online_nodes)
Returns a list of two hashes representing the node affinity of C<$sid>
according to the node affinity rules in C<$rules> and the available nodes in
-C<$online_node_usage>.
+the C<$online_nodes> hash.
The first hash is a hash set of available nodes, i.e. nodes where the
resource C<$sid> is allowed to be assigned to, and the second hash is a hash set
@@ -252,20 +252,20 @@ If there are no available nodes at all, returns C<undef>.
=cut
sub get_node_affinity : prototype($$$) {
- my ($rules, $sid, $online_node_usage) = @_;
+ my ($rules, $sid, $online_nodes) = @_;
my $node_affinity_rule = $get_resource_node_affinity_rule->($rules, $sid);
# default to a node affinity rule with all available nodes
if (!$node_affinity_rule) {
- for my $node ($online_node_usage->list_nodes()) {
+ for my $node (keys %$online_nodes) {
$node_affinity_rule->{nodes}->{$node} = { priority => 0 };
}
}
# add remaining nodes with low priority for non-strict node affinity rules
if (!$node_affinity_rule->{strict}) {
- for my $node ($online_node_usage->list_nodes()) {
+ for my $node (keys %$online_nodes) {
next if defined($node_affinity_rule->{nodes}->{$node});
$node_affinity_rule->{nodes}->{$node} = { priority => -1 };
@@ -276,7 +276,7 @@ sub get_node_affinity : prototype($$$) {
my $prioritized_nodes = {};
while (my ($node, $props) = each %{ $node_affinity_rule->{nodes} }) {
- next if !$online_node_usage->contains_node($node); # node is offline
+ next if !defined($online_nodes->{$node}); # node is offline
$allowed_nodes->{$node} = 1;
$prioritized_nodes->{ $props->{priority} }->{$node} = 1;
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 10/21] compile ha rules to a more compact representation
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (8 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 09/21] rules: node affinity: decouple get_node_affinity helper from Usage class Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 11/21] test: rules: use to_json instead of Data::Dumper for config output Daniel Kral
` (10 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
The initial implementation of fetching a ha resource's rules in the
scheduler and other users is rather inefficient as it needs to go
through the raw rules hash map and gathers quite a lot of information
which doesn't change unless the HA rules need to be re-translated
anyway.
Therefore add a compile stage when re-translating the HA rules, which
compiles each rule plugin's rules into a more compact data structure.
This reduces the unnecessary recomputation of invariant data done in the
hot path of the scheduler's select_service_node(...).
A benchmark done with NYTProf [0] shows the following runtime
improvement, where each duration is the average time needed per call.
The benchmark was done by starting up a 3-node cluster with 1200
configured HA resources, where each HA resource is in a node affinity
rule and a negative resource affinity to two other HA resources.
+----------------------------------+--------+-------+
| Function | Before | After |
+----------------------------------+--------+-------+
| select_service_node | 10.5ms | 44µs |
| get_node_affinity | 5.26ms | 10µs |
| get_resource_affinity | 5.17ms | 12µs |
+----------------------------------+--------+-------+
[0] https://metacpan.org/pod/Devel::NYTProf
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- minor changes due to rebase
- factor out $resource_affinity in get_resource_motion_info(...) to
align it with other usages of these helpers and in preparation of
another patch series which will use $node_affinity too
src/PVE/HA/Config.pm | 9 ++-
src/PVE/HA/Manager.pm | 31 ++++----
src/PVE/HA/Rules.pm | 59 +++++++++++++++
src/PVE/HA/Rules/NodeAffinity.pm | 80 ++++++++++----------
src/PVE/HA/Rules/ResourceAffinity.pm | 105 ++++++++++++++-------------
src/test/test_failover1.pl | 15 +++-
6 files changed, 187 insertions(+), 112 deletions(-)
diff --git a/src/PVE/HA/Config.pm b/src/PVE/HA/Config.pm
index bd87a0ab..548a89e6 100644
--- a/src/PVE/HA/Config.pm
+++ b/src/PVE/HA/Config.pm
@@ -224,7 +224,7 @@ sub read_and_check_rules_config {
return $rules;
}
-sub read_and_check_effective_rules_config {
+sub read_and_compile_rules_config {
my $rules = read_and_check_rules_config();
@@ -239,7 +239,7 @@ sub read_and_check_effective_rules_config {
PVE::HA::Rules->transform($rules, $nodes);
- return $rules;
+ return PVE::HA::Rules->compile($rules, $nodes);
}
sub write_rules_config {
@@ -391,8 +391,9 @@ sub get_resource_motion_info {
my $ss = $manager_status->{service_status};
my $ns = $manager_status->{node_status};
- my $rules = read_and_check_effective_rules_config();
- my ($together, $separate) = get_affinitive_resources($rules, $sid);
+ my $compiled_rules = read_and_compile_rules_config();
+ my $resource_affinity = $compiled_rules->{'resource-affinity'};
+ my ($together, $separate) = get_affinitive_resources($resource_affinity, $sid);
for my $csid (sort keys %$together) {
next if !defined($ss->{$csid});
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index a26bde76..e17e3209 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -126,12 +126,12 @@ sub flush_master_status {
=head3 select_service_node(...)
-=head3 select_service_node($rules, $online_node_usage, $sid, $service_conf, $ss, $node_preference)
+=head3 select_service_node($compiled_rules, $online_node_usage, $sid, $service_conf, $ss, $node_preference)
Used to select the best fitting node for the service C<$sid>, with the
-configuration C<$service_conf>, according to the rules defined in C<$rules>,
-available node utilization in C<$online_node_usage>, the service states in
-C<$ss> and the given C<$node_preference>.
+configuration C<$service_conf>, according to the rules defined in
+C<$compiled_rules>, available node utilization in C<$online_node_usage>, the
+service states in C<$ss> and the given C<$node_preference>.
The C<$node_preference> can be set to:
@@ -148,7 +148,7 @@ The C<$node_preference> can be set to:
=cut
sub select_service_node {
- my ($rules, $online_node_usage, $sid, $service_conf, $ss, $node_preference) = @_;
+ my ($compiled_rules, $online_node_usage, $sid, $service_conf, $ss, $node_preference) = @_;
die "'$node_preference' is not a valid node_preference for select_service_node\n"
if $node_preference !~ m/(none|best-score|try-next)/;
@@ -156,13 +156,15 @@ sub select_service_node {
my $sd = $ss->{$sid};
my ($current_node, $tried_nodes, $maintenance_fallback) =
$sd->@{qw(node failed_nodes maintenance_node)};
+ my ($node_affinity, $resource_affinity) =
+ $compiled_rules->@{qw(node-affinity resource-affinity)};
my $online_nodes = { map { $_ => 1 } $online_node_usage->list_nodes() };
- my ($allowed_nodes, $pri_nodes) = get_node_affinity($rules, $sid, $online_nodes);
+ my ($allowed_nodes, $pri_nodes) = get_node_affinity($node_affinity, $sid, $online_nodes);
return undef if !%$pri_nodes;
- my ($together, $separate) = get_resource_affinity($rules, $sid, $ss, $online_nodes);
+ my ($together, $separate) = get_resource_affinity($resource_affinity, $sid, $ss, $online_nodes);
# stay on current node if possible (avoids random migrations)
if (
@@ -389,7 +391,8 @@ sub execute_migration {
my ($haenv, $ss) = $self->@{qw(haenv ss)};
- my ($together, $separate) = get_affinitive_resources($self->{rules}, $sid);
+ my $resource_affinity = $self->{compiled_rules}->{'resource-affinity'};
+ my ($together, $separate) = get_affinitive_resources($resource_affinity, $sid);
for my $csid (sort keys %$separate) {
next if !defined($ss->{$csid});
@@ -719,7 +722,7 @@ sub manage {
PVE::HA::Groups::migrate_groups_to_resources($self->{groups}, $sc);
if (
- !$self->{rules}
+ !$self->{compiled_rules}
|| $has_changed_nodelist
|| $new_rules->{digest} ne $self->{last_rules_digest}
|| $self->{groups}->{digest} ne $self->{last_groups_digest}
@@ -731,9 +734,9 @@ sub manage {
my $messages = PVE::HA::Rules->transform($new_rules, $nodes);
$haenv->log('info', $_) for @$messages;
- $self->{rules} = $new_rules;
+ $self->{compiled_rules} = PVE::HA::Rules->compile($new_rules, $nodes);
- $self->{last_rules_digest} = $self->{rules}->{digest};
+ $self->{last_rules_digest} = $new_rules->{digest};
$self->{last_groups_digest} = $self->{groups}->{digest};
$self->{last_services_digest} = $services_digest;
}
@@ -991,7 +994,7 @@ sub next_state_request_start {
if ($self->{crs}->{rebalance_on_request_start}) {
my $selected_node = select_service_node(
- $self->{rules},
+ $self->{compiled_rules},
$self->{online_node_usage},
$sid,
$cd,
@@ -1158,7 +1161,7 @@ sub next_state_started {
}
my $node = select_service_node(
- $self->{rules},
+ $self->{compiled_rules},
$self->{online_node_usage},
$sid,
$cd,
@@ -1272,7 +1275,7 @@ sub next_state_recovery {
my $fenced_node = $sd->{node}; # for logging purpose
my $recovery_node = select_service_node(
- $self->{rules},
+ $self->{compiled_rules},
$self->{online_node_usage},
$sid,
$cd,
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index a075feac..69c53356 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -47,6 +47,12 @@ Each I<rule plugin> is required to implement the methods C<L<type()>>,
C<L<properties()>>, and C<L<options>> from the C<L<PVE::SectionConfig>> to
extend the properties of this I<base plugin> with plugin-specific properties.
+Each I<rule plugin> is required to implement the method
+C<L<< plugin_compile()|/$class->plugin_compile(...) >>> to distill a compiled
+representation of the more verbose C<$rules> from the config file, which is
+returned by C<L<< compile()|/$class->compile(...) >>> to be more appropriate
+and efficient for the scheduler and other users.
+
=head2 REGISTERING CHECKS
In order to C<L<< register checks|/$class->register_check(...) >>> for a rule
@@ -453,6 +459,59 @@ sub transform : prototype($$$) {
return $messages;
}
+=head3 $class->plugin_compile(...)
+
+=head3 $class->plugin_compile($rules, $nodes)
+
+B<MANDATORY:> Must be implemented in a I<rule plugin>.
+
+Called in C<$class->compile($rules, $nodes)> in order to get a more compact
+representation of the rule plugin's rules in C<$rules>, which holds only the
+relevant information for the scheduler and other users.
+
+C<$nodes> is a list of the configured cluster nodes.
+
+=cut
+
+sub plugin_compile {
+ my ($class, $rules, $nodes) = @_;
+
+ die "implement in subclass";
+}
+
+=head3 $class->compile(...)
+
+=head3 $class->compile($rules, $nodes)
+
+Compiles and returns a hash, where each key-value pair represents a rule
+plugin's more compact representation compiled from the more verbose rules
+defined in C<$rules>.
+
+C<$nodes> is a list of the configured cluster nodes.
+
+The transformation to the compact representation for each rule plugin is
+implemented in C<L<< plugin_compile()|/$class->plugin_compile(...) >>>.
+
+=cut
+
+sub compile {
+ my ($class, $rules, $nodes) = @_;
+
+ my $compiled_rules = {};
+
+ for my $type (@$types) {
+ my $plugin = $class->lookup($type);
+ my $compiled_plugin_rules = $plugin->plugin_compile($rules, $nodes);
+
+ die "plugin_compile(...) of type '$type' must return hash reference\n"
+ if ref($compiled_plugin_rules) ne 'HASH';
+
+ $compiled_rules->{$type} = $compiled_plugin_rules;
+ }
+
+ return $compiled_rules;
+}
+
=head1 FUNCTIONS
=cut
diff --git a/src/PVE/HA/Rules/NodeAffinity.pm b/src/PVE/HA/Rules/NodeAffinity.pm
index 111ff6fe..adef97d0 100644
--- a/src/PVE/HA/Rules/NodeAffinity.pm
+++ b/src/PVE/HA/Rules/NodeAffinity.pm
@@ -155,6 +155,40 @@ sub get_plugin_check_arguments {
return $result;
}
+sub plugin_compile {
+ my ($class, $rules, $nodes) = @_;
+
+ my $node_affinity = {};
+
+ PVE::HA::Rules::foreach_rule(
+ $rules,
+ sub {
+ my ($rule) = @_;
+
+ my $effective_nodes = dclone($rule->{nodes});
+
+ # add remaining nodes with low priority for non-strict node affinity
+ if (!$rule->{strict}) {
+ for my $node (@$nodes) {
+ next if defined($effective_nodes->{$node});
+
+ $effective_nodes->{$node} = { priority => -1 };
+ }
+ }
+
+ for my $sid (keys $rule->{resources}->%*) {
+ $node_affinity->{$sid} = {
+ nodes => $effective_nodes,
+ };
+ }
+ },
+ type => 'node-affinity',
+ exclude_disabled_rules => 1,
+ );
+
+ return $node_affinity;
+}
+
=head1 NODE AFFINITY RULE CHECKERS
=cut
@@ -217,30 +251,10 @@ __PACKAGE__->register_check(
=cut
-my $get_resource_node_affinity_rule = sub {
- my ($rules, $sid) = @_;
-
- # with the current restriction a resource can only be in one node affinity rule
- my $node_affinity_rule;
- PVE::HA::Rules::foreach_rule(
- $rules,
- sub {
- my ($rule) = @_;
-
- $node_affinity_rule = dclone($rule) if !$node_affinity_rule;
- },
- sid => $sid,
- type => 'node-affinity',
- exclude_disabled_rules => 1,
- );
-
- return $node_affinity_rule;
-};
-
-=head3 get_node_affinity($rules, $sid, $online_nodes)
+=head3 get_node_affinity($node_affinity, $sid, $online_nodes)
Returns a list of two hashes representing the node affinity of C<$sid>
-according to the node affinity rules in C<$rules> and the available nodes in
+according to the node affinity C<$node_affinity> and the available nodes in
the C<$online_nodes> hash.
The first hash is a hash set of available nodes, i.e. nodes where the
@@ -252,30 +266,14 @@ If there are no available nodes at all, returns C<undef>.
=cut
sub get_node_affinity : prototype($$$) {
- my ($rules, $sid, $online_nodes) = @_;
+ my ($node_affinity, $sid, $online_nodes) = @_;
- my $node_affinity_rule = $get_resource_node_affinity_rule->($rules, $sid);
-
- # default to a node affinity rule with all available nodes
- if (!$node_affinity_rule) {
- for my $node (keys %$online_nodes) {
- $node_affinity_rule->{nodes}->{$node} = { priority => 0 };
- }
- }
-
- # add remaining nodes with low priority for non-strict node affinity rules
- if (!$node_affinity_rule->{strict}) {
- for my $node (keys %$online_nodes) {
- next if defined($node_affinity_rule->{nodes}->{$node});
-
- $node_affinity_rule->{nodes}->{$node} = { priority => -1 };
- }
- }
+ return ($online_nodes, $online_nodes) if !defined($node_affinity->{$sid});
my $allowed_nodes = {};
my $prioritized_nodes = {};
- while (my ($node, $props) = each %{ $node_affinity_rule->{nodes} }) {
+ while (my ($node, $props) = each $node_affinity->{$sid}->{nodes}->%*) {
next if !defined($online_nodes->{$node}); # node is offline
$allowed_nodes->{$node} = 1;
diff --git a/src/PVE/HA/Rules/ResourceAffinity.pm b/src/PVE/HA/Rules/ResourceAffinity.pm
index 4a6e8627..c44d4f0b 100644
--- a/src/PVE/HA/Rules/ResourceAffinity.pm
+++ b/src/PVE/HA/Rules/ResourceAffinity.pm
@@ -101,6 +101,35 @@ sub get_plugin_check_arguments {
return $result;
}
+sub plugin_compile {
+ my ($class, $rules, $nodes) = @_;
+
+ my $positive = {};
+ my $negative = {};
+
+ PVE::HA::Rules::foreach_rule(
+ $rules,
+ sub {
+ my ($rule, $ruleid) = @_;
+
+ my $affinity_set = $rule->{affinity} eq 'positive' ? $positive : $negative;
+
+ for my $sid (keys $rule->{resources}->%*) {
+ for my $csid (keys $rule->{resources}->%*) {
+ $affinity_set->{$sid}->{$csid} = 1 if $csid ne $sid;
+ }
+ }
+ },
+ type => 'resource-affinity',
+ exclude_disabled_rules => 1,
+ );
+
+ return {
+ positive => $positive,
+ negative => $negative,
+ };
+}
+
=head1 RESOURCE AFFINITY RULE CHECKERS
=cut
@@ -404,12 +433,12 @@ __PACKAGE__->register_transform(sub {
=cut
-=head3 get_affinitive_resources($rules, $sid)
+=head3 get_affinitive_resources($resource_affinity, $sid)
Returns a list of two hash sets, where the first hash set contains the
resources, which C<$sid> is positively affinitive to, and the second hash
contains the resources, which C<$sid> is negatively affinitive to, acording to
-the resource affinity rules in C<$rules>.
+the resource's resource affinity in C<$resource_affinity>.
Note that a resource C<$sid> becomes part of any negative affinity relation
of its positively affinitive resources.
@@ -430,36 +459,20 @@ affinitive to C<'ct:200'> and C<'ct:201'>, the returned value will be:
=cut
sub get_affinitive_resources : prototype($$) {
- my ($rules, $sid) = @_;
+ my ($resource_affinity, $sid) = @_;
- my $together = {};
- my $separate = {};
-
- PVE::HA::Rules::foreach_rule(
- $rules,
- sub {
- my ($rule, $ruleid) = @_;
-
- my $affinity_set = $rule->{affinity} eq 'positive' ? $together : $separate;
-
- for my $csid (sort keys %{ $rule->{resources} }) {
- $affinity_set->{$csid} = 1 if $csid ne $sid;
- }
- },
- sid => $sid,
- type => 'resource-affinity',
- exclude_disabled_rules => 1,
- );
+ my $together = $resource_affinity->{positive}->{$sid} // {};
+ my $separate = $resource_affinity->{negative}->{$sid} // {};
return ($together, $separate);
}
-=head3 get_resource_affinity($rules, $sid, $ss, $online_nodes)
+=head3 get_resource_affinity($resource_affinity, $sid, $ss, $online_nodes)
Returns a list of two hashes, where the first describes the positive resource
affinity and the second hash describes the negative resource affinity for
-resource C<$sid> according to the resource affinity rules in C<$rules>, the
-service status C<$ss> and the C<$online_nodes> hash.
+resource C<$sid> according to the resource's resource affinity rules in
+C<$resource_affinity>, the service status C<$ss> and the C<$online_nodes> hash.
For the positive resource affinity of a resource C<$sid>, each element in the
hash represents an online node, where other resources, which C<$sid> is in
@@ -488,38 +501,32 @@ resource C<$sid> is in a negative affinity with, the returned value will be:
=cut
sub get_resource_affinity : prototype($$$$) {
- my ($rules, $sid, $ss, $online_nodes) = @_;
+ my ($resource_affinity, $sid, $ss, $online_nodes) = @_;
my $together = {};
my $separate = {};
- PVE::HA::Rules::foreach_rule(
- $rules,
- sub {
- my ($rule) = @_;
+ my ($positive, $negative) = get_affinitive_resources($resource_affinity, $sid);
- for my $csid (keys %{ $rule->{resources} }) {
- next if $csid eq $sid;
+ my $get_used_service_nodes = sub {
+ my ($sid) = @_;
+ my ($state, $node, $target) = $ss->{$sid}->@{qw(state node target)};
+ return PVE::HA::Usage::get_used_service_nodes($online_nodes, $state, $node, $target);
+ };
- my ($state, $node, $target) = $ss->{$csid}->@{qw(state node target)};
- my ($current_node, $target_node) =
- PVE::HA::Usage::get_used_service_nodes($online_nodes, $state, $node, $target);
+ for my $csid (keys $positive->%*) {
+ my ($current_node, $target_node) = $get_used_service_nodes->($csid);
- if ($rule->{affinity} eq 'positive') {
- $together->{$current_node}++ if defined($current_node);
- $together->{$target_node}++ if defined($target_node);
- } elsif ($rule->{affinity} eq 'negative') {
- $separate->{$current_node} = 1 if defined($current_node);
- $separate->{$target_node} = 1 if defined($target_node);
- } else {
- die "unimplemented resource affinity type $rule->{affinity}\n";
- }
- }
- },
- sid => $sid,
- type => 'resource-affinity',
- exclude_disabled_rules => 1,
- );
+ $together->{$current_node}++ if defined($current_node);
+ $together->{$target_node}++ if defined($target_node);
+ }
+
+ for my $csid (keys $negative->%*) {
+ my ($current_node, $target_node) = $get_used_service_nodes->($csid);
+
+ $separate->{$current_node} = 1 if defined($current_node);
+ $separate->{$target_node} = 1 if defined($target_node);
+ }
return ($together, $separate);
}
diff --git a/src/test/test_failover1.pl b/src/test/test_failover1.pl
index 495d4b4b..1d336be0 100755
--- a/src/test/test_failover1.pl
+++ b/src/test/test_failover1.pl
@@ -21,11 +21,13 @@ node-affinity: prefer_node1
nodes node1
EOD
+my $nodes = ['node1', 'node2', 'node3'];
+
+my $compiled_rules = PVE::HA::Rules->compile($rules, $nodes);
+
# Relies on the fact that the basic plugin doesn't use the haenv.
my $online_node_usage = PVE::HA::Usage::Basic->new();
-$online_node_usage->add_node("node1");
-$online_node_usage->add_node("node2");
-$online_node_usage->add_node("node3");
+$online_node_usage->add_node($_) for @$nodes;
my $service_conf = {
node => 'node1',
@@ -46,7 +48,12 @@ sub test {
my $select_node_preference = $try_next ? 'try-next' : 'none';
my $node = PVE::HA::Manager::select_service_node(
- $rules, $online_node_usage, "$sid", $service_conf, $ss, $select_node_preference,
+ $compiled_rules,
+ $online_node_usage,
+ $sid,
+ $service_conf,
+ $ss,
+ $select_node_preference,
);
my (undef, undef, $line) = caller();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 11/21] test: rules: use to_json instead of Data::Dumper for config output
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (9 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 10/21] compile ha rules to a more compact representation Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 12/21] test: rules: add compiled config output to rules config test cases Daniel Kral
` (9 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Makes the output cleaner and won't change unrelated config properties
because of Data::Dumper's space indentation if rule configs are
modified in future patches.
Suggested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
...efaults-for-node-affinity-rules.cfg.expect | 116 ++++----
...lts-for-resource-affinity-rules.cfg.expect | 72 ++---
...nt-node-resource-affinity-rules.cfg.expect | 130 ++++-----
...sistent-resource-affinity-rules.cfg.expect | 10 +-
...egative-resource-affinity-rules.cfg.expect | 52 ++--
...fective-resource-affinity-rules.cfg.expect | 10 +-
...egative-resource-affinity-rules.cfg.expect | 254 +++++++++---------
...ositive-resource-affinity-rules.cfg.expect | 218 +++++++--------
...egative-resource-affinity-rules.cfg.expect | 142 +++++-----
...ositive-resource-affinity-rules.cfg.expect | 136 +++++-----
...ty-with-resource-affinity-rules.cfg.expect | 84 +++---
...rce-refs-in-node-affinity-rules.cfg.expect | 116 ++++----
src/test/test_rules_config.pl | 9 +-
13 files changed, 673 insertions(+), 676 deletions(-)
diff --git a/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect b/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
index 59a2c364..8ea928f2 100644
--- a/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
@@ -1,60 +1,60 @@
--- Log ---
--- Config ---
-$VAR1 = {
- 'digest' => 'c96c9de143221a82e44efa8bb4814b8248a8ea11',
- 'ids' => {
- 'node-affinity-defaults' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:101' => 1
- },
- 'type' => 'node-affinity'
- },
- 'node-affinity-disabled' => {
- 'disable' => 1,
- 'nodes' => {
- 'node2' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:102' => 1
- },
- 'type' => 'node-affinity'
- },
- 'node-affinity-disabled-explicit' => {
- 'disable' => 1,
- 'nodes' => {
- 'node2' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:103' => 1
- },
- 'type' => 'node-affinity'
- },
- 'node-affinity-strict' => {
- 'nodes' => {
- 'node3' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:104' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- }
- },
- 'order' => {
- 'node-affinity-defaults' => 1,
- 'node-affinity-disabled' => 2,
- 'node-affinity-disabled-explicit' => 3,
- 'node-affinity-strict' => 4
- }
- };
+{
+ "digest" : "c96c9de143221a82e44efa8bb4814b8248a8ea11",
+ "ids" : {
+ "node-affinity-defaults" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:101" : 1
+ },
+ "type" : "node-affinity"
+ },
+ "node-affinity-disabled" : {
+ "disable" : 1,
+ "nodes" : {
+ "node2" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:102" : 1
+ },
+ "type" : "node-affinity"
+ },
+ "node-affinity-disabled-explicit" : {
+ "disable" : 1,
+ "nodes" : {
+ "node2" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:103" : 1
+ },
+ "type" : "node-affinity"
+ },
+ "node-affinity-strict" : {
+ "nodes" : {
+ "node3" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:104" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ }
+ },
+ "order" : {
+ "node-affinity-defaults" : 1,
+ "node-affinity-disabled" : 2,
+ "node-affinity-disabled-explicit" : 3,
+ "node-affinity-strict" : 4
+ }
+}
diff --git a/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect
index 7384b0b8..7af19a18 100644
--- a/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect
@@ -1,38 +1,38 @@
--- Log ---
--- Config ---
-$VAR1 = {
- 'digest' => '9ac7cc517f02c41e3403085ec02f6a9259f2ac94',
- 'ids' => {
- 'resource-affinity-defaults' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'resource-affinity-disabled' => {
- 'affinity' => 'negative',
- 'disable' => 1,
- 'resources' => {
- 'vm:201' => 1,
- 'vm:202' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'resource-affinity-disabled-explicit' => {
- 'affinity' => 'negative',
- 'disable' => 1,
- 'resources' => {
- 'vm:301' => 1,
- 'vm:302' => 1
- },
- 'type' => 'resource-affinity'
- }
- },
- 'order' => {
- 'resource-affinity-defaults' => 1,
- 'resource-affinity-disabled' => 2,
- 'resource-affinity-disabled-explicit' => 3
- }
- };
+{
+ "digest" : "9ac7cc517f02c41e3403085ec02f6a9259f2ac94",
+ "ids" : {
+ "resource-affinity-defaults" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "resource-affinity-disabled" : {
+ "affinity" : "negative",
+ "disable" : 1,
+ "resources" : {
+ "vm:201" : 1,
+ "vm:202" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "resource-affinity-disabled-explicit" : {
+ "affinity" : "negative",
+ "disable" : 1,
+ "resources" : {
+ "vm:301" : 1,
+ "vm:302" : 1
+ },
+ "type" : "resource-affinity"
+ }
+ },
+ "order" : {
+ "resource-affinity-defaults" : 1,
+ "resource-affinity-disabled" : 2,
+ "resource-affinity-disabled-explicit" : 3
+ }
+}
diff --git a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
index 80222c86..ad517077 100644
--- a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
@@ -10,68 +10,68 @@ Drop rule 'vm501-vm502-must-be-kept-together', because resources are in multiple
Drop rule 'vm502-vm503-must-be-kept-together', because resources are in multiple node affinity rules.
Drop rule 'vm503-must-be-on-node2', because at least one resource is in a positive resource affinity rule and there are other resources in at least one other node affinity rule already.
--- Config ---
-$VAR1 = {
- 'digest' => '00a70f4c2bdd41aed16b6e5b9860cd9fb7f0f098',
- 'ids' => {
- 'vm101-vm102-must-be-kept-together' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'vm101-vm102-must-be-on-node1' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- },
- 'vm201-must-be-on-node1' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:201' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- },
- 'vm201-vm202-must-be-kept-separate' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:201' => 1,
- 'vm:202' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'vm202-must-be-on-node2' => {
- 'nodes' => {
- 'node2' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:202' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- }
- },
- 'order' => {
- 'vm101-vm102-must-be-kept-together' => 2,
- 'vm101-vm102-must-be-on-node1' => 1,
- 'vm201-must-be-on-node1' => 3,
- 'vm201-vm202-must-be-kept-separate' => 5,
- 'vm202-must-be-on-node2' => 4
- }
- };
+{
+ "digest" : "00a70f4c2bdd41aed16b6e5b9860cd9fb7f0f098",
+ "ids" : {
+ "vm101-vm102-must-be-kept-together" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "vm101-vm102-must-be-on-node1" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ },
+ "vm201-must-be-on-node1" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:201" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ },
+ "vm201-vm202-must-be-kept-separate" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:201" : 1,
+ "vm:202" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "vm202-must-be-on-node2" : {
+ "nodes" : {
+ "node2" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:202" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ }
+ },
+ "order" : {
+ "vm101-vm102-must-be-kept-together" : 2,
+ "vm101-vm102-must-be-on-node1" : 1,
+ "vm201-must-be-on-node1" : 3,
+ "vm201-vm202-must-be-kept-separate" : 5,
+ "vm202-must-be-on-node2" : 4
+ }
+}
diff --git a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
index 8f2338d9..f47828c6 100644
--- a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
@@ -7,8 +7,8 @@ Drop rule 'split-stick-together2', because rule shares two or more resources wit
Drop rule 'stick-together1', because rule shares two or more resources with a negative resource affinity rule.
Drop rule 'stick-together1', because rule shares two or more resources with a negative resource affinity rule.
--- Config ---
-$VAR1 = {
- 'digest' => '80cdc11a1d5bf2d1d500665af1210cd59aabede6',
- 'ids' => {},
- 'order' => {}
- };
+{
+ "digest" : "80cdc11a1d5bf2d1d500665af1210cd59aabede6",
+ "ids" : {},
+ "order" : {}
+}
diff --git a/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect
index 8a2b8797..e2c1ad11 100644
--- a/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect
@@ -2,29 +2,29 @@
Drop rule 'remove-me1', because rule defines more resources than available nodes.
Drop rule 'remove-me2', because rule defines more resources than available nodes.
--- Config ---
-$VAR1 = {
- 'digest' => '68633cedeeb355ef78fe28221ef3f16537b3e788',
- 'ids' => {
- 'do-not-remove-me1' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-remove-me2' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1,
- 'vm:103' => 1
- },
- 'type' => 'resource-affinity'
- }
- },
- 'order' => {
- 'do-not-remove-me1' => 1,
- 'do-not-remove-me2' => 2
- }
- };
+{
+ "digest" : "68633cedeeb355ef78fe28221ef3f16537b3e788",
+ "ids" : {
+ "do-not-remove-me1" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-remove-me2" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "type" : "resource-affinity"
+ }
+ },
+ "order" : {
+ "do-not-remove-me1" : 1,
+ "do-not-remove-me2" : 2
+ }
+}
diff --git a/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect
index b2d468b2..4bbc782a 100644
--- a/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect
@@ -2,8 +2,8 @@
Drop rule 'lonely-resource1', because rule is ineffective as there are less than two resources.
Drop rule 'lonely-resource2', because rule is ineffective as there are less than two resources.
--- Config ---
-$VAR1 = {
- 'digest' => 'fe89f8c8f5acc29f807eaa0cec5974b6e957a596',
- 'ids' => {},
- 'order' => {}
- };
+{
+ "digest" : "fe89f8c8f5acc29f807eaa0cec5974b6e957a596",
+ "ids" : {},
+ "order" : {}
+}
diff --git a/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect
index 09364d41..d3f1c7c3 100644
--- a/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect
@@ -2,130 +2,130 @@
Drop rule 'do-not-infer-inconsistent-negative2', because rule shares two or more resources with a positive resource affinity rule.
Drop rule 'do-not-infer-inconsistent-positive1', because rule shares two or more resources with a negative resource affinity rule.
--- Config ---
-$VAR1 = {
- 'digest' => 'd8724dfe2130bb642b98e021da973aa0ec0695f0',
- 'ids' => {
- '_implicit-negative-infer-simple-positive1-vm:202-vm:204' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:202' => 1,
- 'vm:204' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-infer-simple-positive1-vm:203-vm:204' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:203' => 1,
- 'vm:204' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-infer-two-positive1-vm:301-vm:304' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:301' => 1,
- 'vm:304' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-infer-two-positive1-vm:301-vm:305' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:301' => 1,
- 'vm:305' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-infer-two-positive1-vm:302-vm:304' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:302' => 1,
- 'vm:304' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-infer-two-positive1-vm:303-vm:305' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:303' => 1,
- 'vm:305' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-infer-inconsistent-negative1' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:401' => 1,
- 'vm:404' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-infer-positive1' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1,
- 'vm:103' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-simple-negative1' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:201' => 1,
- 'vm:204' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-simple-positive1' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:201' => 1,
- 'vm:202' => 1,
- 'vm:203' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-two-negative1' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:303' => 1,
- 'vm:304' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-two-negative2' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:302' => 1,
- 'vm:305' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-two-positive1' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:301' => 1,
- 'vm:302' => 1,
- 'vm:303' => 1
- },
- 'type' => 'resource-affinity'
- }
- },
- 'order' => {
- '_implicit-negative-infer-simple-positive1-vm:202-vm:204' => 2,
- '_implicit-negative-infer-simple-positive1-vm:203-vm:204' => 2,
- '_implicit-negative-infer-two-positive1-vm:301-vm:304' => 2,
- '_implicit-negative-infer-two-positive1-vm:301-vm:305' => 2,
- '_implicit-negative-infer-two-positive1-vm:302-vm:304' => 2,
- '_implicit-negative-infer-two-positive1-vm:303-vm:305' => 2,
- 'do-not-infer-inconsistent-negative1' => 8,
- 'do-not-infer-positive1' => 1,
- 'infer-simple-negative1' => 3,
- 'infer-simple-positive1' => 2,
- 'infer-two-negative1' => 5,
- 'infer-two-negative2' => 6,
- 'infer-two-positive1' => 4
- }
- };
+{
+ "digest" : "d8724dfe2130bb642b98e021da973aa0ec0695f0",
+ "ids" : {
+ "_implicit-negative-infer-simple-positive1-vm:202-vm:204" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:202" : 1,
+ "vm:204" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-infer-simple-positive1-vm:203-vm:204" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:203" : 1,
+ "vm:204" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-infer-two-positive1-vm:301-vm:304" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:301" : 1,
+ "vm:304" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-infer-two-positive1-vm:301-vm:305" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:301" : 1,
+ "vm:305" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-infer-two-positive1-vm:302-vm:304" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:302" : 1,
+ "vm:304" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-infer-two-positive1-vm:303-vm:305" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:303" : 1,
+ "vm:305" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-infer-inconsistent-negative1" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:401" : 1,
+ "vm:404" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-infer-positive1" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-simple-negative1" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:201" : 1,
+ "vm:204" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-simple-positive1" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:201" : 1,
+ "vm:202" : 1,
+ "vm:203" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-two-negative1" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:303" : 1,
+ "vm:304" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-two-negative2" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:302" : 1,
+ "vm:305" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-two-positive1" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1
+ },
+ "type" : "resource-affinity"
+ }
+ },
+ "order" : {
+ "_implicit-negative-infer-simple-positive1-vm:202-vm:204" : 2,
+ "_implicit-negative-infer-simple-positive1-vm:203-vm:204" : 2,
+ "_implicit-negative-infer-two-positive1-vm:301-vm:304" : 2,
+ "_implicit-negative-infer-two-positive1-vm:301-vm:305" : 2,
+ "_implicit-negative-infer-two-positive1-vm:302-vm:304" : 2,
+ "_implicit-negative-infer-two-positive1-vm:303-vm:305" : 2,
+ "do-not-infer-inconsistent-negative1" : 8,
+ "do-not-infer-positive1" : 1,
+ "infer-simple-negative1" : 3,
+ "infer-simple-positive1" : 2,
+ "infer-two-negative1" : 5,
+ "infer-two-negative2" : 6,
+ "infer-two-positive1" : 4
+ }
+}
diff --git a/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect
index 33c56c62..3f5cd6d8 100644
--- a/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect
@@ -1,111 +1,111 @@
--- Log ---
--- Config ---
-$VAR1 = {
- 'digest' => '32ae135ef2f8bd84cd12c18af6910dce9d6bc9fa',
- 'ids' => {
- 'do-not-infer-negative1' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- },
- 'node2' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:203' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- },
- 'do-not-infer-negative2' => {
- 'nodes' => {
- 'node3' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:201' => 1
- },
- 'type' => 'node-affinity'
- },
- 'do-not-infer-negative3' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:201' => 1,
- 'vm:203' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-infer-positive1' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1,
- 'vm:103' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-multi-resources1' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- },
- 'node3' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:401' => 1,
- 'vm:402' => 1,
- 'vm:403' => 1,
- 'vm:404' => 1,
- 'vm:405' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- },
- 'infer-multi-resources2' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:401' => 1,
- 'vm:402' => 1,
- 'vm:403' => 1,
- 'vm:404' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-single-resource1' => {
- 'nodes' => {
- 'node3' => {
- 'priority' => 0
- }
- },
- 'resources' => {
- 'vm:301' => 1,
- 'vm:302' => 1,
- 'vm:303' => 1
- },
- 'type' => 'node-affinity'
- },
- 'infer-single-resource2' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:301' => 1,
- 'vm:302' => 1,
- 'vm:303' => 1
- },
- 'type' => 'resource-affinity'
- }
- },
- 'order' => {
- 'do-not-infer-negative1' => 2,
- 'do-not-infer-negative2' => 3,
- 'do-not-infer-negative3' => 4,
- 'do-not-infer-positive1' => 1,
- 'infer-multi-resources1' => 7,
- 'infer-multi-resources2' => 8,
- 'infer-single-resource1' => 5,
- 'infer-single-resource2' => 6
- }
- };
+{
+ "digest" : "32ae135ef2f8bd84cd12c18af6910dce9d6bc9fa",
+ "ids" : {
+ "do-not-infer-negative1" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:203" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ },
+ "do-not-infer-negative2" : {
+ "nodes" : {
+ "node3" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:201" : 1
+ },
+ "type" : "node-affinity"
+ },
+ "do-not-infer-negative3" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:201" : 1,
+ "vm:203" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-infer-positive1" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-multi-resources1" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:401" : 1,
+ "vm:402" : 1,
+ "vm:403" : 1,
+ "vm:404" : 1,
+ "vm:405" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ },
+ "infer-multi-resources2" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:401" : 1,
+ "vm:402" : 1,
+ "vm:403" : 1,
+ "vm:404" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-single-resource1" : {
+ "nodes" : {
+ "node3" : {
+ "priority" : 0
+ }
+ },
+ "resources" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1
+ },
+ "type" : "node-affinity"
+ },
+ "infer-single-resource2" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1
+ },
+ "type" : "resource-affinity"
+ }
+ },
+ "order" : {
+ "do-not-infer-negative1" : 2,
+ "do-not-infer-negative2" : 3,
+ "do-not-infer-negative3" : 4,
+ "do-not-infer-positive1" : 1,
+ "infer-multi-resources1" : 7,
+ "infer-multi-resources2" : 8,
+ "infer-single-resource1" : 5,
+ "infer-single-resource2" : 6
+ }
+}
diff --git a/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect
index 876c2030..0002dc2a 100644
--- a/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect
@@ -1,73 +1,73 @@
--- Log ---
--- Config ---
-$VAR1 = {
- 'digest' => '5695bd62a65966a275a62a01d2d8fbc370d91668',
- 'ids' => {
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:101-vm:105' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:105' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:102-vm:104' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:102' => 1,
- 'vm:104' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:104' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:103' => 1,
- 'vm:104' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:105' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:103' => 1,
- 'vm:105' => 1
- },
- 'type' => 'resource-affinity'
- },
- '_merged-infer-connected-positive1-infer-connected-positive2' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1,
- 'vm:103' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-connected-negative1' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:104' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'infer-connected-negative2' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:102' => 1,
- 'vm:105' => 1
- },
- 'type' => 'resource-affinity'
- }
- },
- 'order' => {
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:101-vm:105' => 2,
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:102-vm:104' => 2,
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:104' => 2,
- '_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:105' => 2,
- '_merged-infer-connected-positive1-infer-connected-positive2' => 1,
- 'infer-connected-negative1' => 3,
- 'infer-connected-negative2' => 4
- }
- };
+{
+ "digest" : "5695bd62a65966a275a62a01d2d8fbc370d91668",
+ "ids" : {
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:101-vm:105" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:105" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:102-vm:104" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:102" : 1,
+ "vm:104" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:104" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:103" : 1,
+ "vm:104" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:105" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:103" : 1,
+ "vm:105" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "_merged-infer-connected-positive1-infer-connected-positive2" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-connected-negative1" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:104" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "infer-connected-negative2" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:102" : 1,
+ "vm:105" : 1
+ },
+ "type" : "resource-affinity"
+ }
+ },
+ "order" : {
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:101-vm:105" : 2,
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:102-vm:104" : 2,
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:104" : 2,
+ "_implicit-negative-_merged-infer-connected-positive1-infer-connected-positive2-vm:103-vm:105" : 2,
+ "_merged-infer-connected-positive1-infer-connected-positive2" : 1,
+ "infer-connected-negative1" : 3,
+ "infer-connected-negative2" : 4
+ }
+}
diff --git a/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect
index e57a792b..935a4f7c 100644
--- a/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect
@@ -1,70 +1,70 @@
--- Log ---
--- Config ---
-$VAR1 = {
- 'digest' => '920d9caac206fc0dd893753bfb2cab3e6d6a9b9b',
- 'ids' => {
- '_merged-merge-positive1-merge-positive3-merge-positive4-merge-positive2-merge-positive5' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:301' => 1,
- 'vm:302' => 1,
- 'vm:303' => 1,
- 'vm:304' => 1,
- 'vm:305' => 1,
- 'vm:306' => 1,
- 'vm:307' => 1,
- 'vm:308' => 1,
- 'vm:309' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-merge-negative1' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-merge-negative2' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:102' => 1,
- 'vm:103' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-merge-negative3' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:104' => 1,
- 'vm:105' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-merge-positive1' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:201' => 1,
- 'vm:202' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'do-not-merge-positive2' => {
- 'affinity' => 'positive',
- 'resources' => {
- 'vm:203' => 1,
- 'vm:204' => 1
- },
- 'type' => 'resource-affinity'
- }
- },
- 'order' => {
- '_merged-merge-positive1-merge-positive3-merge-positive4-merge-positive2-merge-positive5' => 6,
- 'do-not-merge-negative1' => 1,
- 'do-not-merge-negative2' => 2,
- 'do-not-merge-negative3' => 3,
- 'do-not-merge-positive1' => 4,
- 'do-not-merge-positive2' => 5
- }
- };
+{
+ "digest" : "920d9caac206fc0dd893753bfb2cab3e6d6a9b9b",
+ "ids" : {
+ "_merged-merge-positive1-merge-positive3-merge-positive4-merge-positive2-merge-positive5" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-merge-negative1" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-merge-negative2" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-merge-negative3" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:104" : 1,
+ "vm:105" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-merge-positive1" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:201" : 1,
+ "vm:202" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "do-not-merge-positive2" : {
+ "affinity" : "positive",
+ "resources" : {
+ "vm:203" : 1,
+ "vm:204" : 1
+ },
+ "type" : "resource-affinity"
+ }
+ },
+ "order" : {
+ "_merged-merge-positive1-merge-positive3-merge-positive4-merge-positive2-merge-positive5" : 6,
+ "do-not-merge-negative1" : 1,
+ "do-not-merge-negative2" : 2,
+ "do-not-merge-negative3" : 3,
+ "do-not-merge-positive1" : 4,
+ "do-not-merge-positive2" : 5
+ }
+}
diff --git a/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect
index 92d12929..e2d5ee00 100644
--- a/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect
@@ -4,45 +4,45 @@ Drop rule 'vm101-vm102-should-be-on-node1-or-node2', because resources are in a
Drop rule 'vm201-vm202-must-be-kept-together', because resources are in node affinity rules with multiple priorities.
Drop rule 'vm201-vm202-must-be-on-node1-or-node2', because resources are in a resource affinity rule and cannot be in a node affinity rule with multiple priorities.
--- Config ---
-$VAR1 = {
- 'digest' => '722a98914555f296af0916c980a9d6c780f5f072',
- 'ids' => {
- 'vm301-must-be-on-node1-with-prio-1' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 1
- }
- },
- 'resources' => {
- 'vm:301' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- },
- 'vm301-vm302-must-be-kept-together' => {
- 'affinity' => 'negative',
- 'resources' => {
- 'vm:301' => 1,
- 'vm:302' => 1
- },
- 'type' => 'resource-affinity'
- },
- 'vm302-must-be-on-node2-with-prio-2' => {
- 'nodes' => {
- 'node2' => {
- 'priority' => 2
- }
- },
- 'resources' => {
- 'vm:302' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- }
- },
- 'order' => {
- 'vm301-must-be-on-node1-with-prio-1' => 5,
- 'vm301-vm302-must-be-kept-together' => 7,
- 'vm302-must-be-on-node2-with-prio-2' => 6
- }
- };
+{
+ "digest" : "722a98914555f296af0916c980a9d6c780f5f072",
+ "ids" : {
+ "vm301-must-be-on-node1-with-prio-1" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 1
+ }
+ },
+ "resources" : {
+ "vm:301" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ },
+ "vm301-vm302-must-be-kept-together" : {
+ "affinity" : "negative",
+ "resources" : {
+ "vm:301" : 1,
+ "vm:302" : 1
+ },
+ "type" : "resource-affinity"
+ },
+ "vm302-must-be-on-node2-with-prio-2" : {
+ "nodes" : {
+ "node2" : {
+ "priority" : 2
+ }
+ },
+ "resources" : {
+ "vm:302" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ }
+ },
+ "order" : {
+ "vm301-must-be-on-node1-with-prio-1" : 5,
+ "vm301-vm302-must-be-kept-together" : 7,
+ "vm302-must-be-on-node2-with-prio-2" : 6
+ }
+}
diff --git a/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect b/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
index 3fd0c9ca..30633d8c 100644
--- a/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
@@ -3,61 +3,61 @@ Drop rule 'same-resource1', because resource 'vm:201' is already used in another
Drop rule 'same-resource2', because resource 'vm:201' is already used in another node affinity rule.
Drop rule 'same-resource3', because resource 'vm:201' is already used in another node affinity rule.
--- Config ---
-$VAR1 = {
- 'digest' => '5865d23b1a342e7f8cfa68bd0e1da556ca8d28a6',
- 'ids' => {
- 'no-same-resource1' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- },
- 'node2' => {
- 'priority' => 2
- }
- },
- 'resources' => {
- 'vm:101' => 1,
- 'vm:102' => 1,
- 'vm:103' => 1
- },
- 'strict' => 0,
- 'type' => 'node-affinity'
- },
- 'no-same-resource2' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- },
- 'node2' => {
- 'priority' => 2
- }
- },
- 'resources' => {
- 'vm:104' => 1,
- 'vm:105' => 1
- },
- 'strict' => 0,
- 'type' => 'node-affinity'
- },
- 'no-same-resource3' => {
- 'nodes' => {
- 'node1' => {
- 'priority' => 0
- },
- 'node2' => {
- 'priority' => 2
- }
- },
- 'resources' => {
- 'vm:106' => 1
- },
- 'strict' => 1,
- 'type' => 'node-affinity'
- }
- },
- 'order' => {
- 'no-same-resource1' => 1,
- 'no-same-resource2' => 2,
- 'no-same-resource3' => 3
- }
- };
+{
+ "digest" : "5865d23b1a342e7f8cfa68bd0e1da556ca8d28a6",
+ "ids" : {
+ "no-same-resource1" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ }
+ },
+ "resources" : {
+ "vm:101" : 1,
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "strict" : 0,
+ "type" : "node-affinity"
+ },
+ "no-same-resource2" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ }
+ },
+ "resources" : {
+ "vm:104" : 1,
+ "vm:105" : 1
+ },
+ "strict" : 0,
+ "type" : "node-affinity"
+ },
+ "no-same-resource3" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ }
+ },
+ "resources" : {
+ "vm:106" : 1
+ },
+ "strict" : 1,
+ "type" : "node-affinity"
+ }
+ },
+ "order" : {
+ "no-same-resource1" : 1,
+ "no-same-resource2" : 2,
+ "no-same-resource3" : 3
+ }
+}
diff --git a/src/test/test_rules_config.pl b/src/test/test_rules_config.pl
index 3c2dac4b..edfcb3b7 100755
--- a/src/test/test_rules_config.pl
+++ b/src/test/test_rules_config.pl
@@ -6,11 +6,11 @@ use Getopt::Long;
use lib qw(..);
+use JSON;
+
use Test::More;
use Test::MockModule;
-use Data::Dumper;
-
use PVE::HA::Rules;
use PVE::HA::Rules::NodeAffinity;
use PVE::HA::Rules::ResourceAffinity;
@@ -56,10 +56,7 @@ sub check_cfg {
my $messages = PVE::HA::Rules->transform($cfg, $nodes);
print $_ for @$messages;
print "--- Config ---\n";
- {
- local $Data::Dumper::Sortkeys = 1;
- print Dumper($cfg);
- }
+ print to_json($cfg, { canonical => 1, pretty => 1, utf8 => 1 });
select(STDOUT);
}
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 12/21] test: rules: add compiled config output to rules config test cases
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (10 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 11/21] test: rules: use to_json instead of Data::Dumper for config output Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 13/21] rules: node affinity: define node priority outside hash access Daniel Kral
` (8 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Since there exists a compiled representation of the HA rules config now,
which is currently used by both the HA Manager and API endpoints, add
the compiled representation to the test cases to document and follow
changes through these test cases.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
...efaults-for-node-affinity-rules.cfg.expect | 29 +++
...lts-for-resource-affinity-rules.cfg.expect | 15 ++
...nt-node-resource-affinity-rules.cfg.expect | 51 ++++++
...sistent-resource-affinity-rules.cfg.expect | 8 +
...egative-resource-affinity-rules.cfg.expect | 21 +++
...fective-resource-affinity-rules.cfg.expect | 8 +
...egative-resource-affinity-rules.cfg.expect | 88 +++++++++
...ositive-resource-affinity-rules.cfg.expect | 173 ++++++++++++++++++
...egative-resource-affinity-rules.cfg.expect | 44 +++++
...ositive-resource-affinity-rules.cfg.expect | 128 +++++++++++++
...ty-with-resource-affinity-rules.cfg.expect | 30 +++
...rce-refs-in-node-affinity-rules.cfg.expect | 84 +++++++++
src/test/test_rules_config.pl | 3 +
13 files changed, 682 insertions(+)
diff --git a/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect b/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
index 8ea928f2..35d061bd 100644
--- a/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
@@ -58,3 +58,32 @@
"node-affinity-strict" : 4
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {
+ "vm:101" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : -1
+ },
+ "node3" : {
+ "priority" : -1
+ }
+ }
+ },
+ "vm:104" : {
+ "nodes" : {
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ }
+ },
+ "resource-affinity" : {
+ "negative" : {},
+ "positive" : {}
+ }
+}
diff --git a/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect
index 7af19a18..d6a1121e 100644
--- a/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/defaults-for-resource-affinity-rules.cfg.expect
@@ -36,3 +36,18 @@
"resource-affinity-disabled-explicit" : 3
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {},
+ "resource-affinity" : {
+ "negative" : {
+ "vm:101" : {
+ "vm:102" : 1
+ },
+ "vm:102" : {
+ "vm:101" : 1
+ }
+ },
+ "positive" : {}
+ }
+}
diff --git a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
index ad517077..4317292b 100644
--- a/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/inconsistent-node-resource-affinity-rules.cfg.expect
@@ -75,3 +75,54 @@ Drop rule 'vm503-must-be-on-node2', because at least one resource is in a positi
"vm202-must-be-on-node2" : 4
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {
+ "vm:101" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:102" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:201" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:202" : {
+ "nodes" : {
+ "node2" : {
+ "priority" : 0
+ }
+ }
+ }
+ },
+ "resource-affinity" : {
+ "negative" : {
+ "vm:201" : {
+ "vm:202" : 1
+ },
+ "vm:202" : {
+ "vm:201" : 1
+ }
+ },
+ "positive" : {
+ "vm:101" : {
+ "vm:102" : 1
+ },
+ "vm:102" : {
+ "vm:101" : 1
+ }
+ }
+ }
+}
diff --git a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
index f47828c6..70d51ffd 100644
--- a/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/inconsistent-resource-affinity-rules.cfg.expect
@@ -12,3 +12,11 @@ Drop rule 'stick-together1', because rule shares two or more resources with a ne
"ids" : {},
"order" : {}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {},
+ "resource-affinity" : {
+ "negative" : {},
+ "positive" : {}
+ }
+}
diff --git a/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect
index e2c1ad11..42fa7d24 100644
--- a/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/ineffective-negative-resource-affinity-rules.cfg.expect
@@ -28,3 +28,24 @@ Drop rule 'remove-me2', because rule defines more resources than available nodes
"do-not-remove-me2" : 2
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {},
+ "resource-affinity" : {
+ "negative" : {
+ "vm:101" : {
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "vm:102" : {
+ "vm:101" : 1,
+ "vm:103" : 1
+ },
+ "vm:103" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ }
+ },
+ "positive" : {}
+ }
+}
diff --git a/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect
index 4bbc782a..9c61944f 100644
--- a/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/ineffective-resource-affinity-rules.cfg.expect
@@ -7,3 +7,11 @@ Drop rule 'lonely-resource2', because rule is ineffective as there are less than
"ids" : {},
"order" : {}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {},
+ "resource-affinity" : {
+ "negative" : {},
+ "positive" : {}
+ }
+}
diff --git a/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect
index d3f1c7c3..f863c9fa 100644
--- a/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/infer-implicit-negative-resource-affinity-rules.cfg.expect
@@ -129,3 +129,91 @@ Drop rule 'do-not-infer-inconsistent-positive1', because rule shares two or more
"infer-two-positive1" : 4
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {},
+ "resource-affinity" : {
+ "negative" : {
+ "vm:201" : {
+ "vm:204" : 1
+ },
+ "vm:202" : {
+ "vm:204" : 1
+ },
+ "vm:203" : {
+ "vm:204" : 1
+ },
+ "vm:204" : {
+ "vm:201" : 1,
+ "vm:202" : 1,
+ "vm:203" : 1
+ },
+ "vm:301" : {
+ "vm:304" : 1,
+ "vm:305" : 1
+ },
+ "vm:302" : {
+ "vm:304" : 1,
+ "vm:305" : 1
+ },
+ "vm:303" : {
+ "vm:304" : 1,
+ "vm:305" : 1
+ },
+ "vm:304" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1
+ },
+ "vm:305" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1
+ },
+ "vm:401" : {
+ "vm:404" : 1
+ },
+ "vm:404" : {
+ "vm:401" : 1
+ }
+ },
+ "positive" : {
+ "vm:101" : {
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "vm:102" : {
+ "vm:101" : 1,
+ "vm:103" : 1
+ },
+ "vm:103" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ },
+ "vm:201" : {
+ "vm:202" : 1,
+ "vm:203" : 1
+ },
+ "vm:202" : {
+ "vm:201" : 1,
+ "vm:203" : 1
+ },
+ "vm:203" : {
+ "vm:201" : 1,
+ "vm:202" : 1
+ },
+ "vm:301" : {
+ "vm:302" : 1,
+ "vm:303" : 1
+ },
+ "vm:302" : {
+ "vm:301" : 1,
+ "vm:303" : 1
+ },
+ "vm:303" : {
+ "vm:301" : 1,
+ "vm:302" : 1
+ }
+ }
+ }
+}
diff --git a/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect
index 3f5cd6d8..ed339777 100644
--- a/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/infer-node-affinity-for-positive-resource-affinity-rules.cfg.expect
@@ -109,3 +109,176 @@
"infer-single-resource2" : 6
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {
+ "vm:201" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : -1
+ },
+ "node2" : {
+ "priority" : -1
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:203" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:301" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : -1
+ },
+ "node2" : {
+ "priority" : -1
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:302" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : -1
+ },
+ "node2" : {
+ "priority" : -1
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:303" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : -1
+ },
+ "node2" : {
+ "priority" : -1
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:401" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:402" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:403" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:404" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ },
+ "vm:405" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node3" : {
+ "priority" : 0
+ }
+ }
+ }
+ },
+ "resource-affinity" : {
+ "negative" : {
+ "vm:201" : {
+ "vm:203" : 1
+ },
+ "vm:203" : {
+ "vm:201" : 1
+ }
+ },
+ "positive" : {
+ "vm:101" : {
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "vm:102" : {
+ "vm:101" : 1,
+ "vm:103" : 1
+ },
+ "vm:103" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ },
+ "vm:301" : {
+ "vm:302" : 1,
+ "vm:303" : 1
+ },
+ "vm:302" : {
+ "vm:301" : 1,
+ "vm:303" : 1
+ },
+ "vm:303" : {
+ "vm:301" : 1,
+ "vm:302" : 1
+ },
+ "vm:401" : {
+ "vm:402" : 1,
+ "vm:403" : 1,
+ "vm:404" : 1
+ },
+ "vm:402" : {
+ "vm:401" : 1,
+ "vm:403" : 1,
+ "vm:404" : 1
+ },
+ "vm:403" : {
+ "vm:401" : 1,
+ "vm:402" : 1,
+ "vm:404" : 1
+ },
+ "vm:404" : {
+ "vm:401" : 1,
+ "vm:402" : 1,
+ "vm:403" : 1
+ }
+ }
+ }
+}
diff --git a/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect
index 0002dc2a..98c8079a 100644
--- a/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/merge-and-infer-implicit-negative-resource-affinity-rules.cfg.expect
@@ -71,3 +71,47 @@
"infer-connected-negative2" : 4
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {},
+ "resource-affinity" : {
+ "negative" : {
+ "vm:101" : {
+ "vm:104" : 1,
+ "vm:105" : 1
+ },
+ "vm:102" : {
+ "vm:104" : 1,
+ "vm:105" : 1
+ },
+ "vm:103" : {
+ "vm:104" : 1,
+ "vm:105" : 1
+ },
+ "vm:104" : {
+ "vm:101" : 1,
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "vm:105" : {
+ "vm:101" : 1,
+ "vm:102" : 1,
+ "vm:103" : 1
+ }
+ },
+ "positive" : {
+ "vm:101" : {
+ "vm:102" : 1,
+ "vm:103" : 1
+ },
+ "vm:102" : {
+ "vm:101" : 1,
+ "vm:103" : 1
+ },
+ "vm:103" : {
+ "vm:101" : 1,
+ "vm:102" : 1
+ }
+ }
+ }
+}
diff --git a/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect
index 935a4f7c..07461626 100644
--- a/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/merge-connected-positive-resource-affinity-rules.cfg.expect
@@ -68,3 +68,131 @@
"do-not-merge-positive2" : 5
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {},
+ "resource-affinity" : {
+ "negative" : {
+ "vm:101" : {
+ "vm:102" : 1
+ },
+ "vm:102" : {
+ "vm:101" : 1,
+ "vm:103" : 1
+ },
+ "vm:103" : {
+ "vm:102" : 1
+ },
+ "vm:104" : {
+ "vm:105" : 1
+ },
+ "vm:105" : {
+ "vm:104" : 1
+ }
+ },
+ "positive" : {
+ "vm:201" : {
+ "vm:202" : 1
+ },
+ "vm:202" : {
+ "vm:201" : 1
+ },
+ "vm:203" : {
+ "vm:204" : 1
+ },
+ "vm:204" : {
+ "vm:203" : 1
+ },
+ "vm:301" : {
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "vm:302" : {
+ "vm:301" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "vm:303" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "vm:304" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "vm:305" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "vm:306" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "vm:307" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:308" : 1,
+ "vm:309" : 1
+ },
+ "vm:308" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:309" : 1
+ },
+ "vm:309" : {
+ "vm:301" : 1,
+ "vm:302" : 1,
+ "vm:303" : 1,
+ "vm:304" : 1,
+ "vm:305" : 1,
+ "vm:306" : 1,
+ "vm:307" : 1,
+ "vm:308" : 1
+ }
+ }
+ }
+}
diff --git a/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect b/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect
index e2d5ee00..68a2b75f 100644
--- a/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/multi-priority-node-affinity-with-resource-affinity-rules.cfg.expect
@@ -46,3 +46,33 @@ Drop rule 'vm201-vm202-must-be-on-node1-or-node2', because resources are in a re
"vm302-must-be-on-node2-with-prio-2" : 6
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {
+ "vm:301" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 1
+ }
+ }
+ },
+ "vm:302" : {
+ "nodes" : {
+ "node2" : {
+ "priority" : 2
+ }
+ }
+ }
+ },
+ "resource-affinity" : {
+ "negative" : {
+ "vm:301" : {
+ "vm:302" : 1
+ },
+ "vm:302" : {
+ "vm:301" : 1
+ }
+ },
+ "positive" : {}
+ }
+}
diff --git a/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect b/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
index 30633d8c..425de2b1 100644
--- a/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
+++ b/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
@@ -61,3 +61,87 @@ Drop rule 'same-resource3', because resource 'vm:201' is already used in another
"no-same-resource3" : 3
}
}
+--- Compiled Config ---
+{
+ "node-affinity" : {
+ "vm:101" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ },
+ "node3" : {
+ "priority" : -1
+ }
+ }
+ },
+ "vm:102" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ },
+ "node3" : {
+ "priority" : -1
+ }
+ }
+ },
+ "vm:103" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ },
+ "node3" : {
+ "priority" : -1
+ }
+ }
+ },
+ "vm:104" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ },
+ "node3" : {
+ "priority" : -1
+ }
+ }
+ },
+ "vm:105" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ },
+ "node3" : {
+ "priority" : -1
+ }
+ }
+ },
+ "vm:106" : {
+ "nodes" : {
+ "node1" : {
+ "priority" : 0
+ },
+ "node2" : {
+ "priority" : 2
+ }
+ }
+ }
+ },
+ "resource-affinity" : {
+ "negative" : {},
+ "positive" : {}
+ }
+}
diff --git a/src/test/test_rules_config.pl b/src/test/test_rules_config.pl
index edfcb3b7..f0792ff9 100755
--- a/src/test/test_rules_config.pl
+++ b/src/test/test_rules_config.pl
@@ -54,9 +54,12 @@ sub check_cfg {
my $cfg = PVE::HA::Rules->parse_config($cfg_fn, $raw);
PVE::HA::Rules->set_rule_defaults($_) for values %{ $cfg->{ids} };
my $messages = PVE::HA::Rules->transform($cfg, $nodes);
+ my $compiled_cfg = PVE::HA::Rules->compile($cfg, $nodes);
print $_ for @$messages;
print "--- Config ---\n";
print to_json($cfg, { canonical => 1, pretty => 1, utf8 => 1 });
+ print "--- Compiled Config ---\n";
+ print to_json($compiled_cfg, { canonical => 1, pretty => 1, utf8 => 1 });
select(STDOUT);
}
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 13/21] rules: node affinity: define node priority outside hash access
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (11 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 12/21] test: rules: add compiled config output to rules config test cases Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 14/21] move minimum version check helper to ha tools Daniel Kral
` (7 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Suggested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Rules/NodeAffinity.pm | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/PVE/HA/Rules/NodeAffinity.pm b/src/PVE/HA/Rules/NodeAffinity.pm
index adef97d0..d98f896d 100644
--- a/src/PVE/HA/Rules/NodeAffinity.pm
+++ b/src/PVE/HA/Rules/NodeAffinity.pm
@@ -276,8 +276,10 @@ sub get_node_affinity : prototype($$$) {
while (my ($node, $props) = each $node_affinity->{$sid}->{nodes}->%*) {
next if !defined($online_nodes->{$node}); # node is offline
+ my $node_priority = $props->{priority} // 0;
+
$allowed_nodes->{$node} = 1;
- $prioritized_nodes->{ $props->{priority} }->{$node} = 1;
+ $prioritized_nodes->{$node_priority}->{$node} = 1;
}
my $preferred_nodes = {};
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 14/21] move minimum version check helper to ha tools
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (12 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 13/21] rules: node affinity: define node priority outside hash access Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 15/21] manager: move group migration cooldown variable into helper Daniel Kral
` (6 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
Debloats the already quite big Manager package from an independent
helper.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Manager.pm | 30 ++----------------------------
src/PVE/HA/Tools.pm | 27 +++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 28 deletions(-)
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index e17e3209..360ea8cb 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -511,33 +511,6 @@ my $have_groups_been_migrated = sub {
return keys $groups->{ids}->%* < 1;
};
-my $get_version_parts = sub {
- my ($node_version) = @_;
-
- return $node_version =~ m/^(\d+)\.(\d+)(?:\.|-)(\d+)(?:~(\d+))?/;
-};
-
-my $has_node_min_version = sub {
- my ($node_version, $min_version) = @_;
-
- my ($major, $minor, $patch, $rev) = $get_version_parts->($node_version);
- my ($min_major, $min_minor, $min_patch, $min_rev) = $get_version_parts->($min_version);
-
- return 0 if $major < $min_major;
- return 0 if $major == $min_major && $minor < $min_minor;
- return 0 if $major == $min_major && $minor == $min_minor && $patch < $min_patch;
-
- $min_rev //= 0;
- return 0
- if $major == $min_major
- && $minor == $min_minor
- && $patch == $min_patch
- && defined($rev)
- && $rev < $min_rev;
-
- return 1;
-};
-
my $is_lrm_active_or_idle = sub {
my ($ss, $node, $lrm_state) = @_;
@@ -594,7 +567,8 @@ my $assert_cluster_can_migrate_ha_groups = sub {
$haenv->log(
'notice', "ha groups migration: node '$node' has version '$node_version'",
);
- my $has_min_version = $has_node_min_version->($node_version, $HA_RULES_MINVERSION);
+ my $has_min_version =
+ PVE::HA::Tools::has_min_version($node_version, $HA_RULES_MINVERSION);
die "node '$node' needs at least pve-manager version '$HA_RULES_MINVERSION'\n"
if !$has_min_version;
}
diff --git a/src/PVE/HA/Tools.pm b/src/PVE/HA/Tools.pm
index 71eb5d0b..47b4aa03 100644
--- a/src/PVE/HA/Tools.pm
+++ b/src/PVE/HA/Tools.pm
@@ -237,6 +237,33 @@ sub upid_wait {
PVE::ProcFSTools::upid_wait($upid, $waitfunc, 5);
}
+sub has_min_version {
+ my ($version, $min_version) = @_;
+
+ my $get_version_parts = sub {
+ my ($version_str) = @_;
+
+ return $version_str =~ m/^(\d+)\.(\d+)(?:\.|-)(\d+)(?:~(\d+))?/;
+ };
+
+ my ($major, $minor, $patch, $rev) = $get_version_parts->($version);
+ my ($min_major, $min_minor, $min_patch, $min_rev) = $get_version_parts->($min_version);
+
+ return 0 if $major < $min_major;
+ return 0 if $major == $min_major && $minor < $min_minor;
+ return 0 if $major == $min_major && $minor == $min_minor && $patch < $min_patch;
+
+ $min_rev //= 0;
+ return 0
+ if $major == $min_major
+ && $minor == $min_minor
+ && $patch == $min_patch
+ && defined($rev)
+ && $rev < $min_rev;
+
+ return 1;
+}
+
# bash auto completion helper
# NOTE: we use PVE::HA::Config here without declaring an 'use' clause above as
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 15/21] manager: move group migration cooldown variable into helper
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (13 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 14/21] move minimum version check helper to ha tools Daniel Kral
@ 2025-11-03 10:19 ` Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 16/21] api: status: sync active service counting with lrm's helper Daniel Kral
` (5 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:19 UTC (permalink / raw)
To: pve-devel
The variable is only used in the try_persistent_group_migration(...)
helper. While at it, add a comment and change the name to make its
meaning clearer.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Manager.pm | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index 360ea8cb..7c87a2c3 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -46,8 +46,6 @@ eval {
# patches for changing above, as that set is mostly sensible and should be easy to remember once
# spending a bit time in the HA code base.
-my $group_migration_cooldown = 6;
-
sub new {
my ($this, $haenv) = @_;
@@ -612,13 +610,16 @@ my $migrate_group_persistently = sub {
sub try_persistent_group_migration {
my ($self) = @_;
+ # rounds to wait until next ha group migration try
+ my $group_migration_cooldown_rounds = 6;
+
my ($haenv, $ns, $ss) = ($self->{haenv}, $self->{ns}, $self->{ss});
return if $have_groups_been_migrated->($haenv);
$self->{group_migration_round}--;
return if $self->{group_migration_round} > 0;
- $self->{group_migration_round} = $group_migration_cooldown;
+ $self->{group_migration_round} = $group_migration_cooldown_rounds;
$haenv->log('notice', "start ha group migration...");
@@ -628,8 +629,8 @@ sub try_persistent_group_migration {
$haenv->log('err', "ha groups migration failed");
$haenv->log(
'notice',
- "retry ha groups migration in $group_migration_cooldown rounds (~ "
- . $group_migration_cooldown * 10
+ "retry ha groups migration in $group_migration_cooldown_rounds rounds (~ "
+ . $group_migration_cooldown_rounds * 10
. " seconds)",
);
}
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 16/21] api: status: sync active service counting with lrm's helper
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (14 preceding siblings ...)
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 15/21] manager: move group migration cooldown variable into helper Daniel Kral
@ 2025-11-03 10:20 ` Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 17/21] manager: group migration: " Daniel Kral
` (4 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:20 UTC (permalink / raw)
To: pve-devel
There are some inconsistencies between the status API endpoint's and the
LRM's active_service_count(...) helper's counting of active services.
Sync these by:
- Counting a migrating service as active on both the source and target
node's LRM as has been introduced in commit a94a38f9 ("LRM: count
incoming migrations towards a nodes active resources").
- Not counting services as active if these are in error or request_start
state as has been introduced in commit 38545741 ("LRM: do not count
erroneous service as active") and commit 4931b586 ("manager: add new
intermediate state for stop->start transitions") respectively.
- Removing the `sort` as sorting the services is not required for
counting active services.
While at it, move the counting code in the node loop, where it will be
moved in an upcoming patch, to make it easier to see that the logic is
the same as the LRM's helper.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- none
src/PVE/API2/HA/Status.pm | 30 ++++++++++++++++--------------
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/src/PVE/API2/HA/Status.pm b/src/PVE/API2/HA/Status.pm
index 6e13c2c8..3966a4b3 100644
--- a/src/PVE/API2/HA/Status.pm
+++ b/src/PVE/API2/HA/Status.pm
@@ -193,20 +193,22 @@ __PACKAGE__->register_method({
};
}
- # compute active services for all nodes
- my $active_count = {};
- foreach my $sid (sort keys %{ $status->{service_status} }) {
- my $sd = $status->{service_status}->{$sid};
- next if !$sd->{node};
- $active_count->{ $sd->{node} } = 0 if !defined($active_count->{ $sd->{node} });
- my $req_state = $sd->{state};
- next if !defined($req_state);
- next if $req_state eq 'stopped';
- next if $req_state eq 'freeze';
- $active_count->{ $sd->{node} }++;
- }
-
foreach my $node (sort keys %{ $status->{node_status} }) {
+ my $active_count = 0;
+ for my $sid (keys $status->{service_status}->%*) {
+ my $sd = $status->{service_status}->{$sid};
+ my $target = $sd->{target};
+ next
+ if (!$sd->{node} || $sd->{node} ne $nodename)
+ && (!$target || $target ne $nodename);
+ my $req_state = $sd->{state};
+ next if !defined($req_state);
+ next if $req_state eq 'stopped';
+ next if $req_state eq 'freeze';
+ next if $req_state eq 'error';
+ next if $req_state eq 'request_start';
+ $active_count++;
+ }
my $lrm_status = PVE::HA::Config::read_lrm_status($node);
my $id = "lrm:$node";
if (!$lrm_status->{timestamp}) {
@@ -227,7 +229,7 @@ __PACKAGE__->register_method({
if ($lrm_mode ne 'active') {
$status_str = "$lrm_mode mode";
} else {
- if ($lrm_state eq 'wait_for_agent_lock' && !$active_count->{$node}) {
+ if ($lrm_state eq 'wait_for_agent_lock' && !$active_count) {
$status_str = 'idle';
} else {
$status_str = $lrm_state;
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 17/21] manager: group migration: sync active service counting with lrm's helper
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (15 preceding siblings ...)
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 16/21] api: status: sync active service counting with lrm's helper Daniel Kral
@ 2025-11-03 10:20 ` Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 18/21] factor out counting of active services into helper Daniel Kral
` (3 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:20 UTC (permalink / raw)
To: pve-devel
There are some inconsistencies between the HA group migration's
is_lrm_active_or_idle helper's and the LRM's active_service_count(...)
helper's counting of active services. Sync these by:
- Counting a migrating service as active on both the source and target
node's LRM as has been introduced in commit a94a38f9 ("LRM: count
incoming migrations towards a nodes active resources").
- Not counting services as active if these are in error or request_start
state as has been introduced in commit 38545741 ("LRM: do not count
erroneous service as active") and commit 4931b586 ("manager: add new
intermediate state for stop->start transitions") respectively.
- Removing the `sort` as sorting the services is not required for
counting active services.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- none
src/PVE/HA/Manager.pm | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index 7c87a2c3..1682e2a4 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -513,13 +513,16 @@ my $is_lrm_active_or_idle = sub {
my ($ss, $node, $lrm_state) = @_;
my $active_count = 0;
- for my $sid (sort keys %$ss) {
+ for my $sid (keys %$ss) {
my $sd = $ss->{$sid};
- next if $sd->{node} ne $node;
+ my $target = $sd->{target}; # count as active if we are the target.
+ next if (!$sd->{node} || $sd->{node} ne $node) && (!$target || $target ne $node);
my $req_state = $sd->{state};
next if !defined($req_state);
next if $req_state eq 'stopped';
next if $req_state eq 'freeze';
+ next if $req_state eq 'error';
+ next if $req_state eq 'request_start';
$active_count++;
}
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 18/21] factor out counting of active services into helper
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (16 preceding siblings ...)
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 17/21] manager: group migration: " Daniel Kral
@ 2025-11-03 10:20 ` Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 19/21] tree-wide: remove misused function prototype declaractions Daniel Kral
` (2 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:20 UTC (permalink / raw)
To: pve-devel
Make the counting its own helper to be used throughout the code base to
have a single source of truth of what constitutes a active services.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- none
src/PVE/API2/HA/Status.pm | 17 ++---------------
src/PVE/HA/LRM.pm | 20 +-------------------
src/PVE/HA/Manager.pm | 14 +-------------
src/PVE/HA/Tools.pm | 25 +++++++++++++++++++++++++
4 files changed, 29 insertions(+), 47 deletions(-)
diff --git a/src/PVE/API2/HA/Status.pm b/src/PVE/API2/HA/Status.pm
index 3966a4b3..282577cc 100644
--- a/src/PVE/API2/HA/Status.pm
+++ b/src/PVE/API2/HA/Status.pm
@@ -194,21 +194,8 @@ __PACKAGE__->register_method({
}
foreach my $node (sort keys %{ $status->{node_status} }) {
- my $active_count = 0;
- for my $sid (keys $status->{service_status}->%*) {
- my $sd = $status->{service_status}->{$sid};
- my $target = $sd->{target};
- next
- if (!$sd->{node} || $sd->{node} ne $nodename)
- && (!$target || $target ne $nodename);
- my $req_state = $sd->{state};
- next if !defined($req_state);
- next if $req_state eq 'stopped';
- next if $req_state eq 'freeze';
- next if $req_state eq 'error';
- next if $req_state eq 'request_start';
- $active_count++;
- }
+ my $active_count =
+ PVE::HA::Tools::count_active_services($status->{service_status}, $node);
my $lrm_status = PVE::HA::Config::read_lrm_status($node);
my $id = "lrm:$node";
if (!$lrm_status->{timestamp}) {
diff --git a/src/PVE/HA/LRM.pm b/src/PVE/HA/LRM.pm
index b645e5b0..1342c8eb 100644
--- a/src/PVE/HA/LRM.pm
+++ b/src/PVE/HA/LRM.pm
@@ -297,25 +297,7 @@ sub active_service_count {
my $ss = $self->{service_status};
- my $count = 0;
- foreach my $sid (keys %$ss) {
- my $sd = $ss->{$sid};
- my $target = $sd->{target}; # count as active if we are the target.
- next if (!$sd->{node} || $sd->{node} ne $nodename) && (!$target || $target ne $nodename);
- my $req_state = $sd->{state};
- next if !defined($req_state);
- next if $req_state eq 'stopped';
- # NOTE: 'ignored' ones are already dropped by the manager from service_status
- next if $req_state eq 'freeze';
- # erroneous services are not managed by HA, don't count them as active
- next if $req_state eq 'error';
- # request_start is for (optional) better node selection for stop -> started transition
- next if $req_state eq 'request_start';
-
- $count++;
- }
-
- return $count;
+ return PVE::HA::Tools::count_active_services($ss, $nodename);
}
my $wrote_lrm_status_at_startup = 0;
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index 1682e2a4..f5843dd4 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -512,19 +512,7 @@ my $have_groups_been_migrated = sub {
my $is_lrm_active_or_idle = sub {
my ($ss, $node, $lrm_state) = @_;
- my $active_count = 0;
- for my $sid (keys %$ss) {
- my $sd = $ss->{$sid};
- my $target = $sd->{target}; # count as active if we are the target.
- next if (!$sd->{node} || $sd->{node} ne $node) && (!$target || $target ne $node);
- my $req_state = $sd->{state};
- next if !defined($req_state);
- next if $req_state eq 'stopped';
- next if $req_state eq 'freeze';
- next if $req_state eq 'error';
- next if $req_state eq 'request_start';
- $active_count++;
- }
+ my $active_count = PVE::HA::Tools::count_active_services($ss, $node);
return 1 if $lrm_state eq 'active';
return 1 if $lrm_state eq 'wait_for_agent_lock' && !$active_count;
diff --git a/src/PVE/HA/Tools.pm b/src/PVE/HA/Tools.pm
index 47b4aa03..1fa53df8 100644
--- a/src/PVE/HA/Tools.pm
+++ b/src/PVE/HA/Tools.pm
@@ -188,6 +188,31 @@ sub count_fenced_services {
return $count;
}
+sub count_active_services {
+ my ($ss, $node) = @_;
+
+ my $active_count = 0;
+
+ for my $sid (keys %$ss) {
+ my $sd = $ss->{$sid};
+ my $target = $sd->{target}; # count as active if we are the target.
+ next if (!$sd->{node} || $sd->{node} ne $node) && (!$target || $target ne $node);
+ my $req_state = $sd->{state};
+ next if !defined($req_state);
+ next if $req_state eq 'stopped';
+ # NOTE: 'ignored' ones are already dropped by the manager from service_status
+ next if $req_state eq 'freeze';
+ # erroneous services are not managed by HA, don't count them as active
+ next if $req_state eq 'error';
+ # request_start is for (optional) better node selection for stop -> started transition
+ next if $req_state eq 'request_start';
+
+ $active_count++;
+ }
+
+ return $active_count;
+}
+
sub get_verbose_service_state {
my ($service_state, $service_conf) = @_;
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 19/21] tree-wide: remove misused function prototype declaractions
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (17 preceding siblings ...)
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 18/21] factor out counting of active services into helper Daniel Kral
@ 2025-11-03 10:20 ` Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 20/21] rules: fix documentation for inter-rules checker subroutines Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 21/21] rules: add documentation about current feasibility check implementations Daniel Kral
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:20 UTC (permalink / raw)
To: pve-devel
These do not fulfill any purpose and were introduced with false
assumptions about what these guarantee and how these work. Also we
recommend against their usage in our Perl Style Guide [0].
[0] https://pve.proxmox.com/wiki/Perl_Style_Guide#Prototypes
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- NEW!
src/PVE/HA/HashTools.pm | 6 +++---
src/PVE/HA/Rules.pm | 18 +++++++++---------
src/PVE/HA/Rules/NodeAffinity.pm | 2 +-
src/PVE/HA/Rules/ResourceAffinity.pm | 10 +++++-----
4 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/PVE/HA/HashTools.pm b/src/PVE/HA/HashTools.pm
index cf5c7a20..ebe47e38 100644
--- a/src/PVE/HA/HashTools.pm
+++ b/src/PVE/HA/HashTools.pm
@@ -39,7 +39,7 @@ key-value pairs are always set to C<1> or another truthy value.
=cut
-sub set_intersect : prototype($$) {
+sub set_intersect {
my ($hash1, $hash2) = @_;
my $result = { map { $hash1->{$_} && $hash2->{$_} ? ($_ => 1) : () } keys %$hash1 };
@@ -57,7 +57,7 @@ key-value pairs are always set to C<1> or another truthy value.
=cut
-sub set_union : prototype($$) {
+sub set_union {
my ($hash1, $hash2) = @_;
my $result = { map { $_ => 1 } keys %$hash1, keys %$hash2 };
@@ -77,7 +77,7 @@ Returns C<1> if they are disjoint, C<0> otherwise.
=cut
-sub sets_are_disjoint : prototype($$) {
+sub sets_are_disjoint {
my ($hash1, $hash2) = @_;
for my $key (keys %$hash1) {
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index 69c53356..1c7706ce 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -264,7 +264,7 @@ haven't been explicitly set yet.
=cut
-sub set_rule_defaults : prototype($$) {
+sub set_rule_defaults {
my ($class, $rule) = @_;
if (my $plugin = $class->lookup($rule->{type})) {
@@ -303,7 +303,7 @@ Used to register rule checks for a rule plugin.
=cut
-sub register_check : prototype($$$) {
+sub register_check {
my ($class, $check_func, $collect_errors_func) = @_;
my $type = eval { $class->type() };
@@ -322,7 +322,7 @@ Used to register rule transformers for a rule plugin.
=cut
-sub register_transform : prototype($$) {
+sub register_transform {
my ($class, $transform_func) = @_;
my $type = eval { $class->type() };
@@ -345,7 +345,7 @@ implementations.
=cut
-sub get_plugin_check_arguments : prototype($$) {
+sub get_plugin_check_arguments {
my ($class, $rules) = @_;
return {};
@@ -361,7 +361,7 @@ creation of these can be shared inbetween rule check implementations.
=cut
-sub get_check_arguments : prototype($$) {
+sub get_check_arguments {
my ($class, $rules) = @_;
my $global_args = {};
@@ -391,7 +391,7 @@ very last.
=cut
-sub check_feasibility : prototype($$$) {
+sub check_feasibility {
my ($class, $rules, $nodes) = @_;
my $global_errors = {};
@@ -429,7 +429,7 @@ Returns a list of messages with the reasons why rules were removed.
=cut
-sub transform : prototype($$$) {
+sub transform {
my ($class, $rules, $nodes) = @_;
my $messages = [];
@@ -540,7 +540,7 @@ The following key-value pairs for C<$opts> are:
=cut
-sub foreach_rule : prototype($$;%) {
+sub foreach_rule {
my ($rules, $func, %opts) = @_;
my $sid = $opts{sid};
@@ -568,7 +568,7 @@ be used a newly introduced rule afterwards.
=cut
-sub get_next_ordinal : prototype($) {
+sub get_next_ordinal {
my ($rules) = @_;
my $current_order = (sort { $a <=> $b } values %{ $rules->{order} })[0] || 0;
diff --git a/src/PVE/HA/Rules/NodeAffinity.pm b/src/PVE/HA/Rules/NodeAffinity.pm
index d98f896d..3fa1fdb4 100644
--- a/src/PVE/HA/Rules/NodeAffinity.pm
+++ b/src/PVE/HA/Rules/NodeAffinity.pm
@@ -265,7 +265,7 @@ If there are no available nodes at all, returns C<undef>.
=cut
-sub get_node_affinity : prototype($$$) {
+sub get_node_affinity {
my ($node_affinity, $sid, $online_nodes) = @_;
return ($online_nodes, $online_nodes) if !defined($node_affinity->{$sid});
diff --git a/src/PVE/HA/Rules/ResourceAffinity.pm b/src/PVE/HA/Rules/ResourceAffinity.pm
index c44d4f0b..4f5ffca5 100644
--- a/src/PVE/HA/Rules/ResourceAffinity.pm
+++ b/src/PVE/HA/Rules/ResourceAffinity.pm
@@ -458,7 +458,7 @@ affinitive to C<'ct:200'> and C<'ct:201'>, the returned value will be:
=cut
-sub get_affinitive_resources : prototype($$) {
+sub get_affinitive_resources {
my ($resource_affinity, $sid) = @_;
my $together = $resource_affinity->{positive}->{$sid} // {};
@@ -500,7 +500,7 @@ resource C<$sid> is in a negative affinity with, the returned value will be:
=cut
-sub get_resource_affinity : prototype($$$$) {
+sub get_resource_affinity {
my ($resource_affinity, $sid, $ss, $online_nodes) = @_;
my $together = {};
@@ -539,7 +539,7 @@ node C<$node> must be avoided.
=cut
-sub is_allowed_on_node : prototype($$$) {
+sub is_allowed_on_node {
my ($together, $separate, $node) = @_;
return $together->{$node} || !$separate->{$node};
@@ -560,7 +560,7 @@ resource has not failed running there yet.
=cut
-sub apply_positive_resource_affinity : prototype($$) {
+sub apply_positive_resource_affinity {
my ($together, $allowed_nodes) = @_;
for my $node (keys %$together) {
@@ -594,7 +594,7 @@ resource has not failed running there yet.
=cut
-sub apply_negative_resource_affinity : prototype($$) {
+sub apply_negative_resource_affinity {
my ($separate, $allowed_nodes) = @_;
my $forbidden_nodes = { $separate->%* };
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 20/21] rules: fix documentation for inter-rules checker subroutines
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (18 preceding siblings ...)
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 19/21] tree-wide: remove misused function prototype declaractions Daniel Kral
@ 2025-11-03 10:20 ` Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 21/21] rules: add documentation about current feasibility check implementations Daniel Kral
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:20 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- NEW!
src/PVE/HA/Rules.pm | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index 1c7706ce..9d7f12a1 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -594,9 +594,9 @@ my $has_multiple_priorities = sub {
=head3 check_single_priority_node_affinity_in_resource_affinity_rules(...)
Returns all rules in C<$resource_affinity_rules> and C<$node_affinity_rules> as
-a list of lists, each consisting of the rule type and resource id, where at
-least one resource in a resource affinity rule are in node affinity rules,
-which have multiple priority groups defined.
+a list of lists, each consisting of the rule type and rule id, where at
+least one resource in a resource affinity rule is in a node affinity rule,
+which has multiple priority groups defined.
That is, the resource affinity rule cannot be statically checked to be feasible
as the selection of the priority group is dependent on the currently online
@@ -666,9 +666,9 @@ __PACKAGE__->register_check(
=head3 check_single_node_affinity_per_positive_resource_affinity_rule(...)
Returns all rules in C<$positive_rules> and C<$node_affinity_rules> as a list of
-lists, each consisting of the rule type and resource id, where one of the
-resources is used in a positive resource affinity rule and more than one node
-affinity rule.
+lists, each item consisting of the rule type and rule id, where at least one of
+the resources is used in a positive resource affinity rule and more than one
+node affinity rule.
If there are none, the returned list is empty.
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH ha-manager v3 21/21] rules: add documentation about current feasibility check implementations
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
` (19 preceding siblings ...)
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 20/21] rules: fix documentation for inter-rules checker subroutines Daniel Kral
@ 2025-11-03 10:20 ` Daniel Kral
20 siblings, 0 replies; 22+ messages in thread
From: Daniel Kral @ 2025-11-03 10:20 UTC (permalink / raw)
To: pve-devel
These notes should clarify the goals and reasons for the feasibility
checks while giving direction how these can be improved in the future.
Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
changes since v2:
- NEW!
src/PVE/HA/Rules.pm | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/PVE/HA/Rules.pm b/src/PVE/HA/Rules.pm
index 9d7f12a1..c4a2ccea 100644
--- a/src/PVE/HA/Rules.pm
+++ b/src/PVE/HA/Rules.pm
@@ -32,6 +32,13 @@ the feasibility between rules of the same type and and between rules of
different types, and prune the rule set in such a way, that it becomes feasible
again, while minimizing the amount of rules that need to be pruned.
+The feasibility checks must verify that all rules can be applied error-free in
+any runtime state of the HA resources, cluster nodes, and the HA Manager in a
+reasonable amount of time. This might require more rules to be rejected than
+necessary as some checks can become computationally expensive for little benefit
+to the core feature, but this can certainly be reevaluated if there are user
+requests for these cases.
+
More so, the rules given by the config file might not be in the best format to
be used internally or does not contain the implicitly stated rules, which are
induced by the relationship between different rules. Therefore, this package
@@ -598,9 +605,11 @@ a list of lists, each consisting of the rule type and rule id, where at
least one resource in a resource affinity rule is in a node affinity rule,
which has multiple priority groups defined.
-That is, the resource affinity rule cannot be statically checked to be feasible
-as the selection of the priority group is dependent on the currently online
-nodes.
+These cases are currently rejected, because the selection of the highest
+priority class is dependent on the currently online nodes and on the current
+HA resource states and node placements in the case of negative resource affinity
+rules. This makes the verification of these rules' feasibility in all possible
+states computationally hard.
If there are none, the returned list is empty.
@@ -670,6 +679,10 @@ lists, each item consisting of the rule type and rule id, where at least one of
the resources is used in a positive resource affinity rule and more than one
node affinity rule.
+These cases are currently rejected, because there is no definitive method to
+merge multiple node affinity rules, e.g. with different strictness values and
+different priority classes, yet.
+
If there are none, the returned list is empty.
=cut
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2025-11-03 10:23 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-03 10:19 [pve-devel] [PATCH ha-manager v3 00/21] HA rules fixes + performance improvements + cleanup Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 01/21] config: do not add ignored resources to dependent resources Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 02/21] manager: retranslate rules if nodes are added or removed Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 03/21] rules: factor out disjoint rules' resource set helper Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 04/21] rules: resource affinity: inter-consistency check with merged positive rules Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 05/21] rules: add merged positive resource affinity info in global checks Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 06/21] rules: make rules sorting optional in foreach_rule helper Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 07/21] rename rule's canonicalize stage to transform stage Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 08/21] rules: make plugins register transformers instead of plugin_transform Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 09/21] rules: node affinity: decouple get_node_affinity helper from Usage class Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 10/21] compile ha rules to a more compact representation Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 11/21] test: rules: use to_json instead of Data::Dumper for config output Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 12/21] test: rules: add compiled config output to rules config test cases Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 13/21] rules: node affinity: define node priority outside hash access Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 14/21] move minimum version check helper to ha tools Daniel Kral
2025-11-03 10:19 ` [pve-devel] [PATCH ha-manager v3 15/21] manager: move group migration cooldown variable into helper Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 16/21] api: status: sync active service counting with lrm's helper Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 17/21] manager: group migration: " Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 18/21] factor out counting of active services into helper Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 19/21] tree-wide: remove misused function prototype declaractions Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 20/21] rules: fix documentation for inter-rules checker subroutines Daniel Kral
2025-11-03 10:20 ` [pve-devel] [PATCH ha-manager v3 21/21] rules: add documentation about current feasibility check implementations Daniel Kral
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox