]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/HA/Config.pm
read_and_check_resources_config: remove dead if branch
[pve-ha-manager.git] / src / PVE / HA / Config.pm
1 package PVE::HA::Config;
2
3 use strict;
4 use warnings;
5 use JSON;
6
7 use PVE::HA::Tools;
8 use PVE::HA::Groups;
9 use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
10 use PVE::HA::Resources;
11
12 my $manager_status_filename = "ha/manager_status";
13 my $ha_groups_config = "ha/groups.cfg";
14 my $ha_resources_config = "ha/resources.cfg";
15 my $crm_commands_filename = "ha/crm_commands";
16 my $ha_fence_config = "ha/fence.cfg";
17
18 cfs_register_file($crm_commands_filename,
19 sub { my ($fn, $raw) = @_; return defined($raw) ? $raw : ''; },
20 sub { my ($fn, $raw) = @_; return $raw; });
21 cfs_register_file($ha_groups_config,
22 sub { PVE::HA::Groups->parse_config(@_); },
23 sub { PVE::HA::Groups->write_config(@_); });
24 cfs_register_file($ha_resources_config,
25 sub { PVE::HA::Resources->parse_config(@_); },
26 sub { PVE::HA::Resources->write_config(@_); });
27 cfs_register_file($manager_status_filename,
28 \&json_reader,
29 \&json_writer);
30 cfs_register_file($ha_fence_config,
31 \&PVE::HA::FenceConfig::parse_config,
32 \&PVE::HA::FenceConfig::write_config);
33
34 sub json_reader {
35 my ($filename, $data) = @_;
36
37 return defined($data) && length($data) > 0 ? decode_json($data) : {};
38 }
39
40 sub json_writer {
41 my ($filename, $data) = @_;
42
43 return encode_json($data);
44 }
45
46 sub read_lrm_status {
47 my ($node) = @_;
48
49 die "undefined node" if !defined($node);
50
51 my $cfs_path = "nodes/$node/lrm_status";
52
53 my $raw = PVE::Cluster::get_config($cfs_path);
54 if (!defined($raw)) {
55 # ENOENT -> possible deleted node, don't die here as it breaks our node
56 # 'gone' logic
57 warn "unable to read file '/etc/pve/$cfs_path'\n";
58 # unkown mode set explicitly as 'active' is assumed as default..
59 return { mode => 'unknown' } if ! -e "/etc/pve/$cfs_path";
60 }
61
62 return json_reader(undef, $raw);
63 }
64
65 sub write_lrm_status {
66 my ($node, $status_obj) = @_;
67
68 die "undefined node" if !defined($node);
69
70 my $filename = "/etc/pve/nodes/$node/lrm_status";
71
72 PVE::HA::Tools::write_json_to_file($filename, $status_obj);
73 }
74
75 sub parse_groups_config {
76 my ($filename, $raw) = @_;
77
78 return PVE::HA::Groups->parse_config($filename, $raw);
79 }
80
81 sub parse_resources_config {
82 my ($filename, $raw) = @_;
83
84 return PVE::HA::Resources->parse_config($filename, $raw);
85 }
86
87 sub read_resources_config {
88
89 return cfs_read_file($ha_resources_config);
90 }
91
92 # checks if resource exists and sets defaults for unset values
93 sub read_and_check_resources_config {
94
95 my $res = cfs_read_file($ha_resources_config);
96
97 my $vmlist = PVE::Cluster::get_vmlist();
98 my $conf = {};
99
100 foreach my $sid (keys %{$res->{ids}}) {
101 my $d = $res->{ids}->{$sid};
102 my (undef, undef, $name) = parse_sid($sid);
103 $d->{state} = 'started' if !defined($d->{state});
104 $d->{state} = 'started' if $d->{state} eq 'enabled'; # backward compatibility
105 $d->{max_restart} = 1 if !defined($d->{max_restart});
106 $d->{max_relocate} = 1 if !defined($d->{max_relocate});
107 if (PVE::HA::Resources->lookup($d->{type})) {
108 if (my $vmd = $vmlist->{ids}->{$name}) {
109 $d->{node} = $vmd->{node};
110 $conf->{$sid} = $d;
111 } else {
112 if (defined($d->{node})) {
113 $conf->{$sid} = $d;
114 } else {
115 warn "service '$sid' without node\n";
116 }
117 }
118 }
119 }
120
121 return $conf;
122 }
123
124 sub update_resources_config {
125 my ($sid, $param, $delete, $digest) = @_;
126
127 lock_ha_domain(
128 sub {
129 my $cfg = read_resources_config();
130 ($sid, my $type, my $name) = parse_sid($sid);
131
132 PVE::SectionConfig::assert_if_modified($cfg, $digest);
133
134 my $scfg = $cfg->{ids}->{$sid} ||
135 die "no such resource '$sid'\n";
136
137 my $plugin = PVE::HA::Resources->lookup($scfg->{type});
138 my $opts = $plugin->check_config($sid, $param, 0, 1);
139
140 foreach my $k (%$opts) {
141 $scfg->{$k} = $opts->{$k};
142 }
143
144 if ($delete) {
145 my $options = $plugin->private()->{options}->{$type};
146 foreach my $k (PVE::Tools::split_list($delete)) {
147 my $d = $options->{$k} ||
148 die "no such option '$k'\n";
149 die "unable to delete required option '$k'\n"
150 if !$d->{optional};
151 die "unable to delete fixed option '$k'\n"
152 if $d->{fixed};
153 delete $scfg->{$k};
154 }
155 }
156
157 write_resources_config($cfg);
158 }, "update resources config failed");
159 }
160
161 sub parse_sid {
162 my ($sid) = @_;
163
164 my ($type, $name);
165
166 if ($sid =~ m/^(\d+)$/) {
167 $name = $1;
168
169 my $vmlist = PVE::Cluster::get_vmlist();
170 if (defined($vmlist->{ids}->{$name})) {
171 my $vm_type = $vmlist->{ids}->{$name}->{type};
172 if ($vm_type eq 'lxc') {
173 $type = 'ct';
174 } elsif ($vm_type eq 'qemu') {
175 $type = 'vm';
176 } else {
177 die "internal error";
178 }
179 $sid = "$type:$name";
180 }
181 else {
182 die "unable do add resource - VM/CT $1 does not exist\n";
183 }
184 } elsif ($sid =~m/^(\S+):(\S+)$/) {
185 $name = $2;
186 $type = $1;
187 } else {
188 die "unable to parse service id '$sid'\n";
189 }
190
191 return wantarray ? ($sid, $type, $name) : $sid;
192 }
193
194 sub read_group_config {
195
196 return cfs_read_file($ha_groups_config);
197 }
198
199 sub write_group_config {
200 my ($cfg) = @_;
201
202 cfs_write_file($ha_groups_config, $cfg);
203 }
204
205 sub write_resources_config {
206 my ($cfg) = @_;
207
208 cfs_write_file($ha_resources_config, $cfg);
209 }
210
211 sub read_manager_status {
212 my () = @_;
213
214 return cfs_read_file($manager_status_filename);
215 }
216
217 sub write_manager_status {
218 my ($status_obj) = @_;
219
220 cfs_write_file($manager_status_filename, $status_obj);
221 }
222
223 sub read_fence_config {
224 my () = @_;
225
226 cfs_read_file($ha_fence_config);
227 }
228
229 sub write_fence_config {
230 my ($cfg) = @_;
231
232 cfs_write_file($ha_fence_config, $cfg);
233 }
234
235 sub lock_ha_domain {
236 my ($code, $errmsg) = @_;
237
238 my $res = PVE::Cluster::cfs_lock_domain("ha", undef, $code);
239 my $err = $@;
240 if ($err) {
241 $errmsg ? die "$errmsg: $err" : die $err;
242 }
243 return $res;
244 }
245
246 sub queue_crm_commands {
247 my ($cmd) = @_;
248
249 chomp $cmd;
250
251 my $code = sub {
252 my $data = cfs_read_file($crm_commands_filename);
253 $data .= "$cmd\n";
254 cfs_write_file($crm_commands_filename, $data);
255 };
256
257 return lock_ha_domain($code);
258 }
259
260 sub read_crm_commands {
261
262 my $code = sub {
263 my $data = cfs_read_file($crm_commands_filename);
264 cfs_write_file($crm_commands_filename, '');
265 return $data;
266 };
267
268 return lock_ha_domain($code);
269 }
270
271 my $service_check_ha_state = sub {
272 my ($conf, $sid, $has_state) = @_;
273
274 if (my $d = $conf->{ids}->{$sid}) {
275 if (!defined($has_state)) {
276 # ignored service behave as if they were not managed by HA
277 return 0 if defined($d->{state}) && $d->{state} eq 'ignored';
278 return 1;
279 }
280
281 # backward compatibility
282 $has_state = 'started' if $has_state eq 'enabled';
283
284 $d->{state} = 'started' if !defined($d->{state}) ||
285 ($d->{state} eq 'enabled');
286
287 return 1 if $d->{state} eq $has_state;
288 }
289
290 return undef;
291 };
292
293 sub vm_is_ha_managed {
294 my ($vmid, $has_state) = @_;
295
296 my $conf = cfs_read_file($ha_resources_config);
297
298 my $types = PVE::HA::Resources->lookup_types();
299 foreach my $type ('vm', 'ct') {
300 return 1 if &$service_check_ha_state($conf, "$type:$vmid", $has_state);
301 }
302
303 return undef;
304 }
305
306 sub service_is_ha_managed {
307 my ($sid, $has_state, $noerr) = @_;
308
309 my $conf = cfs_read_file($ha_resources_config);
310
311 return 1 if &$service_check_ha_state($conf, $sid, $has_state);
312
313 die "resource '$sid' is not HA managed\n" if !$noerr;
314
315 return undef;
316 }
317
318 sub get_service_status {
319 my ($sid) = @_;
320
321 my $status = { managed => 0 };
322
323 my $conf = cfs_read_file($ha_resources_config);
324
325 if (&$service_check_ha_state($conf, $sid)) {
326 my $manager_status = cfs_read_file($manager_status_filename);
327
328 $status->{managed} = 1;
329 $status->{group} = $conf->{ids}->{$sid}->{group};
330 $status->{state} = $manager_status->{service_status}->{$sid}->{state};
331 }
332
333 return $status;
334 }
335
336 1;