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