[pve-devel] [PATCH v2 cluster 1/4] Add functions to resolve hostnames and iterate corosync nodes

Stefan Reiter s.reiter at proxmox.com
Mon Jul 1 17:22:14 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>
---

v2:
 * resolve_hostname_like_corosync now returns IP addresses if passed
   (this simplifies the iterator function)
 * Use existing nodelist() function
 * Use PVE::Tools::* instead of exporting getaddrinfo-related functions
 * Move IP-version check into iterator function

 data/PVE/Corosync.pm | 89 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/data/PVE/Corosync.pm b/data/PVE/Corosync.pm
index e73e51d..3028e26 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";
 
@@ -256,4 +259,90 @@ 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($version) && defined($ip_version) && $version != $ip_version;
+
+		$func->($node_name, $ip, $version, $node_key)
+		    if defined($ip);
+	    }
+	}
+    }
+}
+
+# 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};
+    if (defined($corosync_strategy)) {
+	$corosync_strategy = lc $corosync_strategy;
+    } else {
+	$corosync_strategy = "ipv6-4"; # corosync default
+    }
+
+    my $resolved_ip4;
+    my $resolved_ip6;
+
+    my @resolved_raw;
+    eval { @resolved_raw = PVE::Tools::getaddrinfo_all($hostname); };
+
+    # ignore resolving error, check if an IP was passed instead
+    if ($@ || !@resolved_raw) {
+	if ($hostname =~ m/^(?:$IPV4RE)$/) {
+	    return ($hostname, 4);
+	} elsif ($hostname =~ m/^(?:$IPV6RE)$/) {
+	    return ($hostname, 6);
+	}
+
+	return (undef, undef);
+    }
+
+    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);
+	}
+
+	last if defined($resolved_ip4) && defined($resolved_ip6);
+    }
+
+    # corosync_strategy specifies the order in which IP addresses are resolved
+    # by corosync. We need to match that order, to ensure we create firewall
+    # rules for the correct address family.
+    my $resolved_ip;
+    if ($corosync_strategy eq "ipv4") {
+	$resolved_ip = $resolved_ip4;
+    } elsif ($corosync_strategy eq "ipv6") {
+	$resolved_ip = $resolved_ip6;
+    } elsif ($corosync_strategy eq "ipv6-4") {
+	$resolved_ip = $resolved_ip6 // $resolved_ip4;
+    } elsif ($corosync_strategy eq "ipv4-6") {
+	$resolved_ip = $resolved_ip4 // $resolved_ip6;
+    }
+
+    return (undef, undef) if !defined($resolved_ip);
+
+    my $ip_version = defined($resolved_ip4) && $resolved_ip eq $resolved_ip4 ? 4 : 6;
+    return ($resolved_ip, $ip_version);
+}
+
 1;
-- 
2.20.1





More information about the pve-devel mailing list