[pve-devel] add subvolume support to pve-storage

Dietmar Maurer dietmar at proxmox.com
Fri May 1 10:55:35 CEST 2015


Hi all,

I want to add subvolume support to improve our container implementation.
I stated using the ZFSPoolPlugin, and simply use a new 'format' called 'subvol'.

While that seems to work, I wonder if that would also work with the 'ZFSPlugin'?
Would
it be possible to create a subvolume one an external server, and export that via
NFS?
Would that work with nexenta or other storage boxes?

Here is a first patch:

diff --git a/PVE/API2/Storage/Content.pm b/PVE/API2/Storage/Content.pm
index 605e455..1b09f53 100644
--- a/PVE/API2/Storage/Content.pm
+++ b/PVE/API2/Storage/Content.pm
@@ -2,6 +2,7 @@ package PVE::API2::Storage::Content;
 
 use strict;
 use warnings;
+use Data::Dumper;
 
 use PVE::SafeSyslog;
 use PVE::Cluster qw(cfs_read_file);
@@ -123,7 +124,7 @@ __PACKAGE__->register_method ({
 	    },
 	    'format' => {
 		type => 'string',
-		enum => ['raw', 'qcow2'],
+		enum => ['raw', 'qcow2', 'subvol'],
 		requires => 'size',
 		optional => 1,
 	    },
diff --git a/PVE/Storage/ZFSPoolPlugin.pm b/PVE/Storage/ZFSPoolPlugin.pm
index b3f3e37..2787426 100644
--- a/PVE/Storage/ZFSPoolPlugin.pm
+++ b/PVE/Storage/ZFSPoolPlugin.pm
@@ -17,6 +17,7 @@ sub type {
 sub plugindata {
     return {
 	content => [ {images => 1}, { images => 1 }],
+	format => [ { raw => 1, subvol => 1 } , 'raw' ],
     };
 }
 
@@ -92,25 +93,35 @@ 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 $size = $2;
-	    my $origin = $3;
-	    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($size);
-	    if ($3 !~ /^-$/) {
-		$zvol->{origin} = $origin;
+	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!^(\w+)-(\d+)-(\S+)$!;
+	$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;
@@ -119,7 +130,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);
     }
 
@@ -174,21 +185,32 @@ 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;
 
-    my $volname = $name;
- 
-    $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid) if
!$volname;
+	$class->zfs_create_zvol($scfg, $volname, $size);
+	my $devname = "/dev/zvol/$scfg->{pool}/$volname";
 
-    $class->zfs_create_zvol($scfg, $volname, $size);
+	run_command("udevadm trigger --subsystem-match block");
+	system("udevadm settle --timeout 10 --exit-if-exists=${devname}");
 
-    my $devname = "/dev/zvol/$scfg->{pool}/$volname";
+    } 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-/;
 
-    run_command("udevadm trigger --subsystem-match block");
-    system("udevadm settle --timeout 10 --exit-if-exists=${devname}");
+	$class->zfs_create_subvol($scfg, $volname, $size);	
+	
+    } else {
+	die "unsupported format '$fmt'";
+    }
 
     return $volname;
 }
@@ -238,7 +260,6 @@ sub list_images {
 	    push @$res, $info;
 	}
     }
-
     return $res;
 }
 
@@ -290,6 +311,16 @@ 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) = @_;
 
@@ -315,7 +346,7 @@ sub zfs_delete_zvol {
 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;
 
@@ -326,8 +357,9 @@ sub zfs_list_zvol {
 	my $image = pop @values;
 	my $pool = join('/', @values);
 
-	next if $image !~ m/^((vm|base)-(\d+)-\S+)$/;
+	next if $image !~ m/^((vm|base|subvol)-(\d+)-\S+)$/;
 	my $owner = $3;
+	my $type = $2;
 
 	my $parent = $zvol->{origin};
 	if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){
@@ -338,7 +370,7 @@ sub zfs_list_zvol {
 	    name => $image,
 	    size => $zvol->{size},
 	    parent => $parent,
-	    format => 'raw',
+	    format => $zvol->{format},
             vmid => $owner
         };
     }



More information about the pve-devel mailing list