{ 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',
+ minimum => 0,
+ 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 => '',
format => 'ip',
optional => 1,
},
+ apiversion => {
+ type => 'integer',
+ description => 'The JOIN_API_VERSION of the new node.',
+ optional => 1,
+ },
}),
},
returns => {
code => sub {
my ($param) = @_;
+ $param->{apiversion} //= 0;
+ if ($param->{apiversion} < (PVE::Cluster::Setup::JOIN_API_VERSION -
+ PVE::Cluster::Setup::JOIN_API_AGE_AS_CLUSTER)) {
+ die "unsupported old API version on joining node ($param->{apiversion},"
+ . " cluster node has " . PVE::Cluster::Setup::JOIN_API_VERSION
+ . "), please upgrade before joining\n";
+ }
+
PVE::Cluster::check_cfs_quorum();
my $vc_errors;
# FIXME: remove in 8.0 or when joining an old node not supporting
# new_node_ip becomes infeasible otherwise
my $legacy_fallback = 0;
- if (!$param->{new_node_ip} && scalar(%$links) == 1) {
+ if (!$param->{new_node_ip} && scalar(%$links) == 1 && $param->{apiversion} == 0) {
my $passed_link_id = (keys %$links)[0];
my $passed_link = delete $links->{$passed_link_id};
$param->{new_node_ip} = $passed_link->{address};
run_command($cmd, 'outfunc' => sub {}, 'errfunc' => sub {},
'errmsg' => "unable to copy ssh ID");
+ $cmd = ['ssh', $host, '-o', 'BatchMode=yes', 'pvecm', 'apiver'];
+ my $remote_apiver = 0;
+ run_command($cmd, 'outfunc' => sub {
+ $remote_apiver = shift;
+ chomp $remote_apiver;
+ }, 'noerr' => 1);
+
+ if ($remote_apiver < (PVE::Cluster::Setup::JOIN_API_VERSION -
+ PVE::Cluster::Setup::JOIN_API_AGE_AS_JOINEE)) {
+ die "error: incompatible join API version on cluster ($remote_apiver,"
+ . " local has " . PVE::Cluster::Setup::JOIN_API_VERSION . "). Make"
+ . " sure all nodes are up-to-date.\n";
+ }
+
$cmd = ['ssh', $host, '-o', 'BatchMode=yes',
'pvecm', 'addnode', $nodename, '--force', 1];
# this will be used as fallback if no links are specified
if (!%$links) {
- push @$cmd, '--link0', $local_ip_address;
+ push @$cmd, '--link0', $local_ip_address if $remote_apiver == 0;
+ push @$cmd, '--new_node_ip', $local_ip_address if $remote_apiver >= 1;
+
print "No cluster network links passed explicitly, fallback to local node"
. " IP '$local_ip_address'\n";
}
our $cmddef = {
+ apiver => [ 'PVE::API2::ClusterConfig', 'join_api_version', [], {}, sub {
+ my $apiver = shift;
+ print "$apiver\n";
+ }],
keygen => [ __PACKAGE__, 'keygen', ['filename']],
create => [ 'PVE::API2::ClusterConfig', 'create', ['clustername']],
add => [ __PACKAGE__, 'add', ['hostname']],
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();
# 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;
+
+ if ($apiver < (JOIN_API_VERSION - JOIN_API_AGE_AS_JOINEE)) {
+ die "error: incompatible join API version on cluster ($apiver, local has "
+ . JOIN_API_VERSION . "). Make sure all nodes are up-to-date.\n";
+ }
+
my $args = {};
$args->{force} = $param->{force} if defined($param->{force});
$args->{nodeid} = $param->{nodeid} if $param->{nodeid};
# this will be used as fallback if no links are specified
if (!%$links) {
- $args->{link0} = $local_ip_address;
+ $args->{link0} = $local_ip_address if $apiver == 0;
+ $args->{new_node_ip} = $local_ip_address if $apiver >= 1;
+
print "No cluster network links passed explicitly, fallback to local node"
. " IP '$local_ip_address'\n";
}
+ 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); };
if (my $err = $@) {