]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage.pm
ZFS: use -p flag and remove zfs_parse_size
[pve-storage.git] / PVE / Storage.pm
index 62d72deaa2ab398f2f1e9d1575a7b75ab47cdf21..c3813944feaefcb86f37526257c0417b16d27501 100755 (executable)
@@ -36,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 => 5;
 # 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 => 4;
 
 # load standard plugins
 PVE::Storage::DirPlugin->register();
@@ -58,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' ) {
@@ -207,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);
@@ -281,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 {
@@ -557,19 +562,58 @@ sub abs_filesystem_path {
     return $path;
 }
 
+my $volname_for_storage = sub {
+    my ($cfg, $volid, $target_storeid) = @_;
+
+    my (undef, $name, $vmid, undef, undef, undef, $format) = parse_volname($cfg, $volid);
+    my $target_scfg = storage_config($cfg, $target_storeid);
+
+    my (undef, $valid_formats) = PVE::Storage::Plugin::default_format($target_scfg);
+    my $format_is_valid = grep { $_ eq $format } @$valid_formats;
+    die "unsupported format '$format' for storage type $target_scfg->{type}\n" if !$format_is_valid;
+
+    (my $name_without_extension = $name) =~ s/\.$format$//;
+
+    if ($target_scfg->{path}) {
+       return "$vmid/$name_without_extension.$format";
+    } else {
+       return "$name_without_extension";
+    }
+};
+
 sub storage_migrate {
-    my ($cfg, $volid, $target_sshinfo, $target_storeid, $target_volname, $base_snapshot, $snapshot, $ratelimit_bps, $insecure, $with_snapshots, $logfunc) = @_;
+    my ($cfg, $volid, $target_sshinfo, $target_storeid, $opts, $logfunc) = @_;
+
+    my $base_snapshot = $opts->{base_snapshot};
+    my $snapshot = $opts->{snapshot};
+    my $ratelimit_bps = $opts->{ratelimit_bps};
+    my $insecure = $opts->{insecure};
+    my $with_snapshots = $opts->{with_snapshots} ? 1 : 0;
+    my $allow_rename = $opts->{allow_rename} ? 1 : 0;
 
     my ($storeid, $volname) = parse_volume_id($volid);
-    $target_volname = $volname if !$target_volname;
 
     my $scfg = storage_config($cfg, $storeid);
 
     # no need to migrate shared content
-    return if $storeid eq $target_storeid && $scfg->{shared};
+    return $volid if $storeid eq $target_storeid && $scfg->{shared};
 
     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_volname;
+    if ($opts->{target_volname}) {
+       $target_volname = $opts->{target_volname};
+    } elsif ($scfg->{type} eq $tcfg->{type}) {
+       $target_volname = $volname;
+    } else {
+       $target_volname = $volname_for_storage->($cfg, $volid, $target_storeid);
+    }
+
     my $target_volid = "${target_storeid}:${target_volname}";
 
     my $target_ip = $target_sshinfo->{ip};
@@ -599,7 +643,11 @@ sub storage_migrate {
        $import_fn = "tcp://$net";
     }
 
-    $with_snapshots = $with_snapshots ? 1 : 0; # sanitize for passing as cli parameter
+    my $target_apiver = 1; # if there is no apiinfo call, assume 1
+    my $get_api_version = [@$ssh, 'pvesm', 'apiinfo'];
+    my $match_api_version = sub { $target_apiver = $1 if $_[0] =~ m!^APIVER (\d+)$!; };
+    eval { run_command($get_api_version, logfunc => $match_api_version); };
+
     my $send = ['pvesm', 'export', $volid, $format, '-', '-with-snapshots', $with_snapshots];
     my $recv = [@$ssh, '--', 'pvesm', 'import', $target_volid, $format, $import_fn, '-with-snapshots', $with_snapshots];
     if (defined($snapshot)) {
@@ -608,6 +656,7 @@ sub storage_migrate {
     if ($migration_snapshot) {
        push @$recv, '-delete-snapshot', $snapshot;
     }
+    push @$recv, '-allow-rename', $allow_rename if $target_apiver >= 5;
 
     if (defined($base_snapshot)) {
        # Check if the snapshot exists on the remote side:
@@ -615,6 +664,19 @@ sub storage_migrate {
        push @$recv, '-base', $base_snapshot;
     }
 
+    my $new_volid;
+    my $pattern = volume_imported_message(undef, 1);
+    my $match_volid_and_log = sub {
+       my $line = shift;
+
+       $new_volid = $1 if ($line =~ $pattern);
+
+       if ($logfunc) {
+           chomp($line);
+           $logfunc->($line);
+       }
+    };
+
     volume_snapshot($cfg, $volid, $snapshot) if $migration_snapshot;
     eval {
        if ($insecure) {
@@ -632,13 +694,8 @@ sub storage_migrate {
            shutdown($socket, 1);
 
            # wait for the remote process to finish
-           if ($logfunc) {
-               while (my $line = <$info>) {
-                   chomp($line);
-                   $logfunc->("[$target_sshinfo->{name}] $line");
-               }
-           } else {
-               1 while <$info>;
+           while (my $line = <$info>) {
+               $match_volid_and_log->("[$target_sshinfo->{name}] $line");
            }
 
            # now close the socket
@@ -648,8 +705,11 @@ sub storage_migrate {
                die "import failed: exit code ".($?>>8)."\n";
            }
        } else {
-           run_command([$send, @cstream, $recv], logfunc => $logfunc);
+           run_command([$send, @cstream, $recv], logfunc => $match_volid_and_log);
        }
+
+       die "unable to get ID of the migrated volume\n"
+           if !defined($new_volid) && $target_apiver >= 5;
     };
     my $err = $@;
     warn "send/receive failed, cleaning up snapshot(s)..\n" if $err;
@@ -658,6 +718,8 @@ sub storage_migrate {
        warn "could not remove source snapshot: $@\n" if $@;
     }
     die $err if $err;
+
+    return $new_volid // $target_volid;
 }
 
 sub vdisk_clone {
@@ -1148,7 +1210,7 @@ sub scan_cifs {
 
 sub scan_zfs {
 
-    my $cmd = ['zfs',  'list', '-t', 'filesystem', '-H', '-o', 'name,avail,used'];
+    my $cmd = ['zfs',  'list', '-t', 'filesystem', '-Hp', '-o', 'name,avail,used'];
 
     my $res = [];
     run_command($cmd, outfunc => sub {
@@ -1156,8 +1218,8 @@ sub scan_zfs {
 
        if ($line =~m/^(\S+)\s+(\S+)\s+(\S+)$/) {
            my ($pool, $size_str, $used_str) = ($1, $2, $3);
-           my $size = PVE::Storage::ZFSPoolPlugin::zfs_parse_size($size_str);
-           my $used = PVE::Storage::ZFSPoolPlugin::zfs_parse_size($used_str);
+           my $size = $size_str + 0;
+           my $used = $used_str + 0;
            # ignore subvolumes generated by our ZFSPoolPlugin
            return if $pool =~ m!/subvol-\d+-[^/]+$!;
            return if $pool =~ m!/basevol-\d+-[^/]+$!;
@@ -1368,6 +1430,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))?))$/) {
@@ -1405,14 +1476,14 @@ sub volume_export {
 }
 
 sub volume_import {
-    my ($cfg, $fh, $volid, $format, $base_snapshot, $with_snapshots) = @_;
+    my ($cfg, $fh, $volid, $format, $base_snapshot, $with_snapshots, $allow_rename) = @_;
 
     my ($storeid, $volname) = parse_volume_id($volid, 1);
     die "cannot import into volume '$volid'\n" if !$storeid;
     my $scfg = storage_config($cfg, $storeid);
     my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
     return $plugin->volume_import($scfg, $storeid, $fh, $volname, $format,
-                                  $base_snapshot, $with_snapshots);
+                                  $base_snapshot, $with_snapshots, $allow_rename) // $volid;
 }
 
 sub volume_export_formats {
@@ -1447,6 +1518,16 @@ sub volume_transfer_formats {
     return @common;
 }
 
+sub volume_imported_message {
+    my ($volid, $want_pattern) = @_;
+
+    if ($want_pattern) {
+       return qr/successfully imported '([^']*)'$/;
+    } else {
+       return "successfully imported '$volid'\n";
+    }
+}
+
 # bash completion helper
 
 sub complete_storage {