]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage.pm
storage_migrate: check if target storage supports content type
[pve-storage.git] / PVE / Storage.pm
index 755eca83d4e3a5b1979813c8e5fd960be27c20e4..60b831090130b81ea394b8656bfccc1b5ed3c554 100755 (executable)
@@ -15,10 +15,12 @@ use Socket;
 
 use PVE::Tools qw(run_command file_read_firstline dir_glob_foreach $IPV6RE);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::DataCenterConfig;
 use PVE::Exception qw(raise_param_exc);
 use PVE::JSONSchema;
 use PVE::INotify;
 use PVE::RPCEnvironment;
+use PVE::SSHInfo;
 
 use PVE::Storage::Plugin;
 use PVE::Storage::DirPlugin;
@@ -34,13 +36,14 @@ use PVE::Storage::GlusterfsPlugin;
 use PVE::Storage::ZFSPoolPlugin;
 use PVE::Storage::ZFSPlugin;
 use PVE::Storage::DRBDPlugin;
+use PVE::Storage::PBSPlugin;
 
 # Storage API version. Icrement it on changes in storage API interface.
-use constant APIVER => 3;
+use constant APIVER => 4;
 # Age is the number of versions we're backward compatible with.
 # This is like having 'current=APIVER' and age='APIAGE' in libtool,
 # see https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
-use constant APIAGE => 2;
+use constant APIAGE => 3;
 
 # load standard plugins
 PVE::Storage::DirPlugin->register();
@@ -56,6 +59,7 @@ PVE::Storage::GlusterfsPlugin->register();
 PVE::Storage::ZFSPoolPlugin->register();
 PVE::Storage::ZFSPlugin->register();
 PVE::Storage::DRBDPlugin->register();
