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