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