Add an enpoint to the API which allows to join an existing PVE cluster by only using the API instead of CLI tools (pvecm).
Use a worker as this operation may need longer than 30 seconds. With the worker we also get a task log entry/window for an UI for free, allowing to give better feedback. Signed-off-by: Thomas Lamprecht <[email protected]> --- data/PVE/API2/ClusterConfig.pm | 127 ++++++++++++++++++++++++++++++++++++++++- debian/control.in | 2 + 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/data/PVE/API2/ClusterConfig.pm b/data/PVE/API2/ClusterConfig.pm index 5bdf970..c4d5a9a 100644 --- a/data/PVE/API2/ClusterConfig.pm +++ b/data/PVE/API2/ClusterConfig.pm @@ -9,6 +9,7 @@ use PVE::RESTHandler; use PVE::RPCEnvironment; use PVE::JSONSchema qw(get_standard_option); use PVE::Cluster; +use PVE::APIClient::LWP; use PVE::Corosync; use base qw(PVE::RESTHandler); @@ -39,7 +40,8 @@ __PACKAGE__->register_method({ my $result = [ { name => 'nodes' }, { name => 'totem' }, - ]; + { name => 'join' }, + ]; return $result; }}); @@ -94,7 +96,6 @@ my $config_change_lock = sub { }); }; - __PACKAGE__->register_method ({ name => 'addnode', path => 'nodes/{node}', @@ -305,6 +306,128 @@ __PACKAGE__->register_method ({ return undef; }}); +__PACKAGE__->register_method ({ + name => 'join', + path => 'join', + method => 'POST', + protected => 1, + description => "Joins this node into an existing cluster.", + parameters => { + additionalProperties => 0, + properties => { + hostname => { + type => 'string', + description => "Hostname (or IP) of an existing cluster member." + }, + nodeid => { + type => 'integer', + description => "Node id for this node.", + minimum => 1, + optional => 1, + }, + votes => { + type => 'integer', + description => "Number of votes for this node", + minimum => 0, + optional => 1, + }, + force => { + type => 'boolean', + description => "Do not throw error if node already exists.", + optional => 1, + }, + ring0_addr => { + type => 'string', format => 'address', + description => "Hostname (or IP) of the corosync ring0 address of this node.". + " Defaults to nodes hostname.", + optional => 1, + }, + ring1_addr => { + type => 'string', format => 'address', + description => "Hostname (or IP) of the corosync ring1 address, this". + " needs an valid configured ring 1 interface in the cluster.", + optional => 1, + }, + fingerprint => get_standard_option('fingerprint-sha256', { + description => "SSL certificate fingerprint. Optional in CLI environment.", + optional => 1, + }), + password => { + description => "Superuser (root) password of peer node.", + type => 'string', + maxLength => 128, + }, + }, + }, + returns => { type => 'string' }, + code => sub { + my ($param) = @_; + + my $nodename = PVE::INotify::nodename(); + my $rpcenv = PVE::RPCEnvironment::get(); + my $authuser = $rpcenv->get_user(); + + my $worker = sub { + PVE::Cluster::setup_sshd_config(); + PVE::Cluster::setup_rootsshconfig(); + PVE::Cluster::setup_ssh_keys(); + + # check if we can join with the given parameters and current node state + my ($ring0_addr, $ring1_addr) = $param->@{'ring0_addr', 'ring1_addr'}; + PVE::Cluster::assert_joinable($ring0_addr, $ring1_addr, $param->{force}); + + # make sure known_hosts is on local filesystem + PVE::Cluster::ssh_unmerge_known_hosts(); + + my $host = $param->{hostname}; + + my $conn_args = { + username => 'root@pam', + password => $param->{password}, + cookie_name => 'PVEAuthCookie', + protocol => 'https', + host => $host, + port => 8006, + }; + + if (my $fp = $param->{fingerprint}) { + $conn_args->{cached_fingerprints} = { uc($fp) => 1 }; + } elsif ($rpcenv->{type} eq 'cli') { + $conn_args->{manual_verification} = 1; + } else { + raise_param_exc({ + fingerprint => "'fingerprint' only optional in interactive CLI environment." + }); + } + + print "Etablishing API connection with host '$host'\n"; + + my $conn = PVE::APIClient::LWP->new(%$conn_args); + $conn->login(); + + # login raises an exception on failure, so if we get here we're good + print "Login succeeded.\n"; + + my $args = {}; + $args->{force} = $param->{force} if defined($param->{force}); + $args->{nodeid} = $param->{nodeid} if $param->{nodeid}; + $args->{votes} = $param->{votes} if defined($param->{votes}); + $args->{ring0_addr} = $ring0_addr if defined($ring0_addr); + $args->{ring1_addr} = $ring1_addr if defined($ring1_addr); + + print "Request addition of this node\n"; + my $res = $conn->post("/cluster/config/nodes/$nodename", $args); + + print "Join request OK, finishing setup locally\n"; + + # added successfuly - now prepare local node + PVE::Cluster::finish_join($nodename, $res->{corosync_conf}, $res->{corosync_authkey}); + }; + + return $rpcenv->fork_worker('clusterjoin', '', $authuser, $worker); + }}); + + __PACKAGE__->register_method({ name => 'totem', path => 'totem', diff --git a/debian/control.in b/debian/control.in index e6ccc90..2fce9f4 100644 --- a/debian/control.in +++ b/debian/control.in @@ -21,6 +21,7 @@ Build-Depends: debhelper (>= 7), libdigest-hmac-perl, dh-systemd, pve-doc-generator, + libpve-apiclient-perl, libuuid-perl Standards-Version: 3.7.3 @@ -47,6 +48,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, @PERLAPI@, faketime, libcrypt-ssleay-perl, libuuid-perl, + libpve-apiclient-perl, lsb-base Breaks: pve-ha-manager (<<2.0-4) Description: Cluster Infrastructure for Proxmox Virtual Environment -- 2.11.0 _______________________________________________ pve-devel mailing list [email protected] https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
