]> git.proxmox.com Git - pve-cluster.git/blame - data/PVE/Corosync.pm
corosync: allow to set link priorities
[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';
b01bc84d 8use Net::IP qw(ip_is_ipv6);
b6973a89
TL
9
10use PVE::Cluster;
11
12my $basedir = "/etc/pve";
13
496de919
TL
14my $conf_array_sections = {
15 node => 1,
16 interface => 1,
17};
18
b6973a89
TL
19# a very simply parser ...
20sub parse_conf {
21 my ($filename, $raw) = @_;
22
23 return {} if !$raw;
24
25 my $digest = Digest::SHA::sha1_hex(defined($raw) ? $raw : '');
26
27 $raw =~ s/#.*$//mg;
28 $raw =~ s/\r?\n/ /g;
29 $raw =~ s/\s+/ /g;
30 $raw =~ s/^\s+//;
31 $raw =~ s/\s*$//;
32
33 my @tokens = split(/\s/, $raw);
34
496de919 35 my $conf = { 'main' => {} };
b6973a89
TL
36
37 my $stack = [];
496de919 38 my $section = $conf->{main};
b6973a89
TL
39
40 while (defined(my $token = shift @tokens)) {
41 my $nexttok = $tokens[0];
42
43 if ($nexttok && ($nexttok eq '{')) {
44 shift @tokens; # skip '{'
496de919
TL
45 my $new_section = {};
46 if ($conf_array_sections->{$token}) {
47 $section->{$token} = [] if !defined($section->{$token});
48 push @{$section->{$token}}, $new_section;
49 } elsif (!defined($section->{$token})) {
50 $section->{$token} = $new_section;
51 } else {
52 die "section '$token' already exists and not marked as array!\n";
53 }
b6973a89
TL
54 push @$stack, $section;
55 $section = $new_section;
56 next;
57 }
58
59 if ($token eq '}') {
60 $section = pop @$stack;
61 die "parse error - uncexpected '}'\n" if !$section;
62 next;
63 }
64
65 my $key = $token;
66 die "missing ':' after key '$key'\n" if ! ($key =~ s/:$//);
67
68 die "parse error - no value for '$key'\n" if !defined($nexttok);
69 my $value = shift @tokens;
70
496de919 71 $section->{$key} = $value;
b6973a89
TL
72 }
73
e7ecad20
TL
74 # make working with the config way easier
75 my ($totem, $nodelist) = $conf->{main}->@{"totem", "nodelist"};
018bbcab
TL
76
77 $nodelist->{node} = {
78 map {
79 $_->{name} // $_->{ring0_addr} => $_
80 } @{$nodelist->{node}}
81 };
82 $totem->{interface} = {
83 map {
84 $_->{linknumber} // $_->{ringnumber} => $_
85 } @{$totem->{interface}}
86 };
e7ecad20 87
b6973a89
TL
88 $conf->{digest} = $digest;
89
90 return $conf;
91}
92
93my $dump_section;
94$dump_section = sub {
95 my ($section, $prefix) = @_;
96
496de919 97 my $raw = '';
b6973a89 98
496de919
TL
99 foreach my $k (sort keys %$section) {
100 my $v = $section->{$k};
101 if (ref($v) eq 'HASH') {
102 $raw .= $prefix . "$k {\n";
103 $raw .= &$dump_section($v, "$prefix ");
104 $raw .= $prefix . "}\n";
105 $raw .= "\n" if !$prefix; # add extra newline at 1st level only
106 } elsif (ref($v) eq 'ARRAY') {
107 foreach my $child (@$v) {
108 $raw .= $prefix . "$k {\n";
109 $raw .= &$dump_section($child, "$prefix ");
110 $raw .= $prefix . "}\n";
111 }
112 } elsif (!ref($v)) {
94291d49 113 die "got undefined value for key '$k'!\n" if !defined($v);
496de919
TL
114 $raw .= $prefix . "$k: $v\n";
115 } else {
116 die "unexpected reference in config hash: $k => ". ref($v) ."\n";
117 }
b6973a89
TL
118 }
119
b6973a89 120 return $raw;
b6973a89
TL
121};
122
123sub write_conf {
124 my ($filename, $conf) = @_;
125
e7ecad20
TL
126 my $c = clone($conf->{main}) // die "no main section";
127
128 # retransform back for easier dumping
129 my $hash_to_array = sub {
130 my ($hash) = @_;
131 return [ $hash->@{sort keys %$hash} ];
132 };
b6973a89 133
e7ecad20
TL
134 $c->{nodelist}->{node} = &$hash_to_array($c->{nodelist}->{node});
135 $c->{totem}->{interface} = &$hash_to_array($c->{totem}->{interface});
136
137 my $raw = &$dump_section($c, '');
b6973a89
TL
138
139 return $raw;
140}
141
2b28b160 142# read only - use atomic_write_conf method to write
b6973a89
TL
143PVE::Cluster::cfs_register_file('corosync.conf', \&parse_conf);
144# this is read/write
145PVE::Cluster::cfs_register_file('corosync.conf.new', \&parse_conf,
146 \&write_conf);
147
148sub check_conf_exists {
149 my ($silent) = @_;
150
151 $silent = $silent // 0;
152
153 my $exists = -f "$basedir/corosync.conf";
154
155 warn "Corosync config '$basedir/corosync.conf' does not exist - is this node part of a cluster?\n"
156 if !$silent && !$exists;
157
158 return $exists;
159}
160
161sub update_nodelist {
162 my ($conf, $nodelist) = @_;
163
e7ecad20 164 $conf->{main}->{nodelist}->{node} = $nodelist;
b6973a89 165
2b28b160 166 atomic_write_conf($conf);
b6973a89
TL
167}
168
169sub nodelist {
170 my ($conf) = @_;
e7ecad20 171 return clone($conf->{main}->{nodelist}->{node});
b6973a89
TL
172}
173
b6973a89
TL
174sub totem_config {
175 my ($conf) = @_;
e7ecad20 176 return clone($conf->{main}->{totem});
b6973a89
TL
177}
178
2b28b160
TL
179# caller must hold corosync.conf cfs lock if used in read-modify-write cycle
180sub atomic_write_conf {
181 my ($conf, $no_increase_version) = @_;
182
183 if (!$no_increase_version) {
184 die "invalid corosync config: unable to read config version\n"
185 if !defined($conf->{main}->{totem}->{config_version});
186 $conf->{main}->{totem}->{config_version}++;
187 }
188
189 PVE::Cluster::cfs_write_file("corosync.conf.new", $conf);
190
191 rename("/etc/pve/corosync.conf.new", "/etc/pve/corosync.conf")
192 || die "activating corosync.conf.new failed - $!\n";
193}
194
b01bc84d
TL
195# for creating a new cluster with the current node
196# params are those from the API/CLI cluster create call
197sub create_conf {
198 my ($nodename, %param) = @_;
199
200 my $clustername = $param{clustername};
201 my $nodeid = $param{nodeid} || 1;
202 my $votes = $param{votes} || 1;
203
204 my $local_ip_address = PVE::Cluster::remote_node_ip($nodename);
b01bc84d 205
046173ce
TL
206 my $link0 = PVE::Cluster::parse_corosync_link($param{link0});
207 $link0->{address} //= $local_ip_address;
b01bc84d
TL
208
209 my $conf = {
210 totem => {
211 version => 2, # protocol version
212 secauth => 'on',
213 cluster_name => $clustername,
214 config_version => 0,
046173ce 215 ip_version => 'ipv4-6',
b01bc84d
TL
216 interface => {
217 0 => {
018bbcab 218 linknumber => 0,
b01bc84d
TL
219 },
220 },
221 },
222 nodelist => {
223 node => {
224 $nodename => {
225 name => $nodename,
226 nodeid => $nodeid,
227 quorum_votes => $votes,
046173ce 228 ring0_addr => $link0->{address},
b01bc84d
TL
229 },
230 },
231 },
232 quorum => {
233 provider => 'corosync_votequorum',
234 },
235 logging => {
236 to_syslog => 'yes',
237 debug => 'off',
238 },
239 };
e7f9c8cc 240 my $totem = $conf->{totem};
b01bc84d 241
e7f9c8cc
TL
242 $totem->{interface}->{0}->{knet_link_priority} = $link0->{priority}
243 if defined($link0->{priority});
b01bc84d 244
e7f9c8cc 245 my $link1 = PVE::Cluster::parse_corosync_link($param{link1});
046173ce 246 if ($link1->{address}) {
b01bc84d 247 $conf->{totem}->{interface}->{1} = {
018bbcab 248 linknumber => 1,
b01bc84d 249 };
e7f9c8cc
TL
250 $totem->{link_mode} = 'passive';
251 $totem->{interface}->{1}->{knet_link_priority} = $link1->{priority}
252 if defined($link1->{priority});
046173ce 253 $conf->{nodelist}->{node}->{$nodename}->{ring1_addr} = $link1->{address};
b01bc84d
TL
254 }
255
256 return { main => $conf };
257}
258
b6973a89 2591;