[pve-devel] [PATCH ha-manager v2 20/26] test: add test cases for rules config

Daniel Kral d.kral at proxmox.com
Fri Jun 20 16:31:32 CEST 2025


Add test cases to verify that the rule checkers correctly identify and
remove ill-defined location and colocation rules from the rules:

- Set defaults when reading location and colocation rules
- Dropping location rules, which specify the same service multiple times
- Dropping colocation rules, which state that two or more services are
  to be kept together and separate at the same time
- Dropping colocation rules, which cannot be fullfilled because of the
  constraints of the location rules of their services
- Dropping colocation rules, which specify less than two services

Signed-off-by: Daniel Kral <d.kral at proxmox.com>
---
changes since v1:
    - dropped connected positive colocation rules check
    - rename illdefined-colocations to ineffective-colocation-rules
    - rename inner-inconsistent-colocations to
      inconsistent-colocation-rules
    - introduced rule set tests for default values
    - introduced rule set tests for duplicate service reference in
      location rules
    - introduced rule set tests for multi-priority location with
      colocation rules


 .gitignore                                    |   1 +
 src/test/Makefile                             |   4 +-
 .../defaults-for-colocation-rules.cfg         |  10 ++
 .../defaults-for-colocation-rules.cfg.expect  |  29 ++++
 .../defaults-for-location-rules.cfg           |  16 +++
 .../defaults-for-location-rules.cfg.expect    |  49 +++++++
 .../duplicate-service-in-location-rules.cfg   |  31 +++++
 ...icate-service-in-location-rules.cfg.expect |  66 +++++++++
 .../inconsistent-colocation-rules.cfg         |  11 ++
 .../inconsistent-colocation-rules.cfg.expect  |  11 ++
 ...inconsistent-location-colocation-rules.cfg |  54 ++++++++
 ...stent-location-colocation-rules.cfg.expect | 130 ++++++++++++++++++
 .../ineffective-colocation-rules.cfg          |   7 +
 .../ineffective-colocation-rules.cfg.expect   |   9 ++
 ...ulti-priority-location-with-colocation.cfg |  19 +++
 ...iority-location-with-colocation.cfg.expect |  47 +++++++
 src/test/test_rules_config.pl                 | 102 ++++++++++++++
 17 files changed, 595 insertions(+), 1 deletion(-)
 create mode 100644 src/test/rules_cfgs/defaults-for-colocation-rules.cfg
 create mode 100644 src/test/rules_cfgs/defaults-for-colocation-rules.cfg.expect
 create mode 100644 src/test/rules_cfgs/defaults-for-location-rules.cfg
 create mode 100644 src/test/rules_cfgs/defaults-for-location-rules.cfg.expect
 create mode 100644 src/test/rules_cfgs/duplicate-service-in-location-rules.cfg
 create mode 100644 src/test/rules_cfgs/duplicate-service-in-location-rules.cfg.expect
 create mode 100644 src/test/rules_cfgs/inconsistent-colocation-rules.cfg
 create mode 100644 src/test/rules_cfgs/inconsistent-colocation-rules.cfg.expect
 create mode 100644 src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg
 create mode 100644 src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg.expect
 create mode 100644 src/test/rules_cfgs/ineffective-colocation-rules.cfg
 create mode 100644 src/test/rules_cfgs/ineffective-colocation-rules.cfg.expect
 create mode 100644 src/test/rules_cfgs/multi-priority-location-with-colocation.cfg
 create mode 100644 src/test/rules_cfgs/multi-priority-location-with-colocation.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-colocation-rules.cfg b/src/test/rules_cfgs/defaults-for-colocation-rules.cfg
