From: Dominik Rusovac <d.rusovac@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-ha-manager 3/3] fix #7557: introduce 'auto-rebalance' property
Date: Mon, 11 May 2026 17:57:34 +0200 [thread overview]
Message-ID: <20260511155734.149101-4-d.rusovac@proxmox.com> (raw)
In-Reply-To: <20260511155734.149101-1-d.rusovac@proxmox.com>
Add 'auto-rebalance' property to HA resources config, which gives users
control over which HA resources may be moved by dynamic CRS during
automatic rebalancing.
The 'auto-rebalance' flag is set to true by default. Disabling
'auto-rebalance' for some HA resource, say vm:100, means that vm:100
will be disregarded as a migration candidate during auto-rebalancing.
Any HA resource with a positive affinity for vm:100 will be disregarded
too.
Tests validate that an entire resource bundle will be disregarded if any
resource belonging to the bundle has 'auto-rebalance' disabled.
Signed-off-by: Dominik Rusovac <d.rusovac@proxmox.com>
---
src/PVE/API2/HA/Resources.pm | 7 ++
src/PVE/API2/HA/Status.pm | 9 +-
src/PVE/HA/Config.pm | 2 +
src/PVE/HA/Manager.pm | 12 ++-
src/PVE/HA/Resources.pm | 6 ++
src/PVE/HA/Resources/PVECT.pm | 1 +
src/PVE/HA/Resources/PVEVM.pm | 1 +
src/PVE/HA/Sim/Hardware.pm | 1 +
src/test/test_resource_bundles.pl | 134 +++++++++++++++++++++++++++++-
9 files changed, 166 insertions(+), 7 deletions(-)
diff --git a/src/PVE/API2/HA/Resources.pm b/src/PVE/API2/HA/Resources.pm
index e0690d5..181cdbb 100644
--- a/src/PVE/API2/HA/Resources.pm
+++ b/src/PVE/API2/HA/Resources.pm
@@ -142,6 +142,13 @@ __PACKAGE__->register_method({
optional => 1,
default => 1,
},
+ 'auto-rebalance' => {
+ description => "HA resource may be migrated during"
+ . " automatic rebalancing.",
+ type => 'boolean',
+ optional => 1,
+ default => 1,
+ },
group => get_standard_option('pve-ha-group-id', { optional => 1 }),
max_restart => {
description => "Maximal number of tries to restart the service on"
diff --git a/src/PVE/API2/HA/Status.pm b/src/PVE/API2/HA/Status.pm
index 4894f3b..c352aa1 100644
--- a/src/PVE/API2/HA/Status.pm
+++ b/src/PVE/API2/HA/Status.pm
@@ -121,6 +121,13 @@ __PACKAGE__->register_method({
optional => 1,
default => 1,
},
+ 'auto-rebalance' => {
+ description => "HA resource may be migrated during"
+ . " automatic rebalancing.",
+ type => 'boolean',
+ optional => 1,
+ default => 1,
+ },
max_relocate => {
description => "For type 'service'.",
type => "integer",
@@ -333,7 +340,7 @@ __PACKAGE__->register_method({
# also return common resource attributes
if (defined($sc)) {
$data->{request_state} = $sc->{state};
- foreach my $key (qw(group max_restart max_relocate failback comment)) {
+ foreach my $key (qw(group max_restart max_relocate failback comment auto-rebalance)) {
$data->{$key} = $sc->{$key} if defined($sc->{$key});
}
}
diff --git a/src/PVE/HA/Config.pm b/src/PVE/HA/Config.pm
index a34a302..54a6503 100644
--- a/src/PVE/HA/Config.pm
+++ b/src/PVE/HA/Config.pm
@@ -118,8 +118,10 @@ sub read_and_check_resources_config {
$d->{state} = 'started' if !defined($d->{state});
$d->{state} = 'started' if $d->{state} eq 'enabled'; # backward compatibility
$d->{failback} = 1 if !defined($d->{failback});
+ $d->{'auto-rebalance'} = 1 if !defined($d->{'auto-rebalance'});
$d->{max_restart} = 1 if !defined($d->{max_restart});
$d->{max_relocate} = 1 if !defined($d->{max_relocate});
+
if (PVE::HA::Resources->lookup($d->{type})) {
if (my $vmd = $vmlist->{ids}->{$name}) {
$d->{node} = $vmd->{node};
diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm
index 2a4b31e..68e3cd6 100644
--- a/src/PVE/HA/Manager.pm
+++ b/src/PVE/HA/Manager.pm
@@ -136,12 +136,14 @@ sub update_crs_scheduler_mode {
# HA resource in the resource bundle and also the key of each resource bundle
# in the returned hash.
sub get_active_stationary_resource_bundles {
- my ($ss, $resource_affinity) = @_;
+ my ($ss, $sc, $resource_affinity) = @_;
my $resource_bundles = {};
OUTER: for my $sid (sort keys %$ss) {
# do not consider non-started resource as 'active' leading resource
next if $ss->{$sid}->{state} ne 'started';
+ # do not consider resource if it may not be moved
+ next if !$sc->{$sid}->{'auto-rebalance'};
my @resources = ($sid);
my $nodes = { $ss->{$sid}->{node} => 1 };
@@ -156,6 +158,8 @@ OUTER: for my $sid (sort keys %$ss) {
next OUTER if $state eq 'migrate' || $state eq 'relocate';
# do not add non-started resource to active bundle
next if $state ne 'started';
+ # do not consider stationary bundle if a dependent resource may not be moved
+ next OUTER if !$sc->{$csid}->{'auto-rebalance'};
$nodes->{$node} = 1;
@@ -182,12 +186,12 @@ OUTER: for my $sid (sort keys %$ss) {
sub get_resource_migration_candidates {
my ($self) = @_;
- my ($ss, $compiled_rules, $online_node_usage) =
- $self->@{qw(ss compiled_rules online_node_usage)};
+ my ($ss, $sc, $compiled_rules, $online_node_usage) =
+ $self->@{qw(ss sc compiled_rules online_node_usage)};
my ($node_affinity, $resource_affinity) =
$compiled_rules->@{qw(node-affinity resource-affinity)};
- my $resource_bundles = get_active_stationary_resource_bundles($ss, $resource_affinity);
+ my $resource_bundles = get_active_stationary_resource_bundles($ss, $sc, $resource_affinity);
my @compact_migration_candidates = ();
for my $leader_sid (sort keys %$resource_bundles) {
diff --git a/src/PVE/HA/Resources.pm b/src/PVE/HA/Resources.pm
index 4238d9b..df7c1ff 100644
--- a/src/PVE/HA/Resources.pm
+++ b/src/PVE/HA/Resources.pm
@@ -71,6 +71,12 @@ EODESC
optional => 1,
default => 1,
},
+ 'auto-rebalance' => {
+ description => "HA resource may be migrated during automatic rebalancing",
+ type => 'boolean',
+ optional => 1,
+ default => 1,
+ },
max_restart => {
description => "Maximal number of tries to restart the resource on"
. " a node after its start failed. When reached, the HA manager will try to"
diff --git a/src/PVE/HA/Resources/PVECT.pm b/src/PVE/HA/Resources/PVECT.pm
index 0943d5e..177b907 100644
--- a/src/PVE/HA/Resources/PVECT.pm
+++ b/src/PVE/HA/Resources/PVECT.pm
@@ -38,6 +38,7 @@ sub options {
group => { optional => 1 },
comment => { optional => 1 },
failback => { optional => 1 },
+ 'auto-rebalance' => { optional => 1 },
max_restart => { optional => 1 },
max_relocate => { optional => 1 },
};
diff --git a/src/PVE/HA/Resources/PVEVM.pm b/src/PVE/HA/Resources/PVEVM.pm
index 1621c15..98ea7d6 100644
--- a/src/PVE/HA/Resources/PVEVM.pm
+++ b/src/PVE/HA/Resources/PVEVM.pm
@@ -38,6 +38,7 @@ sub options {
group => { optional => 1 },
comment => { optional => 1 },
failback => { optional => 1 },
+ 'auto-rebalance' => { optional => 1 },
max_restart => { optional => 1 },
max_relocate => { optional => 1 },
};
diff --git a/src/PVE/HA/Sim/Hardware.pm b/src/PVE/HA/Sim/Hardware.pm
index 82f85c9..e247bfa 100644
--- a/src/PVE/HA/Sim/Hardware.pm
+++ b/src/PVE/HA/Sim/Hardware.pm
@@ -115,6 +115,7 @@ sub read_service_config {
$d->{state} = 'disabled' if !$d->{state};
$d->{state} = 'started' if $d->{state} eq 'enabled'; # backward compatibility
$d->{failback} = 1 if !defined($d->{failback});
+ $d->{'auto-rebalance'} = 1 if !defined($d->{'auto-rebalance'});
$d->{max_restart} = 1 if !defined($d->{max_restart});
$d->{max_relocate} = 1 if !defined($d->{max_relocate});
}
diff --git a/src/test/test_resource_bundles.pl b/src/test/test_resource_bundles.pl
index d38dc51..7df96a9 100755
--- a/src/test/test_resource_bundles.pl
+++ b/src/test/test_resource_bundles.pl
@@ -21,6 +21,10 @@ my $get_active_stationary_resource_bundle_tests = [
node => 'node1',
},
},
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 1 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ },
resource_affinity => {
positive => {},
negative => {},
@@ -46,6 +50,10 @@ my $get_active_stationary_resource_bundle_tests = [
node => 'node1',
},
},
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 1 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ },
resource_affinity => {
positive => {
'vm:101' => {
@@ -79,6 +87,11 @@ my $get_active_stationary_resource_bundle_tests = [
node => 'node1',
},
},
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 1 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ 'vm:103' => { 'auto-rebalance' => 1 },
+ },
resource_affinity => {
positive => {
'vm:101' => {
@@ -118,6 +131,11 @@ my $get_active_stationary_resource_bundle_tests = [
node => 'node1',
},
},
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 1 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ 'vm:103' => { 'auto-rebalance' => 1 },
+ },
resource_affinity => {
positive => {
'vm:101' => {
@@ -159,6 +177,11 @@ my $get_active_stationary_resource_bundle_tests = [
target => 'node1',
},
},
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 1 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ 'vm:103' => { 'auto-rebalance' => 1 },
+ },
resource_affinity => {
positive => {
'vm:101' => {
@@ -196,6 +219,11 @@ my $get_active_stationary_resource_bundle_tests = [
node => 'node3',
},
},
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 1 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ 'vm:103' => { 'auto-rebalance' => 1 },
+ },
resource_affinity => {
positive => {
'vm:101' => {
@@ -215,6 +243,108 @@ my $get_active_stationary_resource_bundle_tests = [
},
resource_bundles => {},
},
+ {
+ description => "singleton resource bundle with disabled auto-rebalance",
+ services => {
+ 'vm:101' => {
+ state => 'started',
+ node => 'node1',
+ },
+ 'vm:102' => {
+ state => 'started',
+ node => 'node1',
+ },
+ },
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 0 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ },
+ resource_affinity => {
+ positive => {},
+ negative => {},
+ },
+ resource_bundles => {
+ 'vm:102' => [
+ 'vm:102',
+ ],
+ },
+ },
+ {
+ description => "resource bundle leader with disabled auto-rebalance",
+ services => {
+ 'vm:101' => {
+ state => 'started',
+ node => 'node1',
+ },
+ 'vm:102' => {
+ state => 'started',
+ node => 'node1',
+ },
+ 'ct:103' => {
+ state => 'started',
+ node => 'node2',
+ },
+ },
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 0 },
+ 'vm:102' => { 'auto-rebalance' => 1 },
+ 'ct:103' => { 'auto-rebalance' => 1 },
+ },
+ resource_affinity => {
+ positive => {
+ 'vm:101' => {
+ 'vm:102' => 1,
+ },
+ 'vm:102' => {
+ 'vm:101' => 1,
+ },
+ },
+ negative => {},
+ },
+ resource_bundles => {
+ 'ct:103' => [
+ 'ct:103',
+ ],
+ },
+ },
+ {
+ description => "some member of resource bundle with disabled auto-rebalance",
+ services => {
+ 'vm:101' => {
+ state => 'started',
+ node => 'node1',
+ },
+ 'vm:102' => {
+ state => 'started',
+ node => 'node1',
+ },
+ 'ct:103' => {
+ state => 'started',
+ node => 'node2',
+ },
+ },
+ service_config => {
+ 'vm:101' => { 'auto-rebalance' => 1 },
+ 'vm:102' => { 'auto-rebalance' => 0 },
+ 'ct:103' => { 'auto-rebalance' => 1 },
+ },
+ resource_affinity => {
+ positive => {
+ 'vm:101' => {
+ 'vm:102' => 1,
+ },
+ 'vm:102' => {
+ 'vm:101' => 1,
+ },
+ },
+ negative => {},
+ },
+ resource_bundles => {
+ 'ct:103' => [
+ 'ct:103',
+ ],
+ },
+ },
];
my $tests = [
@@ -224,9 +354,9 @@ my $tests = [
plan(tests => scalar($tests->@*));
for my $case ($get_active_stationary_resource_bundle_tests->@*) {
- my ($ss, $resource_affinity) = $case->@{qw(services resource_affinity)};
+ my ($ss, $sc, $resource_affinity) = $case->@{qw(services service_config resource_affinity)};
- my $result = PVE::HA::Manager::get_active_stationary_resource_bundles($ss, $resource_affinity);
+ my $result = PVE::HA::Manager::get_active_stationary_resource_bundles($ss, $sc, $resource_affinity);
is_deeply($result, $case->{resource_bundles}, $case->{description});
}
--
2.47.3
next prev parent reply other threads:[~2026-05-11 15:58 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-11 15:57 [PATCH-SERIES ha-manager/manager 0/3] fix #7557: introduce 'auto-rebalance' property Dominik Rusovac
2026-05-11 15:57 ` [PATCH pve-manager 1/3] ui: ha: add auto-rebalance flag Dominik Rusovac
2026-05-12 9:05 ` Daniel Kral
2026-05-11 15:57 ` [PATCH pve-ha-manager 2/3] manager: set service config value in self Dominik Rusovac
2026-05-12 9:06 ` Daniel Kral
2026-05-12 11:55 ` Dominik Rusovac
2026-05-11 15:57 ` Dominik Rusovac [this message]
2026-05-12 9:07 ` [PATCH pve-ha-manager 3/3] fix #7557: introduce 'auto-rebalance' property Daniel Kral
2026-05-12 11:51 ` Dominik Rusovac
2026-05-12 9:21 ` [PATCH-SERIES ha-manager/manager 0/3] " Daniel Kral
2026-05-12 11:53 ` Dominik Rusovac
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260511155734.149101-4-d.rusovac@proxmox.com \
--to=d.rusovac@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.