]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage/ZFSPoolPlugin.pm
remove running from Storage and check it in QemuServer
[pve-storage.git] / PVE / Storage / ZFSPoolPlugin.pm
index 7dc7d3ef6a8e01a7af354c81e829bd65d615b188..cae598dd1c49c0742b580424d1fae32b04284fd8 100644 (file)
@@ -16,7 +16,8 @@ sub type {
 
 sub plugindata {
     return {
-       content => [ {images => 1}, { images => 1 }],
+       content => [ {images => 1, rootdir => 1}, {images => 1 , rootdir => 1}],
+       format => [ { raw => 1, subvol => 1 } , 'raw' ],
     };
 }
 
@@ -92,23 +93,36 @@ sub zfs_parse_zvol_list {
 
     my @lines = split /\n/, $text;
     foreach my $line (@lines) {
-       if ($line =~ /^(.+)\s+([a-zA-Z0-9\.]+|\-)\s+(.+)$/) {
-           my $zvol = {};
-           my @parts = split /\//, $1;
-           my $name = pop @parts;
-           my $pool = join('/', @parts);
-
-           next unless $name =~ m!^(\w+)-(\d+)-(\w+)-(\d+)$!;
-           $name = $pool . '/' . $name;
-
-           $zvol->{pool} = $pool;
-           $zvol->{name} = $name;
-           $zvol->{size} = zfs_parse_size($2);
-           if ($3 !~ /^-$/) {
-               $zvol->{origin} = $3;
+       my ($dataset, $size, $origin, $type, $refquota) = split(/\s+/, $line);
+       next if !($type eq 'volume' || $type eq 'filesystem');
+
+       my $zvol = {};
+       my @parts = split /\//, $dataset;
+       my $name = pop @parts;
+       my $pool = join('/', @parts);
+
+       next unless $name =~ m!^(vm|base|subvol)-(\d+)-(\S+)$!;
+       $zvol->{owner} = $2;
+
+       $name = $pool . '/' . $name;
+
+       $zvol->{pool} = $pool;
+       $zvol->{name} = $name;
+       if ($type eq 'filesystem') {
+           if ($refquota eq 'none') {
+               $zvol->{size} = 0;
+           } else {
+               $zvol->{size} = zfs_parse_size($refquota);
            }
-           push @$list, $zvol;
+           $zvol->{format} = 'subvol';
+       } else {
+           $zvol->{size} = zfs_parse_size($size);
+           $zvol->{format} = 'raw';
        }
+       if ($origin !~ /^-$/) {
+           $zvol->{origin} = $origin;
+       }
+       push @$list, $zvol;
     }
 
     return $list;
@@ -117,7 +131,7 @@ sub zfs_parse_zvol_list {
 sub parse_volname {
     my ($class, $volname) = @_;
 
-    if ($volname =~ m/^(((base|vm)-(\d+)-\S+)\/)?((base)?(vm)?-(\d+)-\S+)$/) {
+    if ($volname =~ m/^(((base|vm)-(\d+)-\S+)\/)?((base)?(vm|subvol)?-(\d+)-\S+)$/) {
        return ('images', $5, $8, $2, $4, $6);
     }
 
@@ -133,8 +147,13 @@ sub path {
 
     my $path = '';
 
-    if($vtype eq "images"){
-       $path = "/dev/zvol/$scfg->{pool}/$volname";
+    if ($vtype eq "images") {
+       if ($volname =~ m/^subvol-/) {
+           # fixme: we currently assume standard mount point?!
+           $path = "$scfg->{pool}/$volname";
+       } else {
+           $path = "/dev/zvol/$scfg->{pool}/$volname";
+       }
     } else {
        die "$vtype is not allowed in ZFSPool!";
     }
@@ -164,7 +183,7 @@ sub zfs_request {
         $msg .= "$line\n";
     };
 
-    run_command($cmd, outfunc => $output, timeout => $timeout);
+    run_command($cmd, errmsg => "zfs error", outfunc => $output, timeout => $timeout);
 
     return $msg;
 }
@@ -172,23 +191,34 @@ sub zfs_request {
 sub alloc_image {
     my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
 
-    die "unsupported format '$fmt'" if $fmt ne 'raw';
+    my $volname = $name;
+    
+    if ($fmt eq 'raw') {
 
-    die "illegal name '$name' - sould be 'vm-$vmid-*'\n"
-    if $name && $name !~ m/^vm-$vmid-/;
+       die "illegal name '$volname' - sould be 'vm-$vmid-*'\n"
+           if $volname && $volname !~ m/^vm-$vmid-/;
+       $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid) 
+           if !$volname;
 
-    $name = $class->zfs_find_free_diskname($storeid, $scfg, $vmid) if !$name;
-    
-    $class->zfs_create_zvol($scfg, $name, $size);
-    run_command ("udevadm trigger --subsystem-match block");
-    run_command ("udevadm settle --timeout 5");
-    
-    for (1..10) {
-       last if -e "/dev/zvol/$scfg->{pool}/$name" ;
-       Time::HiRes::usleep(100);
+       $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}");
+
+    } elsif ( $fmt eq 'subvol') {
+       
+       die "subvolume allocation without name\n" if !$volname;
+       die "illegal name '$volname' - sould be 'subvol-$vmid-*'\n"
+           if $volname !~ m/^subvol-$vmid-/;
+
+       $class->zfs_create_subvol($scfg, $volname, $size);      
+       
+    } else {
+       die "unsupported format '$fmt'";
     }
 
-    return $name;
+    return $volname;
 }
 
 sub free_image {
@@ -236,7 +266,6 @@ sub list_images {
            push @$res, $info;
        }
     }
-
     return $res;
 }
 
@@ -288,40 +317,60 @@ sub zfs_create_zvol {
     $class->zfs_request($scfg, undef, @$cmd);
 }
 
+sub zfs_create_subvol {
+    my ($class, $scfg, $volname, $size) = @_;
+
+    my $dataset = "$scfg->{pool}/$volname";
+    
+    my $cmd = ['create', '-o', "refquota=${size}k", $dataset];
+
+    $class->zfs_request($scfg, undef, @$cmd);
+}
+
 sub zfs_delete_zvol {
     my ($class, $scfg, $zvol) = @_;
 
-    $class->zfs_request($scfg, undef, 'destroy', '-r', "$scfg->{pool}/$zvol");
+    my $err;
+
+    for (my $i = 0; $i < 6; $i++) {
+
+       eval { $class->zfs_request($scfg, undef, 'destroy', '-r', "$scfg->{pool}/$zvol"); };
+       if ($err = $@) {
+           if ($err =~ m/^zfs error:(.*): dataset is busy.*/) {
+               sleep(1);
+           } else {
+               die $err;
+           }
+       } else {
+           last;
+       }
+    }
+
+    die $err if $err;
 }
 
 sub zfs_list_zvol {
     my ($class, $scfg) = @_;
 
-    my $text = $class->zfs_request($scfg, 10, 'list', '-o', 'name,volsize,origin', '-t', 'volume', '-Hr');
+    my $text = $class->zfs_request($scfg, 10, 'list', '-o', 'name,volsize,origin,type,refquota', '-t', 'volume,filesystem', '-Hr');
     my $zvols = zfs_parse_zvol_list($text);
     return undef if !$zvols;
 
     my $list = ();
     foreach my $zvol (@$zvols) {
-       my @values = split('/', $zvol->{name});
-
-       my $image = pop @values;
-       my $pool = join('/', @values);
-
-       next if $image !~ m/^((vm|base)-(\d+)-\S+)$/;
-       my $owner = $3;
-
+       my $pool = $zvol->{pool};
+       my $name = $zvol->{name};
        my $parent = $zvol->{origin};
        if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){
            $parent = $1;
        }
 
-       $list->{$pool}->{$image} = {
-           name => $image,
+       $list->{$pool}->{$name} = {
+           name => $name,
            size => $zvol->{size},
            parent => $parent,
-           format => 'raw',
-            vmid => $owner
+           format => $zvol->{format},
+            vmid => $zvol->{owner},
         };
     }
 
@@ -397,7 +446,7 @@ sub volume_size_info {
 }
 
 sub volume_snapshot {
-    my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
+    my ($class, $scfg, $storeid, $volname, $snap) = @_;
 
     $class->zfs_request($scfg, undef, 'snapshot', "$scfg->{pool}/$volname\@$snap");
 }
@@ -431,8 +480,12 @@ sub activate_storage {
     my @param = ('-o', 'name', '-H');
 
     my $text = zfs_request($class, $scfg, undef, 'zpool_list', @param);
-    if ($text !~ $scfg->{pool}) {
+
+    # Note: $scfg->{pool} can include dataset <pool>/<dataset>
+    my $pool = $scfg->{pool};
+    $pool =~ s!/.*$!!;
+
+    if ($text !~ $pool) {
        run_command("zpool import -d /dev/disk/by-id/ -a");
     }
     return 1;