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