]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/ZFSPlugin.pm
add missing dependency to pve-cluster
[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);
85fda4dd 8use PVE::Storage::ZFSPoolPlugin;
21430e50 9use PVE::RPCEnvironment;
4f914e6e 10
85fda4dd 11use base qw(PVE::Storage::ZFSPoolPlugin);
a7d56be6
MR
12use PVE::Storage::LunCmd::Comstar;
13use PVE::Storage::LunCmd::Istgt;
78a64432 14use PVE::Storage::LunCmd::Iet;
4f914e6e 15
21430e50 16
4f914e6e
MR
17my @ssh_opts = ('-o', 'BatchMode=yes');
18my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
3b219e80 19my $id_rsa_path = '/etc/pve/priv/zfs';
4f914e6e 20
a7d56be6 21my $lun_cmds = {
3b219e80
MR
22 create_lu => 1,
23 delete_lu => 1,
24 import_lu => 1,
25 modify_lu => 1,
26 add_view => 1,
27 list_view => 1,
28 list_lu => 1,
a7d56be6
MR
29};
30
31my $zfs_unknown_scsi_provider = sub {
3b219e80 32 my ($provider) = @_;
a7d56be6 33
3b219e80 34 die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]";
a7d56be6
MR
35};
36
37my $zfs_get_base = sub {
3b219e80
MR
38 my ($scfg) = @_;
39
40 if ($scfg->{iscsiprovider} eq 'comstar') {
41 return PVE::Storage::LunCmd::Comstar::get_base;
42 } elsif ($scfg->{iscsiprovider} eq 'istgt') {
43 return PVE::Storage::LunCmd::Istgt::get_base;
44 } elsif ($scfg->{iscsiprovider} eq 'iet') {
45 return PVE::Storage::LunCmd::Iet::get_base;
46 } else {
47 $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
48 }
a7d56be6
MR
49};
50
4f914e6e 51sub zfs_request {
7730694e 52 my ($class, $scfg, $timeout, $method, @params) = @_;
4f914e6e 53
ef881e10 54 $timeout = PVE::RPCEnvironment->is_worker() ? 60*60 : 10
21430e50 55 if !$timeout;
4f914e6e 56
e8004c9c
DM
57 my $msg = '';
58
3b219e80
MR
59 if ($lun_cmds->{$method}) {
60 if ($scfg->{iscsiprovider} eq 'comstar') {
61 $msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params);
62 } elsif ($scfg->{iscsiprovider} eq 'istgt') {
63 $msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
64 } elsif ($scfg->{iscsiprovider} eq 'iet') {
65 $msg = PVE::Storage::LunCmd::Iet::run_lun_command($scfg, $timeout, $method, @params);
66 } else {
67 $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
68 }
69 } else {
3b219e80 70
e8004c9c
DM
71 my $target = 'root@' . $scfg->{portal};
72
73 my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target];
3b219e80 74
e8004c9c
DM
75 if ($method eq 'zpool_list') {
76 push @$cmd, 'zpool', 'list';
77 } else {
78 push @$cmd, 'zfs', $method;
79 }
3b219e80 80
e8004c9c 81 push @$cmd, @params;
3b219e80 82
e8004c9c
DM
83 my $output = sub {
84 my $line = shift;
85 $msg .= "$line\n";
3b219e80
MR
86 };
87
88 run_command($cmd, outfunc => $output, timeout => $timeout);
89 }
4f914e6e
MR
90
91 return $msg;
92}
93
4f914e6e 94sub zfs_get_lu_name {
7730694e 95 my ($class, $scfg, $zvol) = @_;
4f914e6e 96
3b219e80 97 my $base = $zfs_get_base->($scfg);
34eaae3f
DM
98
99 my $object = ($zvol =~ /^.+\/.+/) ? "$base/$zvol" : "$base/$scfg->{pool}/$zvol";
4f914e6e 100
7730694e 101 my $lu_name = $class->zfs_request($scfg, undef, 'list_lu', $object);
a7d56be6 102
3b219e80
MR
103 return $lu_name if $lu_name;
104
4f914e6e
MR
105 die "Could not find lu_name for zvol $zvol";
106}
107
4f914e6e 108sub zfs_add_lun_mapping_entry {
7730694e 109 my ($class, $scfg, $zvol, $guid) = @_;
4f914e6e 110
6b5bca68
DM
111 if (!defined($guid)) {
112 $guid = $class->zfs_get_lu_name($scfg, $zvol);
4f914e6e 113 }
3b219e80 114
7730694e 115 $class->zfs_request($scfg, undef, 'add_view', $guid);
4f914e6e
MR
116}
117
118sub zfs_delete_lu {
7730694e 119 my ($class, $scfg, $zvol) = @_;
4f914e6e 120
7730694e 121 my $guid = $class->zfs_get_lu_name($scfg, $zvol);
4f914e6e 122
7730694e 123 $class->zfs_request($scfg, undef, 'delete_lu', $guid);
4f914e6e
MR
124}
125
126sub zfs_create_lu {
7730694e 127 my ($class, $scfg, $zvol) = @_;
4f914e6e 128
3b219e80 129 my $base = $zfs_get_base->($scfg);
7730694e 130 my $guid = $class->zfs_request($scfg, undef, 'create_lu', "$base/$scfg->{pool}/$zvol");
4f914e6e
MR
131
132 return $guid;
133}
134
135sub zfs_import_lu {
7730694e 136 my ($class, $scfg, $zvol) = @_;
4f914e6e 137
3b219e80 138 my $base = $zfs_get_base->($scfg);
7730694e 139 $class->zfs_request($scfg, undef, 'import_lu', "$base/$scfg->{pool}/$zvol");
4f914e6e
MR
140}
141
142sub zfs_resize_lu {
7730694e 143 my ($class, $scfg, $zvol, $size) = @_;
4f914e6e 144
7730694e 145 my $guid = $class->zfs_get_lu_name($scfg, $zvol);
4f914e6e 146
7730694e 147 $class->zfs_request($scfg, undef, 'modify_lu', "${size}K", $guid);
4f914e6e
MR
148}
149
150sub zfs_get_lun_number {
7730694e 151 my ($class, $scfg, $guid) = @_;
4f914e6e
MR
152
153 die "could not find lun_number for guid $guid" if !$guid;
154
7730694e 155 return $class->zfs_request($scfg, undef, 'list_view', $guid);
4f914e6e
MR
156}
157
158# Configuration
159
160sub type {
161 return 'zfs';
162}
163
164sub plugindata {
165 return {
6b5bca68 166 content => [ {images => 1}, { images => 1 }],
4f914e6e
MR
167 };
168}
169
170sub properties {
171 return {
6b5bca68
DM
172 iscsiprovider => {
173 description => "iscsi provider",
174 type => 'string',
175 },
176 # this will disable write caching on comstar and istgt.
177 # it is not implemented for iet. iet blockio always operates with
178 # writethrough caching when not in readonly mode
179 nowritecache => {
180 description => "disable write caching on the target",
181 type => 'boolean',
182 },
183 comstar_tg => {
184 description => "target group for comstar views",
185 type => 'string',
186 },
187 comstar_hg => {
188 description => "host group for comstar views",
189 type => 'string',
190 },
4f914e6e
MR
191 };
192}
193
194sub options {
195 return {
6b5bca68
DM
196 nodes => { optional => 1 },
197 disable => { optional => 1 },
198 portal => { fixed => 1 },
199 target => { fixed => 1 },
200 pool => { fixed => 1 },
201 blocksize => { fixed => 1 },
202 iscsiprovider => { fixed => 1 },
203 nowritecache => { optional => 1 },
204 sparse => { optional => 1 },
205 comstar_hg => { optional => 1 },
206 comstar_tg => { optional => 1 },
207 content => { optional => 1 },
9edb99a5 208 bwlimit => { optional => 1 },
4f914e6e
MR
209 };
210}
211
212# Storage implementation
213
4f914e6e 214sub path {
e67069eb
DM
215 my ($class, $scfg, $volname, $storeid, $snapname) = @_;
216
217 die "direct access to snapshots not implemented"
218 if defined($snapname);
4f914e6e
MR
219
220 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
221
222 my $target = $scfg->{target};
223 my $portal = $scfg->{portal};
224
7730694e
DM
225 my $guid = $class->zfs_get_lu_name($scfg, $name);
226 my $lun = $class->zfs_get_lun_number($scfg, $guid);
3b219e80 227
4f914e6e 228 my $path = "iscsi://$portal/$target/$lun";
3b219e80 229
4f914e6e
MR
230 return ($path, $vmid, $vtype);
231}
232
4f914e6e
MR
233sub create_base {
234 my ($class, $storeid, $scfg, $volname) = @_;
235
236 my $snap = '__base__';
237
238 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
239 $class->parse_volname($volname);
240
241 die "create_base not possible with base image\n" if $isBase;
242
243 my $newname = $name;
244 $newname =~ s/^vm-/base-/;
245
246 my $newvolname = $basename ? "$basename/$newname" : "$newname";
247
7730694e
DM
248 $class->zfs_delete_lu($scfg, $name);
249 $class->zfs_request($scfg, undef, 'rename', "$scfg->{pool}/$name", "$scfg->{pool}/$newname");
4f914e6e 250
7730694e
DM
251 my $guid = $class->zfs_create_lu($scfg, $newname);
252 $class->zfs_add_lun_mapping_entry($scfg, $newname, $guid);
4f914e6e
MR
253
254 my $running = undef; #fixme : is create_base always offline ?
255
256 $class->volume_snapshot($scfg, $storeid, $newname, $snap, $running);
257
258 return $newvolname;
259}
260
261sub clone_image {
f236eaf8 262 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
4f914e6e 263
1993540b 264 my $name = $class->SUPER::clone_image($scfg, $storeid, $volname, $vmid, $snap);
4f914e6e 265
1993540b
FG
266 # get ZFS dataset name from PVE volname
267 my (undef, $clonedname) = $class->parse_volname($name);
268
269 my $guid = $class->zfs_create_lu($scfg, $clonedname);
270 $class->zfs_add_lun_mapping_entry($scfg, $clonedname, $guid);
4f914e6e
MR
271
272 return $name;
273}
274
275sub alloc_image {
276 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
b3ba95e4 277
9b61bc9e
WL
278 die "unsupported format '$fmt'" if $fmt ne 'raw';
279
280 die "illegal name '$name' - sould be 'vm-$vmid-*'\n"
281 if $name && $name !~ m/^vm-$vmid-/;
282
4eff810d
WL
283 my $volname = $name;
284
9660f716 285 $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid, $fmt) if !$volname;
9b61bc9e 286
4eff810d 287 $class->zfs_create_zvol($scfg, $volname, $size);
b3ba95e4
WL
288
289 my $guid = $class->zfs_create_lu($scfg, $volname);
290 $class->zfs_add_lun_mapping_entry($scfg, $volname, $guid);
4f914e6e 291
b3ba95e4 292 return $volname;
4f914e6e
MR
293}
294
295sub free_image {
296 my ($class, $storeid, $scfg, $volname, $isBase) = @_;
297
298 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
299
7730694e 300 $class->zfs_delete_lu($scfg, $name);
6b5bca68
DM
301
302 eval { $class->zfs_delete_zvol($scfg, $name); };
303 if (my $err = $@) {
7730694e
DM
304 my $guid = $class->zfs_create_lu($scfg, $name);
305 $class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
4f914e6e 306 die $err;
6b5bca68 307 }
4f914e6e
MR
308
309 return undef;
4f914e6e
MR
310}
311
4f914e6e
MR
312sub volume_resize {
313 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
a4034b9f
WL
314
315 my $new_size = $class->SUPER::volume_resize($scfg, $storeid, $volname, $size, $running);
4f914e6e 316
7730694e 317 $class->zfs_resize_lu($scfg, $volname, $new_size);
a4034b9f
WL
318
319 return $new_size;
4f914e6e
MR
320}
321
27a27646
WL
322sub volume_snapshot_delete {
323 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
324
325 $class->zfs_request($scfg, undef, 'destroy', "$scfg->{pool}/$volname\@$snap");
326}
327
4f914e6e
MR
328sub volume_snapshot_rollback {
329 my ($class, $scfg, $storeid, $volname, $snap) = @_;
330
7730694e 331 $class->zfs_delete_lu($scfg, $volname);
4f914e6e 332
281f9587 333 $class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$volname\@$snap");
4f914e6e 334
7730694e 335 $class->zfs_import_lu($scfg, $volname);
4f914e6e 336
7730694e 337 $class->zfs_add_lun_mapping_entry($scfg, $volname);
4f914e6e
MR
338}
339
7118dd91
DM
340sub storage_can_replicate {
341 my ($class, $scfg, $storeid, $format) = @_;
342
343 return 0;
344}
345
4f914e6e
MR
346sub volume_has_feature {
347 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
348
349 my $features = {
6b5bca68
DM
350 snapshot => { current => 1, snap => 1},
351 clone => { base => 1},
352 template => { current => 1},
353 copy => { base => 1, current => 1},
4f914e6e
MR
354 };
355
356 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
6b5bca68 357 $class->parse_volname($volname);
4f914e6e
MR
358
359 my $key = undef;
5332e6c9
DM
360
361 if ($snapname) {
6b5bca68 362 $key = 'snap';
4f914e6e 363 } else {
6b5bca68 364 $key = $isBase ? 'base' : 'current';
4f914e6e 365 }
5332e6c9 366
4f914e6e
MR
367 return 1 if $features->{$feature}->{$key};
368
369 return undef;
370}
371
aefe82ea 372sub volume_snapshot_list {
8b622c2d 373 my ($class, $scfg, $storeid, $volname) = @_;
aefe82ea
WL
374 # return an empty array if dataset does not exist.
375 die "Volume_snapshot_list is not implemented for ZFS over iSCSI.\n";
376}
377
86d47239
WL
378sub activate_storage {
379 my ($class, $storeid, $scfg, $cache) = @_;
02e797b8
WL
380
381 return 1;
382}
383
384sub deactivate_storage {
385 my ($class, $storeid, $scfg, $cache) = @_;
386
387 return 1;
388}
389
390sub activate_volume {
391 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
392
393 die "unable to activate snapshot from remote zfs storage" if $snapname;
394
395 return 1;
396}
397
398sub deactivate_volume {
399 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
400
401 die "unable to deactivate snapshot from remote zfs storage" if $snapname;
402
86d47239
WL
403 return 1;
404}
405
4f914e6e 4061;