From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id A11191FF153 for ; Mon, 22 Jun 2026 11:43:08 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 8B1D14C4A; Mon, 22 Jun 2026 11:43:06 +0200 (CEST) Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Date: Mon, 22 Jun 2026 11:42:31 +0200 Message-Id: Subject: Re: [PATCH pve-cluster 8/9] fix #7294: cluster: helpers: add cluster-wide version assertion From: "Daniel Kral" To: "David Riley" , X-Mailer: aerc 0.21.0-145-gf21bb67f8cab-dirty References: <20260611145935.147788-1-d.riley@proxmox.com> <20260611145935.147788-9-d.riley@proxmox.com> In-Reply-To: <20260611145935.147788-9-d.riley@proxmox.com> X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1782121342712 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.074 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: DMB5YJMQLNYCWOXDNTDDUOLKEZHTB5CA X-Message-ID-Hash: DMB5YJMQLNYCWOXDNTDDUOLKEZHTB5CA X-MailFrom: d.kral@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On Thu Jun 11, 2026 at 4:59 PM CEST, David Riley wrote: > Add function to assert a minimum version requirement for the whole > cluster. > > Evaluates against the cluster configuration. Dies if any node is > offline, or running an older version, ensuring cluster-wide feature > compatibility before allowing an operation to proceed. > > Prevents older nodes from truncating newly structured user.cfg > entries. > > Link: https://bugzilla.proxmox.com/show_bug.cgi?id=3D7294 > Signed-off-by: David Riley > --- > src/PVE/Cluster/Helpers.pm | 35 +++++++++++++++++++++++++++++++++++ > 1 file changed, 35 insertions(+) As for the previous patch, this could go in PVE::Cluster as code that already depends on some cluster-related functionality has it imported, but no hard feelings about this. > > diff --git a/src/PVE/Cluster/Helpers.pm b/src/PVE/Cluster/Helpers.pm > index a4b41c9..3937e6d 100644 > --- a/src/PVE/Cluster/Helpers.pm > +++ b/src/PVE/Cluster/Helpers.pm > @@ -47,5 +47,40 @@ sub version_cmp { > return 0; > } > =20 > +# Asserts that all configured nodes in the cluster meet a minimum versio= n requirement. > +# Evaluates against the full, static cluster node list rather than only = live nodes. > +# Aborts if any node is offline, unreadable, or running an > +# outdated version, preventing partial feature rollouts from corrupting = cluster state. > +sub assert_min_cluster_version { > + my ($major, $minor, $release) =3D @_; > + > + my $nodestat =3D PVE::Cluster::get_node_kv('version-info') || {}; > + > + my $clinfo =3D PVE::Cluster::get_clinfo() || {}; > + my $nodelist =3D $clinfo->{nodelist} || {}; This might be unnecessary for single-node setups as there we can enforce versioned dependencies much more easily through package version constraints, but it should be noted that the $nodelist hash will be empty if no cluster has been created. If we want to make this assertion also work on single-node setups, this could use PVE::Cluster::get_nodelist(), which fakes the nodelist for a single node. Either way, that should probably go in the subroutine's description. > + > + foreach my $node (sort keys %$nodelist) { nit: for new code we use 'for' in favor of 'foreach' [0] [0] https://pve.proxmox.com/wiki/Perl_Style_Guide#Perl_syntax_choices > + my $raw_json =3D $nodestat->{$node}; > + > + if (!defined($raw_json)) { > + die "cannot execute operation: cluster node '$node' is offli= ne or not reporting its" > + . " version. All nodes must be online to verify cluster-= wide feature support.\n"; > + } > + > + my $vinfo =3D eval { decode_json($raw_json) }; > + if ($@ || !$vinfo || !$vinfo->{version}) { > + die "cannot execute operation: failed to parse cluster versi= on string for node" > + . " '$node'.\n"; > + } > + > + if (!pvecfg_min_version($vinfo->{version}, $major, $minor, $rele= ase)) { > + die "cannot execute operation: cluster node '$node' runs an = older version of Proxmox" > + . " VE ($vinfo->{version}) than the required $major.$min= or.$release. Please" > + . " upgrade all nodes first.\n"; > + } > + } > + return 1; > +} > + > 1; > =20