From 1ccae4490e74d9bee8a74d2c9e044a5527a1b9c7 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 1 May 2015 14:42:49 +0200 Subject: [PATCH] zfspool: new format 'subvol' to handle filesystems inside datasets We use this for containers. --- PVE/API2/Storage/Content.pm | 3 +- PVE/Storage/ZFSPoolPlugin.pm | 119 ++++++++++++++++++++++------------- 2 files changed, 77 insertions(+), 45 deletions(-) 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..37e031c 100644 --- a/PVE/Storage/ZFSPoolPlugin.pm +++ b/PVE/Storage/ZFSPoolPlugin.pm @@ -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,25 +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 $size = $2; - my $origin = $3; - my @parts = split /\//, $1; - my $name = pop @parts; - my $pool = join('/', @parts); + my ($dataset, $size, $origin, $type, $refquota) = split(/\s+/, $line); + next if !($type eq 'volume' || $type eq 'filesystem'); - next unless $name =~ m!^(\w+)-(\d+)-(\w+)-(\d+)$!; - $name = $pool . '/' . $name; + my $zvol = {}; + my @parts = split /\//, $dataset; + my $name = pop @parts; + my $pool = join('/', @parts); - $zvol->{pool} = $pool; - $zvol->{name} = $name; - $zvol->{size} = zfs_parse_size($size); - if ($3 !~ /^-$/) { - $zvol->{origin} = $origin; + 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; @@ -119,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); } @@ -135,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!"; } @@ -174,21 +191,32 @@ sub zfs_request { sub alloc_image { my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_; - die "unsupported format '$fmt'" if $fmt ne 'raw'; - - die "illegal name '$name' - sould be 'vm-$vmid-*'\n" - if $name && $name !~ m/^vm-$vmid-/; - my $volname = $name; - - $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid) if !$volname; + + if ($fmt eq 'raw') { - $class->zfs_create_zvol($scfg, $volname, $size); + 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 $devname = "/dev/zvol/$scfg->{pool}/$volname"; + $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}"); + 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 $volname; } @@ -238,7 +266,6 @@ sub list_images { push @$res, $info; } } - return $res; } @@ -290,6 +317,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,31 +352,25 @@ 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; 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}, }; }