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