]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/API2/HA/Resources.pm
bump version to 2.0-1
[pve-ha-manager.git] / src / PVE / API2 / HA / Resources.pm
1 package PVE::API2::HA::Resources;
2
3 use strict;
4 use warnings;
5
6 use PVE::SafeSyslog;
7 use PVE::Tools qw(extract_param);
8 use PVE::Cluster;
9 use PVE::HA::Config;
10 use PVE::HA::Resources;
11 use HTTP::Status qw(:constants);
12 use Storable qw(dclone);
13 use PVE::JSONSchema qw(get_standard_option);
14 use PVE::RPCEnvironment;
15 use Data::Dumper;
16
17 use PVE::RESTHandler;
18
19 use base qw(PVE::RESTHandler);
20
21 # fixme: use cfs_read_file
22
23 my $resource_type_enum = PVE::HA::Resources->lookup_types();
24
25 my $api_copy_config = sub {
26 my ($cfg, $sid) = @_;
27
28 die "no such resource '$sid'\n" if !$cfg->{ids}->{$sid};
29
30 my $scfg = dclone($cfg->{ids}->{$sid});
31 $scfg->{sid} = $sid;
32 $scfg->{digest} = $cfg->{digest};
33
34 return $scfg;
35 };
36
37 __PACKAGE__->register_method ({
38 name => 'index',
39 path => '',
40 method => 'GET',
41 description => "List HA resources.",
42 permissions => {
43 check => ['perm', '/', [ 'Sys.Audit' ]],
44 },
45 parameters => {
46 additionalProperties => 0,
47 properties => {
48 type => {
49 description => "Only list resources of specific type",
50 type => 'string',
51 enum => $resource_type_enum,
52 optional => 1,
53 },
54 },
55 },
56 returns => {
57 type => 'array',
58 items => {
59 type => "object",
60 properties => { sid => { type => 'string'} },
61 },
62 links => [ { rel => 'child', href => "{sid}" } ],
63 },
64 code => sub {
65 my ($param) = @_;
66
67 my $cfg = PVE::HA::Config::read_resources_config();
68 my $groups = PVE::HA::Config::read_group_config();
69
70 my $res = [];
71 foreach my $sid (keys %{$cfg->{ids}}) {
72 my $scfg = &$api_copy_config($cfg, $sid);
73 next if $param->{type} && $param->{type} ne $scfg->{type};
74 if ($scfg->{group} && !$groups->{ids}->{$scfg->{group}}) {
75 $scfg->{errors}->{group} = "group '$scfg->{group}' does not exist";
76 }
77 push @$res, $scfg;
78 }
79
80 return $res;
81 }});
82
83 __PACKAGE__->register_method ({
84 name => 'read',
85 path => '{sid}',
86 method => 'GET',
87 permissions => {
88 check => ['perm', '/', [ 'Sys.Audit' ]],
89 },
90 description => "Read resource configuration.",
91 parameters => {
92 additionalProperties => 0,
93 properties => {
94 sid => get_standard_option('pve-ha-resource-or-vm-id',
95 { completion => \&PVE::HA::Tools::complete_sid }),
96 },
97 },
98 returns => {},
99 code => sub {
100 my ($param) = @_;
101
102 my $cfg = PVE::HA::Config::read_resources_config();
103
104 my $sid = PVE::HA::Tools::parse_sid($param->{sid});
105
106 return &$api_copy_config($cfg, $sid);
107 }});
108
109 __PACKAGE__->register_method ({
110 name => 'create',
111 protected => 1,
112 path => '',
113 method => 'POST',
114 permissions => {
115 check => ['perm', '/', [ 'Sys.Console' ]],
116 },
117 description => "Create a new HA resource.",
118 parameters => PVE::HA::Resources->createSchema(),
119 returns => { type => 'null' },
120 code => sub {
121 my ($param) = @_;
122
123 # create /etc/pve/ha directory
124 PVE::Cluster::check_cfs_quorum();
125 mkdir("/etc/pve/ha");
126
127 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
128
129 if (my $param_type = extract_param($param, 'type')) {
130 # useless, but do it anyway
131 die "types does not match\n" if $param_type ne $type;
132 }
133
134 my $plugin = PVE::HA::Resources->lookup($type);
135 $plugin->verify_name($name);
136
137 $plugin->exists($name);
138
139 my $opts = $plugin->check_config($sid, $param, 1, 1);
140
141 PVE::HA::Config::lock_ha_domain(
142 sub {
143
144 my $cfg = PVE::HA::Config::read_resources_config();
145
146 if ($cfg->{ids}->{$sid}) {
147 die "resource ID '$sid' already defined\n";
148 }
149
150 $cfg->{ids}->{$sid} = $opts;
151
152 PVE::HA::Config::write_resources_config($cfg)
153
154 }, "create resource failed");
155
156 return undef;
157 }});
158
159 __PACKAGE__->register_method ({
160 name => 'update',
161 protected => 1,
162 path => '{sid}',
163 method => 'PUT',
164 description => "Update resource configuration.",
165 permissions => {
166 check => ['perm', '/', [ 'Sys.Console' ]],
167 },
168 parameters => PVE::HA::Resources->updateSchema(),
169 returns => { type => 'null' },
170 code => sub {
171 my ($param) = @_;
172
173 my $digest = extract_param($param, 'digest');
174 my $delete = extract_param($param, 'delete');
175
176 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
177
178 if (my $param_type = extract_param($param, 'type')) {
179 # useless, but do it anyway
180 die "types does not match\n" if $param_type ne $type;
181 }
182
183 if (my $group = $param->{group}) {
184 my $group_cfg = PVE::HA::Config::read_group_config();
185
186 die "HA group '$group' does not exist\n"
187 if !$group_cfg->{ids}->{$group};
188 }
189
190 PVE::HA::Config::lock_ha_domain(
191 sub {
192
193 my $cfg = PVE::HA::Config::read_resources_config();
194
195 PVE::SectionConfig::assert_if_modified($cfg, $digest);
196
197 my $scfg = $cfg->{ids}->{$sid} ||
198 die "no such resource '$sid'\n";
199
200 my $plugin = PVE::HA::Resources->lookup($scfg->{type});
201 my $opts = $plugin->check_config($sid, $param, 0, 1);
202
203 foreach my $k (%$opts) {
204 $scfg->{$k} = $opts->{$k};
205 }
206
207 if ($delete) {
208 my $options = $plugin->private()->{options}->{$type};
209 foreach my $k (PVE::Tools::split_list($delete)) {
210 my $d = $options->{$k} ||
211 die "no such option '$k'\n";
212 die "unable to delete required option '$k'\n"
213 if !$d->{optional};
214 die "unable to delete fixed option '$k'\n"
215 if $d->{fixed};
216 delete $scfg->{$k};
217 }
218 }
219
220 PVE::HA::Config::write_resources_config($cfg)
221
222 }, "update resource failed");
223
224 return undef;
225 }});
226
227 __PACKAGE__->register_method ({
228 name => 'delete',
229 protected => 1,
230 path => '{sid}',
231 method => 'DELETE',
232 description => "Delete resource configuration.",
233 permissions => {
234 check => ['perm', '/', [ 'Sys.Console' ]],
235 },
236 parameters => {
237 additionalProperties => 0,
238 properties => {
239 sid => get_standard_option('pve-ha-resource-or-vm-id',
240 { completion => \&PVE::HA::Tools::complete_sid }),
241 },
242 },
243 returns => { type => 'null' },
244 code => sub {
245 my ($param) = @_;
246
247 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
248
249 PVE::HA::Config::service_is_ha_managed($sid);
250
251 PVE::HA::Config::lock_ha_domain(
252 sub {
253
254 my $cfg = PVE::HA::Config::read_resources_config();
255
256 delete $cfg->{ids}->{$sid};
257
258 PVE::HA::Config::write_resources_config($cfg)
259
260 }, "delete resource failed");
261
262 return undef;
263 }});
264
265 __PACKAGE__->register_method ({
266 name => 'migrate',
267 protected => 1,
268 path => '{sid}/migrate',
269 method => 'POST',
270 description => "Request resource migration (online) to another node.",
271 permissions => {
272 check => ['perm', '/', [ 'Sys.Console' ]],
273 },
274 parameters => {
275 additionalProperties => 0,
276 properties => {
277 sid => get_standard_option('pve-ha-resource-or-vm-id',
278 { completion => \&PVE::HA::Tools::complete_sid }),
279 node => get_standard_option('pve-node',
280 { completion => \&PVE::Cluster::get_nodelist }),
281 },
282 },
283 returns => { type => 'null' },
284 code => sub {
285 my ($param) = @_;
286
287 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
288
289 PVE::HA::Config::service_is_ha_managed($sid);
290
291 PVE::HA::Config::queue_crm_commands("migrate $sid $param->{node}");
292
293 return undef;
294 }});
295
296 __PACKAGE__->register_method ({
297 name => 'relocate',
298 protected => 1,
299 path => '{sid}/relocate',
300 method => 'POST',
301 description => "Request resource relocatzion to another node. This stops the service on the old node, and restarts it on the target node.",
302 permissions => {
303 check => ['perm', '/', [ 'Sys.Console' ]],
304 },
305 parameters => {
306 additionalProperties => 0,
307 properties => {
308 sid => get_standard_option('pve-ha-resource-or-vm-id',
309 { completion => \&PVE::HA::Tools::complete_sid }),
310 node => get_standard_option('pve-node',
311 { completion => \&PVE::Cluster::get_nodelist }),
312 },
313 },
314 returns => { type => 'null' },
315 code => sub {
316 my ($param) = @_;
317
318 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
319
320 PVE::HA::Config::service_is_ha_managed($sid);
321
322 PVE::HA::Config::queue_crm_commands("relocate $sid $param->{node}");
323
324 return undef;
325 }});
326
327 1;