]>
git.proxmox.com Git - pve-storage.git/blob - PVE/API2/Storage/Status.pm
897b4a77e28b739a590c5ade6046dd87fd1f009f
1 package PVE
:: API2
:: Storage
:: Status
;
13 use PVE
:: API2
:: Storage
:: Content
;
14 use PVE
:: API2
:: Storage
:: PruneBackups
;
15 use PVE
:: API2
:: Storage
:: FileRestore
;
17 use PVE
:: RPCEnvironment
;
18 use PVE
:: JSONSchema
qw(get_standard_option) ;
19 use PVE
:: Exception
qw(raise_param_exc) ;
21 use base
qw(PVE::RESTHandler) ;
23 __PACKAGE__-
> register_method ({
24 subclass
=> "PVE::API2::Storage::PruneBackups" ,
25 path
=> '{storage}/prunebackups' ,
28 __PACKAGE__-
> register_method ({
29 subclass
=> "PVE::API2::Storage::Content" ,
30 # set fragment delimiter (no subdirs) - we need that, because volume
31 # IDs may contain a slash '/'
32 fragmentDelimiter
=> '' ,
33 path
=> '{storage}/content' ,
36 __PACKAGE__-
> register_method ({
37 subclass
=> "PVE::API2::Storage::FileRestore" ,
38 path
=> '{storage}/file-restore' ,
41 __PACKAGE__-
> register_method ({
45 description
=> "Get status for all datastores." ,
47 description
=> "Only list entries where you have 'Datastore.Audit' or 'Datastore.AllocateSpace' permissions on '/storage/<storage>'" ,
53 additionalProperties
=> 0 ,
55 node
=> get_standard_option
( 'pve-node' ),
56 storage
=> get_standard_option
( 'pve-storage-id' , {
57 description
=> "Only list status for specified storage" ,
59 completion
=> \
& PVE
:: Storage
:: complete_storage_enabled
,
62 description
=> "Only list stores which support this content type." ,
63 type
=> 'string' , format
=> 'pve-storage-content-list' ,
65 completion
=> \
& PVE
:: Storage
:: complete_content_type
,
68 description
=> "Only list stores which are enabled (not disabled in config)." ,
73 target
=> get_standard_option
( 'pve-node' , {
74 description
=> "If target is different to 'node', we only lists shared storages which " .
75 "content is accessible on this 'node' and the specified 'target' node." ,
77 completion
=> \
& PVE
:: Cluster
:: get_nodelist
,
80 description
=> "Include information about formats" ,
92 storage
=> get_standard_option
( 'pve-storage-id' ),
94 description
=> "Storage type." ,
98 description
=> "Allowed storage content types." ,
99 type
=> 'string' , format
=> 'pve-storage-content-list' ,
102 description
=> "Set when storage is enabled (not disabled)." ,
107 description
=> "Set when storage is accessible." ,
112 description
=> "Shared flag from storage configuration." ,
117 description
=> "Total storage space in bytes." ,
123 description
=> "Used storage space in bytes." ,
129 description
=> "Available storage space in bytes." ,
135 description
=> "Used fraction (used/total)." ,
137 renderer
=> 'fraction_as_percentage' ,
142 links
=> [ { rel
=> 'child' , href
=> "{storage}" } ],
147 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
148 my $authuser = $rpcenv -> get_user ();
150 my $localnode = PVE
:: INotify
:: nodename
();
152 my $target = $param ->{ target
};
154 undef $target if $target && ( $target eq $localnode || $target eq 'localhost' );
156 my $cfg = PVE
:: Storage
:: config
();
158 my $info = PVE
:: Storage
:: storage_info
( $cfg, $param ->{ content
}, $param ->{ format
});
160 raise_param_exc
({ storage
=> "No such storage." })
161 if $param ->{ storage
} && ! defined ( $info ->{ $param ->{ storage
}});
164 my @sids = PVE
:: Storage
:: storage_ids
( $cfg );
165 foreach my $storeid ( @sids ) {
166 my $data = $info ->{ $storeid };
168 my $privs = [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ];
169 next if ! $rpcenv -> check_any ( $authuser, "/storage/ $storeid " , $privs, 1 );
170 next if $param ->{ storage
} && $param ->{ storage
} ne $storeid ;
172 my $scfg = PVE
:: Storage
:: storage_config
( $cfg, $storeid );
174 next if $param ->{ enabled
} && $scfg ->{ disable
};
177 # check if storage content is accessible on local node and specified target node
178 # we use this on the Clone GUI
180 next if ! $scfg ->{ shared
};
181 next if ! PVE
:: Storage
:: storage_check_node
( $cfg, $storeid, undef , 1 );
182 next if ! PVE
:: Storage
:: storage_check_node
( $cfg, $storeid, $target, 1 );
185 if ( $data ->{ total
}) {
186 $data ->{ used_fraction
} = ( $data ->{ used
} // 0 ) / $data ->{ total
};
189 $res ->{ $storeid } = $data ;
192 return PVE
:: RESTHandler
:: hash_to_array
( $res, 'storage' );
195 __PACKAGE__-
> register_method ({
201 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
204 additionalProperties
=> 0 ,
206 node
=> get_standard_option
( 'pve-node' ),
207 storage
=> get_standard_option
( 'pve-storage-id' ),
215 subdir
=> { type
=> 'string' },
218 links
=> [ { rel
=> 'child' , href
=> "{subdir}" } ],
224 { subdir
=> 'status' },
225 { subdir
=> 'content' },
226 { subdir
=> 'upload' },
228 { subdir
=> 'rrddata' },
229 { subdir
=> 'prunebackups' },
235 __PACKAGE__-
> register_method ({
236 name
=> 'read_status' ,
237 path
=> '{storage}/status' ,
239 description
=> "Read storage status." ,
241 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
246 additionalProperties
=> 0 ,
248 node
=> get_standard_option
( 'pve-node' ),
249 storage
=> get_standard_option
( 'pve-storage-id' ),
259 my $cfg = PVE
:: Storage
:: config
();
261 my $info = PVE
:: Storage
:: storage_info
( $cfg, $param ->{ content
});
263 my $data = $info ->{ $param ->{ storage
}};
265 raise_param_exc
({ storage
=> "No such storage." })
271 __PACKAGE__-
> register_method ({
273 path
=> '{storage}/rrd' ,
275 description
=> "Read storage RRD statistics (returns PNG)." ,
277 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
282 additionalProperties
=> 0 ,
284 node
=> get_standard_option
( 'pve-node' ),
285 storage
=> get_standard_option
( 'pve-storage-id' ),
287 description
=> "Specify the time frame you are interested in." ,
289 enum
=> [ 'hour' , 'day' , 'week' , 'month' , 'year' ],
292 description
=> "The list of datasources you want to display." ,
293 type
=> 'string' , format
=> 'pve-configid-list' ,
296 description
=> "The RRD consolidation function" ,
298 enum
=> [ 'AVERAGE' , 'MAX' ],
306 filename
=> { type
=> 'string' },
312 return PVE
:: RRD
:: create_rrd_graph
(
313 "pve2-storage/ $param ->{node}/ $param ->{storage}" ,
314 $param ->{ timeframe
}, $param ->{ ds
}, $param ->{ cf
});
317 __PACKAGE__-
> register_method ({
319 path
=> '{storage}/rrddata' ,
321 description
=> "Read storage RRD statistics." ,
323 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
328 additionalProperties
=> 0 ,
330 node
=> get_standard_option
( 'pve-node' ),
331 storage
=> get_standard_option
( 'pve-storage-id' ),
333 description
=> "Specify the time frame you are interested in." ,
335 enum
=> [ 'hour' , 'day' , 'week' , 'month' , 'year' ],
338 description
=> "The RRD consolidation function" ,
340 enum
=> [ 'AVERAGE' , 'MAX' ],
355 return PVE
:: RRD
:: create_rrd_data
(
356 "pve2-storage/ $param ->{node}/ $param ->{storage}" ,
357 $param ->{ timeframe
}, $param ->{ cf
});
360 # makes no sense for big images and backup files (because it
361 # create a copy of the file).
362 __PACKAGE__-
> register_method ({
364 path
=> '{storage}/upload' ,
366 description
=> "Upload templates and ISO images." ,
368 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.AllocateTemplate' ]],
372 additionalProperties
=> 0 ,
374 node
=> get_standard_option
( 'pve-node' ),
375 storage
=> get_standard_option
( 'pve-storage-id' ),
377 description
=> "Content type." ,
378 type
=> 'string' , format
=> 'pve-storage-content' ,
381 description
=> "The name of the file to create." ,
385 description
=> "The source file name. This parameter is usually set by the REST handler. You can only overwrite it when connecting to the trusted port on localhost." ,
391 returns
=> { type
=> "string" },
395 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
397 my $user = $rpcenv -> get_user ();
399 my $cfg = PVE
:: Storage
:: config
();
401 my $node = $param ->{ node
};
402 my $scfg = PVE
:: Storage
:: storage_check_enabled
( $cfg, $param ->{ storage
}, $node );
404 die "can't upload to storage type ' $scfg ->{type}' \n "
405 if ! defined ( $scfg ->{ path
});
407 my $content = $param ->{ content
};
409 my $tmpfilename = $param ->{ tmpfilename
};
410 die "missing temporary file name \n " if ! $tmpfilename ;
412 my $size = - s
$tmpfilename ;
413 die "temporary file ' $tmpfilename ' does not exist \n " if ! defined ( $size );
415 my $filename = $param ->{ filename
};
418 $filename =~ s/^.*[\/\\]/ /;
419 $filename =~ s/[^-a-zA-Z0-9_.]/_/g ;
423 if ( $content eq 'iso' ) {
424 if ( $filename !~ m![^/]+$PVE::Storage::iso_extension_re$! ) {
425 raise_param_exc
({ filename
=> "missing '.iso' or '.img' extension" });
427 $path = PVE
:: Storage
:: get_iso_dir
( $cfg, $param ->{ storage
});
428 } elsif ( $content eq 'vztmpl' ) {
429 if ( $filename !~ m![^/]+\.tar\.[gx]z$! ) {
430 raise_param_exc
({ filename
=> "missing '.tar.gz' or '.tar.xz' extension" });
432 $path = PVE
:: Storage
:: get_vztmpl_dir
( $cfg, $param ->{ storage
});
434 raise_param_exc
({ content
=> "upload content type ' $content ' not allowed" });
437 die "storage ' $param ->{storage}' does not support ' $content ' content \n "
438 if ! $scfg ->{ content
}->{ $content };
440 my $dest = " $path/$filename " ;
441 my $dirname = dirname
( $dest );
443 # best effort to match apl_download behaviour
444 chmod 0644 , $tmpfilename ;
446 # we simply overwrite the destination file if it already exists
449 if ( $node ne 'localhost' && $node ne PVE
:: INotify
:: nodename
()) {
450 my $remip = PVE
:: Cluster
:: remote_node_ip
( $node );
452 my @ssh_options = ( '-o' , 'BatchMode=yes' );
454 my @remcmd = ( '/usr/bin/ssh' , @ssh_options, $remip, '--' );
457 # activate remote storage
458 PVE
:: Tools
:: run_command
([ @remcmd, '/usr/sbin/pvesm' , 'status' ,
459 '--storage' , $param ->{ storage
}]);
461 die "can't activate storage ' $param ->{storage}' on node ' $node ': $@\n " if $@ ;
463 PVE
:: Tools
:: run_command
([ @remcmd, '/bin/mkdir' , '-p' , '--' , PVE
:: Tools
:: shell_quote
( $dirname )],
464 errmsg
=> "mkdir failed" );
466 $cmd = [ '/usr/bin/scp' , @ssh_options, '-p' , '--' , $tmpfilename, "[ $remip ]:" . PVE
:: Tools
:: shell_quote
( $dest )];
468 PVE
:: Storage
:: activate_storage
( $cfg, $param ->{ storage
});
469 File
:: Path
:: make_path
( $dirname );
470 $cmd = [ 'cp' , '--' , $tmpfilename, $dest ];
476 print "starting file import from: $tmpfilename\n " ;
477 print "target node: $node\n " ;
478 print "target file: $dest\n " ;
479 print "file size is: $size\n " ;
480 print "command: " . join ( ' ' , @$cmd ) . " \n " ;
482 eval { PVE
:: Tools
:: run_command
( $cmd, errmsg
=> 'import failed' ); };
487 print "finished file import successfully \n " ;
490 my $upid = $rpcenv -> fork_worker ( 'imgcopy' , undef , $user, $worker );
492 # apache removes the temporary file on return, so we need
493 # to wait here to make sure the worker process starts and
494 # opens the file before it gets removed.