[pve-devel] [PATCH cluster 2/5] Enable support for up to 8 corosync links
Stefan Reiter
s.reiter at proxmox.com
Wed Nov 20 17:43:03 CET 2019
add_corosync_link_properties/extract_corosync_link_args are introduced
as helpers to avoid hardcoding links in parameters=>properties on
several occasions, while still providing autocompletion with pvecm by
being seperate parameters instead of an array.
Maximum number of links is given as constant MAX_LINK_COUNT, should it
change in the future.
All necessary functions have been updated to
use the new $links array format instead of seperate $link0/$link1
parameters, and call sites changed accordingly.
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
This patch intentionally removes some verification and fallback code to be
reintroduced in the next one.
data/PVE/API2/ClusterConfig.pm | 51 +++++++------------
data/PVE/CLI/pvecm.pm | 20 ++++----
data/PVE/Cluster/Setup.pm | 22 +++++----
data/PVE/Corosync.pm | 89 +++++++++++++++++++++++-----------
4 files changed, 100 insertions(+), 82 deletions(-)
diff --git a/data/PVE/API2/ClusterConfig.pm b/data/PVE/API2/ClusterConfig.pm
index 3d778b8..241c14e 100644
--- a/data/PVE/API2/ClusterConfig.pm
+++ b/data/PVE/API2/ClusterConfig.pm
@@ -68,10 +68,11 @@ __PACKAGE__->register_method ({
path => '',
method => 'POST',
protected => 1,
- description => "Generate new cluster configuration.",
+ description => "Generate new cluster configuration. If no links given,"
+ . " default to local IP address as link0.",
parameters => {
additionalProperties => 0,
- properties => {
+ properties => PVE::Corosync::add_corosync_link_properties({
clustername => {
description => "The name of the cluster.",
type => 'string', format => 'pve-node',
@@ -84,9 +85,7 @@ __PACKAGE__->register_method ({
minimum => 1,
optional => 1,
},
- link0 => get_standard_option('corosync-link'),
- link1 => get_standard_option('corosync-link'),
- },
+ }),
},
returns => { type => 'string' },
code => sub {
@@ -110,7 +109,7 @@ __PACKAGE__->register_method ({
my $nodename = PVE::INotify::nodename();
# get the corosync basis config for the new cluster
- my $config = PVE::Corosync::create_conf($nodename, %$param);
+ my $config = PVE::Corosync::create_conf($nodename, $param);
print "Writing corosync config to /etc/pve/corosync.conf\n";
PVE::Corosync::atomic_write_conf($config);
@@ -194,7 +193,7 @@ __PACKAGE__->register_method ({
description => "Adds a node to the cluster configuration. This call is for internal use.",
parameters => {
additionalProperties => 0,
- properties => {
+ properties => PVE::Corosync::add_corosync_link_properties({
node => get_standard_option('pve-node'),
nodeid => get_standard_option('corosync-nodeid'),
votes => {
@@ -208,9 +207,7 @@ __PACKAGE__->register_method ({
description => "Do not throw error if node already exists.",
optional => 1,
},
- link0 => get_standard_option('corosync-link'),
- link1 => get_standard_option('corosync-link'),
- },
+ }),
},
returns => {
type => "object",
@@ -256,7 +253,7 @@ __PACKAGE__->register_method ({
while (my ($k, $v) = each %$nodelist) {
next if $k eq $name; # allows re-adding a node if force is set
- for my $linknumber (0..1) {
+ for my $linknumber (0..$PVE::Corosync::max_link_iter) {
my $id = "ring${linknumber}_addr";
next if !defined($v->{$id});
@@ -266,20 +263,10 @@ __PACKAGE__->register_method ({
}
};
- my $link0 = PVE::Corosync::parse_corosync_link($param->{link0});
- my $link1 = PVE::Corosync::parse_corosync_link($param->{link1});
-
- $check_duplicate_addr->($link0);
- $check_duplicate_addr->($link1);
-
- # FIXME: handle all links (0-7), they're all independent now
- $link0->{address} //= $name if exists($totem_cfg->{interface}->{0});
-
- die "corosync: using 'link1' parameter needs a interface with linknumber '1' configured!\n"
- if $link1 && !defined($totem_cfg->{interface}->{1});
-
- die "corosync: totem interface with linknumber 1 configured but 'link1' parameter not defined!\n"
- if defined($totem_cfg->{interface}->{1}) && !defined($link1);
+ my $links = PVE::Corosync::extract_corosync_link_args($param);
+ foreach my $link (values %$links) {
+ $check_duplicate_addr->($link);
+ }
if (defined(my $res = $nodelist->{$name})) {
$param->{nodeid} = $res->{nodeid} if !$param->{nodeid};
@@ -317,13 +304,15 @@ __PACKAGE__->register_method ({
warn $@ if $@;
$nodelist->{$name} = {
- ring0_addr => $link0->{address},
nodeid => $param->{nodeid},
name => $name,
};
- $nodelist->{$name}->{ring1_addr} = $link1->{address} if defined($link1);
$nodelist->{$name}->{quorum_votes} = $param->{votes} if $param->{votes};
+ foreach my $link (keys %$links) {
+ $nodelist->{$name}->{"ring${link}_addr"} = $links->{$link}->{address};
+ }
+
PVE::Cluster::log_msg('notice', 'root at pam', "adding node $name to cluster");
PVE::Corosync::update_nodelist($conf, $nodelist);
@@ -495,7 +484,7 @@ __PACKAGE__->register_method ({
description => "Joins this node into an existing cluster.",
parameters => {
additionalProperties => 0,
- properties => {
+ properties => PVE::Corosync::add_corosync_link_properties({
hostname => {
type => 'string',
description => "Hostname (or IP) of an existing cluster member."
@@ -512,17 +501,13 @@ __PACKAGE__->register_method ({
description => "Do not throw error if node already exists.",
optional => 1,
},
- link0 => get_standard_option('corosync-link', {
- default => "IP resolved by node's hostname",
- }),
- link1 => get_standard_option('corosync-link'),
fingerprint => get_standard_option('fingerprint-sha256'),
password => {
description => "Superuser (root) password of peer node.",
type => 'string',
maxLength => 128,
},
- },
+ }),
},
returns => { type => 'string' },
code => sub {
diff --git a/data/PVE/CLI/pvecm.pm b/data/PVE/CLI/pvecm.pm
index 172ef8c..5856b56 100755
--- a/data/PVE/CLI/pvecm.pm
+++ b/data/PVE/CLI/pvecm.pm
@@ -316,7 +316,7 @@ __PACKAGE__->register_method ({
description => "Adds the current node to an existing cluster.",
parameters => {
additionalProperties => 0,
- properties => {
+ properties => PVE::Corosync::add_corosync_link_properties({
hostname => {
type => 'string',
description => "Hostname (or IP) of an existing cluster member."
@@ -333,8 +333,6 @@ __PACKAGE__->register_method ({
description => "Do not throw error if node already exists.",
optional => 1,
},
- link0 => get_standard_option('corosync-link'),
- link1 => get_standard_option('corosync-link'),
fingerprint => get_standard_option('fingerprint-sha256', {
optional => 1,
}),
@@ -343,7 +341,7 @@ __PACKAGE__->register_method ({
description => "Always use SSH to join, even if peer may do it over API.",
optional => 1,
},
- },
+ }),
},
returns => { type => 'null' },
@@ -355,8 +353,7 @@ __PACKAGE__->register_method ({
my $host = $param->{hostname};
my $local_ip_address = PVE::Cluster::remote_node_ip($nodename);
- my $link0 = PVE::Corosync::parse_corosync_link($param->{link0});
- my $link1 = PVE::Corosync::parse_corosync_link($param->{link1});
+ my $links = PVE::Corosync::extract_corosync_link_args($param);
my $worker = sub {
@@ -381,7 +378,7 @@ __PACKAGE__->register_method ({
# allow fallback to old ssh only join if wished or needed
- PVE::Cluster::Setup::assert_joinable($local_ip_address, $link0, $link1, $param->{force});
+ PVE::Cluster::Setup::assert_joinable($local_ip_address, $links, $param->{force});
PVE::Cluster::Setup::setup_sshd_config();
PVE::Cluster::Setup::setup_rootsshconfig();
@@ -399,10 +396,11 @@ __PACKAGE__->register_method ({
push @$cmd, '--nodeid', $param->{nodeid} if $param->{nodeid};
push @$cmd, '--votes', $param->{votes} if defined($param->{votes});
- # just pass the un-parsed string through, or as we've address as
- # the default_key, we can just pass the fallback directly too
- push @$cmd, '--link0', $param->{link0} // $local_ip_address;
- push @$cmd, '--link1', $param->{link1} if defined($param->{link1});
+
+ foreach my $link (keys %$links) {
+ push @$cmd, "--link$link", PVE::JSONSchema::print_property_string(
+ $links->{$link}, get_standard_option('corosync-link'));
+ }
if (system (@$cmd) != 0) {
my $cmdtxt = join (' ', @$cmd);
diff --git a/data/PVE/Cluster/Setup.pm b/data/PVE/Cluster/Setup.pm
index 5569d6c..e8c5eca 100644
--- a/data/PVE/Cluster/Setup.pm
+++ b/data/PVE/Cluster/Setup.pm
@@ -561,7 +561,7 @@ sub gen_pve_vzdump_files {
# join helpers
sub assert_joinable {
- my ($local_addr, $link0, $link1, $force) = @_;
+ my ($local_addr, $links, $force) = @_;
my $errors = '';
my $error = sub { $errors .= "* $_[0]\n"; };
@@ -604,8 +604,12 @@ sub assert_joinable {
};
$check_ip->($local_addr, 'local node address');
- $check_ip->($link0->{address}, 'ring0') if defined($link0);
- $check_ip->($link1->{address}, 'ring1') if defined($link1);
+
+ if ($links) {
+ foreach my $link (keys %$links) {
+ $check_ip->($links->{$link}->{address}, "link$link");
+ }
+ }
if ($errors) {
warn "detected the following error(s):\n$errors";
@@ -619,11 +623,10 @@ sub join {
my $nodename = PVE::INotify::nodename();
my $local_ip_address = PVE::Cluster::remote_node_ip($nodename);
- my $link0 = PVE::Corosync::parse_corosync_link($param->{link0});
- my $link1 = PVE::Corosync::parse_corosync_link($param->{link1});
+ my $links = PVE::Corosync::extract_corosync_link_args($param);
# check if we can join with the given parameters and current node state
- assert_joinable($local_ip_address, $link0, $link1, $param->{force});
+ assert_joinable($local_ip_address, $links, $param->{force});
setup_sshd_config();
setup_rootsshconfig();
@@ -661,10 +664,9 @@ sub join {
$args->{force} = $param->{force} if defined($param->{force});
$args->{nodeid} = $param->{nodeid} if $param->{nodeid};
$args->{votes} = $param->{votes} if defined($param->{votes});
- # just pass the un-parsed string through, or as we've address as the
- # default_key, we can just pass the fallback directly too
- $args->{link0} = $param->{link0} // $local_ip_address;
- $args->{link1} = $param->{link1} if defined($param->{link1});
+ foreach my $link (keys %$links) {
+ $args->{"link$link"} = PVE::Corosync::print_corosync_link($links->{$link});
+ }
print "Request addition of this node\n";
my $res = eval { $conn->post("/cluster/config/nodes/$nodename", $args); };
diff --git a/data/PVE/Corosync.pm b/data/PVE/Corosync.pm
index 5973aaf..69927ff 100644
--- a/data/PVE/Corosync.pm
+++ b/data/PVE/Corosync.pm
@@ -35,12 +35,15 @@ my $corosync_link_format = {
minimum => 0,
maximum => 255,
default => 0,
- description => "The priority for the link when knet is used in 'passive' mode. Lower value means higher priority.",
+ description => "The priority for the link when knet is used in 'passive'"
+ . " mode (default). Lower value means higher priority. Only"
+ . " valid for cluster create, ignored on node add.",
},
};
my $corosync_link_desc = {
type => 'string', format => $corosync_link_format,
- description => "Address and priority information of a single corosync link.",
+ description => "Address and priority information of a single corosync link."
+ . " (up to 8 links supported; link0..link7)",
optional => 1,
};
PVE::JSONSchema::register_standard_option("corosync-link", $corosync_link_desc);
@@ -53,6 +56,39 @@ sub parse_corosync_link {
return PVE::JSONSchema::parse_property_string($corosync_link_format, $value);
}
+sub print_corosync_link {
+ my ($link) = @_;
+
+ return undef if !defined($link);
+
+ return PVE::JSONSchema::print_property_string($link, $corosync_link_format);
+}
+
+use constant MAX_LINK_COUNT => 8;
+our $max_link_iter = MAX_LINK_COUNT - 1;
+
+sub add_corosync_link_properties {
+ my ($prop) = @_;
+
+ for my $lnum (0..$max_link_iter) {
+ $prop->{"link$lnum"} = PVE::JSONSchema::get_standard_option("corosync-link");
+ }
+
+ return $prop;
+}
+
+sub extract_corosync_link_args {
+ my ($args) = @_;
+
+ my $links = {};
+ for my $lnum (0..$max_link_iter) {
+ $links->{$lnum} = parse_corosync_link($args->{"link$lnum"})
+ if $args->{"link$lnum"};
+ }
+
+ return $links;
+}
+
# a very simply parser ...
sub parse_conf {
my ($filename, $raw) = @_;
@@ -234,16 +270,19 @@ sub atomic_write_conf {
# for creating a new cluster with the current node
# params are those from the API/CLI cluster create call
sub create_conf {
- my ($nodename, %param) = @_;
+ my ($nodename, $param) = @_;
- my $clustername = $param{clustername};
- my $nodeid = $param{nodeid} || 1;
- my $votes = $param{votes} || 1;
+ my $clustername = $param->{clustername};
+ my $nodeid = $param->{nodeid} || 1;
+ my $votes = $param->{votes} || 1;
my $local_ip_address = PVE::Cluster::remote_node_ip($nodename);
- my $link0 = PVE::Cluster::parse_corosync_link($param{link0});
- $link0->{address} //= $local_ip_address;
+ my $links = extract_corosync_link_args($param);
+
+ # if no links given, fall back to local IP as link0
+ $links->{0} = { address => $local_ip_address }
+ if !%$links;
my $conf = {
totem => {
@@ -252,11 +291,8 @@ sub create_conf {
cluster_name => $clustername,
config_version => 0,
ip_version => 'ipv4-6',
- interface => {
- 0 => {
- linknumber => 0,
- },
- },
+ link_mode => 'passive',
+ interface => {},
},
nodelist => {
node => {
@@ -264,7 +300,6 @@ sub create_conf {
name => $nodename,
nodeid => $nodeid,
quorum_votes => $votes,
- ring0_addr => $link0->{address},
},
},
},
@@ -277,19 +312,17 @@ sub create_conf {
},
};
my $totem = $conf->{totem};
+ my $node = $conf->{nodelist}->{node}->{$nodename};
+
+ foreach my $lnum (keys %$links) {
+ my $link = $links->{$lnum};
+
+ $totem->{interface}->{$lnum} = { linknumber => $lnum };
+
+ my $prio = $link->{priority};
+ $totem->{interface}->{$lnum}->{knet_link_priority} = $prio if $prio;
- $totem->{interface}->{0}->{knet_link_priority} = $link0->{priority}
- if defined($link0->{priority});
-
- my $link1 = PVE::Cluster::parse_corosync_link($param{link1});
- if ($link1->{address}) {
- $conf->{totem}->{interface}->{1} = {
- linknumber => 1,
- };
- $totem->{link_mode} = 'passive';
- $totem->{interface}->{1}->{knet_link_priority} = $link1->{priority}
- if defined($link1->{priority});
- $conf->{nodelist}->{node}->{$nodename}->{ring1_addr} = $link1->{address};
+ $node->{"ring${lnum}_addr"} = $link->{address};
}
return { main => $conf };
@@ -354,7 +387,7 @@ sub verify_conf {
if (%$interfaces) {
# if interfaces are defined, *all* links must have a matching interface
# definition, and vice versa
- for my $link (0..1) {
+ for my $link (0..$max_link_iter) {
my $have_interface = defined($interfaces->{$link});
foreach my $node (@node_names) {
my ($optname, $addr) = $find_option->($nodelist->{$node}, $link);
@@ -372,7 +405,7 @@ sub verify_conf {
}
} else {
# without interfaces, only check that links are consistent among nodes
- for my $link (0..1) {
+ for my $link (0..$max_link_iter) {
my $nodes_with_link = {};
foreach my $node (@node_names) {
my ($optname, $addr) = $find_option->($nodelist->{$node}, $link);
--
2.20.1
More information about the pve-devel
mailing list