]> git.proxmox.com Git - pve-ha-manager.git/blame - src/PVE/API2/HA/Resources.pm
api: delete resource: refactor and cleanup indentation
[pve-ha-manager.git] / src / PVE / API2 / HA / Resources.pm
CommitLineData
239a687f
DM
1package PVE::API2::HA::Resources;
2
3use strict;
4use warnings;
5
6use PVE::SafeSyslog;
7use PVE::Tools qw(extract_param);
23944168 8use PVE::Cluster;
239a687f
DM
9use PVE::HA::Config;
10use PVE::HA::Resources;
11use HTTP::Status qw(:constants);
12use Storable qw(dclone);
13use PVE::JSONSchema qw(get_standard_option);
14use PVE::RPCEnvironment;
15
16use PVE::RESTHandler;
17
18use base qw(PVE::RESTHandler);
19
20# fixme: use cfs_read_file
21
239a687f
DM
22my $resource_type_enum = PVE::HA::Resources->lookup_types();
23
239a687f
DM
24my $api_copy_config = sub {
25 my ($cfg, $sid) = @_;
26
95ea5d67
DM
27 die "no such resource '$sid'\n" if !$cfg->{ids}->{$sid};
28
239a687f
DM
29 my $scfg = dclone($cfg->{ids}->{$sid});
30 $scfg->{sid} = $sid;
31 $scfg->{digest} = $cfg->{digest};
32
33 return $scfg;
34};
35
2732fccf
TL
36sub check_service_state {
37 my ($sid, $req_state) = @_;
38
39 my $service_status = PVE::HA::Config::get_service_status($sid);
40 if ($service_status->{managed} && $service_status->{state} eq 'error') {
41 # service in error state, must get disabled before new state request
42 # can be executed
43 return if defined($req_state) && $req_state eq 'disabled';
44 die "service '$sid' in error state, must be disabled and fixed first\n";
45 }
46}
47
239a687f 48__PACKAGE__->register_method ({
95ea5d67 49 name => 'index',
239a687f
DM
50 path => '',
51 method => 'GET',
95ea5d67 52 description => "List HA resources.",
0fcba7ab
TL
53 permissions => {
54 check => ['perm', '/', [ 'Sys.Audit' ]],
55 },
239a687f
DM
56 parameters => {
57 additionalProperties => 0,
58 properties => {
95ea5d67 59 type => {
239a687f 60 description => "Only list resources of specific type",
95ea5d67 61 type => 'string',
239a687f
DM
62 enum => $resource_type_enum,
63 optional => 1,
64 },
65 },
66 },
67 returns => {
68 type => 'array',
69 items => {
70 type => "object",
71 properties => { sid => { type => 'string'} },
72 },
73 links => [ { rel => 'child', href => "{sid}" } ],
74 },
75 code => sub {
76 my ($param) = @_;
77
139a9b90 78 my $cfg = PVE::HA::Config::read_resources_config();
6cdf1da6 79 my $groups = PVE::HA::Config::read_group_config();
239a687f
DM
80
81 my $res = [];
82 foreach my $sid (keys %{$cfg->{ids}}) {
83 my $scfg = &$api_copy_config($cfg, $sid);
84 next if $param->{type} && $param->{type} ne $scfg->{type};
6cdf1da6
DM
85 if ($scfg->{group} && !$groups->{ids}->{$scfg->{group}}) {
86 $scfg->{errors}->{group} = "group '$scfg->{group}' does not exist";
87 }
239a687f
DM
88 push @$res, $scfg;
89 }
90
91 return $res;
92 }});
93
95ea5d67
DM
94__PACKAGE__->register_method ({
95 name => 'read',
96 path => '{sid}',
97 method => 'GET',
0fcba7ab
TL
98 permissions => {
99 check => ['perm', '/', [ 'Sys.Audit' ]],
100 },
95ea5d67
DM
101 description => "Read resource configuration.",
102 parameters => {
103 additionalProperties => 0,
104 properties => {
73bede9b
TL
105 sid => get_standard_option('pve-ha-resource-or-vm-id',
106 { completion => \&PVE::HA::Tools::complete_sid }),
95ea5d67
DM
107 },
108 },
2799edd4
DC
109 returns => {
110 type => 'object',
111 properties => {
112 sid => get_standard_option('pve-ha-resource-or-vm-id'),
113 digest => {
114 type => 'string',
115 description => 'Can be used to prevent concurrent modifications.',
116 },
117 type => {
118 type => 'string',
119 description => 'The type of the resources.',
120 },
121 state => {
122 type => 'string',
123 enum => ['started', 'stopped', 'enabled', 'disabled', 'ignored'],
124 optional => 1,
125 description => "Requested resource state.",
126 },
127 group => get_standard_option('pve-ha-group-id', { optional => 1, }),
128 max_restart => {
129 description => "Maximal number of tries to restart the service on".
130 " a node after its start failed.",
131 type => 'integer',
132 optional => 1,
133 },
134 max_relocate => {
135 description => "Maximal number of service relocate tries when a".
136 " service failes to start.",
137 type => 'integer',
138 optional => 1,
139 },
140 comment => {
141 description => "Description.",
142 type => 'string',
143 optional => 1,
144 },
145 },
146 },
95ea5d67
DM
147 code => sub {
148 my ($param) = @_;
149
139a9b90 150 my $cfg = PVE::HA::Config::read_resources_config();
95ea5d67 151
0087839a 152 my $sid = PVE::HA::Config::parse_sid($param->{sid});
6ca2edcd
DM
153
154 return &$api_copy_config($cfg, $sid);
95ea5d67
DM
155 }});
156
157__PACKAGE__->register_method ({
158 name => 'create',
159 protected => 1,
160 path => '',
161 method => 'POST',
0fcba7ab
TL
162 permissions => {
163 check => ['perm', '/', [ 'Sys.Console' ]],
164 },
95ea5d67
DM
165 description => "Create a new HA resource.",
166 parameters => PVE::HA::Resources->createSchema(),
167 returns => { type => 'null' },
168 code => sub {
169 my ($param) = @_;
170
23944168
DM
171 # create /etc/pve/ha directory
172 PVE::Cluster::check_cfs_quorum();
173 mkdir("/etc/pve/ha");
174
0087839a 175 my ($sid, $type, $name) = PVE::HA::Config::parse_sid(extract_param($param, 'sid'));
95ea5d67
DM
176
177 if (my $param_type = extract_param($param, 'type')) {
178 # useless, but do it anyway
179 die "types does not match\n" if $param_type ne $type;
180 }
181
182 my $plugin = PVE::HA::Resources->lookup($type);
183 $plugin->verify_name($name);
184
a8b14c78
TL
185 $plugin->exists($name);
186
95ea5d67
DM
187 my $opts = $plugin->check_config($sid, $param, 1, 1);
188
66c7e7ef 189 PVE::HA::Config::lock_ha_domain(
95ea5d67
DM
190 sub {
191
139a9b90 192 my $cfg = PVE::HA::Config::read_resources_config();
95ea5d67
DM
193
194 if ($cfg->{ids}->{$sid}) {
195 die "resource ID '$sid' already defined\n";
196 }
197
198 $cfg->{ids}->{$sid} = $opts;
199
e3235f56 200 PVE::HA::Config::write_resources_config($cfg)
95ea5d67
DM
201
202 }, "create resource failed");
203
204 return undef;
205 }});
206
207__PACKAGE__->register_method ({
208 name => 'update',
209 protected => 1,
210 path => '{sid}',
211 method => 'PUT',
212 description => "Update resource configuration.",
0fcba7ab
TL
213 permissions => {
214 check => ['perm', '/', [ 'Sys.Console' ]],
215 },
95ea5d67
DM
216 parameters => PVE::HA::Resources->updateSchema(),
217 returns => { type => 'null' },
218 code => sub {
219 my ($param) = @_;
220
221 my $digest = extract_param($param, 'digest');
222 my $delete = extract_param($param, 'delete');
223
0087839a 224 my ($sid, $type, $name) = PVE::HA::Config::parse_sid(extract_param($param, 'sid'));
95ea5d67
DM
225
226 if (my $param_type = extract_param($param, 'type')) {
227 # useless, but do it anyway
228 die "types does not match\n" if $param_type ne $type;
229 }
230
a93c65d9
TL
231 if (my $group = $param->{group}) {
232 my $group_cfg = PVE::HA::Config::read_group_config();
233
234 die "HA group '$group' does not exist\n"
235 if !$group_cfg->{ids}->{$group};
236 }
237
2732fccf
TL
238 check_service_state($sid, $param->{state});
239
66c7e7ef 240 PVE::HA::Config::lock_ha_domain(
95ea5d67
DM
241 sub {
242
139a9b90 243 my $cfg = PVE::HA::Config::read_resources_config();
95ea5d67
DM
244
245 PVE::SectionConfig::assert_if_modified($cfg, $digest);
246
247 my $scfg = $cfg->{ids}->{$sid} ||
248 die "no such resource '$sid'\n";
249
250 my $plugin = PVE::HA::Resources->lookup($scfg->{type});
251 my $opts = $plugin->check_config($sid, $param, 0, 1);
252
253 foreach my $k (%$opts) {
254 $scfg->{$k} = $opts->{$k};
255 }
256
257 if ($delete) {
258 my $options = $plugin->private()->{options}->{$type};
259 foreach my $k (PVE::Tools::split_list($delete)) {
260 my $d = $options->{$k} ||
261 die "no such option '$k'\n";
262 die "unable to delete required option '$k'\n"
263 if !$d->{optional};
264 die "unable to delete fixed option '$k'\n"
265 if $d->{fixed};
266 delete $scfg->{$k};
267 }
268 }
269
139a9b90 270 PVE::HA::Config::write_resources_config($cfg)
95ea5d67
DM
271
272 }, "update resource failed");
273
274 return undef;
275 }});
276
277__PACKAGE__->register_method ({
278 name => 'delete',
279 protected => 1,
280 path => '{sid}',
281 method => 'DELETE',
282 description => "Delete resource configuration.",
0fcba7ab
TL
283 permissions => {
284 check => ['perm', '/', [ 'Sys.Console' ]],
285 },
95ea5d67
DM
286 parameters => {
287 additionalProperties => 0,
288 properties => {
73bede9b
TL
289 sid => get_standard_option('pve-ha-resource-or-vm-id',
290 { completion => \&PVE::HA::Tools::complete_sid }),
95ea5d67
DM
291 },
292 },
293 returns => { type => 'null' },
294 code => sub {
295 my ($param) = @_;
296
0087839a 297 my ($sid, $type, $name) = PVE::HA::Config::parse_sid(extract_param($param, 'sid'));
95ea5d67 298
60d1b1ca
TL
299 my $cfg = PVE::HA::Config::read_resources_config();
300
301 # cannot use service_is_ha_managed as it ignores 'ignored' services,
302 # see bug report #1602
303 if (!defined($cfg->{ids}) || !defined($cfg->{ids}->{$sid})) {
304 die "cannot delete service '$sid', not HA managed!\n";
305 }
a8b14c78 306
6b68528b 307 PVE::HA::Config::lock_ha_domain(sub {
95ea5d67 308
6b68528b
TL
309 $cfg = PVE::HA::Config::read_resources_config();
310 delete $cfg->{ids}->{$sid} or die "'$sid' not configured!\n";
311 PVE::HA::Config::write_resources_config($cfg);
95ea5d67 312
6b68528b 313 }, "delete resource failed");
95ea5d67
DM
314
315 return undef;
316 }});
239a687f 317
50a87f1f
DM
318__PACKAGE__->register_method ({
319 name => 'migrate',
320 protected => 1,
321 path => '{sid}/migrate',
322 method => 'POST',
323 description => "Request resource migration (online) to another node.",
0fcba7ab
TL
324 permissions => {
325 check => ['perm', '/', [ 'Sys.Console' ]],
326 },
50a87f1f
DM
327 parameters => {
328 additionalProperties => 0,
329 properties => {
73bede9b
TL
330 sid => get_standard_option('pve-ha-resource-or-vm-id',
331 { completion => \&PVE::HA::Tools::complete_sid }),
2eb4a7ef
TL
332 node => get_standard_option('pve-node', {
333 completion => \&PVE::Cluster::complete_migration_target,
334 description => "Target node.",
335 }),
50a87f1f
DM
336 },
337 },
338 returns => { type => 'null' },
339 code => sub {
340 my ($param) = @_;
341
0087839a 342 my ($sid, $type, $name) = PVE::HA::Config::parse_sid(extract_param($param, 'sid'));
6ca2edcd 343
a8b14c78
TL
344 PVE::HA::Config::service_is_ha_managed($sid);
345
2732fccf
TL
346 check_service_state($sid);
347
6ca2edcd 348 PVE::HA::Config::queue_crm_commands("migrate $sid $param->{node}");
50a87f1f
DM
349
350 return undef;
351 }});
352
353__PACKAGE__->register_method ({
354 name => 'relocate',
355 protected => 1,
356 path => '{sid}/relocate',
357 method => 'POST',
358 description => "Request resource relocatzion to another node. This stops the service on the old node, and restarts it on the target node.",
0fcba7ab
TL
359 permissions => {
360 check => ['perm', '/', [ 'Sys.Console' ]],
361 },
50a87f1f
DM
362 parameters => {
363 additionalProperties => 0,
364 properties => {
73bede9b
TL
365 sid => get_standard_option('pve-ha-resource-or-vm-id',
366 { completion => \&PVE::HA::Tools::complete_sid }),
2eb4a7ef
TL
367 node => get_standard_option('pve-node', {
368 completion => \&PVE::Cluster::complete_migration_target,
369 description => "Target node.",
370 }),
50a87f1f
DM
371 },
372 },
373 returns => { type => 'null' },
374 code => sub {
375 my ($param) = @_;
376
0087839a 377 my ($sid, $type, $name) = PVE::HA::Config::parse_sid(extract_param($param, 'sid'));
6ca2edcd 378
a8b14c78
TL
379 PVE::HA::Config::service_is_ha_managed($sid);
380
2732fccf
TL
381 check_service_state($sid);
382
6ca2edcd 383 PVE::HA::Config::queue_crm_commands("relocate $sid $param->{node}");
50a87f1f
DM
384
385 return undef;
386 }});
387
239a687f 3881;