From 767f912db121c361b6d23a11ddaf8a5aab1fb1f3 Mon Sep 17 00:00:00 2001 From: Pawan Date: Tue, 10 Nov 2020 12:21:28 +0530 Subject: [PATCH 1/2] feat(clone): add support for creating the Clone from volume as datasource We can now create the Clone from pvc directly ``` kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pvc-clone spec: storageClassName: openebs-snap dataSource: name: pvc-snap kind: PersistentVolumeClaim accessModes: - ReadWriteOnce resources: requests: storage: 4Gi ``` The ZFS_LocalPV driver will create one internal snapshot of the name same as the new volume name and will create a clone out of it. Also, while destroying the volume the driver will take care of deleting the created snapshot for the clone. Signed-off-by: Pawan --- changelogs/unreleased/234-pawanpraka1 | 1 + pkg/driver/controller.go | 57 +++++++++++++++++++++++++-- pkg/zfs/zfs_util.go | 41 +++++++++++++++++++ 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/234-pawanpraka1 diff --git a/changelogs/unreleased/234-pawanpraka1 b/changelogs/unreleased/234-pawanpraka1 new file mode 100644 index 000000000..a8bc44a28 --- /dev/null +++ b/changelogs/unreleased/234-pawanpraka1 @@ -0,0 +1 @@ +add support for creating the Clone from volume as datasource diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go index 69c815551..66816699e 100644 --- a/pkg/driver/controller.go +++ b/pkg/driver/controller.go @@ -165,8 +165,56 @@ func CreateZFSVolume(req *csi.CreateVolumeRequest) (string, error) { return selected, nil } -// CreateZFSClone create a clone of zfs volume -func CreateZFSClone(req *csi.CreateVolumeRequest, snapshot string) (string, error) { +// CreateVolClone creates the clone from a volume +func CreateVolClone(req *csi.CreateVolumeRequest, srcVol string) (string, error) { + volName := req.GetName() + parameters := req.GetParameters() + // lower case keys, cf CreateZFSVolume() + pool := helpers.GetInsensitiveParameter(¶meters, "poolname") + size := getRoundedCapacity(req.GetCapacityRange().RequiredBytes) + volsize := strconv.FormatInt(int64(size), 10) + + vol, err := zfs.GetZFSVolume(srcVol) + if err != nil { + return "", status.Error(codes.NotFound, err.Error()) + } + + if vol.Spec.PoolName != pool { + return "", status.Errorf(codes.Internal, + "clone: different pool src pool %s dst pool %s", + vol.Spec.PoolName, pool) + } + + if vol.Spec.Capacity != volsize { + return "", status.Error(codes.Internal, "clone: volume size is not matching") + } + + selected := vol.Spec.OwnerNodeID + + labels := map[string]string{zfs.ZFSVolKey: vol.Name} + + // create the clone from the source volume + + volObj, err := volbuilder.NewBuilder(). + WithName(volName). + WithVolumeStatus(zfs.ZFSStatusPending). + WithLabels(labels).Build() + + volObj.Spec = vol.Spec + // use the snapshot name same as new volname + volObj.Spec.SnapName = vol.Name + "@" + volName + + err = zfs.ProvisionVolume(volObj) + if err != nil { + return "", status.Errorf(codes.Internal, + "clone: not able to provision the volume %s", err.Error()) + } + + return selected, nil +} + +// CreateSnapClone creates the clone from a snapshot +func CreateSnapClone(req *csi.CreateVolumeRequest, snapshot string) (string, error) { volName := req.GetName() parameters := req.GetParameters() @@ -243,7 +291,10 @@ func (cs *controller) CreateVolume( if contentSource != nil && contentSource.GetSnapshot() != nil { snapshotID := contentSource.GetSnapshot().GetSnapshotId() - selected, err = CreateZFSClone(req, snapshotID) + selected, err = CreateSnapClone(req, snapshotID) + } else if contentSource != nil && contentSource.GetVolume() != nil { + srcVol := contentSource.GetVolume().GetVolumeId() + selected, err = CreateVolClone(req, srcVol) } else { selected, err = CreateZFSVolume(req) } diff --git a/pkg/zfs/zfs_util.go b/pkg/zfs/zfs_util.go index 213394d0b..6b2ed6530 100644 --- a/pkg/zfs/zfs_util.go +++ b/pkg/zfs/zfs_util.go @@ -401,6 +401,26 @@ func CreateVolume(vol *apis.ZFSVolume) error { func CreateClone(vol *apis.ZFSVolume) error { volume := vol.Spec.PoolName + "/" + vol.Name + if srcVol, ok := vol.Labels[ZFSVolKey]; ok { + // datasource is volume, create the snapshot first + snap := &apis.ZFSSnapshot{} + snap.Name = vol.Name // use volname as snapname + snap.Spec = vol.Spec + // add src vol name + snap.Labels = map[string]string{ZFSVolKey: srcVol} + + klog.Infof("creating snapshot %s@%s for the clone %s", srcVol, snap.Name, volume) + + err := CreateSnapshot(snap) + + if err != nil { + klog.Errorf( + "zfs: could not create snapshot for the clone vol %s snap %s err %v", volume, snap.Name, err, + ) + return err + } + } + if err := getVolume(volume); err != nil { var args []string args = buildCloneCreateArgs(vol) @@ -580,6 +600,27 @@ func DestroyVolume(vol *apis.ZFSVolume) error { ) return err } + + if srcVol, ok := vol.Labels[ZFSVolKey]; ok { + // datasource is volume, delete the dependent snapshot + snap := &apis.ZFSSnapshot{} + snap.Name = vol.Name // snapname is same as volname + snap.Spec = vol.Spec + // add src vol name + snap.Labels = map[string]string{ZFSVolKey: srcVol} + + klog.Infof("destroying snapshot %s@%s for the clone %s", srcVol, snap.Name, volume) + + err := DestroySnapshot(snap) + + if err != nil { + // no need to reconcile as volume has already been deleted + klog.Errorf( + "zfs: could not destroy snapshot for the clone vol %s snap %s err %v", volume, snap.Name, err, + ) + } + } + klog.Infof("destroyed volume %s", volume) return nil From 391652174e3a22de297951913f96fe4537d9f9f9 Mon Sep 17 00:00:00 2001 From: Pawan Date: Wed, 11 Nov 2020 10:56:39 +0530 Subject: [PATCH 2/2] review comment: change the key to ZFSSrcVolKey Signed-off-by: Pawan --- pkg/driver/controller.go | 2 +- pkg/zfs/volume.go | 2 ++ pkg/zfs/zfs_util.go | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go index 66816699e..76d810b6a 100644 --- a/pkg/driver/controller.go +++ b/pkg/driver/controller.go @@ -191,7 +191,7 @@ func CreateVolClone(req *csi.CreateVolumeRequest, srcVol string) (string, error) selected := vol.Spec.OwnerNodeID - labels := map[string]string{zfs.ZFSVolKey: vol.Name} + labels := map[string]string{zfs.ZFSSrcVolKey: vol.Name} // create the clone from the source volume diff --git a/pkg/zfs/volume.go b/pkg/zfs/volume.go index eae636ea2..cb80ac330 100644 --- a/pkg/zfs/volume.go +++ b/pkg/zfs/volume.go @@ -39,6 +39,8 @@ const ( ZFSFinalizer string = "zfs.openebs.io/finalizer" // ZFSVolKey for the ZfsSnapshot CR to store Persistence Volume name ZFSVolKey string = "openebs.io/persistent-volume" + // ZFSSrcVolKey key for the source Volume name + ZFSSrcVolKey string = "openebs.io/source-volume" // PoolNameKey is key for ZFS pool name PoolNameKey string = "openebs.io/poolname" // ZFSNodeKey will be used to insert Label in ZfsVolume CR diff --git a/pkg/zfs/zfs_util.go b/pkg/zfs/zfs_util.go index 6b2ed6530..4e6942ac4 100644 --- a/pkg/zfs/zfs_util.go +++ b/pkg/zfs/zfs_util.go @@ -401,7 +401,7 @@ func CreateVolume(vol *apis.ZFSVolume) error { func CreateClone(vol *apis.ZFSVolume) error { volume := vol.Spec.PoolName + "/" + vol.Name - if srcVol, ok := vol.Labels[ZFSVolKey]; ok { + if srcVol, ok := vol.Labels[ZFSSrcVolKey]; ok { // datasource is volume, create the snapshot first snap := &apis.ZFSSnapshot{} snap.Name = vol.Name // use volname as snapname @@ -601,7 +601,7 @@ func DestroyVolume(vol *apis.ZFSVolume) error { return err } - if srcVol, ok := vol.Labels[ZFSVolKey]; ok { + if srcVol, ok := vol.Labels[ZFSSrcVolKey]; ok { // datasource is volume, delete the dependent snapshot snap := &apis.ZFSSnapshot{} snap.Name = vol.Name // snapname is same as volname