package PVE::Storage::Custom::scstZFSPlugin; eval { require 'PVE/Storage/Custom/Shared/ZFSPluginPlus.pm'; }; use PVE::Storage::Custom::LunCmd::SCST; use Data::Dumper; use PVE::Tools qw(run_command); # inherit on the ZFSPlugin #use base qw(PVE::Storage::ZFSPlugin); @ISA = qw(PVE::Storage::Custom::Shared::ZFSPluginPlus); my @ssh_opts = ('-o', 'BatchMode=yes'); my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts); my $id_rsa_path = '/etc/pve/priv/zfs'; # plugin configuration sub api { return 1; } sub type { return 'scstzfs'; } sub plugindata { return { content => [ { images => 1 }, {images => 1} ] }; } sub properties { return { nowritecache_scst => { description => "disable write cache on the target", type => 'boolean' }, chap_user => { type => 'string' }, chap_pass => { type => 'string' }, volume_prefix => { type => 'string' }, port => { type => 'int' }, ini_group => { type => 'string' }, esos => { type => 'int' } } } sub options { return { nodes => { optional => 1 }, disable => { optional => 1 }, portal => { fixed => 1 }, target => { fixed => 1 }, iscsiprovider => {fixed => 1}, pool => { fixed => 1 }, blocksize => { fixed => 1 }, nowritecache => { optional => 1 }, sparse => { optional => 1 }, comstar_hg => { optional => 1 }, comstar_tg => { optional => 1 }, content => { optional => 1 }, shared => { fixed => 1 }, transport => { optional => 1, default => 'iscsi' }, #iscsi or rdma chap_user => { optional => 1 }, chap_pass => { optional => 1 }, port => { optional => 1 }, volume_prefix => { optional => 1, default => '' }, #prefixes created volume-names - allows to share pool for multiple clusters ini_group => { optional => 1, default => ''}, esos => { optional=>1, default => 0 } }; } my $lun_cmds = { create_lu => 1, delete_lu => 1, import_lu => 1, modify_lu => 1, add_view => 1, list_view => 1, list_lu => 1, }; # SCST-specifics my $zfs_get_base = sub { my ($scfg) = @_; return PVE::Storage::Custom::LunCmd::SCST::get_base; }; sub zfs_get_base { my ($scfg) = @_; return $zfs_get_base->($scfg); } sub zfs_request { my ($class, $scfg, $timeout, $method, @params) = @_; $timeout = PVE::RPCEnvironment::is_worker() ? 60*60 : 10 if !$timeout; my $msg = ''; if ($lun_cmds->{$method}) { $msg = PVE::Storage::Custom::LunCmd::SCST::run_lun_command($scfg, $timeout, $method, @params); } else { my $target = 'root@' . $scfg->{portal}; my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target]; if ($method eq 'zpool_list') { push @$cmd, 'zpool', 'list'; } else { push @$cmd, 'zfs', $method; } push @$cmd, @params; my $output = sub { my $line = shift; $msg .= "$line\n"; }; # print Dumper($cmd); run_command($cmd, outfunc => $output, timeout => $timeout); } return $msg; } # we want to allow copy of snap, currently dies at line 377 in ZFSPlugin.pm sub volume_has_feature { 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, snap=>1}, #added snap }; my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) = $class->parse_volname($volname); my $key = undef; if ($snapname) { $key = 'snap'; } else { $key = $isBase ? 'base' : 'current'; } return 1 if $features->{$feature}->{$key}; return undef; } # # + added port, chap-auth and transport (iser) # + allow access to materialized snapshots sub path { my ($class, $scfg, $volname, $storeid, $snapname) = @_; # die "direct access to snapshots not implemented" # if defined($snapname); my ($vtype, $name, $vmid) = $class->parse_volname($volname); if(defined($snapname)) { $name = $class->materialize_snapshot($scfg,$volname,$snapname); } my $target = $scfg->{target}; my $portal = $scfg->{portal}; my $guid = $class->zfs_get_lu_name($scfg, $name); my $lun = $class->zfs_get_lun_number($scfg, $guid); my $transport = $scfg->{transport}; if($transport eq 'rdma') { $transport = 'iser'; } else { $transport = 'iscsi'; } my $authString = $scfg->{chap_user} && $scfg->{chap_pass} ? "$scfg->{chap_user}%$scfg->{chap_pass}@" : ''; my $port = $scfg->{port} ? ":$scfg->{port}" : ':3260'; #qemu-image insists on port ! my $path = "$transport://$authString$portal$port/$target/$lun"; return ($path, $vmid, $vtype); } # Other parts of Storage implementation is identical to ZFSPlugin and therefore not changed 1;