]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/HA/Config.pm
Tools/Config: refactor lrm status json reading
[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 die "unable to read file '/etc/pve/$cfs_path'\n"
55 if !defined($raw);
56
57 return json_reader(undef, $raw);
58 }
59
60 sub write_lrm_status {
61 my ($node, $status_obj) = @_;
62
63 die "undefined node" if !defined($node);
64
65 my $filename = "/etc/pve/nodes/$node/lrm_status";
66
67 PVE::HA::Tools::write_json_to_file($filename, $status_obj);
68 }
69
70 sub parse_groups_config {
71 my ($filename, $raw) = @_;
72
73 return PVE::HA::Groups->parse_config($filename, $raw);
74 }
75
76 sub parse_resources_config {
77 my ($filename, $raw) = @_;
78
79 return PVE::HA::Resources->parse_config($filename, $raw);
80 }
81
82 sub read_resources_config {
83
84 return cfs_read_file($ha_resources_config);
85 }
86
87 # checks if resource exists and sets defaults for unset values
88 sub read_and_check_resources_config {
89
90 my $res = cfs_read_file($ha_resources_config);
91
92 my $vmlist = PVE::Cluster::get_vmlist();
93 my $conf = {};
94
95 foreach my $sid (keys %{$res->{ids}}) {
96 my $d = $res->{ids}->{$sid};
97 my (undef, undef, $name) = PVE::HA::Tools::parse_sid($sid);
98 $d->{state} = 'started' if !defined($d->{state});
99 $d->{state} = 'started' if $d->{state} eq 'enabled'; # backward compatibility
100 $d->{max_restart} = 1 if !defined($d->{max_restart});
101 $d->{max_relocate} = 1 if !defined($d->{max_relocate});
102 if (PVE::HA::Resources->lookup($d->{type})) {
103 if (my $vmd = $vmlist->{ids}->{$name}) {
104 if (!$vmd) {
105 warn "no such VM '$name'\n";
106 } else {
107 $d->{node} = $vmd->{node};
108 $conf->{$sid} = $d;
109 }
110 } else {
111 if (defined($d->{node})) {
112 $conf->{$sid} = $d;
113 } else {
114 warn "service '$sid' without node\n";
115 }
116 }
117 }
118 }
119
120 return $conf;
121 }
122
123 sub read_group_config {
124
125 return cfs_read_file($ha_groups_config);
126 }
127
128 sub write_group_config {
129 my ($cfg) = @_;
130
131 cfs_write_file($ha_groups_config, $cfg);
132 }
133
134 sub write_resources_config {
135 my ($cfg) = @_;
136
137 cfs_write_file($ha_resources_config, $cfg);
138 }
139
140 sub read_manager_status {
141 my () = @_;
142
143 return cfs_read_file($manager_status_filename);
144 }
145
146 sub write_manager_status {
147 my ($status_obj) = @_;
148
149 cfs_write_file($manager_status_filename, $status_obj);
150 }
151
152 sub read_fence_config {
153 my () = @_;
154
155 cfs_read_file($ha_fence_config);
156 }
157
158 sub write_fence_config {
159 my ($cfg) = @_;
160
161 cfs_write_file($ha_fence_config, $cfg);
162 }
163
164 sub lock_ha_domain {
165 my ($code, $errmsg) = @_;
166
167 my $res = PVE::Cluster::cfs_lock_domain("ha", undef, $code);
168 my $err = $@;
169 if ($err) {
170 $errmsg ? die "$errmsg: $err" : die $err;
171 }
172 return $res;
173 }
174
175 sub queue_crm_commands {
176 my ($cmd) = @_;
177
178 chomp $cmd;
179
180 my $code = sub {
181 my $data = cfs_read_file($crm_commands_filename);
182 $data .= "$cmd\n";
183 cfs_write_file($crm_commands_filename, $data);
184 };
185
186 return lock_ha_domain($code);
187 }
188
189 sub read_crm_commands {
190
191 my $code = sub {
192 my $data = cfs_read_file($crm_commands_filename);
193 cfs_write_file($crm_commands_filename, '');
194 return $data;
195 };
196
197 return lock_ha_domain($code);
198 }
199
200 my $service_check_ha_state = sub {
201 my ($conf, $sid, $has_state) = @_;
202
203 if (my $d = $conf->{ids}->{$sid}) {
204 if (!defined($has_state)) {
205 # ignored service behave as if they were not managed by HA
206 return 0 if defined($d->{state}) && $d->{state} eq 'ignored';
207 return 1;
208 }
209
210 # backward compatibility
211 $has_state = 'started' if $has_state eq 'enabled';
212
213 $d->{state} = 'started' if !defined($d->{state}) ||
214 ($d->{state} eq 'enabled');
215
216 return 1 if $d->{state} eq $has_state;
217 }
218
219 return undef;
220 };
221
222 sub vm_is_ha_managed {
223 my ($vmid, $has_state) = @_;
224
225 my $conf = cfs_read_file($ha_resources_config);
226
227 my $types = PVE::HA::Resources->lookup_types();
228 foreach my $type ('vm', 'ct') {
229 return 1 if &$service_check_ha_state($conf, "$type:$vmid", $has_state);
230 }
231
232 return undef;
233 }
234
235 sub service_is_ha_managed {
236 my ($sid, $has_state, $noerr) = @_;
237
238 my $conf = cfs_read_file($ha_resources_config);
239
240 return 1 if &$service_check_ha_state($conf, $sid, $has_state);
241
242 die "resource '$sid' is not HA managed\n" if !$noerr;
243
244 return undef;
245 }
246
247 sub get_service_status {
248 my ($sid) = @_;
249
250 my $status = { managed => 0 };
251
252 my $conf = cfs_read_file($ha_resources_config);
253
254 if (&$service_check_ha_state($conf, $sid)) {
255 my $manager_status = cfs_read_file($manager_status_filename);
256
257 $status->{managed} = 1;
258 $status->{group} = $conf->{ids}->{$sid}->{group};
259 $status->{state} = $manager_status->{service_status}->{$sid}->{state};
260 }
261
262 return $status;
263 }
264
265 1;