]> git.proxmox.com Git - pve-storage.git/blame - PVE/API2/Storage/Content.pm
api2/storage/content: change to volume_size_info and add return properties
[pve-storage.git] / PVE / API2 / Storage / Content.pm
CommitLineData
b6cf0a66
DM
1package PVE::API2::Storage::Content;
2
3use strict;
4use warnings;
1ccae449 5use Data::Dumper;
b6cf0a66
DM
6
7use PVE::SafeSyslog;
83d7192f 8use PVE::Cluster;
b6cf0a66
DM
9use PVE::Storage;
10use PVE::INotify;
11use PVE::Exception qw(raise_param_exc);
12use PVE::RPCEnvironment;
13use PVE::RESTHandler;
14use PVE::JSONSchema qw(get_standard_option);
65bb9859 15use PVE::SSHInfo;
b6cf0a66
DM
16
17use base qw(PVE::RESTHandler);
18
b6cf0a66 19__PACKAGE__->register_method ({
9148f5b3 20 name => 'index',
b6cf0a66
DM
21 path => '',
22 method => 'GET',
23 description => "List storage content.",
9148f5b3 24 permissions => {
5f642f73
DM
25 check => ['perm', '/storage/{storage}', ['Datastore.Audit', 'Datastore.AllocateSpace'], any => 1],
26 },
b6cf0a66
DM
27 protected => 1,
28 proxyto => 'node',
29 parameters => {
30 additionalProperties => 0,
9148f5b3 31 properties => {
b6cf0a66 32 node => get_standard_option('pve-node'),
f3bd890d
DM
33 storage => get_standard_option('pve-storage-id', {
34 completion => \&PVE::Storage::complete_storage_enabled,
35 }),
9148f5b3 36 content => {
b6cf0a66
DM
37 description => "Only list content of this type.",
38 type => 'string', format => 'pve-storage-content',
39 optional => 1,
98437f4c 40 completion => \&PVE::Storage::complete_content_type,
b6cf0a66 41 },
f3bd890d
DM
42 vmid => get_standard_option('pve-vmid', {
43 description => "Only list images for this VM",
44 optional => 1,
45 completion => \&PVE::Cluster::complete_vmid,
46 }),
b6cf0a66
DM
47 },
48 },
49 returns => {
50 type => 'array',
51 items => {
52 type => "object",
9148f5b3 53 properties => {
26549428
DM
54 volid => {
55 description => "Volume identifier.",
56 type => 'string',
57 },
58 vmid => {
59 description => "Associated Owner VMID.",
60 type => 'integer',
61 optional => 1,
62 },
63 parent => {
64 description => "Volume identifier of parent (for linked cloned).",
65 type => 'string',
66 optional => 1,
67 },
68 'format' => {
69 description => "Format identifier ('raw', 'qcow2', 'subvol', 'iso', 'tgz' ...)",
70 type => 'string',
71 },
72 size => {
73 description => "Volume size in bytes.",
74 type => 'integer',
75 renderer => 'bytes',
76 },
77 used => {
78 description => "Used space. Please note that most storage plugins " .
1ee70938 79 "do not report anything useful here.",
26549428
DM
80 type => 'integer',
81 renderer => 'bytes',
82 optional => 1,
83 },
654a987a 84 ctime => {
1ee70938 85 description => "Creation time (seconds since the UNIX Epoch).",
654a987a
DM
86 type => 'integer',
87 minimum => 0,
88 optional => 1,
89 },
6fef456c
DC
90 notes => {
91 description => "Optional notes. If they contain multiple lines, only the first one is returned here.",
9778e5c2
DC
92 type => 'string',
93 optional => 1,
94 },
95 verification => {
96 description => "Last backup verification result, only useful for PBS storages.",
97 type => 'object',
98 properties => {
99 state => {
100 description => "Last backup verification state.",
101 type => 'string',
102 },
103 upid => {
104 description => "Last backup verification UPID.",
105 type => 'string',
106 },
107 },
108 optional => 1,
109 },
b6cf0a66
DM
110 },
111 },
112 links => [ { rel => 'child', href => "{volid}" } ],
113 },
114 code => sub {
115 my ($param) = @_;
116
b8744249
DM
117 my $rpcenv = PVE::RPCEnvironment::get();
118
119 my $authuser = $rpcenv->get_user();
120
b6cf0a66
DM
121 my $storeid = $param->{storage};
122
83d7192f 123 my $cfg = PVE::Storage::config();
b6cf0a66 124
37ba0aea 125 my $vollist = PVE::Storage::volume_list($cfg, $storeid, $param->{vmid}, $param->{content});
b6cf0a66
DM
126
127 my $res = [];
37ba0aea 128 foreach my $item (@$vollist) {
04a13668 129 eval { PVE::Storage::check_volume_access($rpcenv, $authuser, $cfg, undef, $item->{volid}); };
37ba0aea 130 next if $@;
83a9960c 131 $item->{vmid} = int($item->{vmid}) if (defined($item->{vmid}));
37ba0aea 132 push @$res, $item;
b6cf0a66
DM
133 }
134
9148f5b3 135 return $res;
b6cf0a66
DM
136 }});
137
138__PACKAGE__->register_method ({
9148f5b3 139 name => 'create',
b6cf0a66
DM
140 path => '',
141 method => 'POST',
142 description => "Allocate disk images.",
9148f5b3 143 permissions => {
5f642f73
DM
144 check => ['perm', '/storage/{storage}', ['Datastore.AllocateSpace']],
145 },
b6cf0a66
DM
146 protected => 1,
147 proxyto => 'node',
148 parameters => {
149 additionalProperties => 0,
9148f5b3 150 properties => {
b6cf0a66 151 node => get_standard_option('pve-node'),
f7621c01
DM
152 storage => get_standard_option('pve-storage-id', {
153 completion => \&PVE::Storage::complete_storage_enabled,
154 }),
9148f5b3 155 filename => {
03f03009 156 description => "The name of the file to create.",
b6cf0a66
DM
157 type => 'string',
158 },
f7621c01
DM
159 vmid => get_standard_option('pve-vmid', {
160 description => "Specify owner VM",
161 completion => \&PVE::Cluster::complete_vmid,
162 }),
b6cf0a66
DM
163 size => {
164 description => "Size in kilobyte (1024 bytes). Optional suffixes 'M' (megabyte, 1024K) and 'G' (gigabyte, 1024M)",
165 type => 'string',
166 pattern => '\d+[MG]?',
167 },
168 'format' => {
169 type => 'string',
1ccae449 170 enum => ['raw', 'qcow2', 'subvol'],
b6cf0a66
DM
171 requires => 'size',
172 optional => 1,
173 },
174 },
175 },
176 returns => {
177 description => "Volume identifier",
178 type => 'string',
179 },
180 code => sub {
181 my ($param) = @_;
182
183 my $storeid = $param->{storage};
184 my $name = $param->{filename};
185 my $sizestr = $param->{size};
186
187 my $size;
188 if ($sizestr =~ m/^\d+$/) {
189 $size = $sizestr;
190 } elsif ($sizestr =~ m/^(\d+)M$/) {
191 $size = $1 * 1024;
192 } elsif ($sizestr =~ m/^(\d+)G$/) {
193 $size = $1 * 1024 * 1024;
194 } else {
195 raise_param_exc({ size => "unable to parse size '$sizestr'" });
196 }
197
198 # extract FORMAT from name
8e87d6ee 199 if ($name =~ m/\.(raw|qcow2|vmdk)$/) {
b6cf0a66
DM
200 my $fmt = $1;
201
9148f5b3 202 raise_param_exc({ format => "different storage formats ($param->{format} != $fmt)" })
b6cf0a66
DM
203 if $param->{format} && $param->{format} ne $fmt;
204
205 $param->{format} = $fmt;
206 }
207
83d7192f 208 my $cfg = PVE::Storage::config();
9148f5b3
TM
209
210 my $volid = PVE::Storage::vdisk_alloc ($cfg, $storeid, $param->{vmid},
211 $param->{format},
b6cf0a66
DM
212 $name, $size);
213
214 return $volid;
215 }});
216
217# we allow to pass volume names (without storage prefix) if the storage
218# is specified as separate parameter.
219my $real_volume_id = sub {
220 my ($storeid, $volume) = @_;
221
222 my $volid;
223
224 if ($volume =~ m/:/) {
225 eval {
226 my ($sid, $volname) = PVE::Storage::parse_volume_id ($volume);
5f25af2f 227 die "storage ID missmatch ($sid != $storeid)\n"
b6cf0a66
DM
228 if $storeid && $sid ne $storeid;
229 $volid = $volume;
b755bdb0 230 $storeid = $sid;
b6cf0a66 231 };
9148f5b3
TM
232 raise_param_exc({ volume => $@ }) if $@;
233
b6cf0a66 234 } else {
9148f5b3 235 raise_param_exc({ volume => "no storage speficied - incomplete volume ID" })
b6cf0a66 236 if !$storeid;
9148f5b3 237
b6cf0a66
DM
238 $volid = "$storeid:$volume";
239 }
240
b755bdb0 241 return wantarray ? ($volid, $storeid) : $volid;
b6cf0a66
DM
242};
243
244__PACKAGE__->register_method ({
245 name => 'info',
246 path => '{volume}',
247 method => 'GET',
248 description => "Get volume attributes",
9148f5b3 249 permissions => {
b8744249 250 description => "You need read access for the volume.",
b755bdb0 251 user => 'all',
5f642f73 252 },
b6cf0a66
DM
253 protected => 1,
254 proxyto => 'node',
255 parameters => {
256 additionalProperties => 0,
9148f5b3 257 properties => {
b6cf0a66
DM
258 node => get_standard_option('pve-node'),
259 storage => get_standard_option('pve-storage-id', { optional => 1 }),
260 volume => {
261 description => "Volume identifier",
9148f5b3 262 type => 'string',
b6cf0a66
DM
263 },
264 },
265 },
33696518
DC
266 returns => {
267 type => 'object',
268 properties => {
269 path => {
270 description => "The Path",
271 type => 'string',
272 },
273 size => {
274 description => "Volume size in bytes.",
275 type => 'integer',
276 renderer => 'bytes',
277 },
278 used => {
279 description => "Used space. Please note that most storage plugins " .
280 "do not report anything useful here.",
281 type => 'integer',
282 renderer => 'bytes',
283 },
284 format => {
285 description => "Format identifier ('raw', 'qcow2', 'subvol', 'iso', 'tgz' ...)",
286 type => 'string',
287 },
288 },
289 },
b6cf0a66
DM
290 code => sub {
291 my ($param) = @_;
292
b755bdb0
DM
293 my $rpcenv = PVE::RPCEnvironment::get();
294 my $authuser = $rpcenv->get_user();
295
296 my ($volid, $storeid) = &$real_volume_id($param->{storage}, $param->{volume});
297
83d7192f 298 my $cfg = PVE::Storage::config();
b6cf0a66 299
ec73c0ff 300 PVE::Storage::check_volume_access($rpcenv, $authuser, $cfg, undef, $volid);
b8744249 301
b6cf0a66 302 my $path = PVE::Storage::path($cfg, $volid);
33696518
DC
303 my ($size, $format, $used, $parent) = PVE::Storage::volume_size_info($cfg, $volid);
304 die "volume_size_info on '$volid' failed\n" if !($format && $size);
b6cf0a66
DM
305
306 # fixme: return more attributes?
307 return {
308 path => $path,
309 size => $size,
310 used => $used,
a18f7740 311 format => $format,
b6cf0a66
DM
312 };
313 }});
314
315__PACKAGE__->register_method ({
316 name => 'delete',
317 path => '{volume}',
318 method => 'DELETE',
319 description => "Delete volume",
9148f5b3 320 permissions => {
df6b79c8 321 description => "You need 'Datastore.Allocate' privilege on the storage (or 'Datastore.AllocateSpace' for backup volumes if you have VM.Backup privilege on the VM).",
b755bdb0 322 user => 'all',
5f642f73 323 },
b6cf0a66
DM
324 protected => 1,
325 proxyto => 'node',
326 parameters => {
327 additionalProperties => 0,
9148f5b3 328 properties => {
b6cf0a66 329 node => get_standard_option('pve-node'),
f3bd890d
DM
330 storage => get_standard_option('pve-storage-id', {
331 optional => 1,
332 completion => \&PVE::Storage::complete_storage,
333 }),
b6cf0a66
DM
334 volume => {
335 description => "Volume identifier",
f3bd890d
DM
336 type => 'string',
337 completion => \&PVE::Storage::complete_volume,
b6cf0a66 338 },
1f56f6f8
DJ
339 delay => {
340 type => 'integer',
341 description => "Time to wait for the task to finish. We return 'null' if the task finish within that time.",
342 minimum => 1,
343 maximum => 30,
344 optional => 1,
345 },
b6cf0a66
DM
346 },
347 },
1f56f6f8 348 returns => { type => 'string', optional => 1, },
b6cf0a66
DM
349 code => sub {
350 my ($param) = @_;
351
b755bdb0
DM
352 my $rpcenv = PVE::RPCEnvironment::get();
353 my $authuser = $rpcenv->get_user();
354
83d7192f 355 my $cfg = PVE::Storage::config();
df6b79c8 356
b755bdb0 357 my ($volid, $storeid) = &$real_volume_id($param->{storage}, $param->{volume});
b755bdb0 358
df6b79c8
DM
359 my ($path, $ownervm, $vtype) = PVE::Storage::path($cfg, $volid);
360 if ($vtype eq 'backup' && $ownervm) {
361 $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
362 $rpcenv->check($authuser, "/vms/$ownervm", ['VM.Backup']);
363 } else {
364 $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.Allocate']);
365 }
b6cf0a66 366
1f56f6f8
DJ
367 my $worker = sub {
368 PVE::Storage::vdisk_free ($cfg, $volid);
369 print "Removed volume '$volid'\n";
370 if ($vtype eq 'backup'
371 && $path =~ /(.*\/vzdump-\w+-\d+-\d{4}_\d{2}_\d{2}-\d{2}_\d{2}_\d{2})[^\/]+$/) {
372 my $logpath = "$1.log";
373 # try to cleanup our backup log file too, if still exisiting, #318
374 unlink($logpath) if -e $logpath;
375 }
376 };
a2a04139 377
1f56f6f8
DJ
378 my $id = (defined $ownervm ? "$ownervm@" : '') . $storeid;
379 my $upid = $rpcenv->fork_worker('imgdel', $id, $authuser, $worker);
380 my $background_delay = $param->{delay};
381 if ($background_delay) {
382 my $end_time = time() + $background_delay;
383 my $currently_deleting; # not necessarily true, e.g. sequential api call from cli
384 do {
385 my $task = PVE::Tools::upid_decode($upid);
386 $currently_deleting = PVE::ProcFSTools::check_process_running($task->{pid}, $task->{pstart});
387 sleep 1 if $currently_deleting;
388 } while (time() < $end_time && $currently_deleting);
389
390 if (!$currently_deleting) {
391 my $status = PVE::Tools::upid_read_status($upid);
392 return undef if $status eq 'OK';
393 die $status;
394 }
b1f9d990 395 }
1f56f6f8 396 return $upid;
b6cf0a66
DM
397 }});
398
883eeea6
DM
399__PACKAGE__->register_method ({
400 name => 'copy',
401 path => '{volume}',
402 method => 'POST',
5f642f73 403 description => "Copy a volume. This is experimental code - do not use.",
883eeea6
DM
404 protected => 1,
405 proxyto => 'node',
406 parameters => {
407 additionalProperties => 0,
9148f5b3 408 properties => {
883eeea6
DM
409 node => get_standard_option('pve-node'),
410 storage => get_standard_option('pve-storage-id', { optional => 1}),
411 volume => {
412 description => "Source volume identifier",
9148f5b3 413 type => 'string',
883eeea6
DM
414 },
415 target => {
416 description => "Target volume identifier",
9148f5b3 417 type => 'string',
883eeea6 418 },
9148f5b3 419 target_node => get_standard_option('pve-node', {
883eeea6
DM
420 description => "Target node. Default is local node.",
421 optional => 1,
422 }),
423 },
424 },
9148f5b3 425 returns => {
883eeea6
DM
426 type => 'string',
427 },
428 code => sub {
429 my ($param) = @_;
430
431 my $rpcenv = PVE::RPCEnvironment::get();
432
433 my $user = $rpcenv->get_user();
434
435 my $target_node = $param->{target_node} || PVE::INotify::nodename();
436 # pvesh examples
437 # cd /nodes/localhost/storage/local/content
438 # pve:/> create local:103/vm-103-disk-1.raw -target local:103/vm-103-disk-2.raw
439 # pve:/> create 103/vm-103-disk-1.raw -target 103/vm-103-disk-3.raw
440
441 my $src_volid = &$real_volume_id($param->{storage}, $param->{volume});
442 my $dst_volid = &$real_volume_id($param->{storage}, $param->{target});
443
444 print "DEBUG: COPY $src_volid TO $dst_volid\n";
445
83d7192f 446 my $cfg = PVE::Storage::config();
883eeea6
DM
447
448 # do all parameter checks first
449
450 # then do all short running task (to raise errors befor we go to background)
451
452 # then start the worker task
453 my $worker = sub {
454 my $upid = shift;
455
456 print "DEBUG: starting worker $upid\n";
457
458 my ($target_sid, $target_volname) = PVE::Storage::parse_volume_id($dst_volid);
459 #my $target_ip = PVE::Cluster::remote_node_ip($target_node);
460
461 # you need to get this working (fails currently, because storage_migrate() uses
462 # ssh to connect to local host (which is not needed
65bb9859 463 my $sshinfo = PVE::SSHInfo::get_ssh_info($target_node);
dc3655a1 464 PVE::Storage::storage_migrate($cfg, $src_volid, $sshinfo, $target_sid, {'target_volname' => $target_volname});
883eeea6
DM
465
466 print "DEBUG: end worker $upid\n";
467
468 };
469
470 return $rpcenv->fork_worker('imgcopy', undef, $user, $worker);
471 }});
472
b6cf0a66 4731;