]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/API2/HA/Resources.pm
HA API: Fix permissions
[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 PVE::HA::Config::lock_ha_domain(
184 sub {
185
186 my $cfg = PVE::HA::Config::read_resources_config();
187
188 PVE::SectionConfig::assert_if_modified($cfg, $digest);
189
190 my $scfg = $cfg->{ids}->{$sid} ||
191 die "no such resource '$sid'\n";
192
193 my $plugin = PVE::HA::Resources->lookup($scfg->{type});
194 my $opts = $plugin->check_config($sid, $param, 0, 1);
195
196 foreach my $k (%$opts) {
197 $scfg->{$k} = $opts->{$k};
198 }
199
200 if ($delete) {
201 my $options = $plugin->private()->{options}->{$type};
202 foreach my $k (PVE::Tools::split_list($delete)) {
203 my $d = $options->{$k} ||
204 die "no such option '$k'\n";
205 die "unable to delete required option '$k'\n"
206 if !$d->{optional};
207 die "unable to delete fixed option '$k'\n"
208 if $d->{fixed};
209 delete $scfg->{$k};
210 }
211 }
212
213 PVE::HA::Config::write_resources_config($cfg)
214
215 }, "update resource failed");
216
217 return undef;
218 }});
219
220 __PACKAGE__->register_method ({
221 name => 'delete',
222 protected => 1,
223 path => '{sid}',
224 method => 'DELETE',
225 description => "Delete resource configuration.",
226 permissions => {
227 check => ['perm', '/', [ 'Sys.Console' ]],
228 },
229 parameters => {
230 additionalProperties => 0,
231 properties => {
232 sid => get_standard_option('pve-ha-resource-or-vm-id',
233 { completion => \&PVE::HA::Tools::complete_sid }),
234 },
235 },
236 returns => { type => 'null' },
237 code => sub {
238 my ($param) = @_;
239
240 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
241
242 PVE::HA::Config::service_is_ha_managed($sid);
243
244 PVE::HA::Config::lock_ha_domain(
245 sub {
246
247 my $cfg = PVE::HA::Config::read_resources_config();
248
249 delete $cfg->{ids}->{$sid};
250
251 PVE::HA::Config::write_resources_config($cfg)
252
253 }, "delete resource failed");
254
255 return undef;
256 }});
257
258 __PACKAGE__->register_method ({
259 name => 'migrate',
260 protected => 1,
261 path => '{sid}/migrate',
262 method => 'POST',
263 description => "Request resource migration (online) to another node.",
264 permissions => {
265 check => ['perm', '/', [ 'Sys.Console' ]],
266 },
267 parameters => {
268 additionalProperties => 0,
269 properties => {
270 sid => get_standard_option('pve-ha-resource-or-vm-id',
271 { completion => \&PVE::HA::Tools::complete_sid }),
272 node => get_standard_option('pve-node',
273 { completion => \&PVE::Cluster::get_nodelist }),
274 },
275 },
276 returns => { type => 'null' },
277 code => sub {
278 my ($param) = @_;
279
280 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
281
282 PVE::HA::Config::service_is_ha_managed($sid);
283
284 PVE::HA::Config::queue_crm_commands("migrate $sid $param->{node}");
285
286 return undef;
287 }});
288
289 __PACKAGE__->register_method ({
290 name => 'relocate',
291 protected => 1,
292 path => '{sid}/relocate',
293 method => 'POST',
294 description => "Request resource relocatzion to another node. This stops the service on the old node, and restarts it on the target node.",
295 permissions => {
296 check => ['perm', '/', [ 'Sys.Console' ]],
297 },
298 parameters => {
299 additionalProperties => 0,
300 properties => {
301 sid => get_standard_option('pve-ha-resource-or-vm-id',
302 { completion => \&PVE::HA::Tools::complete_sid }),
303 node => get_standard_option('pve-node',
304 { completion => \&PVE::Cluster::get_nodelist }),
305 },
306 },
307 returns => { type => 'null' },
308 code => sub {
309 my ($param) = @_;
310
311 my ($sid, $type, $name) = PVE::HA::Tools::parse_sid(extract_param($param, 'sid'));
312
313 PVE::HA::Config::service_is_ha_managed($sid);
314
315 PVE::HA::Config::queue_crm_commands("relocate $sid $param->{node}");
316
317 return undef;
318 }});
319
320 1;