[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