[pve-devel] [PATCH v2 cluster 5/5] Add cluster join API version check
Stefan Reiter
s.reiter at proxmox.com
Wed Dec 4 15:04:39 CET 2019
Adds API call GET /cluster/config/apiversion to retrieve remote clusters
join-API version (0 is assumed for versions without this endpoint).
Introduce API_AGE similar to storage plugin API, but with two ages for
cluster/joinee roles. Currently, all versions are intercompatible.
For future usage, a new 'addnode' parameter 'apiversion' is introduced,
to allow introducing API breakages for joining nodes as well.
As a first compatibility check, use new fallback method only if
available. This ensures full compatibility between nodes/clusters with
and without new fallback behaviour.
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
Since the new fallback now also has a fallback (fallback-ception?) to use the
old 'link0' parameter as a remote IP for single-link clusters, this patch is
entirely optional (i.e. everything else from the series should work without it).
If we do plan to implement a concept like this it might make sense to implement
it sooner then later, so when we do need it, the versions are already available
in all (pre-)join checks.
data/PVE/API2/ClusterConfig.pm | 35 +++++++++++++++++++++++++++++++---
data/PVE/Cluster/Setup.pm | 27 ++++++++++++++++++++++----
2 files changed, 55 insertions(+), 7 deletions(-)
diff --git a/data/PVE/API2/ClusterConfig.pm b/data/PVE/API2/ClusterConfig.pm
index 64bebfe..70faad0 100644
--- a/data/PVE/API2/ClusterConfig.pm
+++ b/data/PVE/API2/ClusterConfig.pm
@@ -58,11 +58,29 @@ __PACKAGE__->register_method({
{ name => 'totem' },
{ name => 'join' },
{ name => 'qdevice' },
+ { name => 'apiversion' },
];
return $result;
}});
+__PACKAGE__->register_method ({
+ name => 'join_api_version',
+ path => 'apiversion',
+ method => 'GET',
+ description => "Return the version of the cluster join API available on this node.",
+ permissions => {
+ check => ['perm', '/', [ 'Sys.Audit' ]],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {},
+ },
+ returns => { type => 'integer' },
+ code => sub {
+ return PVE::Cluster::Setup::JOIN_API_VERSION;
+ }});
+
__PACKAGE__->register_method ({
name => 'create',
path => '',
@@ -213,6 +231,11 @@ __PACKAGE__->register_method ({
format => 'ip',
optional => 1,
},
+ apiversion => {
+ type => 'integer',
+ description => 'The JOIN_API_VERSION of the new node.',
+ optional => 1,
+ },
}),
},
returns => {
@@ -235,6 +258,12 @@ __PACKAGE__->register_method ({
code => sub {
my ($param) = @_;
+ $param->{apiversion} //= 0;
+ die "unsupported old API version on joining node ($param->{apiversion}),"
+ . " please upgrade before joining\n"
+ if $param->{apiversion} < (PVE::Cluster::Setup::JOIN_API_VERSION -
+ PVE::Cluster::Setup::JOIN_API_AGE_AS_CLUSTER);
+
PVE::Cluster::check_cfs_quorum();
my $vc_errors;
@@ -299,10 +328,10 @@ __PACKAGE__->register_method ({
} else {
# FIXME: Probably a good idea to remove this once we're sure
# noone would join a node old enough to need this to a recent
- # cluster
+ # cluster, then remove 0 from API_AGE compatible versions
$param->{new_node_ip} = $cluster_links->{0}->{address}
- if !$param->{new_node_ip} && $cluster_links->{0}
- && scalar(%$cluster_links) == 1;
+ if !$param->{new_node_ip} && $cluster_links->{0} &&
+ scalar(%$cluster_links) == 1 && $param->{apiversion} == 0;
# when called without any link parameters, fall back to
# new_node_ip, if all existing nodes only have a single link too
diff --git a/data/PVE/Cluster/Setup.pm b/data/PVE/Cluster/Setup.pm
index 6d880dd..fa7a039 100644
--- a/data/PVE/Cluster/Setup.pm
+++ b/data/PVE/Cluster/Setup.pm
@@ -20,6 +20,13 @@ use PVE::Network;
use PVE::Tools;
use PVE::Certificate;
+# Only relevant for pre-join checks, after join happened versions can differ
+use constant JOIN_API_VERSION => 1;
+# (APIVER-this) is oldest version a new node in addnode can have to be accepted
+use constant JOIN_API_AGE_AS_CLUSTER => 1;
+# (APIVER-this) is oldest version a cluster node can have to still try joining
+use constant JOIN_API_AGE_AS_JOINEE => 1;
+
my $pmxcfs_base_dir = PVE::Cluster::base_dir();
my $pmxcfs_auth_dir = PVE::Cluster::auth_dir();
@@ -664,6 +671,12 @@ sub join {
# login raises an exception on failure, so if we get here we're good
print "Login succeeded.\n";
+ # check cluster join API version
+ my $apiver = eval { $conn->get("/cluster/config/apiversion"); } // 0;
+ die "error: incompatible join API version on cluster ($apiver). Make sure"
+ . " all nodes are up-to-date.\n"
+ if $apiver < (JOIN_API_VERSION - JOIN_API_AGE_AS_JOINEE);
+
my $args = {};
$args->{force} = $param->{force} if defined($param->{force});
$args->{nodeid} = $param->{nodeid} if $param->{nodeid};
@@ -672,11 +685,17 @@ sub join {
$args->{"link$link"} = PVE::Corosync::print_corosync_link($links->{$link});
}
- # this will be used as fallback if no links are specified
- $args->{new_node_ip} = $local_ip_address;
+ if (!%$links) {
+ print "No local links given, will attempt fallback to $local_ip_address\n";
- print "No local links given, will attempt fallback to $local_ip_address\n"
- if !%$links;
+ # specify fallback if no links are given according to remote api version
+ $args->{link0} = $local_ip_address if $apiver == 0;
+ $args->{new_node_ip} = $local_ip_address if $apiver >= 1;
+ }
+
+ if ($apiver >= 1) {
+ $args->{apiversion} = JOIN_API_VERSION;
+ }
print "Request addition of this node\n";
my $res = eval { $conn->post("/cluster/config/nodes/$nodename", $args); };
--
2.20.1
More information about the pve-devel
mailing list