]> git.proxmox.com Git - pve-cluster.git/blame - data/PVE/Corosync.pm
corosync: transform config to allow easier access
[pve-cluster.git] / data / PVE / Corosync.pm
CommitLineData
b6973a89
TL
1package PVE::Corosync;
2
3use strict;
4use warnings;
5
6use Digest::SHA;
496de919 7use Clone 'clone';
b6973a89
TL
8
9use PVE::Cluster;
10
11my $basedir = "/etc/pve";
12
496de919
TL
13my $conf_array_sections = {
14 node => 1,
15 interface => 1,
16};
17
b6973a89
TL
18# a very simply parser ...
19sub parse_conf {
20 my ($filename, $raw) = @_;
21
22 return {} if !$raw;
23
24 my $digest = Digest::SHA::sha1_hex(defined($raw) ? $raw : '');
25
26 $raw =~ s/#.*$//mg;
27 $raw =~ s/\r?\n/ /g;
28 $raw =~ s/\s+/ /g;
29 $raw =~ s/^\s+//;
30 $raw =~ s/\s*$//;
31
32 my @tokens = split(/\s/, $raw);
33
496de919 34 my $conf = { 'main' => {} };
b6973a89
TL
35
36 my $stack = [];
496de919 37 my $section = $conf->{main};
b6973a89
TL
38
39 while (defined(my $token = shift @tokens)) {
40 my $nexttok = $tokens[0];
41
42 if ($nexttok && ($nexttok eq '{')) {
43 shift @tokens; # skip '{'
496de919
TL
44 my $new_section = {};
45 if ($conf_array_sections->{$token}) {
46 $section->{$token} = [] if !defined($section->{$token});
47 push @{$section->{$token}}, $new_section;
48 } elsif (!defined($section->{$token})) {
49 $section->{$token} = $new_section;
50 } else {
51 die "section '$token' already exists and not marked as array!\n";
52 }
b6973a89
TL
53 push @$stack, $section;
54 $section = $new_section;
55 next;
56 }
57
58 if ($token eq '}') {
59 $section = pop @$stack;
60 die "parse error - uncexpected '}'\n" if !$section;
61 next;
62 }
63
64 my $key = $token;
65 die "missing ':' after key '$key'\n" if ! ($key =~ s/:$//);
66
67 die "parse error - no value for '$key'\n" if !defined($nexttok);
68 my $value = shift @tokens;
69
496de919 70 $section->{$key} = $value;
b6973a89
TL
71 }
72
e7ecad20
TL
73 # make working with the config way easier
74 my ($totem, $nodelist) = $conf->{main}->@{"totem", "nodelist"};
75 $nodelist->{node} = { map { $_->{name} // $_->{ring0_addr} => $_ } @{$nodelist->{node}} };
76 $totem->{interface} = { map { $_->{ringnumber} => $_ } @{$totem->{interface}} };
77
b6973a89
TL
78 $conf->{digest} = $digest;
79
80 return $conf;
81}
82
83my $dump_section;
84$dump_section = sub {
85 my ($section, $prefix) = @_;
86
496de919 87 my $raw = '';
b6973a89 88
496de919
TL
89 foreach my $k (sort keys %$section) {
90 my $v = $section->{$k};
91 if (ref($v) eq 'HASH') {
92 $raw .= $prefix . "$k {\n";
93 $raw .= &$dump_section($v, "$prefix ");
94 $raw .= $prefix . "}\n";
95 $raw .= "\n" if !$prefix; # add extra newline at 1st level only
96 } elsif (ref($v) eq 'ARRAY') {
97 foreach my $child (@$v) {
98 $raw .= $prefix . "$k {\n";
99 $raw .= &$dump_section($child, "$prefix ");
100 $raw .= $prefix . "}\n";
101 }
102 } elsif (!ref($v)) {
103 $raw .= $prefix . "$k: $v\n";
104 } else {
105 die "unexpected reference in config hash: $k => ". ref($v) ."\n";
106 }
b6973a89
TL
107 }
108
b6973a89 109 return $raw;
b6973a89
TL
110};
111
112sub write_conf {
113 my ($filename, $conf) = @_;
114
e7ecad20
TL
115 my $c = clone($conf->{main}) // die "no main section";
116
117 # retransform back for easier dumping
118 my $hash_to_array = sub {
119 my ($hash) = @_;
120 return [ $hash->@{sort keys %$hash} ];
121 };
b6973a89 122
e7ecad20
TL
123 $c->{nodelist}->{node} = &$hash_to_array($c->{nodelist}->{node});
124 $c->{totem}->{interface} = &$hash_to_array($c->{totem}->{interface});
125
126 my $raw = &$dump_section($c, '');
b6973a89
TL
127
128 return $raw;
129}
130
131sub conf_version {
132 my ($conf, $noerr, $new_value) = @_;
133
496de919
TL
134 my $totem = $conf->{main}->{totem};
135 if (defined($totem) && defined($totem->{config_version})) {
136 $totem->{config_version} = $new_value if $new_value;
137 return $totem->{config_version};
b6973a89
TL
138 }
139
140 return undef if $noerr;
141
142 die "invalid corosync config - unable to read version\n";
143}
144
145# read only - use "rename corosync.conf.new corosync.conf" to write
146PVE::Cluster::cfs_register_file('corosync.conf', \&parse_conf);
147# this is read/write
148PVE::Cluster::cfs_register_file('corosync.conf.new', \&parse_conf,
149 \&write_conf);
150
151sub check_conf_exists {
152 my ($silent) = @_;
153
154 $silent = $silent // 0;
155
156 my $exists = -f "$basedir/corosync.conf";
157
158 warn "Corosync config '$basedir/corosync.conf' does not exist - is this node part of a cluster?\n"
159 if !$silent && !$exists;
160
161 return $exists;
162}
163
164sub update_nodelist {
165 my ($conf, $nodelist) = @_;
166
167 delete $conf->{digest};
168
169 my $version = conf_version($conf);
170 conf_version($conf, undef, $version + 1);
171
e7ecad20 172 $conf->{main}->{nodelist}->{node} = $nodelist;
b6973a89
TL
173
174 PVE::Cluster::cfs_write_file("corosync.conf.new", $conf);
175
176 rename("/etc/pve/corosync.conf.new", "/etc/pve/corosync.conf")
177 || die "activate corosync.conf.new failed - $!\n";
178}
179
180sub nodelist {
181 my ($conf) = @_;
e7ecad20 182 return clone($conf->{main}->{nodelist}->{node});
b6973a89
TL
183}
184
b6973a89
TL
185sub totem_config {
186 my ($conf) = @_;
e7ecad20 187 return clone($conf->{main}->{totem});
b6973a89
TL
188}
189
1901;