1 package PVE
::Storage
::ZFSPlugin
;
7 use PVE
::Tools
qw(run_command);
8 use PVE
::Storage
::ZFSDirPlugin
;
10 use base
qw(PVE::Storage::ZFSDirPlugin);
11 use PVE
::Storage
::LunCmd
::Comstar
;
12 use PVE
::Storage
::LunCmd
::Istgt
;
13 use PVE
::Storage
::LunCmd
::Iet
;
15 my @ssh_opts = ('-o', 'BatchMode=yes');
16 my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
17 my $id_rsa_path = '/etc/pve/priv/zfs';
29 my $zfs_unknown_scsi_provider = sub {
32 die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]";
35 my $zfs_get_base = sub {
38 if ($scfg->{iscsiprovider
} eq 'comstar') {
39 return PVE
::Storage
::LunCmd
::Comstar
::get_base
;
40 } elsif ($scfg->{iscsiprovider
} eq 'istgt') {
41 return PVE
::Storage
::LunCmd
::Istgt
::get_base
;
42 } elsif ($scfg->{iscsiprovider
} eq 'iet') {
43 return PVE
::Storage
::LunCmd
::Iet
::get_base
;
45 $zfs_unknown_scsi_provider->($scfg->{iscsiprovider
});
50 my ($class, $scfg, $timeout, $method, @params) = @_;
52 $timeout = 5 if !$timeout;
56 if ($lun_cmds->{$method}) {
57 if ($scfg->{iscsiprovider
} eq 'comstar') {
58 $msg = PVE
::Storage
::LunCmd
::Comstar
::run_lun_command
($scfg, $timeout, $method, @params);
59 } elsif ($scfg->{iscsiprovider
} eq 'istgt') {
60 $msg = PVE
::Storage
::LunCmd
::Istgt
::run_lun_command
($scfg, $timeout, $method, @params);
61 } elsif ($scfg->{iscsiprovider
} eq 'iet') {
62 $msg = PVE
::Storage
::LunCmd
::Iet
::run_lun_command
($scfg, $timeout, $method, @params);
64 $zfs_unknown_scsi_provider->($scfg->{iscsiprovider
});
68 my $target = 'root@' . $scfg->{portal
};
70 my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target];
72 if ($method eq 'zpool_list') {
73 push @$cmd, 'zpool', 'list';
75 push @$cmd, 'zfs', $method;
85 run_command
($cmd, outfunc
=> $output, timeout
=> $timeout);
92 my ($class, $scfg, $zvol) = @_;
95 my $base = $zfs_get_base->($scfg);
96 if ($zvol =~ /^.+\/.+/) {
97 $object = "$base/$zvol";
99 $object = "$base/$scfg->{pool}/$zvol";
102 my $lu_name = $class->zfs_request($scfg, undef, 'list_lu', $object);
104 return $lu_name if $lu_name;
106 die "Could not find lu_name for zvol $zvol";
109 sub zfs_add_lun_mapping_entry
{
110 my ($class, $scfg, $zvol, $guid) = @_;
112 if (! defined($guid)) {
113 $guid = $class->zfs_get_lu_name($scfg, $zvol);
116 $class->zfs_request($scfg, undef, 'add_view', $guid);
120 my ($class, $scfg, $zvol) = @_;
122 my $guid = $class->zfs_get_lu_name($scfg, $zvol);
124 $class->zfs_request($scfg, undef, 'delete_lu', $guid);
128 my ($class, $scfg, $zvol) = @_;
130 my $base = $zfs_get_base->($scfg);
131 my $guid = $class->zfs_request($scfg, undef, 'create_lu', "$base/$scfg->{pool}/$zvol");
137 my ($class, $scfg, $zvol) = @_;
139 my $base = $zfs_get_base->($scfg);
140 $class->zfs_request($scfg, undef, 'import_lu', "$base/$scfg->{pool}/$zvol");
144 my ($class, $scfg, $zvol, $size) = @_;
146 my $guid = $class->zfs_get_lu_name($scfg, $zvol);
148 $class->zfs_request($scfg, undef, 'modify_lu', "${size}K", $guid);
151 sub zfs_get_lun_number
{
152 my ($class, $scfg, $guid) = @_;
154 die "could not find lun_number for guid $guid" if !$guid;
156 return $class->zfs_request($scfg, undef, 'list_view', $guid);
167 content
=> [ {images
=> 1}, { images
=> 1 }],
174 description
=> "iscsi provider",
177 # this will disable write caching on comstar and istgt.
178 # it is not implemented for iet. iet blockio always operates with
179 # writethrough caching when not in readonly mode
181 description
=> "disable write caching on the target",
185 description
=> "target group for comstar views",
189 description
=> "host group for comstar views",
197 nodes
=> { optional
=> 1 },
198 disable
=> { optional
=> 1 },
199 portal
=> { fixed
=> 1 },
200 target
=> { fixed
=> 1 },
201 pool
=> { fixed
=> 1 },
202 blocksize
=> { fixed
=> 1 },
203 iscsiprovider
=> { fixed
=> 1 },
204 nowritecache
=> { optional
=> 1 },
205 sparse
=> { optional
=> 1 },
206 comstar_hg
=> { optional
=> 1 },
207 comstar_tg
=> { optional
=> 1 },
208 content
=> { optional
=> 1 },
212 # Storage implementation
215 my ($class, $volname) = @_;
217 if ($volname =~ m/^(((base|vm)-(\d+)-\S+)\/)?
((base
)?
(vm
)?
-(\d
+)-\S
+)$/) {
218 return ('images', $5, $8, $2, $4, $6);
221 die "unable to parse zfs volume name '$volname'\n";
225 my ($class, $scfg, $volname) = @_;
227 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
229 my $target = $scfg->{target
};
230 my $portal = $scfg->{portal
};
232 my $guid = $class->zfs_get_lu_name($scfg, $name);
233 my $lun = $class->zfs_get_lun_number($scfg, $guid);
235 my $path = "iscsi://$portal/$target/$lun";
237 return ($path, $vmid, $vtype);
241 my ($class, $storeid, $scfg, $volname) = @_;
243 my $snap = '__base__';
245 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
246 $class->parse_volname($volname);
248 die "create_base not possible with base image\n" if $isBase;
251 $newname =~ s/^vm-/base-/;
253 my $newvolname = $basename ?
"$basename/$newname" : "$newname";
255 $class->zfs_delete_lu($scfg, $name);
256 $class->zfs_request($scfg, undef, 'rename', "$scfg->{pool}/$name", "$scfg->{pool}/$newname");
258 my $guid = $class->zfs_create_lu($scfg, $newname);
259 $class->zfs_add_lun_mapping_entry($scfg, $newname, $guid);
261 my $running = undef; #fixme : is create_base always offline ?
263 $class->volume_snapshot($scfg, $storeid, $newname, $snap, $running);
269 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
271 $snap ||= '__base__';
273 my ($vtype, $basename, $basevmid, undef, undef, $isBase) =
274 $class->parse_volname($volname);
276 die "clone_image only works on base images\n" if !$isBase;
278 my $name = $class->zfs_find_free_diskname($storeid, $scfg, $vmid);
280 warn "clone $volname: $basename to $name\n";
282 $class->zfs_request($scfg, undef, 'clone', "$scfg->{pool}/$basename\@$snap", "$scfg->{pool}/$name");
284 my $guid = $class->zfs_create_lu($scfg, $name);
285 $class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
291 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
293 die "unsupported format '$fmt'" if $fmt ne 'raw';
295 die "illegal name '$name' - sould be 'vm-$vmid-*'\n"
296 if $name && $name !~ m/^vm-$vmid-/;
298 $name = $class->zfs_find_free_diskname($storeid, $scfg, $vmid) if !$name;
300 $class->zfs_create_zvol($scfg, $name, $size);
301 my $guid = $class->zfs_create_lu($scfg, $name);
302 $class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
308 my ($class, $storeid, $scfg, $volname, $isBase) = @_;
310 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
312 $class->zfs_delete_lu($scfg, $name);
314 $class->zfs_delete_zvol($scfg, $name);
318 my $guid = $class->zfs_create_lu($scfg, $name);
319 $class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
327 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
329 $cache->{zfs
} = $class->zfs_list_zvol($scfg) if !$cache->{zfs
};
330 my $zfspool = $scfg->{pool
};
333 if (my $dat = $cache->{zfs
}->{$zfspool}) {
335 foreach my $image (keys %$dat) {
337 my $volname = $dat->{$image}->{name
};
338 my $parent = $dat->{$image}->{parent
};
341 if ($parent && $parent =~ m/^(\S+)@(\S+)$/) {
342 my ($basename) = ($1);
343 $volid = "$storeid:$basename/$volname";
345 $volid = "$storeid:$volname";
348 my $owner = $dat->{$volname}->{vmid
};
350 my $found = grep { $_ eq $volid } @$vollist;
353 next if defined ($vmid) && ($owner ne $vmid);
356 my $info = $dat->{$volname};
357 $info->{volid
} = $volid;
366 my ($class, $storeid, $scfg, $cache) = @_;
374 ($free, $used) = $class->zfs_get_pool_stats($scfg);
376 $total = $free + $used;
380 return ($total, $free, $used, $active);
383 sub activate_storage
{
384 my ($class, $storeid, $scfg, $cache) = @_;
388 sub deactivate_storage
{
389 my ($class, $storeid, $scfg, $cache) = @_;
393 sub activate_volume
{
394 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
398 sub deactivate_volume
{
399 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
403 sub volume_size_info
{
404 my ($class, $scfg, $storeid, $volname, $timeout) = @_;
406 return $class->zfs_get_zvol_size($scfg, $volname);
410 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
412 my $new_size = ($size/1024);
414 $class->zfs_request($scfg, undef, 'set', 'volsize=' . $new_size . 'k', "$scfg->{pool}/$volname");
415 $class->zfs_resize_lu($scfg, $volname, $new_size);
418 sub volume_snapshot
{
419 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
421 $class->zfs_request($scfg, undef, 'snapshot', "$scfg->{pool}/$volname\@$snap");
424 sub volume_snapshot_rollback
{
425 my ($class, $scfg, $storeid, $volname, $snap) = @_;
427 # abort rollback if snapshot is not the latest
428 my @params = ('-t', 'snapshot', '-o', 'name', '-s', 'creation');
429 my $text = $class->zfs_request($scfg, undef, 'list', @params);
430 my @snapshots = split(/\n/, $text);
431 my $recentsnap = undef;
432 foreach (@snapshots) {
433 if (/$scfg->{pool}\/$volname/) {
438 if ($snap ne $recentsnap) {
439 die "cannot rollback, more recent snapshots exist\n";
442 $class->zfs_delete_lu($scfg, $volname);
444 $class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$volname\@$snap");
446 $class->zfs_import_lu($scfg, $volname);
448 $class->zfs_add_lun_mapping_entry($scfg, $volname);
451 sub volume_snapshot_delete
{
452 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
454 $class->zfs_request($scfg, undef, 'destroy', "$scfg->{pool}/$volname\@$snap");
457 sub volume_has_feature
{
458 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
461 snapshot
=> { current
=> 1, snap
=> 1},
462 clone
=> { base
=> 1},
463 template
=> { current
=> 1},
464 copy
=> { base
=> 1, current
=> 1},
467 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
468 $class->parse_volname($volname);
475 $key = $isBase ?
'base' : 'current';
478 return 1 if $features->{$feature}->{$key};