]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/ZFSPlugin.pm
zfs: remove duplicate $object definition
[pve-storage.git] / PVE / Storage / ZFSPlugin.pm
CommitLineData
4f914e6e
MR
1package PVE::Storage::ZFSPlugin;
2
3use strict;
4use warnings;
5use IO::File;
6use POSIX;
5332e6c9 7use PVE::Tools qw(run_command);
5bb8e010 8use PVE::Storage::ZFSDirPlugin;
4f914e6e 9
5bb8e010 10use base qw(PVE::Storage::ZFSDirPlugin);
a7d56be6
MR
11use PVE::Storage::LunCmd::Comstar;
12use PVE::Storage::LunCmd::Istgt;
78a64432 13use PVE::Storage::LunCmd::Iet;
4f914e6e
MR
14
15my @ssh_opts = ('-o', 'BatchMode=yes');
16my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
3b219e80 17my $id_rsa_path = '/etc/pve/priv/zfs';
4f914e6e 18
a7d56be6 19my $lun_cmds = {
3b219e80
MR
20 create_lu => 1,
21 delete_lu => 1,
22 import_lu => 1,
23 modify_lu => 1,
24 add_view => 1,
25 list_view => 1,
26 list_lu => 1,
a7d56be6
MR
27};
28
29my $zfs_unknown_scsi_provider = sub {
3b219e80 30 my ($provider) = @_;
a7d56be6 31
3b219e80 32 die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]";
a7d56be6
MR
33};
34
35my $zfs_get_base = sub {
3b219e80
MR
36 my ($scfg) = @_;
37
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;
44 } else {
45 $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
46 }
a7d56be6
MR
47};
48
4f914e6e 49sub zfs_request {
7730694e 50 my ($class, $scfg, $timeout, $method, @params) = @_;
4f914e6e 51
5332e6c9 52 $timeout = 5 if !$timeout;
4f914e6e 53
e8004c9c
DM
54 my $msg = '';
55
3b219e80
MR
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);
63 } else {
64 $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
65 }
66 } else {
3b219e80 67
e8004c9c
DM
68 my $target = 'root@' . $scfg->{portal};
69
70 my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target];
3b219e80 71
e8004c9c
DM
72 if ($method eq 'zpool_list') {
73 push @$cmd, 'zpool', 'list';
74 } else {
75 push @$cmd, 'zfs', $method;
76 }
3b219e80 77
e8004c9c 78 push @$cmd, @params;
3b219e80 79
e8004c9c
DM
80 my $output = sub {
81 my $line = shift;
82 $msg .= "$line\n";
3b219e80
MR
83 };
84
85 run_command($cmd, outfunc => $output, timeout => $timeout);
86 }
4f914e6e
MR
87
88 return $msg;
89}
90
4f914e6e 91sub zfs_get_lu_name {
7730694e 92 my ($class, $scfg, $zvol) = @_;
4f914e6e 93
3b219e80 94 my $base = $zfs_get_base->($scfg);
34eaae3f
DM
95
96 my $object = ($zvol =~ /^.+\/.+/) ? "$base/$zvol" : "$base/$scfg->{pool}/$zvol";
4f914e6e 97
7730694e 98 my $lu_name = $class->zfs_request($scfg, undef, 'list_lu', $object);
a7d56be6 99
3b219e80
MR
100 return $lu_name if $lu_name;
101
4f914e6e
MR
102 die "Could not find lu_name for zvol $zvol";
103}
104
4f914e6e 105sub zfs_add_lun_mapping_entry {
7730694e 106 my ($class, $scfg, $zvol, $guid) = @_;
4f914e6e 107
6b5bca68
DM
108 if (!defined($guid)) {
109 $guid = $class->zfs_get_lu_name($scfg, $zvol);
4f914e6e 110 }
3b219e80 111
7730694e 112 $class->zfs_request($scfg, undef, 'add_view', $guid);
4f914e6e
MR
113}
114
115sub zfs_delete_lu {
7730694e 116 my ($class, $scfg, $zvol) = @_;
4f914e6e 117
7730694e 118 my $guid = $class->zfs_get_lu_name($scfg, $zvol);
4f914e6e 119
7730694e 120 $class->zfs_request($scfg, undef, 'delete_lu', $guid);
4f914e6e
MR
121}
122
123sub zfs_create_lu {
7730694e 124 my ($class, $scfg, $zvol) = @_;
4f914e6e 125
3b219e80 126 my $base = $zfs_get_base->($scfg);
7730694e 127 my $guid = $class->zfs_request($scfg, undef, 'create_lu', "$base/$scfg->{pool}/$zvol");
4f914e6e
MR
128
129 return $guid;
130}
131
132sub zfs_import_lu {
7730694e 133 my ($class, $scfg, $zvol) = @_;
4f914e6e 134
3b219e80 135 my $base = $zfs_get_base->($scfg);
7730694e 136 $class->zfs_request($scfg, undef, 'import_lu', "$base/$scfg->{pool}/$zvol");
4f914e6e
MR
137}
138
139sub zfs_resize_lu {
7730694e 140 my ($class, $scfg, $zvol, $size) = @_;
4f914e6e 141
7730694e 142 my $guid = $class->zfs_get_lu_name($scfg, $zvol);
4f914e6e 143
7730694e 144 $class->zfs_request($scfg, undef, 'modify_lu', "${size}K", $guid);
4f914e6e
MR
145}
146
147sub zfs_get_lun_number {
7730694e 148 my ($class, $scfg, $guid) = @_;
4f914e6e
MR
149
150 die "could not find lun_number for guid $guid" if !$guid;
151
7730694e 152 return $class->zfs_request($scfg, undef, 'list_view', $guid);
4f914e6e
MR
153}
154
155# Configuration
156
157sub type {
158 return 'zfs';
159}
160
161sub plugindata {
162 return {
6b5bca68 163 content => [ {images => 1}, { images => 1 }],
4f914e6e
MR
164 };
165}
166
167sub properties {
168 return {
6b5bca68
DM
169 iscsiprovider => {
170 description => "iscsi provider",
171 type => 'string',
172 },
173 # this will disable write caching on comstar and istgt.
174 # it is not implemented for iet. iet blockio always operates with
175 # writethrough caching when not in readonly mode
176 nowritecache => {
177 description => "disable write caching on the target",
178 type => 'boolean',
179 },
180 comstar_tg => {
181 description => "target group for comstar views",
182 type => 'string',
183 },
184 comstar_hg => {
185 description => "host group for comstar views",
186 type => 'string',
187 },
4f914e6e
MR
188 };
189}
190
191sub options {
192 return {
6b5bca68
DM
193 nodes => { optional => 1 },
194 disable => { optional => 1 },
195 portal => { fixed => 1 },
196 target => { fixed => 1 },
197 pool => { fixed => 1 },
198 blocksize => { fixed => 1 },
199 iscsiprovider => { fixed => 1 },
200 nowritecache => { optional => 1 },
201 sparse => { optional => 1 },
202 comstar_hg => { optional => 1 },
203 comstar_tg => { optional => 1 },
204 content => { optional => 1 },
4f914e6e
MR
205 };
206}
207
208# Storage implementation
209
210sub parse_volname {
211 my ($class, $volname) = @_;
212
213 if ($volname =~ m/^(((base|vm)-(\d+)-\S+)\/)?((base)?(vm)?-(\d+)-\S+)$/) {
6b5bca68 214 return ('images', $5, $8, $2, $4, $6);
4f914e6e
MR
215 }
216
217 die "unable to parse zfs volume name '$volname'\n";
218}
219
220sub path {
221 my ($class, $scfg, $volname) = @_;
222
223 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
224
225 my $target = $scfg->{target};
226 my $portal = $scfg->{portal};
227
7730694e
DM
228 my $guid = $class->zfs_get_lu_name($scfg, $name);
229 my $lun = $class->zfs_get_lun_number($scfg, $guid);
3b219e80 230
4f914e6e 231 my $path = "iscsi://$portal/$target/$lun";
3b219e80 232
4f914e6e
MR
233 return ($path, $vmid, $vtype);
234}
235
4f914e6e
MR
236sub create_base {
237 my ($class, $storeid, $scfg, $volname) = @_;
238
239 my $snap = '__base__';
240
241 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
242 $class->parse_volname($volname);
243
244 die "create_base not possible with base image\n" if $isBase;
245
246 my $newname = $name;
247 $newname =~ s/^vm-/base-/;
248
249 my $newvolname = $basename ? "$basename/$newname" : "$newname";
250
7730694e
DM
251 $class->zfs_delete_lu($scfg, $name);
252 $class->zfs_request($scfg, undef, 'rename', "$scfg->{pool}/$name", "$scfg->{pool}/$newname");
4f914e6e 253
7730694e
DM
254 my $guid = $class->zfs_create_lu($scfg, $newname);
255 $class->zfs_add_lun_mapping_entry($scfg, $newname, $guid);
4f914e6e
MR
256
257 my $running = undef; #fixme : is create_base always offline ?
258
259 $class->volume_snapshot($scfg, $storeid, $newname, $snap, $running);
260
261 return $newvolname;
262}
263
264sub clone_image {
f236eaf8 265 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
4f914e6e 266
f236eaf8 267 $snap ||= '__base__';
4f914e6e
MR
268
269 my ($vtype, $basename, $basevmid, undef, undef, $isBase) =
270 $class->parse_volname($volname);
271
272 die "clone_image only works on base images\n" if !$isBase;
273
7730694e 274 my $name = $class->zfs_find_free_diskname($storeid, $scfg, $vmid);
4f914e6e
MR
275
276 warn "clone $volname: $basename to $name\n";
277
7730694e 278 $class->zfs_request($scfg, undef, 'clone', "$scfg->{pool}/$basename\@$snap", "$scfg->{pool}/$name");
4f914e6e 279
7730694e
DM
280 my $guid = $class->zfs_create_lu($scfg, $name);
281 $class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
4f914e6e
MR
282
283 return $name;
284}
285
286sub alloc_image {
287 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
288
289 die "unsupported format '$fmt'" if $fmt ne 'raw';
290
291 die "illegal name '$name' - sould be 'vm-$vmid-*'\n"
3b219e80 292 if $name && $name !~ m/^vm-$vmid-/;
4f914e6e 293
7730694e 294 $name = $class->zfs_find_free_diskname($storeid, $scfg, $vmid) if !$name;
4f914e6e 295
7730694e
DM
296 $class->zfs_create_zvol($scfg, $name, $size);
297 my $guid = $class->zfs_create_lu($scfg, $name);
298 $class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
4f914e6e
MR
299
300 return $name;
301}
302
303sub free_image {
304 my ($class, $storeid, $scfg, $volname, $isBase) = @_;
305
306 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
307
7730694e 308 $class->zfs_delete_lu($scfg, $name);
6b5bca68
DM
309
310 eval { $class->zfs_delete_zvol($scfg, $name); };
311 if (my $err = $@) {
7730694e
DM
312 my $guid = $class->zfs_create_lu($scfg, $name);
313 $class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
4f914e6e 314 die $err;
6b5bca68 315 }
4f914e6e
MR
316
317 return undef;
318}
319
320sub list_images {
321 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
322
7730694e 323 $cache->{zfs} = $class->zfs_list_zvol($scfg) if !$cache->{zfs};
4f914e6e
MR
324 my $zfspool = $scfg->{pool};
325 my $res = [];
326
327 if (my $dat = $cache->{zfs}->{$zfspool}) {
328
6b5bca68 329 foreach my $image (keys %$dat) {
4f914e6e 330
6b5bca68
DM
331 my $volname = $dat->{$image}->{name};
332 my $parent = $dat->{$image}->{parent};
4f914e6e 333
6b5bca68 334 my $volid = undef;
4f914e6e 335 if ($parent && $parent =~ m/^(\S+)@(\S+)$/) {
6b5bca68
DM
336 my ($basename) = ($1);
337 $volid = "$storeid:$basename/$volname";
338 } else {
339 $volid = "$storeid:$volname";
340 }
341
342 my $owner = $dat->{$volname}->{vmid};
343 if ($vollist) {
344 my $found = grep { $_ eq $volid } @$vollist;
345 next if !$found;
346 } else {
347 next if defined ($vmid) && ($owner ne $vmid);
348 }
349
350 my $info = $dat->{$volname};
351 $info->{volid} = $volid;
352 push @$res, $info;
353 }
4f914e6e
MR
354 }
355
356 return $res;
357}
358
4f914e6e
MR
359sub activate_storage {
360 my ($class, $storeid, $scfg, $cache) = @_;
361 return 1;
362}
363
364sub deactivate_storage {
365 my ($class, $storeid, $scfg, $cache) = @_;
366 return 1;
367}
368
369sub activate_volume {
370 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
371 return 1;
372}
373
374sub deactivate_volume {
375 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
376 return 1;
377}
378
4f914e6e
MR
379sub volume_resize {
380 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
381
382 my $new_size = ($size/1024);
383
7730694e
DM
384 $class->zfs_request($scfg, undef, 'set', 'volsize=' . $new_size . 'k', "$scfg->{pool}/$volname");
385 $class->zfs_resize_lu($scfg, $volname, $new_size);
4f914e6e
MR
386}
387
4f914e6e
MR
388sub volume_snapshot_rollback {
389 my ($class, $scfg, $storeid, $volname, $snap) = @_;
390
a315b9ff
CA
391 # abort rollback if snapshot is not the latest
392 my @params = ('-t', 'snapshot', '-o', 'name', '-s', 'creation');
7730694e 393 my $text = $class->zfs_request($scfg, undef, 'list', @params);
a315b9ff
CA
394 my @snapshots = split(/\n/, $text);
395 my $recentsnap = undef;
396 foreach (@snapshots) {
397 if (/$scfg->{pool}\/$volname/) {
398 s/^.*@//;
399 $recentsnap = $_;
6b5bca68 400 }
a315b9ff
CA
401 }
402 if ($snap ne $recentsnap) {
403 die "cannot rollback, more recent snapshots exist\n";
404 }
405
7730694e 406 $class->zfs_delete_lu($scfg, $volname);
4f914e6e 407
7730694e 408 $class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$volname\@$snap");
4f914e6e 409
7730694e 410 $class->zfs_import_lu($scfg, $volname);
4f914e6e 411
7730694e 412 $class->zfs_add_lun_mapping_entry($scfg, $volname);
4f914e6e
MR
413}
414
4f914e6e
MR
415sub volume_has_feature {
416 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
417
418 my $features = {
6b5bca68
DM
419 snapshot => { current => 1, snap => 1},
420 clone => { base => 1},
421 template => { current => 1},
422 copy => { base => 1, current => 1},
4f914e6e
MR
423 };
424
425 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
6b5bca68 426 $class->parse_volname($volname);
4f914e6e
MR
427
428 my $key = undef;
5332e6c9
DM
429
430 if ($snapname) {
6b5bca68 431 $key = 'snap';
4f914e6e 432 } else {
6b5bca68 433 $key = $isBase ? 'base' : 'current';
4f914e6e 434 }
5332e6c9 435
4f914e6e
MR
436 return 1 if $features->{$feature}->{$key};
437
438 return undef;
439}
440
4411;