cleanup: remove thick provisioning code

This commit removes the thick provisioning
code as thick provisioning is deprecated in
cephcsi 3.5.0.

fixes: #2795

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2022-01-25 13:07:15 +05:30
committed by mergify[bot]
parent 4ee4fdfebd
commit 28fef9b379
14 changed files with 53 additions and 908 deletions

View File

@ -167,13 +167,6 @@ func (rv *rbdVolume) createCloneFromImage(ctx context.Context, parentVol *rbdVol
}
}
if rv.ThickProvision {
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 {
log.ErrorLog(ctx, "failed to store volume %s: %v", rv, err)
@ -236,39 +229,27 @@ func (rv *rbdVolume) doSnapClone(ctx context.Context, parentVol *rbdVolume) erro
}
}()
if rv.ThickProvision {
err = tempClone.DeepCopy(&rv.rbdImage)
if err != nil {
return fmt.Errorf("failed to deep copy %q into %q: %w", parentVol, rv, err)
}
} else {
// flatten clone
errFlatten = tempClone.flattenRbdImage(ctx, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth)
if errFlatten != nil {
return errFlatten
}
// flatten clone
errFlatten = tempClone.flattenRbdImage(ctx, 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)
if errClone != nil {
// set errFlatten error to cleanup temporary snapshot and temporary clone
errFlatten = errors.New("failed to create user requested cloned image")
// create snap of temp clone from temporary cloned image
// create final clone
// delete snap of temp clone
errClone = createRBDClone(ctx, tempClone, rv, cloneSnap)
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 errClone
}
return nil
}
func (rv *rbdVolume) flattenCloneImage(ctx context.Context) error {
if rv.ThickProvision {
// thick-provisioned images do not need flattening
return nil
}
tempClone := rv.generateTempClone()
// reducing the limit for cloned images to make sure the limit is in range,
// If the intermediate clone reaches the depth we may need to return ABORT

View File

@ -19,8 +19,6 @@ package rbd
import (
"context"
"errors"
"fmt"
"strconv"
csicommon "github.com/ceph/ceph-csi/internal/csi-common"
"github.com/ceph/ceph-csi/internal/util"
@ -132,8 +130,6 @@ func (cs *ControllerServer) parseVolCreateRequest(
return nil, status.Error(codes.InvalidArgument, err.Error())
}
rbdVol.ThickProvision = isThickProvisionRequest(req.GetParameters())
rbdVol.RequestName = req.GetName()
// Volume Size - Default is 1 GiB
@ -211,11 +207,6 @@ func checkValidCreateVolumeRequest(rbdVol, parentVol *rbdVolume, rbdSnap *rbdSna
return status.Errorf(codes.InvalidArgument, "cannot restore from snapshot %s: %s", rbdSnap, err.Error())
}
err = rbdSnap.isCompatibleThickProvision(rbdVol)
if err != nil {
return status.Errorf(codes.InvalidArgument, "cannot restore from snapshot %s: %s", rbdSnap, err.Error())
}
err = rbdSnap.isCompabitableClone(&rbdVol.rbdImage)
if err != nil {
return status.Errorf(codes.InvalidArgument, "cannot restore from snapshot %s: %s", rbdSnap, err.Error())
@ -227,11 +218,6 @@ func checkValidCreateVolumeRequest(rbdVol, parentVol *rbdVolume, rbdSnap *rbdSna
return status.Errorf(codes.InvalidArgument, "cannot clone from volume %s: %s", parentVol, err.Error())
}
err = parentVol.isCompatibleThickProvision(rbdVol)
if err != nil {
return status.Errorf(codes.InvalidArgument, "cannot clone from volume %s: %s", parentVol, err.Error())
}
err = parentVol.isCompabitableClone(&rbdVol.rbdImage)
if err != nil {
return status.Errorf(codes.InvalidArgument, "cannot clone from volume %s: %s", parentVol, err.Error())
@ -287,7 +273,7 @@ func (cs *ControllerServer) CreateVolume(
if err != nil {
return nil, getGRPCErrorForCreateVolume(err)
} else if found {
return cs.repairExistingVolume(ctx, req, cr, rbdVol, parentVol, rbdSnap)
return cs.repairExistingVolume(ctx, req, cr, rbdVol, rbdSnap)
}
err = checkValidCreateVolumeRequest(rbdVol, parentVol, rbdSnap)
@ -352,50 +338,12 @@ func flattenParentImage(ctx context.Context, rbdVol *rbdVolume, cr *util.Credent
// that the state is corrected to what was requested. It is needed to call this
// when the process of creating a volume was interrupted.
func (cs *ControllerServer) repairExistingVolume(ctx context.Context, req *csi.CreateVolumeRequest,
cr *util.Credentials, rbdVol, parentVol *rbdVolume, rbdSnap *rbdSnapshot) (*csi.CreateVolumeResponse, error) {
cr *util.Credentials, rbdVol *rbdVolume, rbdSnap *rbdSnapshot) (*csi.CreateVolumeResponse, error) {
vcs := req.GetVolumeContentSource()
switch {
// normal CreateVolume without VolumeContentSource
case vcs == nil:
// continue/restart allocating the volume in case it
// should be thick-provisioned
if isThickProvisionRequest(req.GetParameters()) {
err := rbdVol.RepairThickProvision()
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
}
// rbdVol is a restore from snapshot, rbdSnap is passed
case vcs.GetSnapshot() != nil:
// When restoring of a thick-provisioned volume was happening,
// the image should be marked as thick-provisioned, unless it
// was aborted in flight. In order to restart the
// thick-restoring, delete the volume and let the caller retry
// from the start.
if isThickProvisionRequest(req.GetParameters()) {
thick, err := rbdVol.isThickProvisioned()
if err != nil {
return nil, status.Errorf(
codes.Aborted,
"failed to verify thick-provisioned volume %q: %s",
rbdVol,
err)
} else if !thick {
err = rbdVol.deleteImage(ctx)
if err != nil {
return nil, status.Errorf(codes.Aborted, "failed to remove partially cloned volume %q: %s", rbdVol, err)
}
err = undoVolReservation(ctx, rbdVol, cr)
if err != nil {
return nil, status.Errorf(codes.Aborted, "failed to remove volume %q from journal: %s", rbdVol, err)
}
return nil, status.Errorf(
codes.Aborted,
"restoring thick-provisioned volume %q has been interrupted, please retry", rbdVol)
}
}
// restore from snapshot implies rbdSnap != nil
// check if image depth is reached limit and requires flatten
err := checkFlatten(ctx, rbdVol, cr)
@ -418,23 +366,6 @@ func (cs *ControllerServer) repairExistingVolume(ctx context.Context, req *csi.C
// rbdVol is a clone from parentVol
case vcs.GetVolume() != nil:
// When cloning into a thick-provisioned volume was happening,
// the image should be marked as thick-provisioned, unless it
// was aborted in flight. In order to restart the
// thick-cloning, delete the volume and undo the reservation in
// the journal to let the caller retry from the start.
if isThickProvisionRequest(req.GetParameters()) {
thick, err := rbdVol.isThickProvisioned()
if err != nil {
return nil, status.Errorf(
codes.Internal,
"failed to verify thick-provisioned volume %q: %s",
rbdVol,
err)
} else if !thick {
return nil, cleanupThickClone(ctx, parentVol, rbdVol, rbdSnap, cr)
}
}
// expand the image if the requested size is greater than the current size
err := rbdVol.expand()
if err != nil {
@ -447,26 +378,6 @@ func (cs *ControllerServer) repairExistingVolume(ctx context.Context, req *csi.C
return buildCreateVolumeResponse(req, rbdVol), nil
}
// cleanupThickClone will delete the snapshot and volume and undo the reservation.
func cleanupThickClone(ctx context.Context,
rbdVol,
parentVol *rbdVolume,
rbdSnap *rbdSnapshot,
cr *util.Credentials) error {
err := cleanUpSnapshot(ctx, parentVol, rbdSnap, rbdVol)
if err != nil {
return status.Errorf(codes.Internal, "failed to remove partially cloned volume %q: %s", rbdVol, err)
}
err = undoVolReservation(ctx, rbdVol, cr)
if err != nil {
return status.Errorf(codes.Internal, "failed to remove volume %q from journal: %s", rbdVol, err)
}
return status.Errorf(
codes.Internal,
"cloning thick-provisioned volume %q has been interrupted, please retry", rbdVol)
}
// check snapshots on the rbd image, as we have limit from krbd that an image
// cannot have more than 510 snapshot at a given point of time. If the
// snapshots are more than the `maxSnapshotsOnImage` Add a task to flatten all
@ -474,11 +385,6 @@ func cleanupThickClone(ctx context.Context,
// are more than the `minSnapshotOnImage` Add a task to flatten all the
// temporary cloned images.
func flattenTemporaryClonedImages(ctx context.Context, rbdVol *rbdVolume, cr *util.Credentials) error {
if rbdVol.ThickProvision {
// thick-provisioned images do not need flattening
return nil
}
snaps, err := rbdVol.listSnapshots()
if err != nil {
if errors.Is(err, ErrImageNotFound) {
@ -592,27 +498,12 @@ func (cs *ControllerServer) createVolumeFromSnapshot(
// as we are operating on single cluster reuse the connection
parentVol.conn = rbdVol.conn.Copy()
if rbdVol.ThickProvision {
err = parentVol.DeepCopy(&rbdVol.rbdImage)
if err != nil {
return status.Errorf(codes.Internal, "failed to deep copy %q into %q: %v", parentVol, rbdVol, err)
}
err = rbdVol.setThickProvisioned()
if err != nil {
return status.Errorf(codes.Internal, "failed to mark %q thick-provisioned: %s", rbdVol, err)
}
err = parentVol.copyEncryptionConfig(&rbdVol.rbdImage, true)
if err != nil {
return status.Errorf(codes.Internal, err.Error())
}
} else {
// create clone image and delete snapshot
err = rbdVol.cloneRbdImageFromSnapshot(ctx, rbdSnap, parentVol)
if err != nil {
log.ErrorLog(ctx, "failed to clone rbd image %s from snapshot %s: %v", rbdVol, rbdSnap, err)
// create clone image and delete snapshot
err = rbdVol.cloneRbdImageFromSnapshot(ctx, rbdSnap, parentVol)
if err != nil {
log.ErrorLog(ctx, "failed to clone rbd image %s from snapshot %s: %v", rbdVol, rbdSnap, err)
return err
}
return err
}
log.DebugLog(ctx, "create volume %s from snapshot %s", rbdVol, rbdSnap)
@ -1136,31 +1027,6 @@ func cloneFromSnapshot(
}
}
// The clone image created during CreateSnapshot has to be marked as thick.
// As snapshot and volume both are independent we cannot depend on the
// parent volume of the clone to check thick provision during CreateVolume
// from snapshot operation because the parent volume can be deleted anytime
// after snapshot is created.
// TODO: copy thick provision config
thick, err := rbdVol.isThickProvisioned()
if err != nil {
return nil, status.Errorf(codes.Internal, "failed checking thick-provisioning of %q: %s", rbdVol, err)
}
if thick {
// check the thick metadata is already set on the clone image.
thick, err = vol.isThickProvisioned()
if err != nil {
return nil, status.Errorf(codes.Internal, "failed checking thick-provisioning of %q: %s", vol, err)
}
if !thick {
err = vol.setThickProvisioned()
if err != nil {
return nil, status.Errorf(codes.Internal, "failed mark %q thick-provisioned: %s", vol, err)
}
}
}
err = vol.flattenRbdImage(ctx, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth)
if errors.Is(err, ErrFlattenInProgress) {
// if flattening is in progress, return error and do not cleanup
@ -1258,30 +1124,13 @@ func (cs *ControllerServer) doSnapshotClone(
}
}
// The clone image created during CreateSnapshot has to be marked as thick.
// As snapshot and volume both are independent we cannot depend on the
// parent volume of the clone to check thick provision during CreateVolume
// from snapshot operation because the parent volume can be deleted anytime
// after snapshot is created.
thick, err := parentVol.isThickProvisioned()
err = cloneRbd.createSnapshot(ctx, rbdSnap)
if err != nil {
return nil, fmt.Errorf("failed checking thick-provisioning of %q: %w", parentVol, err)
}
// update rbd image name for logging
rbdSnap.RbdImageName = cloneRbd.RbdImageName
log.ErrorLog(ctx, "failed to create snapshot %s: %v", rbdSnap, err)
if thick {
err = cloneRbd.setThickProvisioned()
if err != nil {
return nil, fmt.Errorf("failed mark %q thick-provisioned: %w", cloneRbd, err)
}
} else {
err = cloneRbd.createSnapshot(ctx, rbdSnap)
if err != nil {
// update rbd image name for logging
rbdSnap.RbdImageName = cloneRbd.RbdImageName
log.ErrorLog(ctx, "failed to create snapshot %s: %v", rbdSnap, err)
return cloneRbd, err
}
return cloneRbd, err
}
err = cloneRbd.getImageID()
@ -1542,31 +1391,3 @@ func (cs *ControllerServer) ControllerExpandVolume(
NodeExpansionRequired: nodeExpansion,
}, nil
}
// logThickProvisioningDeprecation makes sure the deprecation warning about
// thick-provisining is logged only once.
var logThickProvisioningDeprecation = true
// isThickProvisionRequest returns true in case the request contains the
// `thickProvision` option set to `true`.
func isThickProvisionRequest(parameters map[string]string) bool {
tp := "thickProvision"
thick, ok := parameters[tp]
if !ok || thick == "" {
return false
}
thickBool, err := strconv.ParseBool(thick)
if err != nil {
return false
}
if logThickProvisioningDeprecation {
log.WarningLogMsg("thick-provisioning is deprecated and will " +
"be removed in a future release")
logThickProvisioningDeprecation = false
}
return thickBool
}

View File

@ -1,69 +0,0 @@
/*
Copyright 2021 The Ceph-CSI Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rbd
import (
"testing"
"github.com/container-storage-interface/spec/lib/go/csi"
)
func TestIsThickProvisionRequest(t *testing.T) {
t.Parallel()
req := &csi.CreateVolumeRequest{
Name: "fake",
Parameters: map[string]string{
"unkownOption": "not-set",
},
}
// pass disabled/invalid values for "thickProvision" option
if isThickProvisionRequest(req.GetParameters()) {
t.Error("request is not for thick-provisioning")
}
req.Parameters["thickProvision"] = ""
if isThickProvisionRequest(req.GetParameters()) {
t.Errorf("request is not for thick-provisioning: %s", req.Parameters["thickProvision"])
}
req.Parameters["thickProvision"] = "false"
if isThickProvisionRequest(req.GetParameters()) {
t.Errorf("request is not for thick-provisioning: %s", req.Parameters["thickProvision"])
}
req.Parameters["thickProvision"] = "off"
if isThickProvisionRequest(req.GetParameters()) {
t.Errorf("request is not for thick-provisioning: %s", req.Parameters["thickProvision"])
}
req.Parameters["thickProvision"] = "no"
if isThickProvisionRequest(req.GetParameters()) {
t.Errorf("request is not for thick-provisioning: %s", req.Parameters["thickProvision"])
}
req.Parameters["thickProvision"] = "**true**"
if isThickProvisionRequest(req.GetParameters()) {
t.Errorf("request is not for thick-provisioning: %s", req.Parameters["thickProvision"])
}
// only "true" should enable thick provisioning
req.Parameters["thickProvision"] = "true"
if !isThickProvisionRequest(req.GetParameters()) {
t.Errorf("request should be for thick-provisioning: %s", req.Parameters["thickProvision"])
}
}

View File

@ -178,7 +178,6 @@ func populateRbdVol(
return nil, status.Error(codes.Internal, err.Error())
}
rv.ThickProvision = isThickProvisionRequest(req.GetVolumeContext())
isStaticVol := parseBoolOption(ctx, req.GetVolumeContext(), staticVol, false)
// get rbd image name from the volume journal
// for static volumes, the image name is actually the volume ID itself

View File

@ -310,7 +310,7 @@ func attachRBDImage(ctx context.Context, volOptions *rbdVolume, device string, c
return devicePath, err
}
func appendNbdDeviceTypeAndOptions(cmdArgs []string, isThick bool, userOptions, cookie string) []string {
func appendNbdDeviceTypeAndOptions(cmdArgs []string, userOptions, cookie string) []string {
cmdArgs = append(cmdArgs, "--device-type", accessTypeNbd)
isUnmap := CheckSliceContains(cmdArgs, "unmap")
@ -328,12 +328,6 @@ func appendNbdDeviceTypeAndOptions(cmdArgs []string, isThick bool, userOptions,
if hasNBDCookieSupport {
cmdArgs = append(cmdArgs, "--options", fmt.Sprintf("cookie=%s", cookie))
}
if isThick {
// When an image is thick-provisioned, any discard/unmap/trim
// requests should not free extents.
cmdArgs = append(cmdArgs, "--options", "notrim")
}
}
if userOptions != "" {
@ -345,22 +339,11 @@ func appendNbdDeviceTypeAndOptions(cmdArgs []string, isThick bool, userOptions,
return cmdArgs
}
func appendKRbdDeviceTypeAndOptions(cmdArgs []string, isThick bool, userOptions string) []string {
cmdArgs = append(cmdArgs, "--device-type", accessTypeKRbd)
isUnmap := CheckSliceContains(cmdArgs, "unmap")
if !isUnmap {
if isThick {
// When an image is thick-provisioned, any discard/unmap/trim
// requests should not free extents.
cmdArgs = append(cmdArgs, "--options", "notrim")
}
}
func appendKRbdDeviceTypeAndOptions(cmdArgs []string, userOptions string) []string {
// Enable mapping and unmapping images from a non-initial network
// namespace (e.g. for Multus CNI). The network namespace must be
// owned by the initial user namespace.
cmdArgs = append(cmdArgs, "--options", "noudev")
cmdArgs = append(cmdArgs, "--device-type", accessTypeKRbd, "--options", "noudev")
if userOptions != "" {
// userOptions is appended after, possibly overriding the above
@ -413,12 +396,6 @@ func createPath(ctx context.Context, volOpt *rbdVolume, device string, cr *util.
isNbd = true
}
// check if the image should stay thick-provisioned
isThick, err := volOpt.isThickProvisioned()
if err != nil {
log.WarningLog(ctx, "failed to detect if image %q is thick-provisioned: %v", volOpt, err)
}
if isNbd {
mapArgs = append(mapArgs, "--log-file",
getCephClientLogFileName(volOpt.VolID, volOpt.LogDir, "rbd-nbd"))
@ -433,9 +410,9 @@ func createPath(ctx context.Context, volOpt *rbdVolume, device string, cr *util.
} else {
mapArgs = append(mapArgs, "map", imagePath)
if isNbd {
mapArgs = appendNbdDeviceTypeAndOptions(mapArgs, isThick, volOpt.MapOptions, volOpt.VolID)
mapArgs = appendNbdDeviceTypeAndOptions(mapArgs, volOpt.MapOptions, volOpt.VolID)
} else {
mapArgs = appendKRbdDeviceTypeAndOptions(mapArgs, isThick, volOpt.MapOptions)
mapArgs = appendKRbdDeviceTypeAndOptions(mapArgs, volOpt.MapOptions)
}
}
@ -543,9 +520,9 @@ func detachRBDImageOrDeviceSpec(
unmapArgs := []string{"unmap", dArgs.imageOrDeviceSpec}
if dArgs.isNbd {
unmapArgs = appendNbdDeviceTypeAndOptions(unmapArgs, false, dArgs.unmapOptions, dArgs.volumeID)
unmapArgs = appendNbdDeviceTypeAndOptions(unmapArgs, dArgs.unmapOptions, dArgs.volumeID)
} else {
unmapArgs = appendKRbdDeviceTypeAndOptions(unmapArgs, false, dArgs.unmapOptions)
unmapArgs = appendKRbdDeviceTypeAndOptions(unmapArgs, dArgs.unmapOptions)
}
_, stderr, err := util.ExecCommand(ctx, rbd, unmapArgs...)

View File

@ -58,18 +58,6 @@ const (
rbdTaskRemoveCmdInvalidString = "No handler found"
rbdTaskRemoveCmdAccessDeniedMessage = "access denied:"
// image metadata key for thick-provisioning.
// As image metadata key starting with '.rbd' will not be copied when we do
// clone or mirroring, deprecating the old key for the same reason use
// 'thickProvisionMetaKey' to set image metadata.
deprecatedthickProvisionMetaKey = ".rbd.csi.ceph.com/thick-provisioned"
thickProvisionMetaKey = "rbd.csi.ceph.com/thick-provisioned"
// these are the metadata set on the image to identify the image is
// thick provisioned or thin provisioned.
thickProvisionMetaData = "true"
thinProvisionMetaData = "false"
// migration label key and value for parameters in volume context.
intreeMigrationKey = "migration"
intreeMigrationLabel = "true"
@ -173,7 +161,6 @@ type rbdVolume struct {
RequestedVolSize int64
DisableInUseChecks bool
readOnly bool
ThickProvision bool
}
// rbdSnapshot represents a CSI snapshot and its RBD snapshot specifics.
@ -382,26 +369,6 @@ func createImage(ctx context.Context, pOpts *rbdVolume, cr *util.Credentials) er
}
}
if pOpts.ThickProvision {
err = pOpts.allocate(0)
if err != nil {
// nolint:errcheck // deleteImage() will log errors in
// case it fails, no need to log them here again
_ = pOpts.deleteImage(ctx)
return fmt.Errorf("failed to thick provision image: %w", err)
}
err = pOpts.setThickProvisioned()
if err != nil {
// nolint:errcheck // deleteImage() will log errors in
// case it fails, no need to log them here again
_ = pOpts.deleteImage(ctx)
return fmt.Errorf("failed to mark image as thick-provisioned: %w", err)
}
}
return nil
}
@ -465,82 +432,6 @@ func (ri *rbdImage) open() (*librbd.Image, error) {
return image, nil
}
// allocate uses the stripe-period of the image to fully allocate (thick
// provision) the image.
func (ri *rbdImage) allocate(offset uint64) error {
// We do not want to call discard, we really want to write zeros to get
// the allocation. This sets the option for the re-used connection, and
// all subsequent images that are opened. That is not a problem, as
// this is the only place images get written.
err := ri.conn.DisableDiscardOnZeroedWriteSame()
if err != nil {
return err
}
image, err := ri.open()
if err != nil {
return err
}
defer image.Close()
st, err := image.Stat()
if err != nil {
return err
}
sc, err := image.GetStripeCount()
if err != nil {
return err
}
// blockSize is the stripe-period: size of the object-size multiplied
// by the stripe-count
blockSize := sc * (1 << st.Order)
zeroBlock := make([]byte, blockSize)
// the actual size of the image as available in the pool, can be
// marginally different from the requested image size
size := st.Size - offset
// In case the remaining space on the volume is smaller than blockSize,
// write a partial block with WriteAt() after this loop.
for size > blockSize {
writeSize := size
// write a maximum of 1GB per WriteSame() call
if size > helpers.GiB {
writeSize = helpers.GiB
}
// round down to the size of a zeroBlock
if (writeSize % blockSize) != 0 {
writeSize = (writeSize / blockSize) * blockSize
}
_, err = image.WriteSame(offset, writeSize, zeroBlock,
rados.OpFlagNone)
if err != nil {
return fmt.Errorf("failed to allocate %d/%d bytes at "+
"offset %d: %w", writeSize, blockSize, offset, err)
}
// write succeeded
size -= writeSize
offset += writeSize
}
// write the last remaining bytes, in case the image size can not be
// written with the optimal blockSize
if size != 0 {
_, err = image.WriteAt(zeroBlock[:size], int64(offset))
if err != nil {
return fmt.Errorf("failed to allocate %d bytes at "+
"offset %d: %w", size, offset, err)
}
}
return nil
}
// isInUse checks if there is a watcher on the image. It returns true if there
// is a watcher on the image, otherwise returns false.
func (ri *rbdImage) isInUse() (bool, error) {
@ -1710,40 +1601,10 @@ func (ri *rbdImage) resize(newSize int64) error {
}
defer image.Close()
thick, err := ri.isThickProvisioned()
if err != nil {
return err
}
// offset is used to track from where on the expansion is done, so that
// the extents can be allocated in case the image is thick-provisioned
var offset uint64
if thick {
st, statErr := image.Stat()
if statErr != nil {
return statErr
}
offset = st.Size
}
err = image.Resize(uint64(util.RoundOffVolSize(newSize) * helpers.MiB))
if err != nil {
return err
}
if thick {
err = ri.allocate(offset)
if err != nil {
resizeErr := image.Resize(offset)
if resizeErr != nil {
err = fmt.Errorf("failed to shrink image (%v) after failed allocation: %w", resizeErr, err)
}
return err
}
}
// update Volsize of rbdVolume object to newSize.
ri.VolSize = newSize
@ -1820,71 +1681,6 @@ func (ri *rbdImage) MigrateMetadata(oldKey, newKey, defaultValue string) (string
return value, nil
}
// setThickProvisioned records in the image metadata that it has been
// thick-provisioned.
func (ri *rbdImage) setThickProvisioned() error {
err := ri.SetMetadata(thickProvisionMetaKey, thickProvisionMetaData)
if err != nil {
return fmt.Errorf("failed to set metadata %q for %q: %w", thickProvisionMetaKey, ri, err)
}
return nil
}
// isThickProvisioned checks in the image metadata if the image has been marked
// as thick-provisioned. This can be used while expanding the image, so that
// the expansion can be allocated too.
func (ri *rbdImage) isThickProvisioned() (bool, error) {
value, err := ri.MigrateMetadata(deprecatedthickProvisionMetaKey, thickProvisionMetaKey, thinProvisionMetaData)
if err != nil {
return false, fmt.Errorf("failed to get metadata %q for %q: %w", thickProvisionMetaKey, ri, err)
}
thick, err := strconv.ParseBool(value)
if err != nil {
return false, fmt.Errorf("failed to convert %q=%q to a boolean: %w", thickProvisionMetaKey, value, err)
}
return thick, nil
}
// RepairThickProvision writes zero bytes to the volume so that it will be
// completely allocated. In case the volume is already marked as
// thick-provisioned, nothing will be done.
func (ri *rbdImage) RepairThickProvision() error {
// if the image has the thick-provisioned metadata, it has been fully
// allocated
done, err := ri.isThickProvisioned()
if err != nil {
return fmt.Errorf("failed to repair thick-provisioning of %q: %w", ri, err)
} else if done {
return nil
}
// in case there are watchers, assume allocating is still happening in
// the background (by an other process?)
background, err := ri.isInUse()
if err != nil {
return fmt.Errorf("failed to get users of %q: %w", ri, err)
} else if background {
return fmt.Errorf("not going to restart thick-provisioning of in-use image %q", ri)
}
// TODO: can this be improved by starting at the offset where
// allocating was aborted/restarted?
err = ri.allocate(0)
if err != nil {
return fmt.Errorf("failed to continue thick-provisioning of %q: %w", ri, err)
}
err = ri.setThickProvisioned()
if err != nil {
return fmt.Errorf("failed to continue thick-provisioning of %q: %w", ri, err)
}
return nil
}
// DeepCopy creates an independent image (dest) from the source image. This
// process may take some time when the image is large.
func (ri *rbdImage) DeepCopy(dest *rbdImage) error {
@ -2004,50 +1800,6 @@ func (ri *rbdImage) isCompabitableClone(dst *rbdImage) error {
return nil
}
func (ri *rbdImage) isCompatibleThickProvision(dst *rbdVolume) error {
thick, err := ri.isThickProvisioned()
if err != nil {
return err
}
switch {
case thick && !dst.ThickProvision:
return fmt.Errorf("cannot create thin volume from thick volume %q", ri)
case !thick && dst.ThickProvision:
return fmt.Errorf("cannot create thick volume from thin volume %q", ri)
}
return nil
}
// FIXME: merge isCompatibleThickProvision of rbdSnapshot and rbdImage to a single
// function.
func (rs *rbdSnapshot) isCompatibleThickProvision(dst *rbdVolume) error {
// During CreateSnapshot the rbd image will be created with the
// snapshot name. Replacing RbdImageName with RbdSnapName so that we
// can check if the image is thick provisioned
vol := generateVolFromSnap(rs)
err := vol.Connect(rs.conn.Creds)
if err != nil {
return err
}
defer vol.Destroy()
thick, err := vol.isThickProvisioned()
if err != nil {
return err
}
switch {
case thick && !dst.ThickProvision:
return fmt.Errorf("cannot create thin volume from thick volume %q", vol)
case !thick && dst.ThickProvision:
return fmt.Errorf("cannot create thick volume from thin volume %q", vol)
}
return nil
}
func (ri *rbdImage) addSnapshotScheduling(
interval admin.Interval,
startTime admin.StartTime) error {