[pve-devel] [RFC cluster] factor out corosync methods to own module
Thomas Lamprecht
t.lamprecht at proxmox.com
Fri Jun 9 13:23:05 CEST 2017
On 06/09/2017 01:06 PM, Fabian Grünbichler wrote:
>> Thomas Lamprecht <t.lamprecht at proxmox.com> hat am 9. Juni 2017 um 12:56 geschrieben:
>>
>>
>> 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>
> +1
>
> Could we then drop the corosync_ prefix from method names?
yes, I agree. I just wanted to be sure that it's clear I did not change
semantics.
But now I think that it probably would have been better to do it
here.already :-)
>> ---
>>
>> 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
>>
>>
>> _______________________________________________
>> 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