| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- package PVE::Storage::Custom::Shared::ZFSPluginPlus;
- 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::ZFSPlugin);
- my @ssh_opts = ('-o', 'BatchMode=yes');
- my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
- my $id_rsa_path = '/etc/pve/priv/zfs';
- # plugin configuration
- # 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 = materialize_snapshot($class,$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}" : '';
- my $path = "$transport://$authString$portal$port/$target/$lun";
- return ($path, $vmid, $vtype);
- }
- my $zfs_get_base = sub {
- my ($scfg) = @_;
- if ($scfg->{iscsiprovider} eq 'comstar') {
- return PVE::Storage::LunCmd::Comstar::get_base;
- } elsif ($scfg->{iscsiprovider} eq 'istgt') {
- return PVE::Storage::LunCmd::Istgt::get_base;
- } elsif ($scfg->{iscsiprovider} eq 'iet') {
- return PVE::Storage::LunCmd::Iet::get_base;
- } elsif ($scfg->{iscsiprovider} eq 'scstzfs') {
- return PVE::Storage::Custom::LunCmd::SCST::get_base;
- } elsif ($scfg->{iscsiprovider} eq 'freenas') {
- return PVE::Storage::Custom::LunCmd::FreeNas::get_base;
- } else {
- # $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
- }
- };
- sub zfs_get_lu_name {
- my ($class, $scfg, $zvol) = @_;
- my $base = $zfs_get_base->($scfg);
- my $object = ($zvol =~ /^.+\/.+/) ? "$base/$zvol" : "$base/$scfg->{pool}/$zvol";
- my $lu_name = $class->zfs_request($scfg, undef, 'list_lu', $object);
- return $lu_name if $lu_name;
- die "Could not find lu_name for zvol $zvol base $base object $object";
- }
- #
- # Allow Activation of Snap via ZFS send/receive
- #
- sub activate_volume {
- my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
- if($snapname) {
- materialize_snapshot($class,$scfg,$volname,$snapname);
- }
- return 1;
- }
- #
- # Allow Deactivation of Snap-Volume
- #
- sub deactivate_volume {
- my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
- if($snapname) {
- $class->dematerialize_snapshot($scfg,$volname,$snapname);
- }
- return 1;
- }
- #
- # materializes a snapshot of volname by zfs send/revc and creates an iscsi LUN
- #
- sub materialize_snapshot {
- my ($class, $scfg, $volname, $snapshot) = @_;
- my $srcVol = get_snapshot_path($class,$scfg,$volname,$snapshot);
- my $snapTmpVol = get_snapshot_tmp_name($class,$scfg,$volname,$snapshot);
- my $base = $class->zfs_get_base($scfg);
- my $object = ($zvol =~ /^.+\/.+/) ? "$base/$snapTmpVol" : "$base/$scfg->{pool}/$snapTmpVol";
- my $lu_name = $class->zfs_request($scfg, undef, 'list_lu', $object);
- if ($lu_name) {
- return $snapTmpVol;
- }
- # create the clone if not present
- my $cloneExists = $class->zfs_volume_exists($scfg,"$scfg->{pool}/$snapTmpVol");
- if(!$cloneExists) {
- $class->zfs_request($scfg, undef,
- 'clone', $srcVol, "$scfg->{pool}/$snapTmpVol"
- );
- }
- # and add it to the iscsi-target
- my $guid = $class->zfs_create_lu($scfg, $snapTmpVol);
- $class->zfs_add_lun_mapping_entry($scfg, $snapTmpVol, $guid);
- return $snapTmpVol;
- }
- #
- # removes lun and zvol for snapshot of volname
- #
- sub dematerialize_snapshot {
- my ($class, $scfg, $volname, $snapname) = @_;
- my $snapTmpVol = $class->get_snapshot_tmp_name($scfg,$volname,$snapname);
- # remove iscsi-target if present
- my $base = $class->zfs_get_base($scfg);
- my $object = ($zvol =~ /^.+\/.+/) ? "$base/$snapTmpVol" : "$base/$scfg->{pool}/$snapTmpVol";
- my $lu_name = $class->zfs_request($scfg, undef, 'list_lu', $object);
- if ($lu_name) {
- $class->free_image(undef, $scfg, $snapTmpVol, undef);
- }
- # destroy clone
- $class->zfs_request($scfg,undef,
- 'destroy', "$scfg->{pool}/$snapTmpVol"
- );
- return 1;
- }
- #
- # Create a copy of snapshot from srcVol and provide as dstVol
- #
- sub volume_snapshot_copy {
- my($class, $scfg, $srcVol, $snapshot, $dstVol) = @_;
- my $materializedPath = $class->materialize_snapshot($scfg, $srcVol, $snapshot);
- # remove iscsi-target of tmp-snap when present
- my $src_guid = $class->zfs_get_lu_name($scfg, $materializedPath);
- if( $src_guid ) {
- $class->zfs_delete_lu($scfg, $materializedPath);
- }
- # free the image in case it is exported
- my $dst_guid = $class->zfs_get_lu_name($scfg, $dstVol);
- if( $dst_guid ) {
- $class->zfs_delete_lu($scfg, $dstVol);
- }
- # zfs-destroy a present volume
- $class->zfs_request($scfg, undef,
- 'destroy','-r',"$scfg->{pool}/$dstVol"
- );
- # create current snap on clone
- $class->zfs_request($scfg,undef,
- 'snapshot',"$scfg->{pool}/$materializedPath\@now"
- );
- # zfs send/rcv from snap of clone
- $class->zfs_request($scfg, 86400,
- "send", '-v', "$scfg->{pool}/$materializedPath\@now", '|',
- 'zfs', 'recv', '-F', "$scfg->{pool}/$dstVol"
- );
- # rollback to @now on dstVol
- $class->zfs_request($scfg, undef,
- 'rollback', "$scfg->{pool}/$dstVol\@now"
- );
- # destroy snap @now on dstVol
- $class->zfs_request($scfg,undef,
- 'destroy', "$scfg->{pool}/$dstVol\@now"
- );
- # destroy tmp snap @now on clone
- $class->zfs_request($scfg,undef,
- 'destroy', "$scfg->{pool}/$materializedPath\@now"
- );
- # attach iscsi-target for dstVol
- my ($vtype, $dst_name, $vmid) = $class->parse_volname($dstVol);
- my $dst_guid = $class->zfs_create_lu($scfg, $dst_name);
- $class->zfs_add_lun_mapping_entry($scfg, $dst_name, $dst_guid);
- }
- sub volume_copy {
- my($class, $scfg, $srcVol, $dstVol) = @_;
- my $snap = 'volume-copy-base-tmp';
- # test if tmp-snapshot exists and should be deleted
- if ( $class->volume_has_snapshot($scfg, "$scfg->{pool}/$srcVol", $snap) ) {
- $class->zfs_request($scfg, undef, 'destroy','-R', "$scfg->{pool}/$srcVol\@$snap");
- }
- # create a tmp snap from base
- $class->zfs_request($scfg, undef, 'snapshot', "$scfg->{pool}/$srcVol\@$snap");
- # volume_snapshot_copy
- $class->volume_snapshot_copy($scfg,$srcVol,$snap,$dstVol);
- # remove the tmp snapshot, including the clone (-R)
- $class->zfs_request($scfg, undef, 'destroy','-R', "$scfg->{pool}/$srcVol\@$snap");
- }
- #
- # Test if snapshot exists in volume
- #
- sub volume_has_snapshot {
- my ($class, $scfg, $volname, $snapname) = @_;
- my $fullSnapName = "$volname\@$snapname";
- my @params = ('-t', 'snapshot', '-o', 'name', '-s', 'creation');
- my $text = $class->zfs_request($scfg, undef, 'list', @params);
- my @snapshots = split(/\n/, $text);
- foreach (@snapshots) {
- if( $_ eq $fullSnapName) {
- return 1;
- }
- }
- }
- #
- # test if a zvol exists
- #
- sub zfs_volume_exists {
- my ($class, $scfg, $volname) = @_;
- my $volumes_text = $class->zfs_request($scfg, 10,
- 'list', '-o', 'name', '-t', 'volume', '-Hr'
- );
- my @lines = split /\n/, $volumes_text;
- foreach my $zvol (@lines) {
- return 1 if $zvol eq $volname;
- }
- }
- #
- # creates tmp-name for zvol a snapshot will be / has been created for volname
- #
- sub get_snapshot_tmp_name {
- my ($class, $scfg, $volname, $snapshot) = @_;
- return $volname.'-clone-at-'.$snapshot;
- }
- #
- # returns zvol name of snapshot for volname
- #
- sub get_snapshot_path {
- my ($class, $scfg, $volname, $snapshot) = @_;
- return "$scfg->{pool}/$volname\@$snapshot";
- }
- # Other parts of Storage implementation is identical to ZFSPlugin and therefore not changed
- 1;
|