]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Pool.pm
ffcc3946ea1330c23db00449e793ab53fd1135fd
[pve-manager.git] / PVE / API2 / Pool.pm
1 package PVE::API2::Pool;
2
3 use strict;
4 use warnings;
5
6 use PVE::AccessControl;
7 use PVE::Cluster qw (cfs_read_file cfs_write_file);
8 use PVE::Exception qw(raise_param_exc);
9 use PVE::INotify;
10 use PVE::Storage;
11
12 use PVE::SafeSyslog;
13
14 use PVE::API2Tools;
15 use PVE::RESTHandler;
16
17 use base qw(PVE::RESTHandler);
18
19 __PACKAGE__->register_method ({
20 name => 'index',
21 path => '',
22 method => 'GET',
23 description => "Pool index.",
24 permissions => {
25 description => "List all pools where you have Pool.Audit permissions on /pool/<pool>.",
26 user => 'all',
27 },
28 parameters => {
29 additionalProperties => 0,
30 properties => {},
31 },
32 returns => {
33 type => 'array',
34 items => {
35 type => "object",
36 properties => {
37 poolid => { type => 'string' },
38 },
39 },
40 links => [ { rel => 'child', href => "{poolid}" } ],
41 },
42 code => sub {
43 my ($param) = @_;
44
45 my $rpcenv = PVE::RPCEnvironment::get();
46 my $authuser = $rpcenv->get_user();
47
48 my $usercfg = $rpcenv->{user_cfg};
49
50
51 my $res = [];
52 for my $pool (sort keys %{$usercfg->{pools}}) {
53 next if !$rpcenv->check($authuser, "/pool/$pool", [ 'Pool.Audit' ], 1);
54
55 my $entry = { poolid => $pool };
56 my $pool_config = $usercfg->{pools}->{$pool};
57 $entry->{comment} = $pool_config->{comment} if defined($pool_config->{comment});
58 push @$res, $entry;
59 }
60
61 return $res;
62 }});
63
64 __PACKAGE__->register_method ({
65 name => 'create_pool',
66 protected => 1,
67 path => '',
68 method => 'POST',
69 permissions => {
70 check => ['perm', '/pool/{poolid}', ['Pool.Allocate']],
71 },
72 description => "Create new pool.",
73 parameters => {
74 additionalProperties => 0,
75 properties => {
76 poolid => {
77 type => 'string',
78 format => 'pve-poolid',
79 },
80 comment => {
81 type => 'string',
82 optional => 1,
83 },
84 },
85 },
86 returns => { type => 'null' },
87 code => sub {
88 my ($param) = @_;
89
90 PVE::AccessControl::lock_user_config(sub {
91 my $usercfg = cfs_read_file("user.cfg");
92 my $pool = $param->{poolid};
93
94 die "pool '$pool' already exists\n" if $usercfg->{pools}->{$pool};
95
96 $usercfg->{pools}->{$pool} = {
97 vms => {},
98 storage => {},
99 };
100
101 $usercfg->{pools}->{$pool}->{comment} = $param->{comment} if $param->{comment};
102
103 cfs_write_file("user.cfg", $usercfg);
104 }, "create pool failed");
105
106 return;
107 }});
108
109 __PACKAGE__->register_method ({
110 name => 'update_pool',
111 protected => 1,
112 path => '{poolid}',
113 method => 'PUT',
114 permissions => {
115 description => "You also need the right to modify permissions on any object you add/delete.",
116 check => ['perm', '/pool/{poolid}', ['Pool.Allocate']],
117 },
118 description => "Update pool data.",
119 parameters => {
120 additionalProperties => 0,
121 properties => {
122 poolid => { type => 'string', format => 'pve-poolid' },
123 comment => { type => 'string', optional => 1 },
124 vms => {
125 description => 'List of guest VMIDs to add or remove from this pool.',
126 type => 'string', format => 'pve-vmid-list',
127 optional => 1,
128 },
129 storage => {
130 description => 'List of storage IDs to add or remove from this pool.',
131 type => 'string', format => 'pve-storage-id-list',
132 optional => 1,
133 },
134 transfer => {
135 description => 'Allow adding a guest even if already in another pool.'
136 .' The guest will be removed from its current pool and added to this one.',
137 type => 'boolean',
138 optional => 1,
139 default => 0,
140 },
141 delete => {
142 description => 'Remove the passed VMIDs and/or storage IDs instead of adding them.',
143 type => 'boolean',
144 optional => 1,
145 default => 0,
146 },
147 },
148 },
149 returns => { type => 'null' },
150 code => sub {
151 my ($param) = @_;
152
153 my $rpcenv = PVE::RPCEnvironment::get();
154 my $authuser = $rpcenv->get_user();
155
156 PVE::AccessControl::lock_user_config(sub {
157 my $usercfg = cfs_read_file("user.cfg");
158 my $pool = $param->{poolid};
159 my $pool_config = $usercfg->{pools}->{$pool};
160
161 die "pool '$pool' does not exist\n" if !$pool_config;
162
163 $pool_config->{comment} = $param->{comment} if defined($param->{comment});
164
165 if (defined($param->{vms})) {
166 for my $vmid (PVE::Tools::split_list($param->{vms})) {
167 $rpcenv->check_perm_modify($authuser, "/vms/$vmid");
168
169 if ($param->{delete}) {
170 die "VM $vmid is not a pool member\n" if !$pool_config->{vms}->{$vmid};
171 delete $pool_config->{vms}->{$vmid};
172 delete $usercfg->{vms}->{$vmid};
173 } else {
174 die "VM $vmid is already a pool member\n" if $pool_config->{vms}->{$vmid};
175 if (defined(my $existing_pool = $usercfg->{vms}->{$vmid})) {
176 die "VM $vmid belongs already to pool '$existing_pool' and 'transfer' is not set\n"
177 if !$param->{transfer};
178
179 $rpcenv->check($authuser, "/pool/$existing_pool", ['Pool.Allocate']);
180 delete $usercfg->{pools}->{$existing_pool}->{vms}->{$vmid};
181 }
182 $pool_config->{vms}->{$vmid} = 1;
183 $usercfg->{vms}->{$vmid} = $pool;
184 }
185 }
186 }
187
188 if (defined($param->{storage})) {
189 for my $storeid (PVE::Tools::split_list($param->{storage})) {
190 $rpcenv->check_perm_modify($authuser, "/storage/$storeid");
191
192 if ($param->{delete}) {
193 die "Storage '$storeid' is not a pool member\n"
194 if !$pool_config->{storage}->{$storeid};
195 delete $pool_config->{storage}->{$storeid};
196 } else {
197 die "Storage '$storeid' is already a pool member\n"
198 if $pool_config->{storage}->{$storeid};
199
200 $pool_config->{storage}->{$storeid} = 1;
201 }
202 }
203 }
204
205 cfs_write_file("user.cfg", $usercfg);
206 }, "update pools failed");
207
208 return;
209 }});
210
211 __PACKAGE__->register_method ({
212 name => 'read_pool',
213 path => '{poolid}',
214 method => 'GET',
215 permissions => {
216 check => ['perm', '/pool/{poolid}', ['Pool.Audit']],
217 },
218 description => "Get pool configuration.",
219 parameters => {
220 additionalProperties => 0,
221 properties => {
222 poolid => {
223 type => 'string',
224 format => 'pve-poolid',
225 },
226 type => {
227 type => 'string',
228 enum => [ 'qemu', 'lxc', 'storage' ],
229 optional => 1,
230 },
231 },
232 },
233 returns => {
234 type => "object",
235 additionalProperties => 0,
236 properties => {
237 comment => {
238 type => 'string',
239 optional => 1,
240 },
241 members => {
242 type => 'array',
243 items => {
244 type => "object",
245 additionalProperties => 1,
246 properties => {
247 type => {
248 type => 'string',
249 enum => [ 'qemu', 'lxc', 'openvz', 'storage' ],
250 },
251 id => {
252 type => 'string',
253 },
254 node => {
255 type => 'string',
256 },
257 vmid => {
258 type => 'integer',
259 optional => 1,
260 },
261 storage => {
262 type => 'string',
263 optional => 1,
264 },
265 },
266 },
267 },
268 },
269 },
270 code => sub {
271 my ($param) = @_;
272
273 my $usercfg = cfs_read_file("user.cfg");
274
275 my $vmlist = PVE::Cluster::get_vmlist() || {};
276 my $idlist = $vmlist->{ids} || {};
277
278 my $rrd = PVE::Cluster::rrd_dump();
279
280 my $pool = $param->{poolid};
281
282 my $pool_config = $usercfg->{pools}->{$pool};
283
284 die "pool '$pool' does not exist\n" if !$pool_config;
285
286 my $members = [];
287 for my $vmid (sort keys %{$pool_config->{vms}}) {
288 my $vmdata = $idlist->{$vmid};
289 next if !$vmdata || defined($param->{type}) && $param->{type} ne $vmdata->{type};
290 my $entry = PVE::API2Tools::extract_vm_stats($vmid, $vmdata, $rrd);
291 push @$members, $entry;
292 }
293
294 my $nodename = PVE::INotify::nodename();
295 my $cfg = PVE::Storage::config();
296 if (!defined($param->{type}) || $param->{type} eq 'storage') {
297 for my $storeid (sort keys %{$pool_config->{storage}}) {
298 my $scfg = PVE::Storage::storage_config ($cfg, $storeid, 1);
299 next if !$scfg;
300
301 my $storage_node = $nodename; # prefer local node
302 if ($scfg->{nodes} && !$scfg->{nodes}->{$storage_node}) {
303 for my $node (sort keys(%{$scfg->{nodes}})) {
304 $storage_node = $node;
305 last;
306 }
307 }
308
309 my $entry = PVE::API2Tools::extract_storage_stats($storeid, $scfg, $storage_node, $rrd);
310 push @$members, $entry;
311 }
312 }
313
314 my $res = {
315 members => $members,
316 };
317 $res->{comment} = $pool_config->{comment} if defined($pool_config->{comment});
318
319 return $res;
320 }});
321
322
323 __PACKAGE__->register_method ({
324 name => 'delete_pool',
325 protected => 1,
326 path => '{poolid}',
327 method => 'DELETE',
328 permissions => {
329 description => "You can only delete empty pools (no members).",
330 check => ['perm', '/pool/{poolid}', ['Pool.Allocate']],
331 },
332 description => "Delete pool.",
333 parameters => {
334 additionalProperties => 0,
335 properties => {
336 poolid => { type => 'string', format => 'pve-poolid' },
337 }
338 },
339 returns => { type => 'null' },
340 code => sub {
341 my ($param) = @_;
342
343 my $rpcenv = PVE::RPCEnvironment::get();
344 my $authuser = $rpcenv->get_user();
345
346 PVE::AccessControl::lock_user_config(sub {
347 my $vmlist = PVE::Cluster::get_vmlist() || {};
348 my $idlist = $vmlist->{ids} || {};
349
350 my $storecfg = PVE::Storage::config();
351 my $usercfg = cfs_read_file("user.cfg");
352
353 my $pool = $param->{poolid};
354
355 my $pool_config = $usercfg->{pools}->{$pool};
356 die "pool '$pool' does not exist\n" if !$pool_config;
357
358 for my $vmid (sort keys %{$pool_config->{vms}}) {
359 next if !$idlist->{$vmid}; # ignore destroyed guests
360 die "pool '$pool' is not empty (contains VM $vmid)\n";
361 }
362
363 for my $storeid (sort keys %{$pool_config->{storage}}) {
364 next if !PVE::Storage::storage_config ($storecfg, $storeid, 1);
365 die "pool '$pool' is not empty (contains storage '$storeid')\n";
366 }
367
368 delete ($usercfg->{pools}->{$pool});
369 PVE::AccessControl::delete_pool_acl($pool, $usercfg);
370
371 cfs_write_file("user.cfg", $usercfg);
372 }, "delete pool failed");
373
374 return;
375 }});
376
377 1;