]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage/ZFSPoolPlugin.pm
remove unused Data::Dumper usages
[pve-storage.git] / PVE / Storage / ZFSPoolPlugin.pm
index 265b557cf9ac603d5c1ca2f20409d1bcec1e8e04..32e53aa0650a98c98e05031f0878d70a98df646d 100644 (file)
@@ -43,6 +43,7 @@ sub options {
        nodes => { optional => 1 },
        disable => { optional => 1 },
        content => { optional => 1 },
+       bwlimit => { optional => 1 },
     };
 }
 
@@ -202,7 +203,7 @@ sub alloc_image {
     
     if ($fmt eq 'raw') {
 
-       die "illegal name '$volname' - sould be 'vm-$vmid-*'\n"
+       die "illegal name '$volname' - should be 'vm-$vmid-*'\n"
            if $volname && $volname !~ m/^vm-$vmid-/;
        $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid, $fmt) 
            if !$volname;
@@ -210,17 +211,22 @@ sub alloc_image {
        $class->zfs_create_zvol($scfg, $volname, $size);
        my $devname = "/dev/zvol/$scfg->{pool}/$volname";
 
-       run_command("udevadm trigger --subsystem-match block");
-       system('udevadm', 'settle', '--timeout', '10', "--exit-if-exists=${devname}");
+       my $timeout = PVE::RPCEnvironment->is_worker() ? 60*5 : 10;
+       for (my $i = 1; $i <= $timeout; $i++) {
+           last if -b $devname;
+           die "Timeout: no zvol after $timeout sec found.\n"
+               if $i == $timeout;
 
+           sleep(1);
+       }
     } elsif ( $fmt eq 'subvol') {
 
-       die "illegal name '$volname' - sould be 'subvol-$vmid-*'\n"
+       die "illegal name '$volname' - should be 'subvol-$vmid-*'\n"
            if $volname && $volname !~ m/^subvol-$vmid-/;
        $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid, $fmt) 
            if !$volname;
 
-       die "illegal name '$volname' - sould be 'subvol-$vmid-*'\n"
+       die "illegal name '$volname' - should be 'subvol-$vmid-*'\n"
            if $volname !~ m/^subvol-$vmid-/;
 
        $class->zfs_create_subvol($scfg, $volname, $size);      
@@ -495,27 +501,21 @@ sub volume_rollback_is_possible {
 }
 
 sub volume_snapshot_list {
-    my ($class, $scfg, $storeid, $volname, $prefix, $ip) = @_;
+    my ($class, $scfg, $storeid, $volname) = @_;
 
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
 
     my $zpath = "$scfg->{pool}/$name";
 
-    $prefix = '' if !defined($prefix);
     my $snaps = [];
 
     my $cmd = ['zfs', 'list', '-r', '-H', '-S', 'name', '-t', 'snap', '-o',
               'name', $zpath];
 
-    if ($ip) {
-       $ip = "[$ip]" if Net::IP::ip_is_ipv6($ip);
-       unshift @$cmd, 'ssh', '-o', ' BatchMode=yes', "root\@${ip}", '--';
-    }
-
     my $outfunc = sub {
        my $line = shift;
 
-       if ($line =~ m/^\Q$zpath\E@(\Q$prefix\E.*)$/) {
+       if ($line =~ m/^\Q$zpath\E@(.*)$/) {
            push @$snaps, $1;
        }
     };
@@ -629,6 +629,14 @@ sub volume_resize {
     return $new_size;
 }
 
+sub storage_can_replicate {
+    my ($class, $scfg, $storeid, $format) = @_;
+
+    return 1 if $format eq 'raw' || $format eq 'subvol';
+
+    return 0;
+}
+
 sub volume_has_feature {
     my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
 
@@ -657,4 +665,85 @@ sub volume_has_feature {
     return undef;
 }
 
+sub volume_export {
+    my ($class, $scfg, $storeid, $fh, $volname, $format, $snapshot, $base_snapshot, $with_snapshots) = @_;
+
+    die "unsupported export stream format for $class: $format\n"
+       if $format ne 'zfs';
+
+    die "$class storage can only export snapshots\n"
+       if !defined($snapshot);
+
+    my $dataset = ($class->parse_volname($volname))[1];
+
+    my $fd = fileno($fh);
+    die "internal error: invalid file handle for volume_export\n"
+       if !defined($fd);
+    $fd = ">&$fd";
+
+    # For zfs we always create a replication stream (-R) which means the remote
+    # side will always delete non-existing source snapshots. This should work
+    # for all our use cases.
+    my $cmd = ['zfs', 'send', '-Rpv'];
+    if (defined($base_snapshot)) {
+       my $arg = $with_snapshots ? '-I' : '-i';
+       push @$cmd, $arg, $base_snapshot;
+    }
+    push @$cmd, '--', "$scfg->{pool}/$dataset\@$snapshot";
+
+    run_command($cmd, output => $fd);
+
+    return;
+}
+
+sub volume_export_formats {
+    my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_;
+
+    my @formats = ('zfs');
+    # TODOs:
+    # push @formats, 'fies' if $volname !~ /^(?:basevol|subvol)-/;
+    # push @formats, 'raw' if !$base_snapshot && !$with_snapshots;
+    return @formats;
+}
+
+sub volume_import {
+    my ($class, $scfg, $storeid, $fh, $volname, $format, $base_snapshot, $with_snapshots) = @_;
+
+    die "unsupported import stream format for $class: $format\n"
+       if $format ne 'zfs';
+
+    my $fd = fileno($fh);
+    die "internal error: invalid file handle for volume_import\n"
+       if !defined($fd);
+
+    my $dataset = ($class->parse_volname($volname))[1];
+    my $zfspath = "$scfg->{pool}/$dataset";
+    my $suffix = defined($base_snapshot) ? "\@$base_snapshot" : '';
+    my $exists = 0 == run_command(['zfs', 'get', '-H', 'name', $zfspath.$suffix],
+                            noerr => 1, errfunc => sub {});
+    if (defined($base_snapshot)) {
+       die "base snapshot '$zfspath\@$base_snapshot' doesn't exist\n" if !$exists;
+    } else {
+       die "volume '$zfspath' already exists\n" if $exists;
+    }
+
+    eval { run_command(['zfs', 'recv', '-F', '--', $zfspath], input => "<&$fd") };
+    if (my $err = $@) {
+       if (defined($base_snapshot)) {
+           eval { run_command(['zfs', 'rollback', '-r', '--', "$zfspath\@$base_snapshot"]) };
+       } else {
+           eval { run_command(['zfs', 'destroy', '-r', '--', $zfspath]) };
+       }
+       die $err;
+    }
+
+    return;
+}
+
+sub volume_import_formats {
+    my ($class, $scfg, $storeid, $volname, $base_snapshot, $with_snapshots) = @_;
+
+    return $class->volume_export_formats($scfg, $storeid, $volname, undef, $base_snapshot, $with_snapshots);
+}
+
 1;