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