From 6cc11c15d369955a723db5d373f30de08b72ddc8 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 27 May 2021 09:09:49 +0200 Subject: [PATCH] rbd: use DeepCopy to create a thick-provisioned clone To create a full-allocated RBD image from a snapshot/clone DeepCopy() can be used. This is needed when the parent of the new volume is thick-provisioner, so that the new volume is independent of the parent and thick-provisioned as well. Signed-off-by: Niels de Vos --- internal/rbd/clone.go | 50 +++++++++++++++++++++++++++++----------- internal/rbd/rbd_util.go | 26 +++++++++++++++++++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/internal/rbd/clone.go b/internal/rbd/clone.go index a38a89c6f..f2297c5ba 100644 --- a/internal/rbd/clone.go +++ b/internal/rbd/clone.go @@ -152,6 +152,8 @@ func (rv *rbdVolume) createCloneFromImage(ctx context.Context, parentVol *rbdVol } defer j.Destroy() + // TODO: if rv exists, delete the image and start over? + err = rv.doSnapClone(ctx, parentVol) if err != nil { return status.Error(codes.Internal, err.Error()) @@ -170,6 +172,19 @@ func (rv *rbdVolume) createCloneFromImage(ctx context.Context, parentVol *rbdVol } } + // TODO: copy thick provision config + thick, err := parentVol.isThickProvisioned() + if err != nil { + return fmt.Errorf("failed checking thick-provisioning of %q: %w", parentVol, err) + } + + if thick { + err = rv.setThickProvisioned() + if err != nil { + return fmt.Errorf("failed mark %q thick-provisioned: %w", rv, err) + } + } + err = j.StoreImageID(ctx, rv.JournalPool, rv.ReservedID, rv.ImageID) if err != nil { util.ErrorLog(ctx, "failed to store volume %s: %v", rv, err) @@ -221,19 +236,28 @@ func (rv *rbdVolume) doSnapClone(ctx context.Context, parentVol *rbdVolume) erro } } }() - // flatten clone - errFlatten = tempClone.flattenRbdImage(ctx, rv.conn.Creds, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth) - if errFlatten != nil { - return errFlatten - } - // create snap of temp clone from temporary cloned image - // create final clone - // delete snap of temp clone - errClone = createRBDClone(ctx, tempClone, rv, cloneSnap, rv.conn.Creds) - if errClone != nil { - // set errFlatten error to cleanup temporary snapshot and temporary clone - errFlatten = errors.New("failed to create user requested cloned image") - return errClone + + if rv.ThickProvision { + err = tempClone.DeepCopy(rv) + if err != nil { + return fmt.Errorf("failed to deep copy %q into %q: %w", parentVol, rv, err) + } + } else { + // flatten clone + errFlatten = tempClone.flattenRbdImage(ctx, rv.conn.Creds, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth) + if errFlatten != nil { + return errFlatten + } + + // create snap of temp clone from temporary cloned image + // create final clone + // delete snap of temp clone + errClone = createRBDClone(ctx, tempClone, rv, cloneSnap, rv.conn.Creds) + if errClone != nil { + // set errFlatten error to cleanup temporary snapshot and temporary clone + errFlatten = errors.New("failed to create user requested cloned image") + return errClone + } } return nil diff --git a/internal/rbd/rbd_util.go b/internal/rbd/rbd_util.go index 039109950..afac474c0 100644 --- a/internal/rbd/rbd_util.go +++ b/internal/rbd/rbd_util.go @@ -1508,6 +1508,32 @@ func (rv *rbdVolume) RepairThickProvision() error { return nil } +// DeepCopy creates an independent image (dest) from the source image. This +// process may take some time when the image is large. +func (rv *rbdVolume) DeepCopy(dest *rbdVolume) error { + opts := librbd.NewRbdImageOptions() + defer opts.Destroy() + + // when doing DeepCopy, also flatten the new image + err := opts.SetUint64(librbd.ImageOptionFlatten, 1) + if err != nil { + return err + } + + err = dest.openIoctx() + if err != nil { + return err + } + + image, err := rv.open() + if err != nil { + return err + } + defer image.Close() + + return image.DeepCopy(dest.ioctx, dest.RbdImageName, opts) +} + func (rv *rbdVolume) listSnapshots() ([]librbd.SnapInfo, error) { image, err := rv.open() if err != nil {