]> git.proxmox.com Git - pve-cluster.git/blame - data/PVE/Corosync.pm
factor out corosync methods to own module
[pve-cluster.git] / data / PVE / Corosync.pm
CommitLineData
b6973a89
TL
1package PVE::Corosync;
2
3use strict;
4use warnings;
5
6use Digest::SHA;
7
8use PVE::Cluster;
9
10my $basedir = "/etc/pve";
11
12# a very simply parser ...
13sub parse_conf {
14 my ($filename, $raw) = @_;
15
16 return {} if !$raw;
17
18 my $digest = Digest::SHA::sha1_hex(defined($raw) ? $raw : '');
19
20 $raw =~ s/#.*$//mg;
21 $raw =~ s/\r?\n/ /g;
22 $raw =~ s/\s+/ /g;
23 $raw =~ s/^\s+//;
24 $raw =~ s/\s*$//;
25
26 my @tokens = split(/\s/, $raw);
27
28 my $conf = { section => 'main', children => [] };
29
30 my $stack = [];
31 my $section = $conf;
32
33 while (defined(my $token = shift @tokens)) {
34 my $nexttok = $tokens[0];
35
36 if ($nexttok && ($nexttok eq '{')) {
37 shift @tokens; # skip '{'
38 my $new_section = {
39 section => $token,
40 children => [],
41 };
42 push @{$section->{children}}, $new_section;
43 push @$stack, $section;
44 $section = $new_section;
45 next;
46 }
47
48 if ($token eq '}') {
49 $section = pop @$stack;
50 die "parse error - uncexpected '}'\n" if !$section;
51 next;
52 }
53
54 my $key = $token;
55 die "missing ':' after key '$key'\n" if ! ($key =~ s/:$//);
56
57 die "parse error - no value for '$key'\n" if !defined($nexttok);
58 my $value = shift @tokens;
59
60 push @{$section->{children}}, { key => $key, value => $value };
61 }
62
63 $conf->{digest} = $digest;
64
65 return $conf;
66}
67
68my $dump_section;
69$dump_section = sub {
70 my ($section, $prefix) = @_;
71
72 my $raw = $prefix . $section->{section} . " {\n";
73
74 my @list = grep { defined($_->{key}) } @{$section->{children}};
75 foreach my $child (sort {$a->{key} cmp $b->{key}} @list) {
76 $raw .= $prefix . " $child->{key}: $child->{value}\n";
77 }
78
79 @list = grep { defined($_->{section}) } @{$section->{children}};
80 foreach my $child (sort {$a->{section} cmp $b->{section}} @list) {
81 $raw .= &$dump_section($child, "$prefix ");
82 }
83
84 $raw .= $prefix . "}\n\n";
85
86 return $raw;
87
88};
89
90sub write_conf {
91 my ($filename, $conf) = @_;
92
93 my $raw = '';
94
95 my $prefix = '';
96
97 die "no main section" if $conf->{section} ne 'main';
98
99 my @list = grep { defined($_->{key}) } @{$conf->{children}};
100 foreach my $child (sort {$a->{key} cmp $b->{key}} @list) {
101 $raw .= "$child->{key}: $child->{value}\n";
102 }
103
104 @list = grep { defined($_->{section}) } @{$conf->{children}};
105 foreach my $child (sort {$a->{section} cmp $b->{section}} @list) {
106 $raw .= &$dump_section($child, $prefix);
107 }
108
109 return $raw;
110}
111
112sub conf_version {
113 my ($conf, $noerr, $new_value) = @_;
114
115 foreach my $child (@{$conf->{children}}) {
116 next if !defined($child->{section});
117 if ($child->{section} eq 'totem') {
118 foreach my $e (@{$child->{children}}) {
119 next if !defined($e->{key});
120 if ($e->{key} eq 'config_version') {
121 if ($new_value) {
122 $e->{value} = $new_value;
123 return $new_value;
124 } elsif (my $version = int($e->{value})) {
125 return $version;
126 }
127 last;
128 }
129 }
130 }
131 }
132
133 return undef if $noerr;
134
135 die "invalid corosync config - unable to read version\n";
136}
137
138# read only - use "rename corosync.conf.new corosync.conf" to write
139PVE::Cluster::cfs_register_file('corosync.conf', \&parse_conf);
140# this is read/write
141PVE::Cluster::cfs_register_file('corosync.conf.new', \&parse_conf,
142 \&write_conf);
143
144sub check_conf_exists {
145 my ($silent) = @_;
146
147 $silent = $silent // 0;
148
149 my $exists = -f "$basedir/corosync.conf";
150
151 warn "Corosync config '$basedir/corosync.conf' does not exist - is this node part of a cluster?\n"
152 if !$silent && !$exists;
153
154 return $exists;
155}
156
157sub update_nodelist {
158 my ($conf, $nodelist) = @_;
159
160 delete $conf->{digest};
161
162 my $version = conf_version($conf);
163 conf_version($conf, undef, $version + 1);
164
165 my $children = [];
166 foreach my $v (values %$nodelist) {
167 next if !($v->{ring0_addr} || $v->{name});
168 my $kv = [];
169 foreach my $k (keys %$v) {
170 push @$kv, { key => $k, value => $v->{$k} };
171 }
172 my $ns = { section => 'node', children => $kv };
173 push @$children, $ns;
174 }
175
176 foreach my $main (@{$conf->{children}}) {
177 next if !defined($main->{section});
178 if ($main->{section} eq 'nodelist') {
179 $main->{children} = $children;
180 last;
181 }
182 }
183
184
185 PVE::Cluster::cfs_write_file("corosync.conf.new", $conf);
186
187 rename("/etc/pve/corosync.conf.new", "/etc/pve/corosync.conf")
188 || die "activate corosync.conf.new failed - $!\n";
189}
190
191sub nodelist {
192 my ($conf) = @_;
193
194 my $nodelist = {};
195
196 foreach my $main (@{$conf->{children}}) {
197 next if !defined($main->{section});
198 if ($main->{section} eq 'nodelist') {
199 foreach my $ne (@{$main->{children}}) {
200 next if !defined($ne->{section}) || ($ne->{section} ne 'node');
201 my $node = { quorum_votes => 1 };
202 my $name;
203 foreach my $child (@{$ne->{children}}) {
204 next if !defined($child->{key});
205 $node->{$child->{key}} = $child->{value};
206 # use 'name' over 'ring0_addr' if set
207 if ($child->{key} eq 'name') {
208 delete $nodelist->{$name} if $name;
209 $name = $child->{value};
210 $nodelist->{$name} = $node;
211 } elsif(!$name && $child->{key} eq 'ring0_addr') {
212 $name = $child->{value};
213 $nodelist->{$name} = $node;
214 }
215 }
216 }
217 }
218 }
219
220 return $nodelist;
221}
222
223# get a hash representation of the corosync config totem section
224sub totem_config {
225 my ($conf) = @_;
226
227 my $res = {};
228
229 foreach my $main (@{$conf->{children}}) {
230 next if !defined($main->{section}) ||
231 $main->{section} ne 'totem';
232
233 foreach my $e (@{$main->{children}}) {
234
235 if ($e->{section} && $e->{section} eq 'interface') {
236 my $entry = {};
237
238 $res->{interface} = {};
239
240 foreach my $child (@{$e->{children}}) {
241 next if !defined($child->{key});
242 $entry->{$child->{key}} = $child->{value};
243 if($child->{key} eq 'ringnumber') {
244 $res->{interface}->{$child->{value}} = $entry;
245 }
246 }
247
248 } elsif ($e->{key}) {
249 $res->{$e->{key}} = $e->{value};
250 }
251 }
252 }
253
254 return $res;
255}
256
2571;