my @ssh_opts = ('-o', 'BatchMode=yes');
my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
+my $id_rsa_path = '/etc/pve/priv/zfs';
my $lun_cmds = {
- create_lu => 1,
- delete_lu => 1,
- import_lu => 1,
- modify_lu => 1,
- add_view => 1,
- list_view => 1,
- list_lu => 1,
+ create_lu => 1,
+ delete_lu => 1,
+ import_lu => 1,
+ modify_lu => 1,
+ add_view => 1,
+ list_view => 1,
+ list_lu => 1,
};
my $zfs_unknown_scsi_provider = sub {
- my ($provider) = @_;
+ my ($provider) = @_;
- die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]";
+ die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]";
};
my $zfs_get_base = sub {
- my ($scfg) = @_;
-
- if ($scfg->{iscsiprovider} eq 'comstar') {
- return PVE::Storage::LunCmd::Comstar::get_base;
- } elsif ($scfg->{iscsiprovider} eq 'istgt') {
- return PVE::Storage::LunCmd::Istgt::get_base;
- } elsif ($scfg->{iscsiprovider} eq 'iet') {
- return PVE::Storage::LunCmd::Iet::get_base;
- } else {
- $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
- }
+ my ($scfg) = @_;
+
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ return PVE::Storage::LunCmd::Comstar::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ return PVE::Storage::LunCmd::Istgt::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+ return PVE::Storage::LunCmd::Iet::get_base;
+ } else {
+ $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
+ }
};
sub zfs_request {
my $cmdmap;
my $zfscmd;
my $target;
- my $msg;
+ my $msg;
$timeout = 5 if !$timeout;
- if ($lun_cmds->{$method}) {
- if ($scfg->{iscsiprovider} eq 'comstar') {
- $msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params);
- } elsif ($scfg->{iscsiprovider} eq 'istgt') {
- $msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
- } elsif ($scfg->{iscsiprovider} eq 'iet') {
- $msg = PVE::Storage::LunCmd::Iet::run_lun_command($scfg, $timeout, $method, @params);
- } else {
- $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
- }
- } else {
- if ($method eq 'zpool_list') {
- $zfscmd = 'zpool';
- $method = 'list',
- } else {
- $zfscmd = 'zfs';
- }
-
- $target = 'root@' . $scfg->{portal};
-
- my $cmd = [@ssh_cmd, $target, $zfscmd, $method, @params];
-
- $msg = '';
-
- my $output = sub {
- my $line = shift;
- $msg .= "$line\n";
- };
-
- run_command($cmd, outfunc => $output, timeout => $timeout);
- }
+ if ($lun_cmds->{$method}) {
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ $msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ $msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+ $msg = PVE::Storage::LunCmd::Iet::run_lun_command($scfg, $timeout, $method, @params);
+ } else {
+ $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
+ }
+ } else {
+ if ($method eq 'zpool_list') {
+ $zfscmd = 'zpool';
+ $method = 'list',
+ } else {
+ $zfscmd = 'zfs';
+ }
+
+ $target = 'root@' . $scfg->{portal};
+
+ my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $zfscmd, $method, @params];
+
+ $msg = '';
+
+ my $output = sub {
+ my $line = shift;
+ $msg .= "$line\n";
+ };
+
+ run_command($cmd, outfunc => $output, timeout => $timeout);
+ }
return $msg;
}
return 0 if !$text;
if ($text =~ m/^(\d+(\.\d+)?)([TGMK])?$/) {
- my ($size, $reminder, $unit) = ($1, $2, $3);
- return $size if !$unit;
- if ($unit eq 'K') {
- $size *= 1024;
- } elsif ($unit eq 'M') {
- $size *= 1024*1024;
- } elsif ($unit eq 'G') {
- $size *= 1024*1024*1024;
- } elsif ($unit eq 'T') {
- $size *= 1024*1024*1024*1024;
- }
-
- if ($reminder) {
- $size = ceil($size);
- }
- return $size;
+ my ($size, $reminder, $unit) = ($1, $2, $3);
+ return $size if !$unit;
+ if ($unit eq 'K') {
+ $size *= 1024;
+ } elsif ($unit eq 'M') {
+ $size *= 1024*1024;
+ } elsif ($unit eq 'G') {
+ $size *= 1024*1024*1024;
+ } elsif ($unit eq 'T') {
+ $size *= 1024*1024*1024*1024;
+ }
+
+ if ($reminder) {
+ $size = ceil($size);
+ }
+ return $size;
} else {
- return 0;
+ return 0;
}
}
my $used = 0;
my $text = zfs_request($scfg, undef, 'get', '-o', 'value', '-Hp',
- 'available,used', $scfg->{pool});
+ 'available,used', $scfg->{pool});
my @lines = split /\n/, $text;
if($lines[0] =~ /^(\d+)$/) {
- $available = $1;
+ $available = $1;
}
if($lines[1] =~ /^(\d+)$/) {
- $used = $1;
+ $used = $1;
}
return ($available, $used);
my @lines = split /\n/, $text;
foreach my $line (@lines) {
- if ($line =~ /^(.+)\s+([a-zA-Z0-9\.]+|\-)\s+(.+)$/) {
- my $zvol = {};
- my $name;
- my $disk;
- my @zvols = split /\//, $1;
- my $pool = $zvols[0];
-
- if (scalar(@zvols) == 2 && $zvols[0] !~ /^rpool$/) {
- $disk = $zvols[1];
- next unless $disk =~ m!^(\w+)-(\d+)-(\w+)-(\d+)$!;
- $name = $pool . '/' . $disk;
- } else {
- next;
- }
-
- $zvol->{name} = $name;
- $zvol->{size} = zfs_parse_size($2);
- if ($3 !~ /^-$/) {
- $zvol->{origin} = $3;
- }
- push @$list, $zvol;
- }
+ if ($line =~ /^(.+)\s+([a-zA-Z0-9\.]+|\-)\s+(.+)$/) {
+ my $zvol = {};
+ my @parts = split /\//, $1;
+ my $name = pop @parts;
+ my $pool = join('/', @parts);
+
+ if ($pool !~ /^rpool$/) {
+ next unless $name =~ m!^(\w+)-(\d+)-(\w+)-(\d+)$!;
+ $name = $pool . '/' . $name;
+ } else {
+ next;
+ }
+
+ $zvol->{pool} = $pool;
+ $zvol->{name} = $name;
+ $zvol->{size} = zfs_parse_size($2);
+ if ($3 !~ /^-$/) {
+ $zvol->{origin} = $3;
+ }
+ push @$list, $zvol;
+ }
}
return $list;
my ($scfg, $zvol) = @_;
my $object;
- my $base = $zfs_get_base->($scfg);
+ my $base = $zfs_get_base->($scfg);
if ($zvol =~ /^.+\/.+/) {
$object = "$base/$zvol";
} else {
my $lu_name = zfs_request($scfg, undef, 'list_lu', $object);
- return $lu_name if $lu_name;
-
+ return $lu_name if $lu_name;
+
die "Could not find lu_name for zvol $zvol";
}
my $text = zfs_request($scfg, undef, 'get', '-Hp', 'volsize', "$scfg->{pool}/$zvol");
if($text =~ /volsize\s(\d+)/){
- return $1;
+ return $1;
}
die "Could not get zvol size";
my ($scfg, $zvol, $guid) = @_;
if (! defined($guid)) {
- $guid = zfs_get_lu_name($scfg, $zvol);
+ $guid = zfs_get_lu_name($scfg, $zvol);
}
-
+
zfs_request($scfg, undef, 'add_view', $guid);
}
sub zfs_create_lu {
my ($scfg, $zvol) = @_;
- my $base = $zfs_get_base->($scfg);
+ my $base = $zfs_get_base->($scfg);
my $guid = zfs_request($scfg, undef, 'create_lu', "$base/$scfg->{pool}/$zvol");
return $guid;
sub zfs_import_lu {
my ($scfg, $zvol) = @_;
- my $base = $zfs_get_base->($scfg);
+ my $base = $zfs_get_base->($scfg);
zfs_request($scfg, undef, 'import_lu', "$base/$scfg->{pool}/$zvol");
}
sub zfs_create_zvol {
my ($scfg, $zvol, $size) = @_;
+
+ my $sparse = '';
+ if ($scfg->{sparse}) {
+ $sparse = '-s';
+ }
- zfs_request($scfg, undef, 'create', '-b', $scfg->{blocksize}, '-V', "${size}k", "$scfg->{pool}/$zvol");
+ zfs_request($scfg, undef, 'create', $sparse, '-b', $scfg->{blocksize}, '-V', "${size}k", "$scfg->{pool}/$zvol");
}
sub zfs_delete_zvol {
sub zfs_list_zvol {
my ($scfg) = @_;
- my $text = zfs_request($scfg, 10, 'list', '-o', 'name,volsize,origin', '-Hr');
+ my $text = zfs_request($scfg, 10, 'list', '-o', 'name,volsize,origin', '-t', 'volume', '-Hr');
my $zvols = zfs_parse_zvol_list($text);
return undef if !$zvols;
my $list = ();
foreach my $zvol (@$zvols) {
- my @values = split('/', $zvol->{name});
-
- my $pool = $values[0];
- my $image = $values[1];
-
- next if $image !~ m/^((vm|base)-(\d+)-\S+)$/;
- my $owner = $3;
-
- my $parent = $zvol->{origin};
- if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){
- $parent = $1;
- }
-
- $list->{$pool}->{$image} = {
- name => $image,
- size => $zvol->{size},
- parent => $parent,
- format => 'raw',
- vmid => $owner
- };
+ 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 $parent = $zvol->{origin};
+ if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){
+ $parent = $1;
+ }
+
+ $list->{$pool}->{$image} = {
+ name => $image,
+ size => $zvol->{size},
+ parent => $parent,
+ format => 'raw',
+ vmid => $owner
+ };
}
return $list;
sub plugindata {
return {
- content => [ {images => 1}, { images => 1 }],
+ content => [ {images => 1}, { images => 1 }],
};
}
sub properties {
return {
- iscsiprovider => {
- description => "iscsi provider",
- type => 'string',
- },
- blocksize => {
- description => "block size",
- type => 'string',
- }
+ iscsiprovider => {
+ description => "iscsi provider",
+ type => 'string',
+ },
+ blocksize => {
+ description => "block size",
+ type => 'string',
+ },
+ # this will disable write caching on comstar and istgt.
+ # it is not implemented for iet. iet blockio always operates with
+ # writethrough caching when not in readonly mode
+ nowritecache => {
+ description => "disable write caching on the target",
+ type => 'boolean',
+ },
+ sparse => {
+ description => "use sparse volumes",
+ type => 'boolean',
+ }
};
}
nodes => { optional => 1 },
disable => { optional => 1 },
portal => { fixed => 1 },
- target => { fixed => 1 },
+ target => { fixed => 1 },
pool => { fixed => 1 },
- blocksize => { fixed => 1 },
- iscsiprovider => { fixed => 1 },
- content => { optional => 1 },
+ blocksize => { fixed => 1 },
+ iscsiprovider => { fixed => 1 },
+ nowritecache => { optional => 1 },
+ sparse => { optional => 1 },
+ content => { optional => 1 },
};
}
my ($class, $volname) = @_;
if ($volname =~ m/^(((base|vm)-(\d+)-\S+)\/)?((base)?(vm)?-(\d+)-\S+)$/) {
- return ('images', $5, $8, $2, $4, $6);
+ return ('images', $5, $8, $2, $4, $6);
}
die "unable to parse zfs volume name '$volname'\n";
my $guid = zfs_get_lu_name($scfg, $name);
my $lun = zfs_get_lun_number($scfg, $guid);
-
+
my $path = "iscsi://$portal/$target/$lun";
-
+
return ($path, $vmid, $vtype);
}
die "unsupported format '$fmt'" if $fmt ne 'raw';
die "illegal name '$name' - sould be 'vm-$vmid-*'\n"
- if $name && $name !~ m/^vm-$vmid-/;
+ if $name && $name !~ m/^vm-$vmid-/;
$name = &$find_free_diskname($storeid, $scfg, $vmid);
if (my $dat = $cache->{zfs}->{$zfspool}) {
- foreach my $image (keys %$dat) {
+ foreach my $image (keys %$dat) {
- my $volname = $dat->{$image}->{name};
- my $parent = $dat->{$image}->{parent};
+ my $volname = $dat->{$image}->{name};
+ my $parent = $dat->{$image}->{parent};
- my $volid = undef;
+ my $volid = undef;
if ($parent && $parent =~ m/^(\S+)@(\S+)$/) {
- my ($basename) = ($1);
- $volid = "$storeid:$basename/$volname";
- } else {
- $volid = "$storeid:$volname";
- }
-
- my $owner = $dat->{$volname}->{vmid};
- if ($vollist) {
- my $found = grep { $_ eq $volid } @$vollist;
- next if !$found;
- } else {
- next if defined ($vmid) && ($owner ne $vmid);
- }
-
- my $info = $dat->{$volname};
- $info->{volid} = $volid;
- push @$res, $info;
- }
+ my ($basename) = ($1);
+ $volid = "$storeid:$basename/$volname";
+ } else {
+ $volid = "$storeid:$volname";
+ }
+
+ my $owner = $dat->{$volname}->{vmid};
+ if ($vollist) {
+ my $found = grep { $_ eq $volid } @$vollist;
+ next if !$found;
+ } else {
+ next if defined ($vmid) && ($owner ne $vmid);
+ }
+
+ my $info = $dat->{$volname};
+ $info->{volid} = $volid;
+ push @$res, $info;
+ }
}
return $res;
my $active = 0;
eval {
- ($free, $used) = zfs_get_pool_stats($scfg);
- $active = 1;
- $total = $free + $used;
+ ($free, $used) = zfs_get_pool_stats($scfg);
+ $active = 1;
+ $total = $free + $used;
};
warn $@ if $@;
my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
my $features = {
- snapshot => { current => 1, snap => 1},
- clone => { base => 1},
- template => { current => 1},
- copy => { base => 1, current => 1},
+ snapshot => { current => 1, snap => 1},
+ clone => { base => 1},
+ template => { current => 1},
+ copy => { base => 1, current => 1},
};
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
- $class->parse_volname($volname);
+ $class->parse_volname($volname);
my $key = undef;
if ($snapname) {
- $key = 'snap';
+ $key = 'snap';
} else {
- $key = $isBase ? 'base' : 'current';
+ $key = $isBase ? 'base' : 'current';
}
return 1 if $features->{$feature}->{$key};