new file mode 100644
index 0000000..8e68030
--- /dev/null
+++ b/src/test/rules_cfgs/defaults-for-colocation-rules.cfg
@@ -0,0 +1,10 @@
+# Case 1: Colocation rules are enabled by default, so set it so if it isn't yet.
+colocation: colocation-defaults
+	services vm:101,vm:102
+	affinity separate
+
+# Case 2: Colocation rule is disabled, it shouldn't be enabled afterwards.
+colocation: colocation-disabled
+	services vm:201,vm:202
+	affinity separate
+	state disabled
diff --git a/src/test/rules_cfgs/defaults-for-colocation-rules.cfg.expect b/src/test/rules_cfgs/defaults-for-colocation-rules.cfg.expect
new file mode 100644
index 0000000..faafec8
--- /dev/null
+++ b/src/test/rules_cfgs/defaults-for-colocation-rules.cfg.expect
@@ -0,0 +1,29 @@
+--- Log ---
+--- Config ---
+$VAR1 = {
+          'digest' => '692081df1aaf03092f67cd415f5c66222b1d55e2',
+          'ids' => {
+                     'colocation-defaults' => {
+                                                'affinity' => 'separate',
+                                                'services' => {
+                                                                'vm:101' => 1,
+                                                                'vm:102' => 1
+                                                              },
+                                                'state' => 'enabled',
+                                                'type' => 'colocation'
+                                              },
+                     'colocation-disabled' => {
+                                                'affinity' => 'separate',
+                                                'services' => {
+                                                                'vm:201' => 1,
+                                                                'vm:202' => 1
+                                                              },
+                                                'state' => 'disabled',
+                                                'type' => 'colocation'
+                                              }
+                   },
+          'order' => {
+                       'colocation-defaults' => 1,
+                       'colocation-disabled' => 2
+                     }
+        };
diff --git a/src/test/rules_cfgs/defaults-for-location-rules.cfg b/src/test/rules_cfgs/defaults-for-location-rules.cfg
new file mode 100644
index 0000000..728558d
--- /dev/null
+++ b/src/test/rules_cfgs/defaults-for-location-rules.cfg
@@ -0,0 +1,16 @@
+# Case 1: Location rules are enabled and loose by default, so set it so if it isn't yet.
+location: location-defaults
+	services vm:101
+	nodes node1
+
+# Case 2: Location rule is disabled, it shouldn't be enabled afterwards.
+location: location-disabled
+	services vm:102
+	nodes node2
+	state disabled
+
+# Case 3: Location rule is set to strict, so it shouldn't be loose afterwards.
+location: location-strict
+	services vm:103
+	nodes node3
+	strict 1
diff --git a/src/test/rules_cfgs/defaults-for-location-rules.cfg.expect b/src/test/rules_cfgs/defaults-for-location-rules.cfg.expect
new file mode 100644
index 0000000..d534deb
--- /dev/null
+++ b/src/test/rules_cfgs/defaults-for-location-rules.cfg.expect
@@ -0,0 +1,49 @@
+--- Log ---
+--- Config ---
+$VAR1 = {
+          'digest' => '27c1704d1a5497b314f8f5717b633184c1a1aacf',
+          'ids' => {
+                     'location-defaults' => {
+                                              'nodes' => {
+                                                           'node1' => {
+                                                                        'priority' => 0
+                                                                      }
+                                                         },
+                                              'services' => {
+                                                              'vm:101' => 1
+                                                            },
+                                              'state' => 'enabled',
+                                              'type' => 'location'
+                                            },
+                     'location-disabled' => {
+                                              'nodes' => {
+                                                           'node2' => {
+                                                                        'priority' => 0
+                                                                      }
+                                                         },
+                                              'services' => {
+                                                              'vm:102' => 1
+                                                            },
+                                              'state' => 'disabled',
+                                              'type' => 'location'
+                                            },
+                     'location-strict' => {
+                                            'nodes' => {
+                                                         'node3' => {
+                                                                      'priority' => 0
+                                                                    }
+                                                       },
+                                            'services' => {
+                                                            'vm:103' => 1
+                                                          },
+                                            'state' => 'enabled',
+                                            'strict' => 1,
+                                            'type' => 'location'
+                                          }
+                   },
+          'order' => {
+                       'location-defaults' => 1,
+                       'location-disabled' => 2,
+                       'location-strict' => 3
+                     }
+        };
diff --git a/src/test/rules_cfgs/duplicate-service-in-location-rules.cfg b/src/test/rules_cfgs/duplicate-service-in-location-rules.cfg
new file mode 100644
index 0000000..7409bd7
--- /dev/null
+++ b/src/test/rules_cfgs/duplicate-service-in-location-rules.cfg
@@ -0,0 +1,31 @@
+# Case 1: Do not remove two location rules, which do not share services.
+location: no-same-service1
+	services vm:101,vm:102,vm:103
+	nodes node1,node2:2
+	strict 0
+
+location: no-same-service2
+	services vm:104,vm:105
+	nodes node1,node2:2
+	strict 0
+
+location: no-same-service3
+	services vm:106
+	nodes node1,node2:2
+	strict 1
+
+# Case 2: Remove location rules, which share the same service between them.
+location: same-service1
+	services vm:201
+	nodes node1,node2:2
+	strict 0
+
+location: same-service2
+	services vm:201,vm:202
+	nodes node3
+	strict 1
+
+location: same-service3
+	services vm:201,vm:203,vm:204
+	nodes node1:2,node3:3
+	strict 0
diff --git a/src/test/rules_cfgs/duplicate-service-in-location-rules.cfg.expect b/src/test/rules_cfgs/duplicate-service-in-location-rules.cfg.expect
new file mode 100644
index 0000000..39b95bd
--- /dev/null
+++ b/src/test/rules_cfgs/duplicate-service-in-location-rules.cfg.expect
@@ -0,0 +1,66 @@
+--- Log ---
+Drop rule 'same-service1', because service 'vm:201' is already used in another location rule.
+Drop rule 'same-service2', because service 'vm:201' is already used in another location rule.
+Drop rule 'same-service3', because service 'vm:201' is already used in another location rule.
+--- Config ---
+$VAR1 = {
+          'digest' => '07ee7a87159672339144fb9d1021b54905e09632',
+          'ids' => {
+                     'no-same-service1' => {
+                                             'nodes' => {
+                                                          'node1' => {
+                                                                       'priority' => 0
+                                                                     },
+                                                          'node2' => {
+                                                                       'priority' => 2
+                                                                     }
+                                                        },
+                                             'services' => {
+                                                             'vm:101' => 1,
+                                                             'vm:102' => 1,
+                                                             'vm:103' => 1
+                                                           },
+                                             'state' => 'enabled',
+                                             'strict' => 0,
+                                             'type' => 'location'
+                                           },
+                     'no-same-service2' => {
+                                             'nodes' => {
+                                                          'node1' => {
+                                                                       'priority' => 0
+                                                                     },
+                                                          'node2' => {
+                                                                       'priority' => 2
+                                                                     }
+                                                        },
+                                             'services' => {
+                                                             'vm:104' => 1,
+                                                             'vm:105' => 1
+                                                           },
+                                             'state' => 'enabled',
+                                             'strict' => 0,
+                                             'type' => 'location'
+                                           },
+                     'no-same-service3' => {
+                                             'nodes' => {
+                                                          'node1' => {
+                                                                       'priority' => 0
+                                                                     },
+                                                          'node2' => {
+                                                                       'priority' => 2
+                                                                     }
+                                                        },
+                                             'services' => {
+                                                             'vm:106' => 1
+                                                           },
+                                             'state' => 'enabled',
+                                             'strict' => 1,
+                                             'type' => 'location'
+                                           }
+                   },
+          'order' => {
+                       'no-same-service1' => 1,
+                       'no-same-service2' => 2,
+                       'no-same-service3' => 3
+                     }
+        };
diff --git a/src/test/rules_cfgs/inconsistent-colocation-rules.cfg b/src/test/rules_cfgs/inconsistent-colocation-rules.cfg
new file mode 100644
index 0000000..3199bfb
--- /dev/null
+++ b/src/test/rules_cfgs/inconsistent-colocation-rules.cfg
@@ -0,0 +1,11 @@
+colocation: keep-apart1
+	services vm:102,vm:103
+	affinity separate
+
+colocation: keep-apart2
+	services vm:102,vm:104,vm:106
+	affinity separate
+
+colocation: stick-together1
+	services vm:101,vm:102,vm:103,vm:104,vm:106
+	affinity together
diff --git a/src/test/rules_cfgs/inconsistent-colocation-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-colocation-rules.cfg.expect
new file mode 100644
index 0000000..b1989a8
--- /dev/null
+++ b/src/test/rules_cfgs/inconsistent-colocation-rules.cfg.expect
@@ -0,0 +1,11 @@
+--- Log ---
+Drop rule 'keep-apart1', because rule shares two or more services with 'stick-together1'.
+Drop rule 'keep-apart2', because rule shares two or more services with 'stick-together1'.
+Drop rule 'stick-together1', because rule shares two or more services with 'keep-apart1'.
+Drop rule 'stick-together1', because rule shares two or more services with 'keep-apart2'.
+--- Config ---
+$VAR1 = {
+          'digest' => '469bc45c05ffbb123a277fc0fda48e0132fc9046',
+          'ids' => {},
+          'order' => {}
+        };
diff --git a/src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg b/src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg
new file mode 100644
index 0000000..ed6b82d
--- /dev/null
+++ b/src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg
@@ -0,0 +1,54 @@
+# Case 1: Remove no positive colocation rule, where there is exactly one node to keep them together.
+location: vm101-vm102-must-be-on-node1
+	services vm:101,vm:102
+	nodes node1
+	strict 1
+
+colocation: vm101-vm102-must-be-kept-together
+	services vm:101,vm:102
+	affinity together
+
+# Case 2: Remove no negative colocation rule, where there are exactly enough nodes available to keep them apart.
+location: vm201-must-be-on-node1
+	services vm:201
+	nodes node1
+	strict 1
+
+location: vm202-must-be-on-node2
+	services vm:202
+	nodes node2
+	strict 1
+
+colocation: vm201-vm202-must-be-kept-separate
+	services vm:201,vm:202
+	affinity separate
+
+# Case 1: Remove the positive colocation rules, where two services are restricted to a different node.
+location: vm301-must-be-on-node1
+	services vm:301
+	nodes node1
+	strict 1
+
+location: vm301-must-be-on-node2
+	services vm:302
+	nodes node2
+	strict 1
+
+colocation: vm301-vm302-must-be-kept-together
+	services vm:301,vm:302
+	affinity together
+
+# Case 2: Remove the negative colocation rule, where two services are restricted to less nodes than needed to keep them apart.
+location: vm401-must-be-on-node1
+	services vm:401
+	nodes node1
+	strict 1
+
+location: vm402-must-be-on-node1
+	services vm:402
+	nodes node1
+	strict 1
+
+colocation: vm401-vm402-must-be-kept-separate
+	services vm:401,vm:402
+	affinity separate
diff --git a/src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg.expect b/src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg.expect
new file mode 100644
index 0000000..bed4668
--- /dev/null
+++ b/src/test/rules_cfgs/inconsistent-location-colocation-rules.cfg.expect
@@ -0,0 +1,130 @@
+--- Log ---
+Drop rule 'vm301-vm302-must-be-kept-together', because two or more services are restricted to different nodes.
+Drop rule 'vm401-vm402-must-be-kept-separate', because two or more services are restricted to less nodes than available to the services.
+--- Config ---
+$VAR1 = {
+          'digest' => '27e76e13c20a1d11c5cbcf14434bcd54655fdd40',
+          'ids' => {
+                     'vm101-vm102-must-be-kept-together' => {
+                                                              'affinity' => 'together',
+                                                              'services' => {
+                                                                              'vm:101' => 1,
+                                                                              'vm:102' => 1
+                                                                            },
+                                                              'state' => 'enabled',
+                                                              'type' => 'colocation'
+                                                            },
+                     'vm101-vm102-must-be-on-node1' => {
+                                                         'nodes' => {
+                                                                      'node1' => {
+                                                                                   'priority' => 0
+                                                                                 }
+                                                                    },
+                                                         'services' => {
+                                                                         'vm:101' => 1,
+                                                                         'vm:102' => 1
+                                                                       },
+                                                         'state' => 'enabled',
+                                                         'strict' => 1,
+                                                         'type' => 'location'
+                                                       },
+                     'vm201-must-be-on-node1' => {
+                                                   'nodes' => {
+                                                                'node1' => {
+                                                                             'priority' => 0
+                                                                           }
+                                                              },
+                                                   'services' => {
+                                                                   'vm:201' => 1
+                                                                 },
+                                                   'state' => 'enabled',
+                                                   'strict' => 1,
+                                                   'type' => 'location'
+                                                 },
+                     'vm201-vm202-must-be-kept-separate' => {
+                                                              'affinity' => 'separate',
+                                                              'services' => {
+                                                                              'vm:201' => 1,
+                                                                              'vm:202' => 1
+                                                                            },
+                                                              'state' => 'enabled',
+                                                              'type' => 'colocation'
+                                                            },
+                     'vm202-must-be-on-node2' => {
+                                                   'nodes' => {
+                                                                'node2' => {
+                                                                             'priority' => 0
+                                                                           }
+                                                              },
+                                                   'services' => {
+                                                                   'vm:202' => 1
+                                                                 },
+                                                   'state' => 'enabled',
+                                                   'strict' => 1,
+                                                   'type' => 'location'
+                                                 },
+                     'vm301-must-be-on-node1' => {
+                                                   'nodes' => {
+                                                                'node1' => {
+                                                                             'priority' => 0
+                                                                           }
+                                                              },
+                                                   'services' => {
+                                                                   'vm:301' => 1
+                                                                 },
+                                                   'state' => 'enabled',
+                                                   'strict' => 1,
+                                                   'type' => 'location'
+                                                 },
+                     'vm301-must-be-on-node2' => {
+                                                   'nodes' => {
+                                                                'node2' => {
+                                                                             'priority' => 0
+                                                                           }
+                                                              },
+                                                   'services' => {
+                                                                   'vm:302' => 1
+                                                                 },
+                                                   'state' => 'enabled',
+                                                   'strict' => 1,
+                                                   'type' => 'location'
+                                                 },
+                     'vm401-must-be-on-node1' => {
+                                                   'nodes' => {
+                                                                'node1' => {
+                                                                             'priority' => 0
+                                                                           }
+                                                              },
+                                                   'services' => {
+                                                                   'vm:401' => 1
+                                                                 },
+                                                   'state' => 'enabled',
+                                                   'strict' => 1,
+                                                   'type' => 'location'
+                                                 },
+                     'vm402-must-be-on-node1' => {
+                                                   'nodes' => {
+                                                                'node1' => {
+                                                                             'priority' => 0
+                                                                           }
+                                                              },
+                                                   'services' => {
+                                                                   'vm:402' => 1
+                                                                 },
+                                                   'state' => 'enabled',
+                                                   'strict' => 1,
+                                                   'type' => 'location'
+                                                 }
+                   },
+          '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,
+                       'vm301-must-be-on-node1' => 6,
+                       'vm301-must-be-on-node2' => 7,
+                       'vm401-must-be-on-node1' => 9,
+                       'vm402-must-be-on-node1' => 10
+                     }
+        };
diff --git a/src/test/rules_cfgs/ineffective-colocation-rules.cfg b/src/test/rules_cfgs/ineffective-colocation-rules.cfg
new file mode 100644
index 0000000..4f2338e
--- /dev/null
+++ b/src/test/rules_cfgs/ineffective-colocation-rules.cfg
@@ -0,0 +1,7 @@
+colocation: lonely-service1
+	services vm:101
+	affinity together
+
+colocation: lonely-service2
+	services vm:101
+	affinity separate
diff --git a/src/test/rules_cfgs/ineffective-colocation-rules.cfg.expect b/src/test/rules_cfgs/ineffective-colocation-rules.cfg.expect
new file mode 100644
index 0000000..3741ba7
--- /dev/null
+++ b/src/test/rules_cfgs/ineffective-colocation-rules.cfg.expect
@@ -0,0 +1,9 @@
+--- Log ---
+Drop rule 'lonely-service1', because rule is ineffective as there are less than two services.
+Drop rule 'lonely-service2', because rule is ineffective as there are less than two services.
+--- Config ---
+$VAR1 = {
+          'digest' => '47bdd78898e4193aa113ab05b0fd7aaaeb08109d',
+          'ids' => {},
+          'order' => {}
+        };
diff --git a/src/test/rules_cfgs/multi-priority-location-with-colocation.cfg b/src/test/rules_cfgs/multi-priority-location-with-colocation.cfg
new file mode 100644
index 0000000..889fe47
--- /dev/null
+++ b/src/test/rules_cfgs/multi-priority-location-with-colocation.cfg
@@ -0,0 +1,19 @@
+# Case 1: Remove colocation rules, where there is a loose location rule with multiple priority groups set for the nodes.
+location: vm101-vm102-should-be-on-node1-or-node2
+	services vm:101,vm:102
+	nodes node1:1,node2:2
+	strict 0
+
+colocation: vm101-vm102-must-be-kept-separate
+	services vm:101,vm:102
+	affinity separate
+
+# Case 2: Remove colocation rules, where there is a strict location rule with multiple priority groups set for the nodes.
+location: vm201-vm202-must-be-on-node1-or-node2
+	services vm:201,vm:202
+	nodes node1:1,node2:2
+	strict 1
+
+colocation: vm201-vm202-must-be-kept-together
+	services vm:201,vm:202
+	affinity together
diff --git a/src/test/rules_cfgs/multi-priority-location-with-colocation.cfg.expect b/src/test/rules_cfgs/multi-priority-location-with-colocation.cfg.expect
new file mode 100644
index 0000000..47c7af5
--- /dev/null
+++ b/src/test/rules_cfgs/multi-priority-location-with-colocation.cfg.expect
@@ -0,0 +1,47 @@
+--- Log ---
+Drop rule 'vm101-vm102-must-be-kept-separate', because services are in location rules with multiple priorities.
+Drop rule 'vm201-vm202-must-be-kept-together', because services are in location rules with multiple priorities.
+--- Config ---
+$VAR1 = {
+          'digest' => '30292457018bf1ae4fcf9bea92199233c877bf28',
+          'ids' => {
+                     'vm101-vm102-should-be-on-node1-or-node2' => {
+                                                                    'nodes' => {
+                                                                                 'node1' => {
+                                                                                              'priority' => 1
+                                                                                            },
+                                                                                 'node2' => {
+                                                                                              'priority' => 2
+                                                                                            }
+                                                                               },
+                                                                    'services' => {
+                                                                                    'vm:101' => 1,
+                                                                                    'vm:102' => 1
+                                                                                  },
+                                                                    'state' => 'enabled',
+                                                                    'strict' => 0,
+                                                                    'type' => 'location'
+                                                                  },
+                     'vm201-vm202-must-be-on-node1-or-node2' => {
+                                                                  'nodes' => {
+                                                                               'node1' => {
+                                                                                            'priority' => 1
+                                                                                          },
+                                                                               'node2' => {
+                                                                                            'priority' => 2
+                                                                                          }
+                                                                             },
+                                                                  'services' => {
+                                                                                  'vm:201' => 1,
+                                                                                  'vm:202' => 1
+                                                                                },
+                                                                  'state' => 'enabled',
+                                                                  'strict' => 1,
+                                                                  'type' => 'location'
+                                                                }
+                   },
+          'order' => {
+                       'vm101-vm102-should-be-on-node1-or-node2' => 1,
+                       'vm201-vm202-must-be-on-node1-or-node2' => 3
+                     }
+        };
diff --git a/src/test/test_rules_config.pl b/src/test/test_rules_config.pl
new file mode 100755
index 0000000..6e6b7de
--- /dev/null
+++ b/src/test/test_rules_config.pl
@@ -0,0 +1,102 @@
+#!/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::Location;
+use PVE::HA::Rules::Colocation;
+
+PVE::HA::Rules::Location->register();
+PVE::HA::Rules::Colocation->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