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