2011-08-23 07:43:03 +02:00
package PVE::Storage ;
use strict ;
2013-10-01 13:07:46 +02:00
use warnings ;
2015-08-10 16:53:19 +02:00
use Data::Dumper ;
2013-10-01 13:07:46 +02:00
2011-08-23 07:43:03 +02:00
use POSIX ;
use IO::Select ;
use IO::File ;
use File::Basename ;
use File::Path ;
use Cwd 'abs_path' ;
2012-05-23 12:43:28 +02:00
use Socket ;
2011-08-23 07:43:03 +02:00
2015-08-31 11:02:25 +02:00
use PVE::Tools qw( run_command file_read_firstline $IPV6RE ) ;
2016-03-25 15:07:24 +01:00
use PVE::Cluster qw( cfs_read_file cfs_write_file cfs_lock_file ) ;
2011-08-23 07:43:03 +02:00
use PVE::Exception qw( raise_param_exc ) ;
use PVE::JSONSchema ;
use PVE::INotify ;
2012-03-07 12:32:02 +01:00
use PVE::RPCEnvironment ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
use PVE::Storage::Plugin ;
use PVE::Storage::DirPlugin ;
use PVE::Storage::LVMPlugin ;
2015-11-09 11:05:57 +01:00
use PVE::Storage::LvmThinPlugin ;
2012-05-16 10:56:29 +02:00
use PVE::Storage::NFSPlugin ;
use PVE::Storage::ISCSIPlugin ;
2012-05-31 10:31:01 +02:00
use PVE::Storage::RBDPlugin ;
2012-07-16 07:26:08 +02:00
use PVE::Storage::SheepdogPlugin ;
2012-07-17 12:57:44 +02:00
use PVE::Storage::ISCSIDirectPlugin ;
2013-08-12 09:59:00 +02:00
use PVE::Storage::GlusterfsPlugin ;
2015-01-24 14:13:24 +01:00
use PVE::Storage::ZFSPoolPlugin ;
2013-10-02 04:58:10 +02:00
use PVE::Storage::ZFSPlugin ;
2015-03-20 17:44:15 +01:00
use PVE::Storage::DRBDPlugin ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
# load and initialize all plugins
PVE::Storage::DirPlugin - > register ( ) ;
PVE::Storage::LVMPlugin - > register ( ) ;
2015-11-09 11:05:57 +01:00
PVE::Storage::LvmThinPlugin - > register ( ) ;
2012-05-16 10:56:29 +02:00
PVE::Storage::NFSPlugin - > register ( ) ;
PVE::Storage::ISCSIPlugin - > register ( ) ;
2012-05-31 10:31:01 +02:00
PVE::Storage::RBDPlugin - > register ( ) ;
2012-07-16 07:26:08 +02:00
PVE::Storage::SheepdogPlugin - > register ( ) ;
2012-07-17 12:57:44 +02:00
PVE::Storage::ISCSIDirectPlugin - > register ( ) ;
2013-08-12 09:59:00 +02:00
PVE::Storage::GlusterfsPlugin - > register ( ) ;
2015-01-24 14:13:24 +01:00
PVE::Storage::ZFSPoolPlugin - > register ( ) ;
2013-10-02 04:58:10 +02:00
PVE::Storage::ZFSPlugin - > register ( ) ;
2015-03-20 17:44:15 +01:00
PVE::Storage::DRBDPlugin - > register ( ) ;
2012-05-16 10:56:29 +02:00
PVE::Storage::Plugin - > init ( ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ UDEVADM = '/sbin/udevadm' ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
# PVE::Storage utility functions
2011-08-23 07:43:03 +02:00
sub config {
return cfs_read_file ( "storage.cfg" ) ;
}
2016-03-25 15:07:24 +01:00
sub write_config {
my ( $ cfg ) = @ _ ;
cfs_write_file ( 'storage.cfg' , $ cfg ) ;
}
2011-08-23 07:43:03 +02:00
sub lock_storage_config {
my ( $ code , $ errmsg ) = @ _ ;
cfs_lock_file ( "storage.cfg" , undef , $ code ) ;
my $ err = $@ ;
if ( $ err ) {
$ errmsg ? die "$errmsg: $err" : die $ err ;
}
}
sub storage_config {
my ( $ cfg , $ storeid , $ noerr ) = @ _ ;
die "no storage id specified\n" if ! $ storeid ;
2013-08-12 11:56:41 +02:00
2011-08-23 07:43:03 +02:00
my $ scfg = $ cfg - > { ids } - > { $ storeid } ;
die "storage '$storeid' does not exists\n" if ( ! $ noerr && ! $ scfg ) ;
return $ scfg ;
}
sub storage_check_node {
my ( $ cfg , $ storeid , $ node , $ noerr ) = @ _ ;
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
if ( $ scfg - > { nodes } ) {
$ node = PVE::INotify:: nodename ( ) if ! $ node || ( $ node eq 'localhost' ) ;
if ( ! $ scfg - > { nodes } - > { $ node } ) {
2012-03-28 06:36:38 +02:00
die "storage '$storeid' is not available on node '$node'\n" if ! $ noerr ;
2011-08-23 07:43:03 +02:00
return undef ;
}
}
return $ scfg ;
}
sub storage_check_enabled {
my ( $ cfg , $ storeid , $ node , $ noerr ) = @ _ ;
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
if ( $ scfg - > { disable } ) {
die "storage '$storeid' is disabled\n" if ! $ noerr ;
return undef ;
}
return storage_check_node ( $ cfg , $ storeid , $ node , $ noerr ) ;
}
sub storage_ids {
my ( $ cfg ) = @ _ ;
2012-05-16 10:56:29 +02:00
return keys % { $ cfg - > { ids } } ;
2011-08-23 07:43:03 +02:00
}
2012-05-16 10:56:29 +02:00
sub file_size_info {
my ( $ filename , $ timeout ) = @ _ ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
return PVE::Storage::Plugin:: file_size_info ( $ filename , $ timeout ) ;
2011-08-23 07:43:03 +02:00
}
2012-07-27 08:00:12 +02:00
sub volume_size_info {
my ( $ cfg , $ volid , $ timeout ) = @ _ ;
2012-07-31 11:01:02 +02:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
if ( $ storeid ) {
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
return $ plugin - > volume_size_info ( $ scfg , $ storeid , $ volname , $ timeout ) ;
} elsif ( $ volid =~ m | ^ ( / . + ) $| && - e $ volid ) {
return file_size_info ( $ volid , $ timeout ) ;
} else {
return 0 ;
}
2012-07-27 08:00:12 +02:00
}
2012-08-06 11:57:26 +02:00
sub volume_resize {
my ( $ cfg , $ volid , $ size , $ running ) = @ _ ;
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
if ( $ storeid ) {
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
return $ plugin - > volume_resize ( $ scfg , $ storeid , $ volname , $ size , $ running ) ;
} elsif ( $ volid =~ m | ^ ( / . + ) $| && - e $ volid ) {
2015-02-12 09:40:17 +01:00
die "resize file/device '$volid' is not possible\n" ;
2012-08-06 11:57:26 +02:00
} else {
2015-02-12 09:40:17 +01:00
die "unable to parse volume ID '$volid'\n" ;
2012-08-06 11:57:26 +02:00
}
}
2015-02-12 08:41:35 +01:00
sub volume_rollback_is_possible {
my ( $ cfg , $ volid , $ snap ) = @ _ ;
2015-04-27 10:09:18 +02:00
2015-02-12 08:41:35 +01:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
if ( $ storeid ) {
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
return $ plugin - > volume_rollback_is_possible ( $ scfg , $ storeid , $ volname , $ snap ) ;
} elsif ( $ volid =~ m | ^ ( / . + ) $| && - e $ volid ) {
2015-02-12 09:40:17 +01:00
die "snapshot rollback file/device '$volid' is not possible\n" ;
2015-02-12 08:41:35 +01:00
} else {
2015-02-12 09:40:17 +01:00
die "unable to parse volume ID '$volid'\n" ;
2015-02-12 08:41:35 +01:00
}
}
2012-09-06 10:27:51 +02:00
sub volume_snapshot {
2015-05-06 09:57:35 +02:00
my ( $ cfg , $ volid , $ snap ) = @ _ ;
2012-09-06 10:27:51 +02:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
if ( $ storeid ) {
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2015-05-06 09:57:35 +02:00
return $ plugin - > volume_snapshot ( $ scfg , $ storeid , $ volname , $ snap ) ;
2012-09-06 10:27:51 +02:00
} elsif ( $ volid =~ m | ^ ( / . + ) $| && - e $ volid ) {
2015-02-12 09:40:17 +01:00
die "snapshot file/device '$volid' is not possible\n" ;
2012-09-06 10:27:51 +02:00
} else {
2015-02-12 09:40:17 +01:00
die "unable to parse volume ID '$volid'\n" ;
2012-09-06 10:27:51 +02:00
}
}
2012-09-06 10:27:58 +02:00
sub volume_snapshot_rollback {
my ( $ cfg , $ volid , $ snap ) = @ _ ;
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
if ( $ storeid ) {
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2015-02-12 09:28:52 +01:00
$ plugin - > volume_rollback_is_possible ( $ scfg , $ storeid , $ volname , $ snap ) ;
2012-09-06 10:27:58 +02:00
return $ plugin - > volume_snapshot_rollback ( $ scfg , $ storeid , $ volname , $ snap ) ;
} elsif ( $ volid =~ m | ^ ( / . + ) $| && - e $ volid ) {
2015-02-12 09:40:17 +01:00
die "snapshot rollback file/device '$volid' is not possible\n" ;
2012-09-06 10:27:58 +02:00
} else {
2015-02-12 09:40:17 +01:00
die "unable to parse volume ID '$volid'\n" ;
2012-09-06 10:27:58 +02:00
}
}
2012-09-06 10:28:05 +02:00
sub volume_snapshot_delete {
my ( $ cfg , $ volid , $ snap , $ running ) = @ _ ;
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
if ( $ storeid ) {
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2012-09-07 12:59:26 +02:00
return $ plugin - > volume_snapshot_delete ( $ scfg , $ storeid , $ volname , $ snap , $ running ) ;
2012-09-06 10:28:05 +02:00
} elsif ( $ volid =~ m | ^ ( / . + ) $| && - e $ volid ) {
2015-02-12 09:40:17 +01:00
die "snapshot delete file/device '$volid' is not possible\n" ;
2012-09-06 10:28:05 +02:00
} else {
2015-02-12 09:40:17 +01:00
die "unable to parse volume ID '$volid'\n" ;
2012-09-06 10:28:05 +02:00
}
}
2012-12-27 16:07:13 +01:00
sub volume_has_feature {
my ( $ cfg , $ feature , $ volid , $ snap , $ running ) = @ _ ;
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
if ( $ storeid ) {
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
return $ plugin - > volume_has_feature ( $ scfg , $ feature , $ storeid , $ volname , $ snap , $ running ) ;
} elsif ( $ volid =~ m | ^ ( / . + ) $| && - e $ volid ) {
return undef ;
} else {
return undef ;
}
}
2012-05-16 10:56:29 +02:00
sub get_image_dir {
my ( $ cfg , $ storeid , $ vmid ) = @ _ ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ path = $ plugin - > get_subdir ( $ scfg , 'images' ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
return $ vmid ? "$path/$vmid" : $ path ;
2011-08-23 07:43:03 +02:00
}
2012-05-16 10:56:29 +02:00
sub get_private_dir {
2011-08-23 07:43:03 +02:00
my ( $ cfg , $ storeid , $ vmid ) = @ _ ;
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ path = $ plugin - > get_subdir ( $ scfg , 'rootdir' ) ;
2011-11-07 11:14:32 +01:00
2012-05-16 10:56:29 +02:00
return $ vmid ? "$path/$vmid" : $ path ;
2011-11-07 11:14:32 +01:00
}
2011-08-23 07:43:03 +02:00
sub get_iso_dir {
my ( $ cfg , $ storeid ) = @ _ ;
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
return $ plugin - > get_subdir ( $ scfg , 'iso' ) ;
2011-08-23 07:43:03 +02:00
}
sub get_vztmpl_dir {
my ( $ cfg , $ storeid ) = @ _ ;
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
return $ plugin - > get_subdir ( $ scfg , 'vztmpl' ) ;
2011-08-23 07:43:03 +02:00
}
2011-10-18 11:23:31 +02:00
sub get_backup_dir {
my ( $ cfg , $ storeid ) = @ _ ;
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
return $ plugin - > get_subdir ( $ scfg , 'backup' ) ;
2011-08-23 07:43:03 +02:00
}
# library implementation
sub parse_vmid {
my $ vmid = shift ;
die "VMID '$vmid' contains illegal characters\n" if $ vmid !~ m/^\d+$/ ;
return int ( $ vmid ) ;
}
2013-09-25 10:26:54 +02:00
sub parse_volname {
my ( $ cfg , $ volid ) = @ _ ;
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2015-08-12 07:34:58 +02:00
# returns ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format)
2013-09-25 10:26:54 +02:00
return $ plugin - > parse_volname ( $ volname ) ;
}
2011-08-23 07:43:03 +02:00
sub parse_volume_id {
my ( $ volid , $ noerr ) = @ _ ;
2013-01-31 11:36:49 +01:00
return PVE::Storage::Plugin:: parse_volume_id ( $ volid , $ noerr ) ;
2011-08-23 07:43:03 +02:00
}
2013-01-31 08:42:26 +01:00
sub volume_is_base {
my ( $ cfg , $ volid ) = @ _ ;
my ( $ sid , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
return 0 if ! $ sid ;
if ( my $ scfg = $ cfg - > { ids } - > { $ sid } ) {
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2013-08-12 11:56:41 +02:00
my ( $ vtype , $ name , $ vmid , $ basename , $ basevmid , $ isBase ) =
2013-01-31 08:42:26 +01:00
$ plugin - > parse_volname ( $ volname ) ;
return $ isBase ? 1 : 0 ;
} else {
# stale volid with undefined storage - so we can just guess
if ( $ volid =~ m/base-/ ) {
return 1 ;
}
}
return 0 ;
}
2011-08-23 07:43:03 +02:00
# try to map a filesystem path to a volume identifier
sub path_to_volume_id {
my ( $ cfg , $ path ) = @ _ ;
my $ ids = $ cfg - > { ids } ;
2012-05-16 10:56:29 +02:00
my ( $ sid , $ volname ) = parse_volume_id ( $ path , 1 ) ;
2011-08-23 07:43:03 +02:00
if ( $ sid ) {
2012-05-16 10:56:29 +02:00
if ( my $ scfg = $ ids - > { $ sid } ) {
2013-01-31 08:47:27 +01:00
if ( $ scfg - > { path } ) {
2012-05-16 10:56:29 +02:00
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
my ( $ vtype , $ name , $ vmid ) = $ plugin - > parse_volname ( $ volname ) ;
2011-08-23 07:43:03 +02:00
return ( $ vtype , $ path ) ;
}
}
return ( '' ) ;
}
2013-08-12 11:56:41 +02:00
# Note: abs_path() return undef if $path doesn not exist
2012-04-18 12:34:39 +02:00
# for example when nfs storage is not mounted
$ path = abs_path ( $ path ) || $ path ;
2011-08-23 07:43:03 +02:00
foreach my $ sid ( keys %$ ids ) {
2012-05-16 10:56:29 +02:00
my $ scfg = $ ids - > { $ sid } ;
next if ! $ scfg - > { path } ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
my $ imagedir = $ plugin - > get_subdir ( $ scfg , 'images' ) ;
my $ isodir = $ plugin - > get_subdir ( $ scfg , 'iso' ) ;
my $ tmpldir = $ plugin - > get_subdir ( $ scfg , 'vztmpl' ) ;
my $ backupdir = $ plugin - > get_subdir ( $ scfg , 'backup' ) ;
my $ privatedir = $ plugin - > get_subdir ( $ scfg , 'rootdir' ) ;
2011-08-23 07:43:03 +02:00
if ( $ path =~ m !^$imagedir/(\d+)/([^/\s]+)$! ) {
my $ vmid = $ 1 ;
my $ name = $ 2 ;
2013-01-31 08:14:55 +01:00
my $ vollist = $ plugin - > list_images ( $ sid , $ scfg , $ vmid ) ;
foreach my $ info ( @$ vollist ) {
my ( $ storeid , $ volname ) = parse_volume_id ( $ info - > { volid } ) ;
my $ volpath = $ plugin - > path ( $ scfg , $ volname , $ storeid ) ;
if ( $ volpath eq $ path ) {
return ( 'images' , $ info - > { volid } ) ;
}
}
2011-08-23 07:43:03 +02:00
} elsif ( $ path =~ m !^$isodir/([^/]+\.[Ii][Ss][Oo])$! ) {
my $ name = $ 1 ;
2013-08-12 11:56:41 +02:00
return ( 'iso' , "$sid:iso/$name" ) ;
2011-08-23 07:43:03 +02:00
} elsif ( $ path =~ m !^$tmpldir/([^/]+\.tar\.gz)$! ) {
my $ name = $ 1 ;
return ( 'vztmpl' , "$sid:vztmpl/$name" ) ;
2011-11-14 07:11:36 +01:00
} elsif ( $ path =~ m !^$privatedir/(\d+)$! ) {
my $ vmid = $ 1 ;
return ( 'rootdir' , "$sid:rootdir/$vmid" ) ;
2012-12-13 12:51:52 +01:00
} elsif ( $ path =~ m !^$backupdir/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz|vma|vma\.gz|vma\.lzo))$! ) {
2011-10-18 11:23:31 +02:00
my $ name = $ 1 ;
2013-08-12 11:56:41 +02:00
return ( 'iso' , "$sid:backup/$name" ) ;
2011-08-23 07:43:03 +02:00
}
}
# can't map path to volume id
return ( '' ) ;
}
sub path {
2013-05-02 05:51:49 +02:00
my ( $ cfg , $ volid , $ snapname ) = @ _ ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2013-05-02 05:51:49 +02:00
my ( $ path , $ owner , $ vtype ) = $ plugin - > path ( $ scfg , $ volname , $ storeid , $ snapname ) ;
2012-02-01 11:47:44 +01:00
return wantarray ? ( $ path , $ owner , $ vtype ) : $ path ;
2011-08-23 07:43:03 +02:00
}
2013-10-01 12:26:19 +02:00
sub abs_filesystem_path {
my ( $ cfg , $ volid ) = @ _ ;
my $ path ;
if ( PVE::Storage:: parse_volume_id ( $ volid , 1 ) ) {
PVE::Storage:: activate_volumes ( $ cfg , [ $ volid ] ) ;
$ path = PVE::Storage:: path ( $ cfg , $ volid ) ;
} else {
if ( - f $ volid ) {
my $ abspath = abs_path ( $ volid ) ;
if ( $ abspath && $ abspath =~ m | ^ ( / . + ) $| ) {
$ path = $ 1 ; # untaint any path
}
}
}
die "can't find file '$volid'\n" if ! ( $ path && - f $ path ) ;
return $ path ;
}
2011-08-23 07:43:03 +02:00
sub storage_migrate {
my ( $ cfg , $ volid , $ target_host , $ target_storeid , $ target_volname ) = @ _ ;
2012-07-16 06:45:13 +02:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
2011-08-23 07:43:03 +02:00
$ target_volname = $ volname if ! $ target_volname ;
2012-07-16 06:45:13 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
# no need to migrate shared content
return if $ storeid eq $ target_storeid && $ scfg - > { shared } ;
2012-07-16 06:45:13 +02:00
my $ tcfg = storage_config ( $ cfg , $ target_storeid ) ;
2011-08-23 07:43:03 +02:00
my $ target_volid = "${target_storeid}:${target_volname}" ;
my $ errstr = "unable to migrate '$volid' to '${target_volid}' on host '$target_host'" ;
2012-10-31 12:16:56 +01:00
my $ sshoptions = "-o 'BatchMode=yes'" ;
2011-08-23 07:43:03 +02:00
my $ ssh = "/usr/bin/ssh $sshoptions" ;
local $ ENV { RSYNC_RSH } = $ ssh ;
2012-05-16 10:56:29 +02:00
# only implemented for file system based storage
if ( $ scfg - > { path } ) {
if ( $ tcfg - > { path } ) {
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ src_plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
my $ dst_plugin = PVE::Storage::Plugin - > lookup ( $ tcfg - > { type } ) ;
2012-07-16 06:45:13 +02:00
my $ src = $ src_plugin - > path ( $ scfg , $ volname , $ storeid ) ;
my $ dst = $ dst_plugin - > path ( $ tcfg , $ target_volname , $ target_storeid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ dirname = dirname ( $ dst ) ;
2011-08-23 07:43:03 +02:00
if ( $ tcfg - > { shared } ) { # we can do a local copy
2013-08-12 11:56:41 +02:00
2011-11-25 07:42:26 +01:00
run_command ( [ '/bin/mkdir' , '-p' , $ dirname ] ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
run_command ( [ '/bin/cp' , $ src , $ dst ] ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
} else {
2013-08-12 11:56:41 +02:00
run_command ( [ '/usr/bin/ssh' , "root\@${target_host}" ,
2012-05-16 10:56:29 +02:00
'/bin/mkdir' , '-p' , $ dirname ] ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
# we use rsync with --sparse, so we can't use --inplace,
# so we remove file on the target if it already exists to
# save space
my ( $ size , $ format ) = PVE::Storage::Plugin:: file_size_info ( $ src ) ;
if ( $ format && ( $ format eq 'raw' ) && $ size ) {
2013-08-12 11:56:41 +02:00
run_command ( [ '/usr/bin/ssh' , "root\@${target_host}" ,
2012-05-16 10:56:29 +02:00
'rm' , '-f' , $ dst ] ,
outfunc = > sub { } ) ;
}
2011-08-23 07:43:03 +02:00
2016-03-21 16:13:14 +01:00
my $ cmd ;
if ( $ format eq 'subvol' ) {
$ cmd = [ '/usr/bin/rsync' , '--progress' , '-X' , '-A' , '--numeric-ids' ,
'-aH' , '--delete' , '--no-whole-file' , '--inplace' ,
'--one-file-system' , "$src/" , "[root\@${target_host}]:$dst" ] ;
} else {
$ cmd = [ '/usr/bin/rsync' , '--progress' , '--sparse' , '--whole-file' ,
$ src , "[root\@${target_host}]:$dst" ] ;
}
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ percent = - 1 ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
run_command ( $ cmd , outfunc = > sub {
my $ line = shift ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
if ( $ line =~ m/^\s*(\d+\s+(\d+)%\s.*)$/ ) {
if ( $ 2 > $ percent ) {
$ percent = $ 2 ;
print "rsync status: $1\n" ;
* STDOUT - > flush ( ) ;
}
} else {
print "$line\n" ;
* STDOUT - > flush ( ) ;
}
} ) ;
}
} else {
die "$errstr - target type '$tcfg->{type}' not implemented\n" ;
}
2015-04-27 10:09:18 +02:00
2015-04-27 09:27:47 +02:00
} elsif ( $ scfg - > { type } eq 'zfspool' ) {
2015-04-27 10:09:18 +02:00
if ( $ tcfg - > { type } eq 'zfspool' ) {
2015-04-27 09:27:47 +02:00
2015-04-27 10:09:18 +02:00
die "$errstr - pool on target has not same name as source!"
if $ tcfg - > { pool } ne $ scfg - > { pool } ;
2015-04-27 09:27:47 +02:00
2015-04-27 10:09:18 +02:00
my ( undef , $ volname ) = parse_volname ( $ cfg , $ volid ) ;
2015-04-27 09:27:47 +02:00
my $ zfspath = "$scfg->{pool}\/$volname" ;
2016-03-16 14:24:36 +01:00
my $ snap = [ 'zfs' , 'snapshot' , "$zfspath\@__migration__" ] ;
2015-04-27 09:27:47 +02:00
2016-03-16 14:24:36 +01:00
my $ send = [ [ 'zfs' , 'send' , '-Rpv' , "$zfspath\@__migration__" ] , [ 'ssh' , "root\@$target_host" ,
'zfs' , 'recv' , $ zfspath ] ] ;
2015-04-27 09:27:47 +02:00
2016-03-16 14:24:36 +01:00
my $ destroy_target = [ 'ssh' , "root\@$target_host" , 'zfs' , 'destroy' , "$zfspath\@__migration__" ] ;
2015-04-27 09:27:47 +02:00
run_command ( $ snap ) ;
2015-04-27 10:09:18 +02:00
eval {
2015-04-27 09:27:47 +02:00
run_command ( $ send ) ;
} ;
my $ err ;
if ( $ err = $@ ) {
2016-03-16 14:24:36 +01:00
run_command ( [ 'zfs' , 'destroy' , "$zfspath\@__migration__" ] ) ;
2015-04-27 09:27:47 +02:00
die $ err ;
2015-04-27 10:09:18 +02:00
}
run_command ( $ destroy_target ) ;
2015-04-27 09:27:47 +02:00
} else {
die "$errstr - target type $tcfg->{type} is not valid\n" ;
}
2012-05-16 10:56:29 +02:00
} else {
die "$errstr - source type '$scfg->{type}' not implemented\n" ;
2011-08-23 07:43:03 +02:00
}
}
2013-01-30 12:55:37 +01:00
sub vdisk_clone {
2014-07-04 10:30:53 +02:00
my ( $ cfg , $ volid , $ vmid , $ snap ) = @ _ ;
2013-08-12 11:56:41 +02:00
2013-01-30 12:55:37 +01:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2013-08-12 11:56:41 +02:00
2013-01-30 12:55:37 +01:00
activate_storage ( $ cfg , $ storeid ) ;
# lock shared storage
return $ plugin - > cluster_lock_storage ( $ storeid , $ scfg - > { shared } , undef , sub {
2014-07-04 10:30:53 +02:00
my $ volname = $ plugin - > clone_image ( $ scfg , $ storeid , $ volname , $ vmid , $ snap ) ;
2013-01-30 12:55:37 +01:00
return "$storeid:$volname" ;
} ) ;
}
sub vdisk_create_base {
my ( $ cfg , $ volid ) = @ _ ;
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2013-08-12 11:56:41 +02:00
2013-01-30 12:55:37 +01:00
activate_storage ( $ cfg , $ storeid ) ;
# lock shared storage
return $ plugin - > cluster_lock_storage ( $ storeid , $ scfg - > { shared } , undef , sub {
my $ volname = $ plugin - > create_base ( $ storeid , $ scfg , $ volname ) ;
return "$storeid:$volname" ;
} ) ;
}
2012-05-16 10:56:29 +02:00
sub vdisk_alloc {
my ( $ cfg , $ storeid , $ vmid , $ fmt , $ name , $ size ) = @ _ ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
die "no storage id specified\n" if ! $ storeid ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
PVE::JSONSchema:: parse_storage_id ( $ storeid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
die "no VMID specified\n" if ! $ vmid ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
$ vmid = parse_vmid ( $ vmid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ defformat = PVE::Storage::Plugin:: default_format ( $ scfg ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
$ fmt = $ defformat if ! $ fmt ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
activate_storage ( $ cfg , $ storeid ) ;
2011-11-25 09:43:29 +01:00
2012-05-16 10:56:29 +02:00
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
# lock shared storage
return $ plugin - > cluster_lock_storage ( $ storeid , $ scfg - > { shared } , undef , sub {
2015-07-21 08:44:06 +02:00
my $ old_umask = umask ( umask | 0037 ) ;
my $ volname = eval { $ plugin - > alloc_image ( $ storeid , $ scfg , $ vmid , $ fmt , $ name , $ size ) } ;
my $ err = $@ ;
umask $ old_umask ;
die $ err if $ err ;
2012-05-16 10:56:29 +02:00
return "$storeid:$volname" ;
} ) ;
2011-08-23 07:43:03 +02:00
}
2012-05-16 10:56:29 +02:00
sub vdisk_free {
my ( $ cfg , $ volid ) = @ _ ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2013-08-12 11:56:41 +02:00
2012-05-16 10:56:29 +02:00
activate_storage ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ cleanup_worker ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
# lock shared storage
$ plugin - > cluster_lock_storage ( $ storeid , $ scfg - > { shared } , undef , sub {
2013-02-01 06:55:39 +01:00
2015-08-12 06:04:41 +02:00
my ( $ vtype , $ name , $ vmid , undef , undef , $ isBase , $ format ) =
2013-02-01 06:55:39 +01:00
$ plugin - > parse_volname ( $ volname ) ;
if ( $ isBase ) {
my $ vollist = $ plugin - > list_images ( $ storeid , $ scfg ) ;
foreach my $ info ( @$ vollist ) {
my ( undef , $ tmpvolname ) = parse_volume_id ( $ info - > { volid } ) ;
2013-02-05 12:55:28 +01:00
my $ basename = undef ;
my $ basevmid = undef ;
2013-02-01 06:55:39 +01:00
2013-02-05 12:55:28 +01:00
eval {
2013-08-12 11:56:41 +02:00
( undef , undef , undef , $ basename , $ basevmid ) =
2013-02-05 12:55:28 +01:00
$ plugin - > parse_volname ( $ tmpvolname ) ;
} ;
2013-02-01 06:55:39 +01:00
2013-02-14 08:41:15 +01:00
if ( $ basename && defined ( $ basevmid ) && $ basevmid == $ vmid && $ basename eq $ name ) {
2013-02-01 06:55:39 +01:00
die "base volume '$volname' is still in use " .
"(use by '$tmpvolname')\n" ;
}
}
}
2015-08-12 06:04:41 +02:00
$ cleanup_worker = $ plugin - > free_image ( $ storeid , $ scfg , $ volname , $ isBase , $ format ) ;
2012-05-16 10:56:29 +02:00
} ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
return if ! $ cleanup_worker ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ rpcenv = PVE::RPCEnvironment:: get ( ) ;
my $ authuser = $ rpcenv - > get_user ( ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
$ rpcenv - > fork_worker ( 'imgdel' , undef , $ authuser , $ cleanup_worker ) ;
2011-08-23 07:43:03 +02:00
}
#list iso or openvz template ($tt = <iso|vztmpl|backup>)
sub template_list {
my ( $ cfg , $ storeid , $ tt ) = @ _ ;
2013-08-12 11:56:41 +02:00
die "unknown template type '$tt'\n"
if ! ( $ tt eq 'iso' || $ tt eq 'vztmpl' || $ tt eq 'backup' ) ;
2011-08-23 07:43:03 +02:00
my $ ids = $ cfg - > { ids } ;
storage_check_enabled ( $ cfg , $ storeid ) if ( $ storeid ) ;
my $ res = { } ;
# query the storage
foreach my $ sid ( keys %$ ids ) {
next if $ storeid && $ storeid ne $ sid ;
my $ scfg = $ ids - > { $ sid } ;
my $ type = $ scfg - > { type } ;
next if ! storage_check_enabled ( $ cfg , $ sid , undef , 1 ) ;
next if $ tt eq 'iso' && ! $ scfg - > { content } - > { iso } ;
next if $ tt eq 'vztmpl' && ! $ scfg - > { content } - > { vztmpl } ;
next if $ tt eq 'backup' && ! $ scfg - > { content } - > { backup } ;
2012-05-16 10:56:29 +02:00
activate_storage ( $ cfg , $ sid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
if ( $ scfg - > { path } ) {
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ path = $ plugin - > get_subdir ( $ scfg , $ tt ) ;
2011-08-23 07:43:03 +02:00
foreach my $ fn ( <$path/*> ) {
my $ info ;
if ( $ tt eq 'iso' ) {
next if $ fn !~ m !/([^/]+\.[Ii][Ss][Oo])$! ;
$ info = { volid = > "$sid:iso/$1" , format = > 'iso' } ;
} elsif ( $ tt eq 'vztmpl' ) {
2015-07-01 10:06:24 +02:00
next if $ fn !~ m !/([^/]+\.tar\.([gx]z))$! ;
2011-08-23 07:43:03 +02:00
2015-07-01 10:06:24 +02:00
$ info = { volid = > "$sid:vztmpl/$1" , format = > "t$2" } ;
2011-08-23 07:43:03 +02:00
} elsif ( $ tt eq 'backup' ) {
2012-12-13 12:51:52 +01:00
next if $ fn !~ m !/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz|vma|vma\.gz|vma\.lzo))$! ;
2013-08-12 11:56:41 +02:00
2011-08-23 07:43:03 +02:00
$ info = { volid = > "$sid:backup/$1" , format = > $ 2 } ;
}
$ info - > { size } = - s $ fn ;
push @ { $ res - > { $ sid } } , $ info ;
}
}
@ { $ res - > { $ sid } } = sort { lc ( $ a - > { volid } ) cmp lc ( $ b - > { volid } ) } @ { $ res - > { $ sid } } if $ res - > { $ sid } ;
}
return $ res ;
}
2012-07-27 08:00:12 +02:00
2011-08-23 07:43:03 +02:00
sub vdisk_list {
my ( $ cfg , $ storeid , $ vmid , $ vollist ) = @ _ ;
my $ ids = $ cfg - > { ids } ;
storage_check_enabled ( $ cfg , $ storeid ) if ( $ storeid ) ;
my $ res = { } ;
# prepare/activate/refresh all storages
my $ storage_list = [] ;
if ( $ vollist ) {
foreach my $ volid ( @$ vollist ) {
2012-05-16 10:56:29 +02:00
my ( $ sid , undef ) = parse_volume_id ( $ volid ) ;
next if ! defined ( $ ids - > { $ sid } ) ;
2011-08-23 07:43:03 +02:00
next if ! storage_check_enabled ( $ cfg , $ sid , undef , 1 ) ;
push @$ storage_list , $ sid ;
}
} else {
foreach my $ sid ( keys %$ ids ) {
next if $ storeid && $ storeid ne $ sid ;
next if ! storage_check_enabled ( $ cfg , $ sid , undef , 1 ) ;
push @$ storage_list , $ sid ;
}
}
2012-05-16 10:56:29 +02:00
my $ cache = { } ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
activate_storage_list ( $ cfg , $ storage_list , $ cache ) ;
2011-08-23 07:43:03 +02:00
foreach my $ sid ( keys %$ ids ) {
2012-05-16 10:56:29 +02:00
next if $ storeid && $ storeid ne $ sid ;
next if ! storage_check_enabled ( $ cfg , $ sid , undef , 1 ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ scfg = $ ids - > { $ sid } ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
$ res - > { $ sid } = $ plugin - > list_images ( $ sid , $ scfg , $ vmid , $ vollist , $ cache ) ;
2011-08-23 07:43:03 +02:00
@ { $ res - > { $ sid } } = sort { lc ( $ a - > { volid } ) cmp lc ( $ b - > { volid } ) } @ { $ res - > { $ sid } } if $ res - > { $ sid } ;
}
return $ res ;
}
2015-10-01 06:50:19 +02:00
sub volume_list {
my ( $ cfg , $ storeid , $ vmid , $ content ) = @ _ ;
my @ ctypes = qw( images vztmpl iso backup ) ;
my $ cts = $ content ? [ $ content ] : [ @ ctypes ] ;
my $ scfg = PVE::Storage:: storage_config ( $ cfg , $ storeid ) ;
my $ res = [] ;
foreach my $ ct ( @$ cts ) {
my $ data ;
if ( $ ct eq 'images' ) {
$ data = vdisk_list ( $ cfg , $ storeid , $ vmid ) ;
} elsif ( $ ct eq 'iso' && ! defined ( $ vmid ) ) {
$ data = template_list ( $ cfg , $ storeid , 'iso' ) ;
} elsif ( $ ct eq 'vztmpl' && ! defined ( $ vmid ) ) {
$ data = template_list ( $ cfg , $ storeid , 'vztmpl' ) ;
} elsif ( $ ct eq 'backup' ) {
$ data = template_list ( $ cfg , $ storeid , 'backup' ) ;
foreach my $ item ( @ { $ data - > { $ storeid } } ) {
if ( defined ( $ vmid ) ) {
@ { $ data - > { $ storeid } } = grep { $ _ - > { volid } =~ m/\S+-$vmid-\S+/ } @ { $ data - > { $ storeid } } ;
}
}
}
next if ! $ data || ! $ data - > { $ storeid } ;
foreach my $ item ( @ { $ data - > { $ storeid } } ) {
$ item - > { content } = $ ct ;
push @$ res , $ item ;
}
}
return $ res ;
}
2011-08-23 07:43:03 +02:00
sub uevent_seqnum {
my $ filename = "/sys/kernel/uevent_seqnum" ;
my $ seqnum = 0 ;
2012-05-16 10:56:29 +02:00
if ( my $ fh = IO::File - > new ( $ filename , "r" ) ) {
2011-08-23 07:43:03 +02:00
my $ line = <$fh> ;
if ( $ line =~ m/^(\d+)$/ ) {
2012-05-16 10:56:29 +02:00
$ seqnum = int ( $ 1 ) ;
2011-08-23 07:43:03 +02:00
}
close ( $ fh ) ;
}
return $ seqnum ;
}
2012-08-01 07:16:33 +02:00
sub activate_storage {
2012-05-16 10:56:29 +02:00
my ( $ cfg , $ storeid , $ cache ) = @ _ ;
2011-08-23 07:43:03 +02:00
2012-08-01 07:16:33 +02:00
$ cache = { } if ! $ cache ;
2011-08-23 07:43:03 +02:00
my $ scfg = storage_check_enabled ( $ cfg , $ storeid ) ;
2012-05-16 10:56:29 +02:00
return if $ cache - > { activated } - > { $ storeid } ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
$ cache - > { uevent_seqnum } = uevent_seqnum ( ) if ! $ cache - > { uevent_seqnum } ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
if ( $ scfg - > { base } ) {
my ( $ baseid , undef ) = parse_volume_id ( $ scfg - > { base } ) ;
2012-08-01 07:16:33 +02:00
activate_storage ( $ cfg , $ baseid , $ cache ) ;
}
if ( ! $ plugin - > check_connection ( $ storeid , $ scfg ) ) {
die "storage '$storeid' is not online\n" ;
2011-08-23 07:43:03 +02:00
}
2012-05-16 10:56:29 +02:00
$ plugin - > activate_storage ( $ storeid , $ scfg , $ cache ) ;
2011-08-23 07:43:03 +02:00
my $ newseq = uevent_seqnum ( ) ;
# only call udevsettle if there are events
2012-05-16 10:56:29 +02:00
if ( $ newseq > $ cache - > { uevent_seqnum } ) {
2011-08-23 07:43:03 +02:00
my $ timeout = 30 ;
system ( "$UDEVADM settle --timeout=$timeout" ) ; # ignore errors
2012-05-16 10:56:29 +02:00
$ cache - > { uevent_seqnum } = $ newseq ;
2011-08-23 07:43:03 +02:00
}
2012-05-16 10:56:29 +02:00
$ cache - > { activated } - > { $ storeid } = 1 ;
2011-08-23 07:43:03 +02:00
}
sub activate_storage_list {
2012-05-16 10:56:29 +02:00
my ( $ cfg , $ storeid_list , $ cache ) = @ _ ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
$ cache = { } if ! $ cache ;
2011-08-23 07:43:03 +02:00
foreach my $ storeid ( @$ storeid_list ) {
2012-08-01 07:16:33 +02:00
activate_storage ( $ cfg , $ storeid , $ cache ) ;
2011-08-23 07:43:03 +02:00
}
}
2012-05-16 10:56:29 +02:00
sub deactivate_storage {
my ( $ cfg , $ storeid ) = @ _ ;
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ cache = { } ;
$ plugin - > deactivate_storage ( $ storeid , $ scfg , $ cache ) ;
2011-08-23 07:43:03 +02:00
}
sub activate_volumes {
2015-09-18 11:57:07 +02:00
my ( $ cfg , $ vollist , $ snapname ) = @ _ ;
2011-11-25 07:27:25 +01:00
return if ! ( $ vollist && scalar ( @$ vollist ) ) ;
2011-08-23 07:43:03 +02:00
my $ storagehash = { } ;
foreach my $ volid ( @$ vollist ) {
2012-05-16 10:56:29 +02:00
my ( $ storeid , undef ) = parse_volume_id ( $ volid ) ;
2011-08-23 07:43:03 +02:00
$ storagehash - > { $ storeid } = 1 ;
}
2012-05-16 10:56:29 +02:00
my $ cache = { } ;
activate_storage_list ( $ cfg , [ keys %$ storagehash ] , $ cache ) ;
2011-08-23 07:43:03 +02:00
foreach my $ volid ( @$ vollist ) {
2012-05-23 13:11:20 +02:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2015-09-18 11:57:07 +02:00
$ plugin - > activate_volume ( $ storeid , $ scfg , $ volname , $ snapname , $ cache ) ;
2011-08-23 07:43:03 +02:00
}
}
sub deactivate_volumes {
2015-09-18 11:57:07 +02:00
my ( $ cfg , $ vollist , $ snapname ) = @ _ ;
2011-08-23 07:43:03 +02:00
2011-11-25 07:27:25 +01:00
return if ! ( $ vollist && scalar ( @$ vollist ) ) ;
2012-05-16 10:56:29 +02:00
my $ cache = { } ;
2011-11-25 07:27:25 +01:00
my @ errlist = ( ) ;
2011-08-23 07:43:03 +02:00
foreach my $ volid ( @$ vollist ) {
2012-05-16 10:56:29 +02:00
my ( $ storeid , $ volname ) = parse_volume_id ( $ volid ) ;
2011-08-23 07:43:03 +02:00
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2013-08-12 11:56:41 +02:00
2012-05-16 10:56:29 +02:00
eval {
2015-09-18 11:57:07 +02:00
$ plugin - > deactivate_volume ( $ storeid , $ scfg , $ volname , $ snapname , $ cache ) ;
2012-05-16 10:56:29 +02:00
} ;
if ( my $ err = $@ ) {
warn $ err ;
push @ errlist , $ volid ;
2011-08-23 07:43:03 +02:00
}
}
2011-11-25 07:27:25 +01:00
die "volume deativation failed: " . join ( ' ' , @ errlist )
if scalar ( @ errlist ) ;
2011-08-23 07:43:03 +02:00
}
2013-08-12 11:56:41 +02:00
sub storage_info {
2011-08-23 07:43:03 +02:00
my ( $ cfg , $ content ) = @ _ ;
my $ ids = $ cfg - > { ids } ;
my $ info = { } ;
2015-08-07 09:44:22 +02:00
my @ ctypes = PVE::Tools:: split_list ( $ content ) ;
2011-08-23 07:43:03 +02:00
my $ slist = [] ;
foreach my $ storeid ( keys %$ ids ) {
next if ! storage_check_enabled ( $ cfg , $ storeid , undef , 1 ) ;
2015-08-10 10:05:01 +02:00
if ( defined ( $ content ) ) {
my $ want_ctype = 0 ;
foreach my $ ctype ( @ ctypes ) {
if ( $ ids - > { $ storeid } - > { content } - > { $ ctype } ) {
$ want_ctype = 1 ;
last ;
}
2015-08-07 09:44:22 +02:00
}
2015-08-10 10:05:01 +02:00
next if ! $ want_ctype ;
2015-08-07 09:44:22 +02:00
}
2015-08-10 10:05:01 +02:00
2011-08-23 07:43:03 +02:00
my $ type = $ ids - > { $ storeid } - > { type } ;
2013-08-12 11:56:41 +02:00
$ info - > { $ storeid } = {
2011-08-23 07:43:03 +02:00
type = > $ type ,
2013-08-12 11:56:41 +02:00
total = > 0 ,
avail = > 0 ,
used = > 0 ,
2011-08-26 06:23:24 +02:00
shared = > $ ids - > { $ storeid } - > { shared } ? 1 : 0 ,
2012-05-16 10:56:29 +02:00
content = > PVE::Storage::Plugin:: content_hash_to_string ( $ ids - > { $ storeid } - > { content } ) ,
2011-08-23 07:43:03 +02:00
active = > 0 ,
} ;
push @$ slist , $ storeid ;
}
2012-05-16 10:56:29 +02:00
my $ cache = { } ;
2011-08-23 07:43:03 +02:00
foreach my $ storeid ( keys %$ ids ) {
my $ scfg = $ ids - > { $ storeid } ;
next if ! $ info - > { $ storeid } ;
2012-08-01 07:16:33 +02:00
eval { activate_storage ( $ cfg , $ storeid , $ cache ) ; } ;
if ( my $ err = $@ ) {
warn $ err ;
next ;
}
2012-05-16 10:56:29 +02:00
my $ plugin = PVE::Storage::Plugin - > lookup ( $ scfg - > { type } ) ;
2012-07-19 11:22:35 +02:00
my ( $ total , $ avail , $ used , $ active ) ;
eval { ( $ total , $ avail , $ used , $ active ) = $ plugin - > status ( $ storeid , $ scfg , $ cache ) ; } ;
warn $@ if $@ ;
2012-05-16 10:56:29 +02:00
next if ! $ active ;
2013-08-12 11:56:41 +02:00
$ info - > { $ storeid } - > { total } = $ total ;
$ info - > { $ storeid } - > { avail } = $ avail ;
$ info - > { $ storeid } - > { used } = $ used ;
2012-05-16 10:56:29 +02:00
$ info - > { $ storeid } - > { active } = $ active ;
2011-08-23 07:43:03 +02:00
}
return $ info ;
}
sub resolv_server {
my ( $ server ) = @ _ ;
2013-08-12 11:56:41 +02:00
2015-05-12 09:42:37 +02:00
my ( $ packed_ip , $ family ) ;
eval {
my @ res = PVE::Tools:: getaddrinfo_all ( $ server ) ;
$ family = $ res [ 0 ] - > { family } ;
$ packed_ip = ( PVE::Tools:: unpack_sockaddr_in46 ( $ res [ 0 ] - > { addr } ) ) [ 2 ] ;
} ;
2011-08-23 07:43:03 +02:00
if ( defined $ packed_ip ) {
2015-06-09 10:01:48 +02:00
return Socket:: inet_ntop ( $ family , $ packed_ip ) ;
2011-08-23 07:43:03 +02:00
}
return undef ;
}
sub scan_nfs {
my ( $ server_in ) = @ _ ;
my $ server ;
if ( ! ( $ server = resolv_server ( $ server_in ) ) ) {
die "unable to resolve address for server '${server_in}'\n" ;
}
my $ cmd = [ '/sbin/showmount' , '--no-headers' , '--exports' , $ server ] ;
my $ res = { } ;
2011-11-25 07:42:26 +01:00
run_command ( $ cmd , outfunc = > sub {
2011-08-23 07:43:03 +02:00
my $ line = shift ;
# note: howto handle white spaces in export path??
if ( $ line =~ m !^(/\S+)\s+(.+)$! ) {
$ res - > { $ 1 } = $ 2 ;
}
} ) ;
return $ res ;
}
2015-01-26 09:05:09 +01:00
sub scan_zfs {
2015-08-27 14:53:12 +02:00
my $ cmd = [ 'zfs' , 'list' , '-t' , 'filesystem' , '-H' , '-o' , 'name,avail,used' ] ;
2015-01-26 09:05:09 +01:00
my $ res = [] ;
run_command ( $ cmd , outfunc = > sub {
my $ line = shift ;
if ( $ line =~ m/^(\S+)\s+(\S+)\s+(\S+)$/ ) {
2015-08-27 14:53:12 +02:00
my ( $ pool , $ size_str , $ used_str ) = ( $ 1 , $ 2 , $ 3 ) ;
2015-01-26 09:05:09 +01:00
my $ size = PVE::Storage::ZFSPoolPlugin:: zfs_parse_size ( $ size_str ) ;
2015-08-27 14:53:12 +02:00
my $ used = PVE::Storage::ZFSPoolPlugin:: zfs_parse_size ( $ used_str ) ;
2015-08-28 11:01:41 +02:00
# ignore subvolumes generated by our ZFSPoolPlugin
return if $ pool =~ m !/subvol-\d+-[^/]+$! ;
2015-08-27 14:53:12 +02:00
push @$ res , { pool = > $ pool , size = > $ size , free = > $ size - $ used } ;
2015-01-26 09:05:09 +01:00
}
} ) ;
return $ res ;
}
2011-08-23 07:43:03 +02:00
sub resolv_portal {
my ( $ portal , $ noerr ) = @ _ ;
2015-08-31 11:02:25 +02:00
my ( $ server , $ port ) = PVE::Tools:: parse_host_and_port ( $ portal ) ;
if ( $ server ) {
2011-08-23 07:43:03 +02:00
if ( my $ ip = resolv_server ( $ server ) ) {
$ server = $ ip ;
2015-08-31 11:02:25 +02:00
$ server = "[$server]" if $ server =~ /^$IPV6RE$/ ;
2011-08-23 07:43:03 +02:00
return $ port ? "$server:$port" : $ server ;
}
}
return undef if $ noerr ;
raise_param_exc ( { portal = > "unable to resolve portal address '$portal'" } ) ;
}
# idea is from usbutils package (/usr/bin/usb-devices) script
sub __scan_usb_device {
my ( $ res , $ devpath , $ parent , $ level ) = @ _ ;
return if ! - d $ devpath ;
return if $ level && $ devpath !~ m/^.*[-.](\d+)$/ ;
my $ port = $ level ? int ( $ 1 - 1 ) : 0 ;
my $ busnum = int ( file_read_firstline ( "$devpath/busnum" ) ) ;
my $ devnum = int ( file_read_firstline ( "$devpath/devnum" ) ) ;
my $ d = {
port = > $ port ,
level = > $ level ,
busnum = > $ busnum ,
devnum = > $ devnum ,
speed = > file_read_firstline ( "$devpath/speed" ) ,
class = > hex ( file_read_firstline ( "$devpath/bDeviceClass" ) ) ,
vendid = > file_read_firstline ( "$devpath/idVendor" ) ,
prodid = > file_read_firstline ( "$devpath/idProduct" ) ,
} ;
if ( $ level ) {
my $ usbpath = $ devpath ;
$ usbpath =~ s | ^ . * / \ d + \ - || ;
$ d - > { usbpath } = $ usbpath ;
}
my $ product = file_read_firstline ( "$devpath/product" ) ;
$ d - > { product } = $ product if $ product ;
2013-08-12 11:56:41 +02:00
2011-08-23 07:43:03 +02:00
my $ manu = file_read_firstline ( "$devpath/manufacturer" ) ;
$ d - > { manufacturer } = $ manu if $ manu ;
my $ serial = > file_read_firstline ( "$devpath/serial" ) ;
$ d - > { serial } = $ serial if $ serial ;
push @$ res , $ d ;
foreach my $ subdev ( <$devpath/$busnum-*> ) {
next if $ subdev !~ m | / $ busnum - [ 0 - 9 ] + ( \ . [ 0 - 9 ] + ) * $| ;
__scan_usb_device ( $ res , $ subdev , $ devnum , $ level + 1 ) ;
}
} ;
sub scan_usb {
my $ devlist = [] ;
foreach my $ device ( </sys/bus/usb/devices/usb*> ) {
__scan_usb_device ( $ devlist , $ device , 0 , 0 ) ;
}
return $ devlist ;
}
sub scan_iscsi {
my ( $ portal_in ) = @ _ ;
my $ portal ;
2012-05-16 10:56:29 +02:00
if ( ! ( $ portal = resolv_portal ( $ portal_in ) ) ) {
2011-08-23 07:43:03 +02:00
die "unable to parse/resolve portal address '${portal_in}'\n" ;
}
2012-05-16 10:56:29 +02:00
return PVE::Storage::ISCSIPlugin:: iscsi_discovery ( $ portal ) ;
2011-08-23 07:43:03 +02:00
}
sub storage_default_format {
my ( $ cfg , $ storeid ) = @ _ ;
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2012-05-16 10:56:29 +02:00
return PVE::Storage::Plugin:: default_format ( $ scfg ) ;
2011-08-23 07:43:03 +02:00
}
sub vgroup_is_used {
my ( $ cfg , $ vgname ) = @ _ ;
foreach my $ storeid ( keys % { $ cfg - > { ids } } ) {
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
if ( $ scfg - > { type } eq 'lvm' && $ scfg - > { vgname } eq $ vgname ) {
return 1 ;
}
}
return undef ;
}
sub target_is_used {
my ( $ cfg , $ target ) = @ _ ;
foreach my $ storeid ( keys % { $ cfg - > { ids } } ) {
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
if ( $ scfg - > { type } eq 'iscsi' && $ scfg - > { target } eq $ target ) {
return 1 ;
}
}
return undef ;
}
sub volume_is_used {
my ( $ cfg , $ volid ) = @ _ ;
foreach my $ storeid ( keys % { $ cfg - > { ids } } ) {
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ storeid ) ;
2011-08-23 07:43:03 +02:00
if ( $ scfg - > { base } && $ scfg - > { base } eq $ volid ) {
return 1 ;
}
}
return undef ;
}
sub storage_is_used {
my ( $ cfg , $ storeid ) = @ _ ;
foreach my $ sid ( keys % { $ cfg - > { ids } } ) {
2012-05-16 10:56:29 +02:00
my $ scfg = storage_config ( $ cfg , $ sid ) ;
2011-08-23 07:43:03 +02:00
next if ! $ scfg - > { base } ;
2012-05-16 10:56:29 +02:00
my ( $ st ) = parse_volume_id ( $ scfg - > { base } ) ;
2011-08-23 07:43:03 +02:00
return 1 if $ st && $ st eq $ storeid ;
}
return undef ;
}
sub foreach_volid {
my ( $ list , $ func ) = @ _ ;
return if ! $ list ;
foreach my $ sid ( keys %$ list ) {
foreach my $ info ( @ { $ list - > { $ sid } } ) {
my $ volid = $ info - > { volid } ;
2012-05-16 10:56:29 +02:00
my ( $ sid1 , $ volname ) = parse_volume_id ( $ volid , 1 ) ;
2011-08-23 07:43:03 +02:00
if ( $ sid1 && $ sid1 eq $ sid ) {
& $ func ( $ volid , $ sid , $ info ) ;
} else {
warn "detected strange volid '$volid' in volume list for '$sid'\n" ;
}
}
}
}
2015-09-18 09:33:09 +02:00
# bash completion helper
sub complete_storage {
2015-10-01 06:28:22 +02:00
my ( $ cmdname , $ pname , $ cvalue ) = @ _ ;
2015-09-18 09:33:09 +02:00
2015-10-01 06:28:22 +02:00
my $ cfg = PVE::Storage:: config ( ) ;
2015-10-01 06:26:35 +02:00
2015-10-01 06:28:22 +02:00
return $ cmdname eq 'add' ? [] : [ PVE::Storage:: storage_ids ( $ cfg ) ] ;
2015-09-18 09:33:09 +02:00
}
sub complete_storage_enabled {
2015-10-01 06:28:22 +02:00
my ( $ cmdname , $ pname , $ cvalue ) = @ _ ;
2015-09-18 09:33:09 +02:00
2015-10-01 06:28:22 +02:00
my $ res = [] ;
2015-09-18 09:33:09 +02:00
2015-10-01 06:28:22 +02:00
my $ cfg = PVE::Storage:: config ( ) ;
foreach my $ sid ( keys % { $ cfg - > { ids } } ) {
next if ! storage_check_enabled ( $ cfg , $ sid , undef , 1 ) ;
push @$ res , $ sid ;
}
return $ res ;
2015-09-18 09:33:09 +02:00
}
2015-10-01 07:12:07 +02:00
sub complete_content_type {
my ( $ cmdname , $ pname , $ cvalue ) = @ _ ;
return [ qw( rootdir images vztmpl iso backup ) ] ;
}
2015-10-01 07:59:05 +02:00
sub complete_volume {
my ( $ cmdname , $ pname , $ cvalue ) = @ _ ;
my $ cfg = config ( ) ;
my $ storage_list = complete_storage_enabled ( ) ;
2015-10-01 10:56:19 +02:00
if ( $ cvalue =~ m/^([^:]+):/ ) {
$ storage_list = [ $ 1 ] ;
} else {
if ( scalar ( @$ storage_list ) > 1 ) {
# only list storage IDs to avoid large listings
my $ res = [] ;
foreach my $ storeid ( @$ storage_list ) {
# Hack: simply return 2 artificial values, so that
# completions does not finish
push @$ res , "$storeid:volname" , "$storeid:..." ;
}
return $ res ;
}
}
2015-10-01 07:59:05 +02:00
my $ res = [] ;
foreach my $ storeid ( @$ storage_list ) {
my $ vollist = PVE::Storage:: volume_list ( $ cfg , $ storeid ) ;
foreach my $ item ( @$ vollist ) {
push @$ res , $ item - > { volid } ;
}
}
return $ res ;
}
2011-08-23 07:43:03 +02:00
1 ;