[pve-devel] [RFC cluster] factor out corosync methods to own module
Fabian Grünbichler
f.gruenbichler at proxmox.com
Fri Jun 9 13:06:28 CEST 2017
> 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?
> ---
>
> 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