The old parser itself was simple and easy but resulted in quite a bit of headache when changing corosync config sections, especially if multiple section levelsshould be touched.
Move to a more practical internal format which represents the corosync configuration in hash Signed-off-by: Thomas Lamprecht <t.lampre...@proxmox.com> --- changes v1 -> v2: * put section which we know that they will be arrays early in arrays. as suggested by dietmar data/PVE/API2/ClusterConfig.pm | 4 +- data/PVE/Corosync.pm | 173 +++++++++---------------- data/test/corosync_configs/multiple-nodes.conf | 6 +- data/test/corosync_configs/single-node.conf | 6 +- 4 files changed, 67 insertions(+), 122 deletions(-) diff --git a/data/PVE/API2/ClusterConfig.pm b/data/PVE/API2/ClusterConfig.pm index 354b8ad..46db3da 100644 --- a/data/PVE/API2/ClusterConfig.pm +++ b/data/PVE/API2/ClusterConfig.pm @@ -98,7 +98,9 @@ __PACKAGE__->register_method({ my $conf = PVE::Cluster::cfs_read_file('corosync.conf'); - return PVE::Corosync::totem_config($conf); + my $totem_cfg = $conf->{main}->{totem}; + + return $totem_cfg; }}); 1; diff --git a/data/PVE/Corosync.pm b/data/PVE/Corosync.pm index 59b813e..3c4c8c0 100644 --- a/data/PVE/Corosync.pm +++ b/data/PVE/Corosync.pm @@ -4,11 +4,17 @@ use strict; use warnings; use Digest::SHA; +use Clone 'clone'; use PVE::Cluster; my $basedir = "/etc/pve"; +my $conf_array_sections = { + node => 1, + interface => 1, +}; + # a very simply parser ... sub parse_conf { my ($filename, $raw) = @_; @@ -25,21 +31,25 @@ sub parse_conf { my @tokens = split(/\s/, $raw); - my $conf = { section => 'main', children => [] }; + my $conf = { 'main' => {} }; my $stack = []; - my $section = $conf; + my $section = $conf->{main}; 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; + my $new_section = {}; + if ($conf_array_sections->{$token}) { + $section->{$token} = [] if !defined($section->{$token}); + push @{$section->{$token}}, $new_section; + } elsif (!defined($section->{$token})) { + $section->{$token} = $new_section; + } else { + die "section '$token' already exists and not marked as array!\n"; + } push @$stack, $section; $section = $new_section; next; @@ -57,7 +67,7 @@ sub parse_conf { die "parse error - no value for '$key'\n" if !defined($nexttok); my $value = shift @tokens; - push @{$section->{children}}, { key => $key, value => $value }; + $section->{$key} = $value; } $conf->{digest} = $digest; @@ -69,20 +79,28 @@ my $dump_section; $dump_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"; - } + my $raw = ''; - @list = grep { defined($_->{section}) } @{$section->{children}}; - foreach my $child (sort {$a->{section} cmp $b->{section}} @list) { - $raw .= &$dump_section($child, "$prefix "); + foreach my $k (sort keys %$section) { + my $v = $section->{$k}; + if (ref($v) eq 'HASH') { + $raw .= $prefix . "$k {\n"; + $raw .= &$dump_section($v, "$prefix "); + $raw .= $prefix . "}\n"; + $raw .= "\n" if !$prefix; # add extra newline at 1st level only + } elsif (ref($v) eq 'ARRAY') { + foreach my $child (@$v) { + $raw .= $prefix . "$k {\n"; + $raw .= &$dump_section($child, "$prefix "); + $raw .= $prefix . "}\n"; + } + } elsif (!ref($v)) { + $raw .= $prefix . "$k: $v\n"; + } else { + die "unexpected reference in config hash: $k => ". ref($v) ."\n"; + } } - $raw .= $prefix . "}\n\n"; - return $raw; }; @@ -90,21 +108,9 @@ $dump_section = sub { sub write_conf { my ($filename, $conf) = @_; - my $raw = ''; - - my $prefix = ''; - - die "no main section" if $conf->{section} ne 'main'; + die "no main section" if !defined($conf->{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_section($child, $prefix); - } + my $raw = &$dump_section($conf->{main}, ''); return $raw; } @@ -112,22 +118,10 @@ sub write_conf { sub 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; - } - } - } + my $totem = $conf->{main}->{totem}; + if (defined($totem) && defined($totem->{config_version})) { + $totem->{config_version} = $new_value if $new_value; + return $totem->{config_version}; } return undef if $noerr; @@ -162,25 +156,7 @@ sub update_nodelist { my $version = conf_version($conf); 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; - } - } - + $conf->{main}->{nodelist}->{node} = [values %$nodelist]; PVE::Cluster::cfs_write_file("corosync.conf.new", $conf); @@ -193,65 +169,32 @@ sub nodelist { 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; - } - } - } + my $nodes = $conf->{main}->{nodelist}->{node}; + + foreach my $node (@$nodes) { + # use 'name' over 'ring0_addr' if set + my $name = $node->{name} // $node->{ring0_addr}; + if ($name) { + $nodelist->{$name} = $node; } } return $nodelist; } -# get a hash representation of the corosync config totem section sub 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 = {}; + # we reorder elements from totem->interface and don't want to change $conf + my $totem = clone($conf->{main}->{totem}); + my $ifs = $totem->{interface}; - $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}; - } - } + $totem->{interface} = {}; + foreach my $if (@$ifs) { + $totem->{interface}->{$if->{ringnumber}} = $if; } - return $res; + return $totem; } 1; diff --git a/data/test/corosync_configs/multiple-nodes.conf b/data/test/corosync_configs/multiple-nodes.conf index a67cc0c..c951f5b 100644 --- a/data/test/corosync_configs/multiple-nodes.conf +++ b/data/test/corosync_configs/multiple-nodes.conf @@ -41,9 +41,6 @@ quorum { totem { cluster_name: cloud config_version: 1 - ip_version: ipv4 - secauth: on - version: 2 interface { bindnetaddr: 192.168.0.42 ringnumber: 0 @@ -52,5 +49,8 @@ totem { bindnetaddr: 192.168.1.42 ringnumber: 1 } + ip_version: ipv4 + secauth: on + version: 2 } diff --git a/data/test/corosync_configs/single-node.conf b/data/test/corosync_configs/single-node.conf index 7a23f9a..c65a3e5 100644 --- a/data/test/corosync_configs/single-node.conf +++ b/data/test/corosync_configs/single-node.conf @@ -19,12 +19,12 @@ quorum { totem { cluster_name: cloud config_version: 1 - ip_version: ipv4 - secauth: on - version: 2 interface { bindnetaddr: 192.168.0.42 ringnumber: 0 } + ip_version: ipv4 + secauth: on + version: 2 } -- 2.11.0 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel