[pve-devel] [PATCH v2 cluster 5/5] Add cluster join API version check
Thomas Lamprecht
t.lamprecht at proxmox.com
Tue Dec 17 07:32:41 CET 2019
On 12/4/19 3:04 PM, Stefan Reiter wrote:
> 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' },
maybe add minimum 0 and
description => "Cluster Join API version, currently " . PVE::Cluster::Setup::JOIN_API_VERSION
?
> + 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);
multiline post-if, also it's always nice to have both in the error message,
is-value and expected-value.
> +
> 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;
> +
isn't it enough if we have the actual version, and the AGE one can join us?
Then all is clear, the third one does not brings additional value or?
> 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;
nit, omit the semicolon in the eval for readability.
> + 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);
hard to read, switch to "normal" if.
> +
> 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); };
>
More information about the pve-devel
mailing list