+PVE::Storage::PBSPlugin->register();
 
 # load third-party plugins
 if ( -d '/usr/share/perl5/PVE/Storage/Custom' ) {
@@ -99,6 +103,8 @@ PVE::Storage::Plugin->init();
 
 my $UDEVADM = '/sbin/udevadm';
 
+our $iso_extension_re = qr/\.(?:iso|img)/i;
+
 #  PVE::Storage utility functions
 
 sub config {
@@ -128,7 +134,7 @@ sub storage_config {
 
     my $scfg = $cfg->{ids}->{$storeid};
 
-    die "storage '$storeid' does not exists\n" if (!$noerr && !$scfg);
+    die "storage '$storeid' does not exist\n" if (!$noerr && !$scfg);
 
     return $scfg;
 }
@@ -203,6 +209,9 @@ sub volume_size_info {
 sub volume_resize {
     my ($cfg, $volid, $size, $running) = @_;
 
+    my $padding = (1024 - $size % 1024) % 1024;
+    $size = $size + $padding;
+
     my ($storeid, $volname) = parse_volume_id($volid, 1);
     if ($storeid) {
         my $scfg = storage_config($cfg, $storeid);
@@ -277,13 +286,13 @@ sub volume_snapshot_delete {
 }
 
 sub volume_has_feature {
-    my ($cfg, $feature, $volid, $snap, $running) = @_;
+    my ($cfg, $feature, $volid, $snap, $running, $opts) = @_;
 
     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);
+        return $plugin->volume_has_feature($scfg, $feature, $storeid, $volname, $snap, $running, $opts);
     } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
        return undef;
     } else {
@@ -501,7 +510,7 @@ sub path_to_volume_id {
                    return ('images', $info->{volid});
                }
            }
-       } elsif ($path =~ m!^$isodir/([^/]+\.[Ii][Ss][Oo])$!) {
+       } elsif ($path =~ m!^$isodir/([^/]+$iso_extension_re)$!) {
            my $name = $1;
            return ('iso', "$sid:iso/$name");
        } elsif ($path =~ m!^$tmpldir/([^/]+\.tar\.gz)$!) {
@@ -536,8 +545,8 @@ sub abs_filesystem_path {
     my ($cfg, $volid) = @_;
 
     my $path;
-    if (PVE::Storage::parse_volume_id ($volid, 1)) {
-       PVE::Storage::activate_volumes($cfg, [ $volid ]);
+    if (parse_volume_id ($volid, 1)) {
+       activate_volumes($cfg, [ $volid ]);
        $path = PVE::Storage::path($cfg, $volid);
     } else {
        if (-f $volid) {
@@ -566,13 +575,17 @@ sub storage_migrate {
 
     my $tcfg = storage_config($cfg, $target_storeid);
 
+    my $vtype = (parse_volname($cfg, $volid))[0];
+
+    die "content type '$vtype' is not available on storage '$target_storeid'\n"
+       if !$tcfg->{content}->{$vtype};
+
     my $target_volid = "${target_storeid}:${target_volname}";
 
     my $target_ip = $target_sshinfo->{ip};
-    my $errstr = "unable to migrate '$volid' to '${target_volid}' on host '$target_sshinfo->{name}'";
 
-    my $ssh = PVE::Cluster::ssh_info_to_command($target_sshinfo);
-    my $ssh_base = PVE::Cluster::ssh_info_to_command_base($target_sshinfo);
+    my $ssh = PVE::SSHInfo::ssh_info_to_command($target_sshinfo);
+    my $ssh_base = PVE::SSHInfo::ssh_info_to_command_base($target_sshinfo);
     local $ENV{RSYNC_RSH} = PVE::Tools::cmd2string($ssh_base);
 
     my @cstream = ([ '/usr/bin/cstream', '-t', $ratelimit_bps ])
@@ -586,7 +599,7 @@ sub storage_migrate {
        }
     }
 
-    my @formats = volume_transfer_formats($cfg, $volid, $volid, $snapshot, $base_snapshot, $with_snapshots);
+    my @formats = volume_transfer_formats($cfg, $volid, $target_volid, $snapshot, $base_snapshot, $with_snapshots);
     die "cannot migrate from storage type '$scfg->{type}' to '$tcfg->{type}'\n" if !@formats;
     my $format = $formats[0];
 
@@ -598,7 +611,7 @@ sub storage_migrate {
 
     $with_snapshots = $with_snapshots ? 1 : 0; # sanitize for passing as cli parameter
     my $send = ['pvesm', 'export', $volid, $format, '-', '-with-snapshots', $with_snapshots];
-    my $recv = [@$ssh, '--', 'pvesm', 'import', $volid, $format, $import_fn, '-with-snapshots', $with_snapshots];
+    my $recv = [@$ssh, '--', 'pvesm', 'import', $target_volid, $format, $import_fn, '-with-snapshots', $with_snapshots];
     if (defined($snapshot)) {
        push @$send, '-snapshot', $snapshot
     }
@@ -623,11 +636,21 @@ sub storage_migrate {
                or die "failed to connect to tunnel at $ip:$port\n";
            # we won't be reading from the socket
            shutdown($socket, 0);
-           run_command([$send, @cstream], output => '>&'.fileno($socket));
+           run_command([$send, @cstream], output => '>&'.fileno($socket), errfunc => $logfunc);
            # don't close the connection entirely otherwise the receiving end
            # might not get all buffered data (and fails with 'connection reset by peer')
            shutdown($socket, 1);
-           1 while <$info>; # wait for the remote process to finish
+
+           # wait for the remote process to finish
+           if ($logfunc) {
+               while (my $line = <$info>) {
+                   chomp($line);
+                   $logfunc->("[$target_sshinfo->{name}] $line");
+               }
+           } else {
+               1 while <$info>;
+           }
+
            # now close the socket
            close($socket);
            if (!close($info)) { # does waitpid()
@@ -1355,6 +1378,15 @@ sub extract_vzdump_config_vma {
 sub extract_vzdump_config {
     my ($cfg, $volid) = @_;
 
+    my ($storeid, $volname) = parse_volume_id($volid);
+    if (defined($storeid)) {
+       my $scfg = storage_config($cfg, $storeid);
+       if ($scfg->{type} eq 'pbs') {
+           storage_check_enabled($cfg, $storeid);
+           return PVE::Storage::PBSPlugin->extract_vzdump_config($scfg, $volname, $storeid);
+       }
+    }
+
     my $archive = abs_filesystem_path($cfg, $volid);
 
     if ($volid =~ /vzdump-(lxc|openvz)-\d+-(\d{4})_(\d{2})_(\d{2})-(\d{2})_(\d{2})_(\d{2})\.(tgz|(tar(\.(gz|lzo))?))$/) {