]>
git.proxmox.com Git - pve-storage.git/blob - PVE/API2/Storage/Status.pm
1 package PVE
:: API2
:: Storage
:: Status
;
10 use PVE
:: Cluster
qw(cfs_read_file) ;
12 use PVE
:: API2
:: Storage
:: Content
;
14 use PVE
:: RPCEnvironment
;
15 use PVE
:: JSONSchema
qw(get_standard_option) ;
16 use PVE
:: Exception
qw(raise_param_exc) ;
18 use base
qw(PVE::RESTHandler) ;
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' ,
28 __PACKAGE__-
> register_method ({
32 description
=> "Get status for all datastores." ,
34 description
=> "Only list entries where you have 'Datastore.Audit' or 'Datastore.AllocateSpace' permissions on '/storage/<storage>'" ,
40 additionalProperties
=> 0 ,
42 node
=> get_standard_option
( 'pve-node' ),
43 storage
=> get_standard_option
( 'pve-storage-id' , {
44 description
=> "Only list status for specified storage" ,
46 completion
=> \
& PVE
:: Storage
:: complete_storage_enabled
,
49 description
=> "Only list stores which support this content type." ,
50 type
=> 'string' , format
=> 'pve-storage-content-list' ,
52 completion
=> \
& PVE
:: Storage
:: complete_content_type
,
55 description
=> "Only list stores which are enabled (not disabled in config)." ,
60 target
=> get_standard_option
( 'pve-node' , {
61 description
=> "If target is different to 'node', we only lists shared storages which " .
62 "content is accessible on this 'node' and the specified 'target' node." ,
64 completion
=> \
& PVE
:: Cluster
:: get_nodelist
,
72 properties
=> { storage
=> { type
=> 'string' } },
74 links
=> [ { rel
=> 'child' , href
=> "{storage}" } ],
79 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
80 my $authuser = $rpcenv -> get_user ();
82 my $localnode = PVE
:: INotify
:: nodename
();
84 my $target = $param ->{ target
};
86 undef $target if $target && ( $target eq $localnode || $target eq 'localhost' );
88 my $cfg = cfs_read_file
( "storage.cfg" );
90 my $info = PVE
:: Storage
:: storage_info
( $cfg, $param ->{ content
});
92 raise_param_exc
({ storage
=> "No such storage." })
93 if $param ->{ storage
} && ! defined ( $info ->{ $param ->{ storage
}});
96 my @sids = PVE
:: Storage
:: storage_ids
( $cfg );
97 foreach my $storeid ( @sids ) {
98 next if ! $info ->{ $storeid };
99 my $privs = [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ];
100 next if ! $rpcenv -> check_any ( $authuser, "/storage/ $storeid " , $privs, 1 );
101 next if $param ->{ storage
} && $param ->{ storage
} ne $storeid ;
103 my $scfg = PVE
:: Storage
:: storage_config
( $cfg, $storeid );
105 next if $param ->{ enabled
} && $scfg ->{ disable
};
108 # check if storage content is accessible on local node and specified target node
109 # we use this on the Clone GUI
111 next if ! $scfg ->{ shared
};
112 next if ! PVE
:: Storage
:: storage_check_node
( $cfg, $storeid, undef , 1 );
113 next if ! PVE
:: Storage
:: storage_check_node
( $cfg, $storeid, $target, 1 );
116 $res ->{ $storeid } = $info ->{ $storeid };
119 return PVE
:: RESTHandler
:: hash_to_array
( $res, 'storage' );
122 __PACKAGE__-
> register_method ({
128 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
131 additionalProperties
=> 0 ,
133 node
=> get_standard_option
( 'pve-node' ),
134 storage
=> get_standard_option
( 'pve-storage-id' ),
142 subdir
=> { type
=> 'string' },
145 links
=> [ { rel
=> 'child' , href
=> "{subdir}" } ],
151 { subdir
=> 'status' },
152 { subdir
=> 'content' },
153 { subdir
=> 'upload' },
155 { subdir
=> 'rrddata' },
161 __PACKAGE__-
> register_method ({
162 name
=> 'read_status' ,
163 path
=> '{storage}/status' ,
165 description
=> "Read storage status." ,
167 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
172 additionalProperties
=> 0 ,
174 node
=> get_standard_option
( 'pve-node' ),
175 storage
=> get_standard_option
( 'pve-storage-id' ),
185 my $cfg = cfs_read_file
( "storage.cfg" );
187 my $info = PVE
:: Storage
:: storage_info
( $cfg, $param ->{ content
});
189 my $data = $info ->{ $param ->{ storage
}};
191 raise_param_exc
({ storage
=> "No such storage." })
197 __PACKAGE__-
> register_method ({
199 path
=> '{storage}/rrd' ,
201 description
=> "Read storage RRD statistics (returns PNG)." ,
203 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
208 additionalProperties
=> 0 ,
210 node
=> get_standard_option
( 'pve-node' ),
211 storage
=> get_standard_option
( 'pve-storage-id' ),
213 description
=> "Specify the time frame you are interested in." ,
215 enum
=> [ 'hour' , 'day' , 'week' , 'month' , 'year' ],
218 description
=> "The list of datasources you want to display." ,
219 type
=> 'string' , format
=> 'pve-configid-list' ,
222 description
=> "The RRD consolidation function" ,
224 enum
=> [ 'AVERAGE' , 'MAX' ],
232 filename
=> { type
=> 'string' },
238 return PVE
:: Cluster
:: create_rrd_graph
(
239 "pve2-storage/ $param ->{node}/ $param ->{storage}" ,
240 $param ->{ timeframe
}, $param ->{ ds
}, $param ->{ cf
});
244 __PACKAGE__-
> register_method ({
246 path
=> '{storage}/rrddata' ,
248 description
=> "Read storage RRD statistics." ,
250 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
255 additionalProperties
=> 0 ,
257 node
=> get_standard_option
( 'pve-node' ),
258 storage
=> get_standard_option
( 'pve-storage-id' ),
260 description
=> "Specify the time frame you are interested in." ,
262 enum
=> [ 'hour' , 'day' , 'week' , 'month' , 'year' ],
265 description
=> "The RRD consolidation function" ,
267 enum
=> [ 'AVERAGE' , 'MAX' ],
282 return PVE
:: Cluster
:: create_rrd_data
(
283 "pve2-storage/ $param ->{node}/ $param ->{storage}" ,
284 $param ->{ timeframe
}, $param ->{ cf
});
287 # makes no sense for big images and backup files (because it
288 # create a copy of the file).
289 __PACKAGE__-
> register_method ({
291 path
=> '{storage}/upload' ,
293 description
=> "Upload templates and ISO images." ,
295 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.AllocateTemplate' ]],
299 additionalProperties
=> 0 ,
301 node
=> get_standard_option
( 'pve-node' ),
302 storage
=> get_standard_option
( 'pve-storage-id' ),
304 description
=> "Content type." ,
305 type
=> 'string' , format
=> 'pve-storage-content' ,
308 description
=> "The name of the file to create." ,
312 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." ,
318 returns
=> { type
=> "string" },
322 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
324 my $user = $rpcenv -> get_user ();
326 my $cfg = cfs_read_file
( "storage.cfg" );
328 my $node = $param ->{ node
};
329 my $scfg = PVE
:: Storage
:: storage_check_enabled
( $cfg, $param ->{ storage
}, $node );
331 die "cant upload to storage type ' $scfg ->{type}' \n "
332 if !( $scfg ->{ type
} eq 'dir' || $scfg ->{ type
} eq 'nfs' || $scfg ->{ type
} eq 'glusterfs' );
334 my $content = $param ->{ content
};
336 my $tmpfilename = $param ->{ tmpfilename
};
337 die "missing temporary file name \n " if ! $tmpfilename ;
339 my $size = - s
$tmpfilename ;
340 die "temporary file ' $tmpfilename ' does not exists \n " if ! defined ( $size );
342 my $filename = $param ->{ filename
};
345 $filename =~ s/^.*[\/\\]/ /;
346 $filename =~ s/[;:,=\s\x80-\xff]/_/g ;
350 if ( $content eq 'iso' ) {
351 if ( $filename !~ m![^/]+\.[Ii][Ss][Oo]$! ) {
352 raise_param_exc
({ filename
=> "missing '.iso' extension" });
354 $path = PVE
:: Storage
:: get_iso_dir
( $cfg, $param ->{ storage
});
355 } elsif ( $content eq 'vztmpl' ) {
356 if ( $filename !~ m![^/]+\.tar\.gz$! ) {
357 raise_param_exc
({ filename
=> "missing '.tar.gz' extension" });
359 $path = PVE
:: Storage
:: get_vztmpl_dir
( $cfg, $param ->{ storage
});
361 raise_param_exc
({ content
=> "upload content type ' $content ' not allowed" });
364 die "storage ' $param ->{storage}' does not support ' $content ' content \n "
365 if ! $scfg ->{ content
}->{ $content };
367 my $dest = " $path/$filename " ;
368 my $dirname = dirname
( $dest );
370 # we simply overwrite when destination when file already exists
373 if ( $node ne 'localhost' && $node ne PVE
:: INotify
:: nodename
()) {
374 my $remip = PVE
:: Cluster
:: remote_node_ip
( $node );
376 my @ssh_options = ( '-o' , 'BatchMode=yes' );
378 my @remcmd = ( '/usr/bin/ssh' , @ssh_options, $remip, '--' );
381 # activate remote storage
382 PVE
:: Tools
:: run_command
([ @remcmd, '/usr/sbin/pvesm' , 'status' ,
383 '--storage' , $param ->{ storage
}]);
385 die "can't activate storage ' $param ->{storage}' on node ' $node ' \n " if $@ ;
387 PVE
:: Tools
:: run_command
([ @remcmd, '/bin/mkdir' , '-p' , '--' , PVE
:: Tools
:: shell_quote
( $dirname )],
388 errmsg
=> "mkdir failed" );
390 $cmd = [ '/usr/bin/scp' , @ssh_options, '--' , $tmpfilename, "[ $remip ]:" . PVE
:: Tools
:: shell_quote
( $dest )];
392 PVE
:: Storage
:: activate_storage
( $cfg, $param ->{ storage
});
393 File
:: Path
:: make_path
( $dirname );
394 $cmd = [ 'cp' , '--' , $tmpfilename, $dest ];
400 print "starting file import from: $tmpfilename\n " ;
401 print "target node: $node\n " ;
402 print "target file: $dest\n " ;
403 print "file size is: $size\n " ;
404 print "command: " . join ( ' ' , @$cmd ) . " \n " ;
406 eval { PVE
:: Tools
:: run_command
( $cmd, errmsg
=> 'import failed' ); };
411 print "finished file import successfully \n " ;
414 my $upid = $rpcenv -> fork_worker ( 'imgcopy' , undef , $user, $worker );
416 # apache removes the temporary file on return, so we need
417 # to wait here to make sure the worker process starts and
418 # opens the file before it gets removed.