[pve-devel] [RFC cluster] factor out corosync methods to own module

Thomas Lamprecht t.lamprecht at proxmox.com
Fri Jun 9 12:56:01 CEST 2017


PVE::Cluster is already quite big, the corosync part is ~250 lines
long of 1900 total. Further the corosync part is only needed in a few
specialised places (API2/ClusterConfig and CLI/pvecm).
This speaks for factoring out this part in a separate perl module as
most modules which use Cluster load the corosync parts for no reason.
Further, cluster handling through API may even add more corosync
related methods.

Signed-off-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
---

 data/PVE/API2/ClusterConfig.pm |   6 +-
 data/PVE/CLI/pvecm.pm          |  17 +--
 data/PVE/Cluster.pm            | 245 ---------------------------------------
 data/PVE/Corosync.pm           | 257 +++++++++++++++++++++++++++++++++++++++++
 data/PVE/Makefile.am           |   2 +-
 5 files changed, 271 insertions(+), 256 deletions(-)
 create mode 100644 data/PVE/Corosync.pm

diff --git a/data/PVE/API2/ClusterConfig.pm b/data/PVE/API2/ClusterConfig.pm
index 65b376f..c41b476 100644
--- a/data/PVE/API2/ClusterConfig.pm
+++ b/data/PVE/API2/ClusterConfig.pm
@@ -2,12 +2,14 @@ package PVE::API2::ClusterConfig;
 
 use strict;
 use warnings;
+
 use PVE::Tools;
 use PVE::SafeSyslog;
 use PVE::RESTHandler;
 use PVE::RPCEnvironment;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Cluster;
+use PVE::Corosync;
 
 use base qw(PVE::RESTHandler);
 
@@ -69,7 +71,7 @@ __PACKAGE__->register_method({
 
 
 	my $conf = PVE::Cluster::cfs_read_file('corosync.conf');
-	my $nodelist = PVE::Cluster::corosync_nodelist($conf);
+	my $nodelist = PVE::Corosync::corosync_nodelist($conf);
 
 	return PVE::RESTHandler::hash_to_array($nodelist, 'node');
     }});
@@ -96,7 +98,7 @@ __PACKAGE__->register_method({
 
 	my $conf = PVE::Cluster::cfs_read_file('corosync.conf');
 
-	return PVE::Cluster::corosync_totem_config($conf);
+	return PVE::Corosync::corosync_totem_config($conf);
     }});
 
 1;
diff --git a/data/PVE/CLI/pvecm.pm b/data/PVE/CLI/pvecm.pm
index 6cd5084..d8ce424 100755
--- a/data/PVE/CLI/pvecm.pm
+++ b/data/PVE/CLI/pvecm.pm
@@ -16,6 +16,7 @@ use PVE::Cluster;
 use PVE::INotify;
 use PVE::JSONSchema;
 use PVE::CLIHandler;
+use PVE::Corosync;
 
 use base qw(PVE::CLIHandler);
 
