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