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