@@ -312,9 +313,9 @@ __PACKAGE__->register_method ({
 
 	my $conf = PVE::Cluster::cfs_read_file("corosync.conf");
 
-	my $nodelist = PVE::Cluster::corosync_nodelist($conf);
+	my $nodelist = PVE::Corosync::corosync_nodelist($conf);
 
-	my $totem_cfg = PVE::Cluster::corosync_totem_config($conf);
+	my $totem_cfg = PVE::Corosync::corosync_totem_config($conf);
 
 	my $name = $param->{node};
 
@@ -390,7 +391,7 @@ __PACKAGE__->register_method ({
 	$nodelist->{$name}->{ring1_addr} = $param->{ring1_addr} if $param->{ring1_addr};
 	$nodelist->{$name}->{quorum_votes} = $param->{votes} if $param->{votes};
 
-	PVE::Cluster::corosync_update_nodelist($conf, $nodelist);
+	PVE::Corosync::corosync_update_nodelist($conf, $nodelist);
 
 	exit (0);
     }});
@@ -419,7 +420,7 @@ __PACKAGE__->register_method ({
 
 	my $conf = PVE::Cluster::cfs_read_file("corosync.conf");
 
-	my $nodelist = PVE::Cluster::corosync_nodelist($conf);
+	my $nodelist = PVE::Corosync::corosync_nodelist($conf);
 
 	my $node;
 	my $nodeid;
@@ -442,7 +443,7 @@ __PACKAGE__->register_method ({
 
 	delete $nodelist->{$node};
 
-	PVE::Cluster::corosync_update_nodelist($conf, $nodelist);
+	PVE::Corosync::corosync_update_nodelist($conf, $nodelist);
 
 	PVE::Tools::run_command(['corosync-cfgtool','-k', $nodeid])
 	    if defined($nodeid);
@@ -674,7 +675,7 @@ __PACKAGE__->register_method ({
     code => sub {
 	my ($param) = @_;
 
-	PVE::Cluster::check_corosync_conf_exists();
+	PVE::Corosync::check_corosync_conf_exists();
 
 	my $cmd = ['corosync-quorumtool', '-siH'];
 
@@ -697,7 +698,7 @@ __PACKAGE__->register_method ({
     code => sub {
 	my ($param) = @_;
 
-	PVE::Cluster::check_corosync_conf_exists();
+	PVE::Corosync::check_corosync_conf_exists();
 
 	my $cmd = ['corosync-quorumtool', '-l'];
 
@@ -726,7 +727,7 @@ __PACKAGE__->register_method ({
     code => sub {
 	my ($param) = @_;
 
-	PVE::Cluster::check_corosync_conf_exists();
+	PVE::Corosync::check_corosync_conf_exists();
 
 	my $cmd = ['corosync-quorumtool', '-e', $param->{expected}];
 
diff --git a/data/PVE/Cluster.pm b/data/PVE/Cluster.pm
index 731acc5..66bcc7b 100644
--- a/data/PVE/Cluster.pm
+++ b/data/PVE/Cluster.pm
@@ -1433,251 +1433,6 @@ cfs_register_file('datacenter.cfg',
 		  \&parse_datacenter_config,
 		  \&write_datacenter_config);
 
-# a very simply parser ...
-sub parse_corosync_conf {
-    my ($filename, $raw) = @_;
-
-    return {} if !$raw;
-
-    my $digest = Digest::SHA::sha1_hex(defined($raw) ? $raw : '');
-
-    $raw =~ s/#.*$//mg;
-    $raw =~ s/\r?\n/ /g;
-    $raw =~ s/\s+/ /g;
-    $raw =~ s/^\s+//;
-    $raw =~ s/\s*$//;
-
-    my @tokens = split(/\s/, $raw);
-
-    my $conf = { section => 'main', children => [] };
-
-    my $stack = [];
-    my $section = $conf;
-
-    while (defined(my $token = shift @tokens)) {
-	my $nexttok = $tokens[0];
-
-	if ($nexttok && ($nexttok eq '{')) {
-	    shift @tokens; # skip '{'
-	    my $new_section = {
-		section => $token,
-		children => [],
-	    };
-	    push @{$section->{children}}, $new_section;
-	    push @$stack, $section;
-	    $section = $new_section;
-	    next;
-	}
-
-	if ($token eq '}') {
-	    $section = pop @$stack;
-	    die "parse error - uncexpected '}'\n" if !$section;
-	    next;
-	}
-
-	my $key = $token;
-	die "missing ':' after key '$key'\n" if ! ($key =~ s/:$//);
-
-	die "parse error - no value for '$key'\n" if !defined($nexttok);
-	my $value = shift @tokens;
-
-	push @{$section->{children}}, { key => $key, value => $value };
-    }
-
-    $conf->{digest} = $digest;
-
-    return $conf;
-}
-
-my $dump_corosync_section;
-$dump_corosync_section = sub {
-    my ($section, $prefix) = @_;
-
-    my $raw = $prefix . $section->{section} . " {\n";
-
-    my @list = grep { defined($_->{key}) } @{$section->{children}};
-    foreach my $child (sort {$a->{key} cmp $b->{key}} @list) {
-	$raw .= $prefix . "  $child->{key}: $child->{value}\n";
-    }
-
-    @list = grep { defined($_->{section}) } @{$section->{children}};
-    foreach my $child (sort {$a->{section} cmp $b->{section}} @list) {
-	$raw .= &$dump_corosync_section($child, "$prefix  ");
-    }
-
-    $raw .= $prefix . "}\n\n";
-
-    return $raw;
-
-};
-
-sub write_corosync_conf {
-    my ($filename, $conf) = @_;
-
-    my $raw = '';
-
-    my $prefix = '';
-
-    die "no main section" if $conf->{section} ne 'main';
-
-    my @list = grep { defined($_->{key}) } @{$conf->{children}};
-    foreach my $child (sort {$a->{key} cmp $b->{key}} @list) {
-	$raw .= "$child->{key}: $child->{value}\n";
-    }
-
-    @list = grep { defined($_->{section}) } @{$conf->{children}};
-    foreach my $child (sort {$a->{section} cmp $b->{section}} @list) {
-	$raw .= &$dump_corosync_section($child, $prefix);
-    }
-
-    return $raw;
-}
-
-sub corosync_conf_version {
-    my ($conf, $noerr, $new_value) = @_;
-
-    foreach my $child (@{$conf->{children}}) {
-	next if !defined($child->{section});
-	if ($child->{section} eq 'totem') {
-	    foreach my $e (@{$child->{children}}) {
-		next if !defined($e->{key});
-		if ($e->{key} eq 'config_version') {
-		    if ($new_value) {
-			$e->{value} = $new_value;
-			return $new_value;
-		    } elsif (my $version = int($e->{value})) {
-			return $version;
-		    }
-		    last;
-		}
-	    }
-	}
-    }
-
-    return undef if $noerr;
-
-    die "invalid corosync config - unable to read version\n";
-}
-
-# read only - use "rename corosync.conf.new corosync.conf" to write
-PVE::Cluster::cfs_register_file('corosync.conf', \&parse_corosync_conf);
-# this is read/write
-PVE::Cluster::cfs_register_file('corosync.conf.new', \&parse_corosync_conf,
-				\&write_corosync_conf);
-
-sub check_corosync_conf_exists {
-    my ($silent) = @_;
-
-    $silent = $silent // 0;
-
-    my $exists = -f "$basedir/corosync.conf";
-
-    warn "Corosync config '$basedir/corosync.conf' does not exist - is this node part of a cluster?\n"
-	if !$silent && !$exists;
-
-    return $exists;
-}
-
-sub corosync_update_nodelist {
-    my ($conf, $nodelist) = @_;
-
-    delete $conf->{digest};
-
-    my $version = corosync_conf_version($conf);
-    corosync_conf_version($conf, undef, $version + 1);
-
-    my $children = [];
-    foreach my $v (values %$nodelist) {
-	next if !($v->{ring0_addr} || $v->{name});
-	my $kv = [];
-	foreach my $k (keys %$v) {
-	    push @$kv, { key => $k, value => $v->{$k} };
-	}
-	my $ns = { section => 'node', children => $kv };
-	push @$children, $ns;
-    }
-
-    foreach my $main (@{$conf->{children}}) {
-	next if !defined($main->{section});
-	if ($main->{section} eq 'nodelist') {
-	    $main->{children} = $children;
-	    last;
-	}
-    }
-
-
-    cfs_write_file("corosync.conf.new", $conf);
-
-    rename("/etc/pve/corosync.conf.new", "/etc/pve/corosync.conf")
-	|| die "activate  corosync.conf.new failed - $!\n";
-}
-
-sub corosync_nodelist {
-    my ($conf) = @_;
-
-    my $nodelist = {};
-
-    foreach my $main (@{$conf->{children}}) {
-	next if !defined($main->{section});
-	if ($main->{section} eq 'nodelist') {
-	    foreach my $ne (@{$main->{children}}) {
-		next if !defined($ne->{section}) || ($ne->{section} ne 'node');
-		my $node = { quorum_votes => 1 };
-		my $name;
-		foreach my $child (@{$ne->{children}}) {
-		    next if !defined($child->{key});
-		    $node->{$child->{key}} = $child->{value};
-		    # use 'name' over 'ring0_addr' if set
-		    if ($child->{key} eq 'name') {
-			delete $nodelist->{$name} if $name;
-			$name = $child->{value};
-			$nodelist->{$name} = $node;
-		    } elsif(!$name && $child->{key} eq 'ring0_addr') {
-			$name = $child->{value};
-			$nodelist->{$name} = $node;
-		    }
-		}
-	    }
-	}
-    }
-
-    return $nodelist;
-}
-
-# get a hash representation of the corosync config totem section
-sub corosync_totem_config {
-    my ($conf) = @_;
-
-    my $res = {};
-
-    foreach my $main (@{$conf->{children}}) {
-	next if !defined($main->{section}) ||
-	    $main->{section} ne 'totem';
-
-	foreach my $e (@{$main->{children}}) {
-
-	    if ($e->{section} && $e->{section} eq 'interface') {
-		my $entry = {};
-
-		$res->{interface} = {};
-
-		foreach my $child (@{$e->{children}}) {
-		    next if !defined($child->{key});
-		    $entry->{$child->{key}} = $child->{value};
-		    if($child->{key} eq 'ringnumber') {
-			$res->{interface}->{$child->{value}} = $entry;
-		    }
-		}
-
-	    } elsif  ($e->{key}) {
-		$res->{$e->{key}} = $e->{value};
-	    }
-	}
-    }
-
-    return $res;
-}
-
 # X509 Certificate cache helper
 
 my $cert_cache_nodes = {};
diff --git a/data/PVE/Corosync.pm b/data/PVE/Corosync.pm
new file mode 100644
index 0000000..6a0e486
--- /dev/null
+++ b/data/PVE/Corosync.pm
@@ -0,0 +1,257 @@
+package PVE::Corosync;
+
+use strict;
+use warnings;
+
+use Digest::SHA;
+
+use PVE::Cluster;
+
+my $basedir = "/etc/pve";
+
+# a very simply parser ...
+sub parse_corosync_conf {
+    my ($filename, $raw) = @_;
+
+    return {} if !$raw;
+
+    my $digest = Digest::SHA::sha1_hex(defined($raw) ? $raw : '');
+
+    $raw =~ s/#.*$//mg;
+    $raw =~ s/\r?\n/ /g;
+    $raw =~ s/\s+/ /g;
+    $raw =~ s/^\s+//;
+    $raw =~ s/\s*$//;
+
+    my @tokens = split(/\s/, $raw);
+
+    my $conf = { section => 'main', children => [] };
+
+    my $stack = [];
+    my $section = $conf;
+
+    while (defined(my $token = shift @tokens)) {
+	my $nexttok = $tokens[0];
+
+	if ($nexttok && ($nexttok eq '{')) {
+	    shift @tokens; # skip '{'
+	    my $new_section = {
+		section => $token,
+		children => [],
+	    };
+	    push @{$section->{children}}, $new_section;
+	    push @$stack, $section;
+	    $section = $new_section;
+	    next;
+	}
+
+	if ($token eq '}') {
+	    $section = pop @$stack;
+	    die "parse error - uncexpected '}'\n" if !$section;
+	    next;
+	}
+
+	my $key = $token;
+	die "missing ':' after key '$key'\n" if ! ($key =~ s/:$//);
+
+	die "parse error - no value for '$key'\n" if !defined($nexttok);
+	my $value = shift @tokens;
+
+	push @{$section->{children}}, { key => $key, value => $value };
+    }
+
+    $conf->{digest} = $digest;
+
+    return $conf;
+}
+
+my $dump_corosync_section;
+$dump_corosync_section = sub {
+    my ($section, $prefix) = @_;
+
+    my $raw = $prefix . $section->{section} . " {\n";
+
+    my @list = grep { defined($_->{key}) } @{$section->{children}};
+    foreach my $child (sort {$a->{key} cmp $b->{key}} @list) {
+	$raw .= $prefix . "  $child->{key}: $child->{value}\n";
+    }
+
+    @list = grep { defined($_->{section}) } @{$section->{children}};
+    foreach my $child (sort {$a->{section} cmp $b->{section}} @list) {
+	$raw .= &$dump_corosync_section($child, "$prefix  ");
+    }
+
+    $raw .= $prefix . "}\n\n";
+
+    return $raw;
+
+};
+
+sub write_corosync_conf {
+    my ($filename, $conf) = @_;
+
+    my $raw = '';
+
+    my $prefix = '';
+
+    die "no main section" if $conf->{section} ne 'main';
+
+    my @list = grep { defined($_->{key}) } @{$conf->{children}};
+    foreach my $child (sort {$a->{key} cmp $b->{key}} @list) {
+	$raw .= "$child->{key}: $child->{value}\n";
+    }
+
+    @list = grep { defined($_->{section}) } @{$conf->{children}};
+    foreach my $child (sort {$a->{section} cmp $b->{section}} @list) {
+	$raw .= &$dump_corosync_section($child, $prefix);
+    }
+
+    return $raw;
+}
+
+sub corosync_conf_version {
+    my ($conf, $noerr, $new_value) = @_;
+
+    foreach my $child (@{$conf->{children}}) {
+	next if !defined($child->{section});
+	if ($child->{section} eq 'totem') {
+	    foreach my $e (@{$child->{children}}) {
+		next if !defined($e->{key});
+		if ($e->{key} eq 'config_version') {
+		    if ($new_value) {
+			$e->{value} = $new_value;
+			return $new_value;
+		    } elsif (my $version = int($e->{value})) {
+			return $version;
+		    }
+		    last;
+		}
+	    }
+	}
+    }
+
+    return undef if $noerr;
+
+    die "invalid corosync config - unable to read version\n";
+}
+
+# read only - use "rename corosync.conf.new corosync.conf" to write
+PVE::Cluster::cfs_register_file('corosync.conf', \&parse_corosync_conf);
+# this is read/write
+PVE::Cluster::cfs_register_file('corosync.conf.new', \&parse_corosync_conf,
+				\&write_corosync_conf);
+
+sub check_corosync_conf_exists {
+    my ($silent) = @_;
+
+    $silent = $silent // 0;
+
+    my $exists = -f "$basedir/corosync.conf";
+
+    warn "Corosync config '$basedir/corosync.conf' does not exist - is this node part of a cluster?\n"
+	if !$silent && !$exists;
+
+    return $exists;
+}
+
+sub corosync_update_nodelist {
+    my ($conf, $nodelist) = @_;
+
+    delete $conf->{digest};
+
+    my $version = corosync_conf_version($conf);
+    corosync_conf_version($conf, undef, $version + 1);
+
+    my $children = [];
+    foreach my $v (values %$nodelist) {
+	next if !($v->{ring0_addr} || $v->{name});
+	my $kv = [];
+	foreach my $k (keys %$v) {
+	    push @$kv, { key => $k, value => $v->{$k} };
+	}
+	my $ns = { section => 'node', children => $kv };
+	push @$children, $ns;
+    }
+
+    foreach my $main (@{$conf->{children}}) {
+	next if !defined($main->{section});
+	if ($main->{section} eq 'nodelist') {
+	    $main->{children} = $children;
+	    last;
+	}
+    }
+
+
+    PVE::Cluster::cfs_write_file("corosync.conf.new", $conf);
+
+    rename("/etc/pve/corosync.conf.new", "/etc/pve/corosync.conf")
+	|| die "activate  corosync.conf.new failed - $!\n";
+}
+
+sub corosync_nodelist {
+    my ($conf) = @_;
+
+    my $nodelist = {};
+
+    foreach my $main (@{$conf->{children}}) {
+	next if !defined($main->{section});
+	if ($main->{section} eq 'nodelist') {
+	    foreach my $ne (@{$main->{children}}) {
+		next if !defined($ne->{section}) || ($ne->{section} ne 'node');
+		my $node = { quorum_votes => 1 };
+		my $name;
+		foreach my $child (@{$ne->{children}}) {
+		    next if !defined($child->{key});
+		    $node->{$child->{key}} = $child->{value};
+		    # use 'name' over 'ring0_addr' if set
+		    if ($child->{key} eq 'name') {
+			delete $nodelist->{$name} if $name;
+			$name = $child->{value};
+			$nodelist->{$name} = $node;
+		    } elsif(!$name && $child->{key} eq 'ring0_addr') {
+			$name = $child->{value};
+			$nodelist->{$name} = $node;
+		    }
+		}
+	    }
+	}
+    }
+
+    return $nodelist;
+}
+
+# get a hash representation of the corosync config totem section
+sub corosync_totem_config {
+    my ($conf) = @_;
+
+    my $res = {};
+
+    foreach my $main (@{$conf->{children}}) {
+	next if !defined($main->{section}) ||
+	    $main->{section} ne 'totem';
+
+	foreach my $e (@{$main->{children}}) {
+
+	    if ($e->{section} && $e->{section} eq 'interface') {
+		my $entry = {};
+
+		$res->{interface} = {};
+
+		foreach my $child (@{$e->{children}}) {
+		    next if !defined($child->{key});
+		    $entry->{$child->{key}} = $child->{value};
+		    if($child->{key} eq 'ringnumber') {
+			$res->{interface}->{$child->{value}} = $entry;
+		    }
+		}
+
+	    } elsif  ($e->{key}) {
+		$res->{$e->{key}} = $e->{value};
+	    }
+	}
+    }
+
+    return $res;
+}
+
+1;
diff --git a/data/PVE/Makefile.am b/data/PVE/Makefile.am
index 6a38431..1f576b0 100644
--- a/data/PVE/Makefile.am
+++ b/data/PVE/Makefile.am
@@ -25,7 +25,7 @@ man5_MANS = datacenter.cfg.5
 
 IPCC_so_SOURCES = IPCC.c ppport.h
 
-pvelib_DATA = IPCC.pm Cluster.pm
+pvelib_DATA = IPCC.pm Cluster.pm Corosync.pm
 pvelibdir = $(PERL_VENDORLIB)/PVE
 
 noinst_DATA = pvecm.bash-completion
-- 
2.11.0





More information about the pve-devel mailing list