]> git.proxmox.com Git - pve-storage.git/blame - PVE/API2/Storage/Status.pm
parse backup files and return owner vmid
[pve-storage.git] / PVE / API2 / Storage / Status.pm
CommitLineData
b6cf0a66
DM
1package PVE::API2::Storage::Status;
2
3use strict;
4use warnings;
5
7814e05f
DM
6use File::Path;
7use File::Basename;
8use PVE::Tools;
9use PVE::INotify;
b6cf0a66
DM
10use PVE::Cluster qw(cfs_read_file);
11use PVE::Storage;
12use PVE::API2::Storage::Content;
13use PVE::RESTHandler;
14use PVE::RPCEnvironment;
15use PVE::JSONSchema qw(get_standard_option);
16use PVE::Exception qw(raise_param_exc);
17
18use base qw(PVE::RESTHandler);
19
b6cf0a66
DM
20__PACKAGE__->register_method ({
21 subclass => "PVE::API2::Storage::Content",
22 # set fragment delimiter (no subdirs) - we need that, because volume
23 # IDs may contain a slash '/'
24 fragmentDelimiter => '',
25 path => '{storage}/content',
26});
27
28__PACKAGE__->register_method ({
29 name => 'index',
30 path => '',
31 method => 'GET',
32 description => "Get status for all datastores.",
5f642f73
DM
33 permissions => {
34 description => "Only list entries where you have 'Datastore.Audit' or 'Datastore.AllocateSpace' permissions on '/storage/<storage>'",
35 user => 'all',
36 },
b6cf0a66
DM
37 protected => 1,
38 proxyto => 'node',
39 parameters => {
40 additionalProperties => 0,
41 properties => {
42 node => get_standard_option('pve-node'),
43 storage => get_standard_option
44 ('pve-storage-id', {
45 description => "Only list status for specified storage",
46 optional => 1,
47 }),
48 content => {
49 description => "Only list stores which support this content type.",
50 type => 'string', format => 'pve-storage-content',
51 optional => 1,
52 },
53 },
54 },
55 returns => {
56 type => 'array',
57 items => {
58 type => "object",
59 properties => { storage => { type => 'string' } },
60 },
61 links => [ { rel => 'child', href => "{storage}" } ],
62 },
63 code => sub {
64 my ($param) = @_;
65
5f642f73
DM
66 my $rpcenv = PVE::RPCEnvironment::get();
67 my $authuser = $rpcenv->get_user();
68
b6cf0a66
DM
69 my $cfg = cfs_read_file("storage.cfg");
70
71 my $info = PVE::Storage::storage_info($cfg, $param->{content});
72
5f642f73
DM
73 raise_param_exc({ storage => "No such storage." })
74 if $param->{storage} && !defined($info->{$param->{storage}});
75
76 my $res = {};
77 my @sids = PVE::Storage::storage_ids($cfg);
78 foreach my $storeid (@sids) {
79 my $privs = [ 'Datastore.Audit', 'Datastore.AllocateSpace' ];
80 next if !$rpcenv->check_any($authuser, "/storage/$storeid", $privs, 1);
81 next if $param->{storage} && $param->{storage} ne $storeid;
82 $res->{$storeid} = $info->{$storeid};
b6cf0a66 83 }
5f642f73
DM
84
85 return PVE::RESTHandler::hash_to_array($res, 'storage');
b6cf0a66
DM
86 }});
87
88__PACKAGE__->register_method ({
89 name => 'diridx',
90 path => '{storage}',
91 method => 'GET',
92 description => "",
5f642f73
DM
93 permissions => {
94 check => ['perm', '/storage/{storage}', ['Datastore.Audit', 'Datastore.AllocateSpace'], any => 1],
95 },
b6cf0a66
DM
96 parameters => {
97 additionalProperties => 0,
98 properties => {
99 node => get_standard_option('pve-node'),
100 storage => get_standard_option('pve-storage-id'),
101 },
102 },
103 returns => {
104 type => 'array',
105 items => {
106 type => "object",
107 properties => {
108 subdir => { type => 'string' },
109 },
110 },
111 links => [ { rel => 'child', href => "{subdir}" } ],
112 },
113 code => sub {
114 my ($param) = @_;
115
116 my $res = [
117 { subdir => 'status' },
118 { subdir => 'content' },
7814e05f 119 { subdir => 'upload' },
b6cf0a66
DM
120 { subdir => 'rrd' },
121 { subdir => 'rrddata' },
122 ];
123
124 return $res;
125 }});
126
127__PACKAGE__->register_method ({
128 name => 'read_status',
129 path => '{storage}/status',
130 method => 'GET',
131 description => "Read storage status.",
5f642f73
DM
132 permissions => {
133 check => ['perm', '/storage/{storage}', ['Datastore.Audit', 'Datastore.AllocateSpace'], any => 1],
134 },
b6cf0a66
DM
135 protected => 1,
136 proxyto => 'node',
137 parameters => {
138 additionalProperties => 0,
139 properties => {
140 node => get_standard_option('pve-node'),
141 storage => get_standard_option('pve-storage-id'),
142 },
143 },
144 returns => {
145 type => "object",
146 properties => {},
147 },
148 code => sub {
149 my ($param) = @_;
150
151 my $cfg = cfs_read_file("storage.cfg");
152
153 my $info = PVE::Storage::storage_info($cfg, $param->{content});
154
155 my $data = $info->{$param->{storage}};
156
157 raise_param_exc({ storage => "No such storage." })
158 if !defined($data);
159
160 return $data;
161 }});
162
163__PACKAGE__->register_method ({
164 name => 'rrd',
165 path => '{storage}/rrd',
166 method => 'GET',
167 description => "Read storage RRD statistics (returns PNG).",
5f642f73
DM
168 permissions => {
169 check => ['perm', '/storage/{storage}', ['Datastore.Audit', 'Datastore.AllocateSpace'], any => 1],
170 },
b6cf0a66
DM
171 protected => 1,
172 proxyto => 'node',
173 parameters => {
174 additionalProperties => 0,
175 properties => {
176 node => get_standard_option('pve-node'),
177 storage => get_standard_option('pve-storage-id'),
178 timeframe => {
179 description => "Specify the time frame you are interested in.",
180 type => 'string',
181 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
182 },
183 ds => {
184 description => "The list of datasources you want to display.",
185 type => 'string', format => 'pve-configid-list',
186 },
187 cf => {
188 description => "The RRD consolidation function",
189 type => 'string',
190 enum => [ 'AVERAGE', 'MAX' ],
191 optional => 1,
192 },
193 },
194 },
195 returns => {
196 type => "object",
197 properties => {
198 filename => { type => 'string' },
199 },
200 },
201 code => sub {
202 my ($param) = @_;
203
204 return PVE::Cluster::create_rrd_graph(
205 "pve2-storage/$param->{node}/$param->{storage}",
206 $param->{timeframe}, $param->{ds}, $param->{cf});
207
208 }});
209
210__PACKAGE__->register_method ({
211 name => 'rrddata',
212 path => '{storage}/rrddata',
213 method => 'GET',
214 description => "Read storage RRD statistics.",
5f642f73
DM
215 permissions => {
216 check => ['perm', '/storage/{storage}', ['Datastore.Audit', 'Datastore.AllocateSpace'], any => 1],
217 },
b6cf0a66
DM
218 protected => 1,
219 proxyto => 'node',
220 parameters => {
221 additionalProperties => 0,
222 properties => {
223 node => get_standard_option('pve-node'),
224 storage => get_standard_option('pve-storage-id'),
225 timeframe => {
226 description => "Specify the time frame you are interested in.",
227 type => 'string',
228 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
229 },
230 cf => {
231 description => "The RRD consolidation function",
232 type => 'string',
233 enum => [ 'AVERAGE', 'MAX' ],
234 optional => 1,
235 },
236 },
237 },
238 returns => {
239 type => "array",
240 items => {
241 type => "object",
242 properties => {},
243 },
244 },
245 code => sub {
246 my ($param) = @_;
247
248 return PVE::Cluster::create_rrd_data(
249 "pve2-storage/$param->{node}/$param->{storage}",
250 $param->{timeframe}, $param->{cf});
251 }});
7814e05f
DM
252
253__PACKAGE__->register_method ({
254 name => 'upload',
255 path => '{storage}/upload',
256 method => 'POST',
257 description => "Upload file.",
5f642f73
DM
258 permissions => {
259 check => ['perm', '/storage/{storage}', ['Datastore.AllocateSpace']],
260 },
7814e05f
DM
261 protected => 1,
262 parameters => {
263 additionalProperties => 0,
264 properties => {
265 node => get_standard_option('pve-node'),
266 storage => get_standard_option('pve-storage-id'),
267 content => {
268 description => "Content type.",
269 type => 'string', format => 'pve-storage-content',
270 },
271 filename => {
272 description => "The name of the file to create.",
273 type => 'string',
274 },
275 tmpfilename => {
276 description => "The source file name. This parameter is usually set by the REST handler. You can only overwrite it when connecting to the trustet port on localhost.",
277 type => 'string',
278 optional => 1,
279 },
280 },
281 },
282 returns => { type => "string" },
283 code => sub {
284 my ($param) = @_;
285
286 my $rpcenv = PVE::RPCEnvironment::get();
287
288 my $user = $rpcenv->get_user();
289
290 my $cfg = cfs_read_file("storage.cfg");
291
292 my $node = $param->{node};
293 my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node);
294
295 die "cant upload to storage type '$scfg->{type}'"
296 if !($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs');
297
298 my $content = $param->{content};
299
300 my $tmpfilename = $param->{tmpfilename};
301 die "missing temporary file name\n" if !$tmpfilename;
302
303 my $size = -s $tmpfilename;
304 die "temporary file '$tmpfilename' does not exists\n" if !defined($size);
305
306 my $filename = $param->{filename};
307
308 chomp $filename;
309 $filename =~ s/^.*[\/\\]//;
310 $filename =~ s/\s/_/g;
311
312 my $path;
313
314 if ($content eq 'iso') {
315 if ($filename !~ m![^/]+\.[Ii][Ss][Oo]$!) {
316 raise_param_exc({ filename => "missing '.iso' extension" });
317 }
318 $path = PVE::Storage::get_iso_dir($cfg, $param->{storage});
319 } elsif ($content eq 'vztmpl') {
320 if ($filename !~ m![^/]+\.tar\.gz$!) {
321 raise_param_exc({ filename => "missing '.tar.gz' extension" });
322 }
4ea5bca4 323 $path = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
7814e05f
DM
324 } elsif ($content eq 'backup') {
325 if ($filename !~ m!/([^/]+\.(tar|tgz))$!) {
326 raise_param_exc({ filename => "missing '.(tar|tgz)' extension" });
327 }
4ea5bca4 328 $path = PVE::Storage::get_backup_dir($cfg, $param->{storage});
7814e05f
DM
329 } else {
330 raise_param_exc({ content => "upload content type '$content' not implemented" });
331 }
332
333 die "storage '$param->{storage}' does not support '$content' content\n"
334 if !$scfg->{content}->{$content};
335
336 my $dest = "$path/$filename";
337 my $dirname = dirname($dest);
338
339 # we simply overwrite when destination when file already exists
340
341 my $cmd;
342 if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
343 my $remip = PVE::Cluster::remote_node_ip($node);
344
345 my @ssh_options = ('-o', 'BatchMode=yes', '-c', 'blowfish-cbc');
346
347 my @remcmd = ('/usr/bin/ssh', @ssh_options, $remip);
348
349 eval {
350 # activate remote storage
351 PVE::Tools::run_command([@remcmd, '/usr/sbin/pvesm', 'status',
352 '--storage', $param->{storage}]);
353 };
354 die "can't activate storage '$param->{storage}' on node '$node'\n" if $@;
355
356 PVE::Tools::run_command([@remcmd, '/bin/mkdir', '-p', $dirname],
357 errmsg => "mkdir failed");
358
359 $cmd = ['/usr/bin/scp', @ssh_options, $tmpfilename, "$remip:$dest"];
360 } else {
361 PVE::Storage::activate_storage($cfg, $param->{storage});
4ea5bca4 362 File::Path::make_path($dirname);
7814e05f
DM
363 $cmd = ['cp', $tmpfilename, $dest];
364 }
365
366 my $worker = sub {
367 my $upid = shift;
368
369 print "starting file import from: $tmpfilename\n";
370 print "target node: $node\n";
371 print "target file: $dest\n";
372 print "file size is: $size\n";
373 print "command: " . join(' ', @$cmd) . "\n";
374
375 eval { PVE::Tools::run_command($cmd, errmsg => 'import failed'); };
376 if (my $err = $@) {
377 unlink $dest;
378 die $err;
379 }
380 print "finished file import successfully\n";
381 };
382
383 return $rpcenv->fork_worker('imgcopy', undef, $user, $worker);
384 }});
b6cf0a66
DM
385
3861;