[pve-devel] [PATCH cluster 1/7] Add functions to resolve hostnames and iterate corosync nodes
Stefan Reiter
s.reiter at proxmox.com
Mon Jul 22 15:21:49 CEST 2019
The sub 'for_all_corosync_addresses' iterates through all nodes in a
passed corosync config and calls a specified function for every ringX_addr
on every node it finds (provided the IP-version matches the specified
one or undef was specified).
All ringX_addr entries that cannot be parsed as an IP address will be
best-effort resolved as hostnames. This has to happen in the exact same
way as corosync does internally, to ensure consistency with firewall
rules.
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
(cherry picked from commit 5c82c8c84c178fd54d1a610d8f4c718a28f4df14)
Includes modifications for stable-5 (corosync 2 resolves differently) as
well as the following fixups:
corosync: stylistic changes
Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
(cherry picked from commit 53d5168dff4b6a174f4dc48b7403171ac163844a)
corosync: refactor and reuse IP RE matching
this avoids calling getaddrinfo for the usual case of plain IPs, and
makes the code a bit easier to read as well.
Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
(cherry picked from commit 3e067ee35cea12f93d4a4fb48a2ed7376b838fa9)
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
data/PVE/Corosync.pm | 95 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
Squashed the followups together here, to keep the series a bit shorter (corosync
2 edits are on top of the followup'd code). Hope that's okay :)
diff --git a/data/PVE/Corosync.pm b/data/PVE/Corosync.pm
index 2fb392a..6a27ab0 100644
--- a/data/PVE/Corosync.pm
+++ b/data/PVE/Corosync.pm
@@ -5,9 +5,12 @@ use warnings;
use Digest::SHA;
use Clone 'clone';
+use Socket qw(AF_INET AF_INET6 inet_ntop);
use Net::IP qw(ip_is_ipv6);
use PVE::Cluster;
+use PVE::Tools;
+use PVE::Tools qw($IPV4RE $IPV6RE);
my $basedir = "/etc/pve";
@@ -264,4 +267,96 @@ sub create_conf {
return { main => $conf };
}
+sub for_all_corosync_addresses {
+ my ($corosync_conf, $ip_version, $func) = @_;
+
+ my $nodelist = nodelist($corosync_conf);
+ return if !defined($nodelist);
+
+ # iterate sorted to make rules deterministic (for change detection)
+ foreach my $node_name (sort keys %$nodelist) {
+ my $node_config = $nodelist->{$node_name};
+ foreach my $node_key (sort keys %$node_config) {
+ if ($node_key =~ /^(ring|link)\d+_addr$/) {
+ my $node_address = $node_config->{$node_key};
+
+ my($ip, $version) = resolve_hostname_like_corosync($node_address, $corosync_conf);
+ next if !defined($ip);
+ next if defined($version) && defined($ip_version) && $version != $ip_version;
+
+ $func->($node_name, $ip, $version, $node_key);
+ }
+ }
+ }
+}
+
+# NOTE: Corosync actually only resolves on startup or config change, but we
+# currently do not have an easy way to synchronize our behaviour to that.
+sub resolve_hostname_like_corosync {
+ my ($hostname, $corosync_conf) = @_;
+
+ my $corosync_strategy = $corosync_conf->{main}->{totem}->{ip_version};
+ $corosync_strategy = lc ($corosync_strategy // "any");
+
+ my $match_ip_and_version = sub {
+ my ($addr) = @_;
+
+ return undef if !defined($addr);
+
+ if ($addr =~ m/^$IPV4RE$/) {
+ return ($addr, 4);
+ } elsif ($addr =~ m/^$IPV6RE$/) {
+ return ($addr, 6);
+ }
+
+ return undef;
+ };
+
+ my ($resolved_ip, $ip_version) = $match_ip_and_version->($hostname);
+
+ return ($resolved_ip, $ip_version) if defined($resolved_ip);
+
+ my $resolved_ip4;
+ my $resolved_ip6;
+
+ my @resolved_raw;
+ eval { @resolved_raw = PVE::Tools::getaddrinfo_all($hostname); };
+
+ return undef if ($@ || !@resolved_raw);
+
+ foreach my $socket_info (@resolved_raw) {
+ next if !$socket_info->{addr};
+
+ my ($family, undef, $host) = PVE::Tools::unpack_sockaddr_in46($socket_info->{addr});
+
+ if ($family == AF_INET && !defined($resolved_ip4)) {
+ $resolved_ip4 = inet_ntop(AF_INET, $host);
+ } elsif ($family == AF_INET6 && !defined($resolved_ip6)) {
+ $resolved_ip6 = inet_ntop(AF_INET6, $host);
+ }
+
+ if ($corosync_strategy eq "any"
+ && ($family == AF_INET || $family == AF_INET6)) {
+ # "any" means return first one found by getaddrinfo
+ return $match_ip_and_version->($resolved_ip4 // $resolved_ip6);
+ }
+
+ last if defined($resolved_ip4) && defined($resolved_ip6);
+ }
+
+ # corosync_strategy specifies the which IP address family is resolved by
+ # corosync. We need to match that, to ensure we create firewall rules for
+ # the correct one.
+ if ($corosync_strategy eq "ipv4") {
+ $resolved_ip = $resolved_ip4;
+ } elsif ($corosync_strategy eq "ipv6") {
+ $resolved_ip = $resolved_ip6;
+ } elsif ($corosync_strategy eq "any") {
+ # shouldn't get here, but just in case
+ $resolved_ip = $resolved_ip4 // $resolved_ip6;
+ }
+
+ return $match_ip_and_version->($resolved_ip);
+}
+
1;
--
2.20.1
More information about the pve-devel
mailing list