[pve-devel] [PATCH cluster v3 07/14] api/cluster: add join endpoint

Fabian Grünbichler f.gruenbichler at proxmox.com
Tue Dec 19 15:01:15 CET 2017


On Tue, Dec 19, 2017 at 12:52:32PM +0100, Thomas Lamprecht wrote:
> 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.
> ---
> 
> changes v2 -> v3:
> * use worker

same issue as with create:

$ pvecm add clustertest01
Please enter superuser (root) password for 'clustertest01':
Password for root at clustertest01:
Etablishing API connection with host 'clustertest01'
The authenticity of host 'clustertest01' can't be established.
X509 SHA256 key fingerprint is
60:0F:DE:48:C7:1B:48:4D:F2:BF:EF:C2:98:8E:5B:F1:9B:95:EB:97:99:33:2F:C3:B6:15:CC:10:E9:D0:EC:54.
Are you sure you want to continue connecting (yes/no)? yes
Login succeeded.
Request addition of this node
Join request OK, finishing setup locally
stopping pve-cluster service
backup old database
waiting for quorum...OK
generating node certificates
merge known_hosts file
node certificate changed, restart pveproxy and pvedaemon services
successfully added node 'clustertest02' to cluster.
ipcc_send_rec[4] failed: Transport endpoint is not connected

> 
>  data/PVE/API2/ClusterConfig.pm | 128 ++++++++++++++++++++++++++++++++++++++++-
>  debian/control.in              |   2 +
>  2 files changed, 128 insertions(+), 2 deletions(-)
> 
> diff --git a/data/PVE/API2/ClusterConfig.pm b/data/PVE/API2/ClusterConfig.pm
> index ca2e038..add9a13 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',
> @@ -308,6 +309,129 @@ __PACKAGE__->register_method ({
>  	return undef;
>      }});
>  
> +__PACKAGE__->register_method ({
> +    name => 'join',
> +    path => 'join',
> +    method => 'POST',
> +    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 => {
> +		description => "SSL certificate fingerprint. Optional in CLI environment.",
> +		type => 'string',
> +		pattern => '^(:?[A-Z0-9][A-Z0-9]:){31}[A-Z0-9][A-Z0-9]$',
> +		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 at pam',
> +		password => $param->{password},
> +		cookie_name => 'PVEAuthCookie',
> +		protocol => 'https',
> +		host => $host,
> +		port => 8006,
> +	    };
> +
> +	    if (my $fp = $param->{fingerprint}) {
> +		$conn_args->{cached_fingerprints} = { $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 = { node => $nodename };
> +	    $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", $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
> pve-devel at pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel




More information about the pve-devel mailing list