From: Kefu Chai <k.chai@proxmox.com>
To: pve-devel@lists.proxmox.com
Cc: Thomas Lamprecht <t.lamprecht@proxmox.com>,
Jonas Theisen <j.theisen@proxmox.com>
Subject: [PATCH manager v2 2/4] migrations: cephx: judge cipher support by the version a daemon runs
Date: Mon, 19 Oct 2026 16:23:44 +0800 [thread overview]
Message-ID: <20261019082346.1781960-3-k.chai@proxmox.com> (raw)
In-Reply-To: <20261019082346.1781960-1-k.chai@proxmox.com>
The cipher check needs both the running and the installed version of a
daemon. Only pvestatd reports the installed version, and only on our
nodes. So a daemon on another host always failed the check, and a
stretch cluster with an external tiebreaker could not migrate.
For such a daemon, check only the running version and warn that Ceph on
that host must not be downgraded. A stopped daemon still fails the
check, as nothing reports its version.
Signed-off-by: Kefu Chai <k.chai@proxmox.com>
---
bin/pve-cephx-rotate-service-keys | 47 ++++++++++++++++----
test/CephKeyMigrationScript_test.pl | 66 +++++++++++++++++++++++++++++
2 files changed, 104 insertions(+), 9 deletions(-)
diff --git a/bin/pve-cephx-rotate-service-keys b/bin/pve-cephx-rotate-service-keys
index 84daff6d..8048944b 100755
--- a/bin/pve-cephx-rotate-service-keys
+++ b/bin/pve-cephx-rotate-service-keys
@@ -189,9 +189,13 @@ my sub daemon_location($type, $id, $host) {
};
}
-my sub assert_member($node, $entity) {
+my sub assert_located($node, $entity) {
die "Cannot manage '$entity': no valid host name was reported\n"
if !defined($node) || ref($node) || !length($node);
+}
+
+my sub assert_member($node, $entity) {
+ assert_located($node, $entity);
die "Cannot manage '$entity' on '$node', which is not a node of this Proxmox VE cluster\n"
if !cluster_nodes()->{$node};
}
@@ -2545,31 +2549,49 @@ my sub preflight_nodes($info, $plan, $opts) {
(@touched, grep { defined($_->{node}) && $lockbox_nodes->{ $_->{node} } } @all);
}
- my (@outdated, @unknown_versions);
+ # The running version tells whether a daemon can use the new cipher now, and Ceph reports it for
+ # every daemon. The installed version tells whether it still can after a restart, and only
+ # pvestatd reports it, for our nodes. A daemon elsewhere is checked by its running version only,
+ # so it has to be running.
+ my (@outdated, @unknown_versions, @running_only);
for my $daemon (@judged) {
- if (!eval { assert_member($daemon->{node}, "$daemon->{type}.$daemon->{id}"); 1 }) {
+ my $label = "$daemon->{type}.$daemon->{id}";
+ my $stopped = ($daemon->{recovered} || $daemon->{down}) ? 1 : 0;
+
+ if (!eval { assert_located($daemon->{node}, $label); 1 }) {
push @unknown_versions, $@;
next;
}
- if (!$daemon->{recovered} && !$daemon->{down}) {
+ if (!$daemon->{managed} && $stopped) {
+ push @unknown_versions,
+ "cannot verify the Ceph version of $label: it does not run, and its host"
+ . " '$daemon->{node}' is not a node of this Proxmox VE cluster";
+ next;
+ }
+
+ if (!$stopped) {
if (!defined($daemon->{version}) || $daemon->{version} eq '') {
push @unknown_versions,
- "could not verify the running Ceph version of $daemon->{entity} on node"
+ "could not verify the running Ceph version of $label on node"
. " '$daemon->{node}'";
} elsif (!version_has_cipher($daemon->{version})) {
push @outdated,
- "$daemon->{entity} on node '$daemon->{node}' runs "
- . short_version($daemon->{version});
+ "$label on node '$daemon->{node}' runs " . short_version($daemon->{version});
+ } elsif (!$daemon->{managed}) {
+ push @running_only,
+ "$label on '$daemon->{node}' runs " . short_version($daemon->{version});
}
}
+ next if !$daemon->{managed};
+
if (!defined($daemon->{binary}) || $daemon->{binary} eq '') {
push @unknown_versions,
- "could not verify the installed Ceph version of $daemon->{entity} on node"
+ "could not verify the installed Ceph version of $label on node"
. " '$daemon->{node}'; check that 'pvestatd' runs there";
} elsif (!version_has_cipher($daemon->{binary})) {
push @outdated,
- "$daemon->{entity} on node '$daemon->{node}' would restart into "
+ "$label on node '$daemon->{node}' would restart into "
. short_version($daemon->{binary});
}
}
@@ -2636,6 +2658,13 @@ my sub preflight_nodes($info, $plan, $opts) {
}
}
+ if (@running_only) {
+ log_warn("These daemons run on hosts this cluster does not manage, so only the version they"
+ . " run was checked, not the one installed there. Keep Ceph on those hosts at this"
+ . " version or newer, or they lose the new cipher on their next restart:");
+ log_steps(\@running_only);
+ }
+
if (@unknown_versions) {
log_fail("Could not verify '$CIPHER' support for every service daemon:");
log_steps(\@unknown_versions);
diff --git a/test/CephKeyMigrationScript_test.pl b/test/CephKeyMigrationScript_test.pl
index fc9811d7..6ccadbc4 100755
--- a/test/CephKeyMigrationScript_test.pl
+++ b/test/CephKeyMigrationScript_test.pl
@@ -6171,6 +6171,7 @@ for my $case ([0, 0], [1, 0], [0, 1], [1, 1]) {
id => "$_",
entity => "mon.$_",
node => "node$_",
+ managed => 1,
store => 'file',
version => '20.2.4-pve4',
binary => '20.2.4-pve4',
@@ -8030,4 +8031,69 @@ for my $case ([0, 0], [1, 0], [0, 1], [1, 1]) {
);
}
+# a daemon outside the cluster has no installed version, so only its running version counts
+{
+ my $info = sub {
+ my (%tiebreaker) = @_;
+ my $mons = [
+ (
+ map { {
+ type => 'mon',
+ id => "$_",
+ entity => "mon.$_",
+ node => "node$_",
+ managed => 1,
+ store => 'file',
+ version => '20.2.4-pve4',
+ binary => '20.2.4-pve4',
+ } } 1 .. 2
+ ),
+ {
+ type => 'mon',
+ id => 'tiebreaker',
+ # all monitors share the 'mon.' key, as the inventory records
+ entity => 'mon.',
+ node => 'tiebreaker.invalid',
+ managed => 0,
+ store => 'file',
+ version => '20.2.4-pve4',
+ %tiebreaker,
+ },
+ ];
+ return {
+ daemons => { mon => $mons, mgr => [], mds => [], osd => [] },
+ monmap_mons => [map { $_->{id} } @$mons],
+ rados => MonCountRados->new(),
+ };
+ };
+ my $preflight = sub {
+ my ($info) = @_;
+ my ($output, $verdict) = ('');
+ {
+ local *STDOUT;
+ open(STDOUT, '>', \$output) or die $!;
+ $verdict = $HOOKS->{preflight_nodes}
+ ->($info, { daemons => [], lockbox_keys => [], service_cipher => 1 }, {});
+ }
+ return ($verdict, $output // '');
+ };
+
+ my ($verdict, $output) = $preflight->($info->());
+ is($verdict, 1, 'a capable monitor outside the cluster does not block the cipher switch');
+ like(
+ $output,
+ qr/does not manage.*mon\.tiebreaker on 'tiebreaker\.invalid' runs 20\.2\.4/s,
+ 'naming it with the version it was judged by',
+ );
+
+ ($verdict, $output) = $preflight->($info->(version => '19.2.5'));
+ is($verdict, -1, 'an outdated monitor outside the cluster still blocks');
+ like($output, qr/mon\.tiebreaker on node 'tiebreaker\.invalid' runs 19\.2\.5/, 'as outdated');
+
+ ($verdict, $output) = $preflight->($info->(down => 1));
+ is($verdict, -1, 'a stopped monitor outside the cluster cannot be judged');
+ like($output, qr/mon\.tiebreaker.*not a node/, 'which names the membership');
+
+}
+
done_testing();
--
2.47.3
next prev parent reply other threads:[~2026-10-19 8:24 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 8:23 [PATCH manager v2 0/4] cephx: migrate keys with a monitor this cluster does not manage Kefu Chai
2026-10-19 8:23 ` [PATCH manager v2 1/4] migrations: cephx: ask a monitor about itself through one route Kefu Chai
2026-10-19 8:23 ` Kefu Chai [this message]
2026-10-19 8:23 ` [PATCH manager v2 3/4] migrations: cephx: rotate the monitor key with monitors we do not manage Kefu Chai
2026-10-19 8:23 ` [PATCH manager v2 4/4] migrations: cephx: name the hosts keeping key copies we cannot write Kefu Chai
2026-10-19 14:47 ` [PATCH manager v2 0/4] cephx: migrate keys with a monitor this cluster does not manage Jonas Theisen
2026-10-19 18:23 ` applied: " Thomas Lamprecht
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=20261019082346.1781960-3-k.chai@proxmox.com \
--to=k.chai@proxmox.com \
--cc=j.theisen@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
--cc=t.lamprecht@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox