[pve-devel] [PATCH ha-manager v3 12/15] test: add test cases for rules config

Daniel Kral d.kral at proxmox.com
Fri Jul 4 20:16:52 CEST 2025


Add test cases to verify that the rule checkers correctly identify and
remove HA rules from the rules to make the rule set feasible. For now,
there only are HA Node Affinity rules, which verify:

- Node Affinity rules retrieve the correct optional default values
- Node Affinity rules, which specify the same HA resource more than
  once, are dropped from the rule set

Signed-off-by: Daniel Kral <d.kral at proxmox.com>
---
 .gitignore                                    |   1 +
 src/test/Makefile                             |   4 +-
 .../defaults-for-node-affinity-rules.cfg      |  22 ++++
 ...efaults-for-node-affinity-rules.cfg.expect |  60 +++++++++++
 ...e-resource-refs-in-node-affinity-rules.cfg |  31 ++++++
 ...rce-refs-in-node-affinity-rules.cfg.expect |  63 +++++++++++
 src/test/test_rules_config.pl                 | 100 ++++++++++++++++++
 7 files changed, 280 insertions(+), 1 deletion(-)
 create mode 100644 src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg
 create mode 100644 src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
 create mode 100644 src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg
 create mode 100644 src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
 create mode 100755 src/test/test_rules_config.pl

diff --git a/.gitignore b/.gitignore
index c35280e..35de63f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
 /src/test/test-*/status/*
 /src/test/fence_cfgs/*.cfg.commands
 /src/test/fence_cfgs/*.cfg.write
+/src/test/rules_cfgs/*.cfg.output
diff --git a/src/test/Makefile b/src/test/Makefile
index e54959f..6da9e10 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -5,6 +5,7 @@ all:
 test:
 	@echo "-- start regression tests --"
 	./test_failover1.pl
+	./test_rules_config.pl
 	./ha-tester.pl
 	./test_fence_config.pl
 	@echo "-- end regression tests (success) --"
@@ -12,4 +13,5 @@ test:
 .PHONY: clean
 clean:
 	rm -rf *~ test-*/log  test-*/*~ test-*/status \
-	fence_cfgs/*.cfg.commands fence_cfgs/*.write
+	fence_cfgs/*.cfg.commands fence_cfgs/*.write \
+	rules_cfgs/*.cfg.output
diff --git a/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg b/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg
new file mode 100644
index 0000000..c8b2f2d
--- /dev/null
+++ b/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg
@@ -0,0 +1,22 @@
+# Case 1: Node Affinity rules are enabled and loose by default, so set it so if it isn't yet.
+node-affinity: node-affinity-defaults
+	resources vm:101
+	nodes node1
+
+# Case 2: Node Affinity rule is disabled, it shouldn't be enabled afterwards.
+node-affinity: node-affinity-disabled
+	resources vm:102
+	nodes node2
+	disable
+
+# Case 3: Node Affinity rule is disabled with explicit 1 set, it shouldn't be enabled afterwards.
+node-affinity: node-affinity-disabled-explicit
+	resources vm:103
+	nodes node2
+	disable 1
+
+# Case 4: Node Affinity rule is set to strict, so it shouldn't be loose afterwards.
+node-affinity: node-affinity-strict
+	resources vm:104
+	nodes node3
+	strict 1
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
new file mode 100644
index 0000000..59a2c36
--- /dev/null
+++ b/src/test/rules_cfgs/defaults-for-node-affinity-rules.cfg.expect
@@ -0,0 +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
+                     }
+        };
diff --git a/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg b/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg
new file mode 100644
index 0000000..1e279e7
--- /dev/null
+++ b/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg
@@ -0,0 +1,31 @@
+# Case 1: Do not remove two Node Affinity rules, which do not share resources.
+node-affinity: no-same-resource1
+	resources vm:101,vm:102,vm:103
+	nodes node1,node2:2
+	strict 0
+
+node-affinity: no-same-resource2
+	resources vm:104,vm:105
+	nodes node1,node2:2
+	strict 0
+
+node-affinity: no-same-resource3
+	resources vm:106
+	nodes node1,node2:2
+	strict 1
+
+# Case 2: Remove Node Affinity rules, which share the same resource between them.
+node-affinity: same-resource1
+	resources vm:201
+	nodes node1,node2:2
+	strict 0
+
+node-affinity: same-resource2
+	resources vm:201,vm:202
+	nodes node3
+	strict 1
+
+node-affinity: same-resource3
+	resources vm:201,vm:203,vm:204
+	nodes node1:2,node3:3
+	strict 0
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
new file mode 100644
index 0000000..3fd0c9c
--- /dev/null
+++ b/src/test/rules_cfgs/multiple-resource-refs-in-node-affinity-rules.cfg.expect
@@ -0,0 +1,63 @@
+--- Log ---
+Drop rule 'same-resource1', because resource 'vm:201' is already used in another node affinity rule.
+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
+                     }
+        };
diff --git a/src/test/test_rules_config.pl b/src/test/test_rules_config.pl
new file mode 100755
index 0000000..824afed
--- /dev/null
+++ b/src/test/test_rules_config.pl
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+use lib qw(..);
+
+use Test::More;
+use Test::MockModule;
+
+use Data::Dumper;
+
+use PVE::HA::Rules;
+use PVE::HA::Rules::NodeAffinity;
+
+PVE::HA::Rules::NodeAffinity->register();
+
+PVE::HA::Rules->init(property_isolation => 1);
+
+my $opt_nodiff;
+
+if (!GetOptions("nodiff" => \$opt_nodiff)) {
+    print "usage: $0 [test.cfg] [--nodiff]\n";
+    exit -1;
+}
+
+sub _log {
+    my ($fh, $source, $message) = @_;
+
+    chomp $message;
+    $message = "[$source] $message" if $source;
+
+    print "$message\n";
+
+    $fh->print("$message\n");
+    $fh->flush();
+}
+
+sub check_cfg {
+    my ($cfg_fn, $outfile) = @_;
+
+    my $raw = PVE::Tools::file_get_contents($cfg_fn);
+
+    open(my $LOG, '>', "$outfile");
+    select($LOG);
+    $| = 1;
+
+    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);
+    print $_ for @$messages;
+    print "--- Config ---\n";
+    {
+        local $Data::Dumper::Sortkeys = 1;
+        print Dumper($cfg);
+    }
+
+    select(STDOUT);
+}
+
+sub run_test {
+    my ($cfg_fn) = @_;
+
+    print "* check: $cfg_fn\n";
+
+    my $outfile = "$cfg_fn.output";
+    my $expect = "$cfg_fn.expect";
+
+    eval { check_cfg($cfg_fn, $outfile); };
+    if (my $err = $@) {
+        die "Test '$cfg_fn' failed:\n$err\n";
+    }
+
+    return if $opt_nodiff;
+
+    my $res;
+
+    if (-f $expect) {
+        my $cmd = ['diff', '-u', $expect, $outfile];
+        $res = system(@$cmd);
+        die "test '$cfg_fn' failed\n" if $res != 0;
+    } else {
+        $res = system('cp', $outfile, $expect);
+        die "test '$cfg_fn' failed\n" if $res != 0;
+    }
+
+    print "* end rules test: $cfg_fn (success)\n\n";
+}
+
+# exec tests
+
+if (my $testcfg = shift) {
+    run_test($testcfg);
+} else {
+    for my $cfg (<rules_cfgs/*cfg>) {
+        run_test($cfg);
+    }
+}
-- 
2.39.5





More information about the pve-devel mailing list