mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-01-31 00:59:30 +00:00
rbd: delete group only if its primary
This is a work of Nikhil which need to be applied on top of this PR to test the feature. Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
parent
22966e8a1c
commit
2b96558842
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/ceph/ceph-csi/internal/rbd/types"
|
"github.com/ceph/ceph-csi/internal/rbd/types"
|
||||||
"github.com/ceph/ceph-csi/internal/util/log"
|
"github.com/ceph/ceph-csi/internal/util/log"
|
||||||
|
|
||||||
|
"github.com/csi-addons/spec/lib/go/replication"
|
||||||
"github.com/csi-addons/spec/lib/go/volumegroup"
|
"github.com/csi-addons/spec/lib/go/volumegroup"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
@ -206,27 +207,41 @@ func (vs *VolumeGroupServer) DeleteVolumeGroup(
|
|||||||
|
|
||||||
log.DebugLog(ctx, "VolumeGroup %q has been found", req.GetVolumeGroupId())
|
log.DebugLog(ctx, "VolumeGroup %q has been found", req.GetVolumeGroupId())
|
||||||
|
|
||||||
// verify that the volume group is empty
|
volumes, mirror, err := mgr.GetMirrorSource(ctx, req.GetVolumeGroupId(), &replication.ReplicationSource{
|
||||||
|
Type: &replication.ReplicationSource_Volumegroup{
|
||||||
|
Volumegroup: &replication.ReplicationSource_VolumeGroupSource{
|
||||||
|
VolumeGroupId: req.GetVolumeGroupId(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, getGRPCError(err)
|
||||||
|
}
|
||||||
|
defer destoryVolumes(ctx, volumes)
|
||||||
|
|
||||||
|
vgrMirrorInfo, err := mirror.GetMirroringInfo(ctx)
|
||||||
|
|
||||||
|
// verify that the volume group is empty, if the group is primary
|
||||||
|
if vgrMirrorInfo.IsPrimary() {
|
||||||
volumes, err := vg.ListVolumes(ctx)
|
volumes, err := vg.ListVolumes(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(
|
return nil, status.Errorf(
|
||||||
codes.NotFound,
|
codes.NotFound,
|
||||||
"could not list volumes for voluem group %q: %s",
|
"could not list volumes for volume group %q: %s",
|
||||||
req.GetVolumeGroupId(),
|
req.GetVolumeGroupId(),
|
||||||
err.Error())
|
err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
log.DebugLog(ctx, "VolumeGroup %q contains %d volumes", req.GetVolumeGroupId(), len(volumes))
|
log.DebugLog(ctx, "VolumeGroup %q contains %d volumes", req.GetVolumeGroupId(), len(volumes))
|
||||||
|
|
||||||
if len(volumes) != 0 {
|
if len(volumes) != 0 {
|
||||||
return nil, status.Errorf(
|
return nil, status.Errorf(
|
||||||
codes.FailedPrecondition,
|
codes.FailedPrecondition,
|
||||||
"rejecting to delete non-empty volume group %q",
|
"rejecting to delete non-empty volume group %q",
|
||||||
req.GetVolumeGroupId())
|
req.GetVolumeGroupId())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// delete the volume group
|
// delete the volume group
|
||||||
err = vg.Delete(ctx)
|
err = vg.Delete(ctx, vgrMirrorInfo, mirror)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal,
|
return nil, status.Errorf(codes.Internal,
|
||||||
"failed to delete volume group %q: %s",
|
"failed to delete volume group %q: %s",
|
||||||
@ -336,10 +351,27 @@ func (vs *VolumeGroupServer) ModifyVolumeGroupMembership(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vol, mirror, err := mgr.GetMirrorSource(ctx, req.GetVolumeGroupId(), &replication.ReplicationSource{
|
||||||
|
Type: &replication.ReplicationSource_Volumegroup{
|
||||||
|
Volumegroup: &replication.ReplicationSource_VolumeGroupSource{
|
||||||
|
VolumeGroupId: req.GetVolumeGroupId(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, getGRPCError(err)
|
||||||
|
}
|
||||||
|
defer destoryVolumes(ctx, vol)
|
||||||
|
|
||||||
|
vgrMirrorInfo, err := mirror.GetMirroringInfo(ctx)
|
||||||
|
|
||||||
|
// Skip removing images from group if the group is secondary
|
||||||
|
removeImageFromGroup := vgrMirrorInfo.IsPrimary()
|
||||||
|
|
||||||
// remove the volume that should not be part of the group
|
// remove the volume that should not be part of the group
|
||||||
for _, id := range toRemove {
|
for _, id := range toRemove {
|
||||||
vol := beforeIDs[id]
|
vol := beforeIDs[id]
|
||||||
err = vg.RemoveVolume(ctx, vol)
|
err = vg.RemoveVolume(ctx, vol, removeImageFromGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(
|
return nil, status.Errorf(
|
||||||
codes.Internal,
|
codes.Internal,
|
||||||
|
@ -26,6 +26,8 @@ import (
|
|||||||
librbd "github.com/ceph/go-ceph/rbd"
|
librbd "github.com/ceph/go-ceph/rbd"
|
||||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||||
"github.com/csi-addons/spec/lib/go/volumegroup"
|
"github.com/csi-addons/spec/lib/go/volumegroup"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/ceph/ceph-csi/internal/rbd/types"
|
"github.com/ceph/ceph-csi/internal/rbd/types"
|
||||||
"github.com/ceph/ceph-csi/internal/util"
|
"github.com/ceph/ceph-csi/internal/util"
|
||||||
@ -184,7 +186,7 @@ func (vg *volumeGroup) Create(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vg *volumeGroup) Delete(ctx context.Context) error {
|
func (vg *volumeGroup) Delete(ctx context.Context, vgrMirrorInfo types.MirrorInfo, mirror types.Mirror) error {
|
||||||
name, err := vg.GetName(ctx)
|
name, err := vg.GetName(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -195,6 +197,41 @@ func (vg *volumeGroup) Delete(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cleanup only omap data if the following condition is met
|
||||||
|
// Mirroring is enabled on the group
|
||||||
|
// Local group is secondary
|
||||||
|
// Local group is in up+replaying state
|
||||||
|
log.DebugLog(ctx, "volume group %v is in %v state and is primary %v", vg, vgrMirrorInfo.GetState, vgrMirrorInfo.IsPrimary())
|
||||||
|
if vgrMirrorInfo != nil && vgrMirrorInfo.GetState() == librbd.MirrorGroupEnabled.String() && !vgrMirrorInfo.IsPrimary() {
|
||||||
|
// If the group is in a secondary state and its up+replaying means its
|
||||||
|
// an healthy secondary and the group is primary somewhere in the
|
||||||
|
// remote cluster and the local group is getting replayed. Delete the
|
||||||
|
// OMAP data generated as we cannot delete the secondary group. When
|
||||||
|
// the group on the primary cluster gets deleted/mirroring disabled,
|
||||||
|
// the group on all the remote (secondary) clusters will get
|
||||||
|
// auto-deleted. This helps in garbage collecting the OMAP, VR, VGR,
|
||||||
|
// VGRC, PVC and PV objects after failback operation.
|
||||||
|
if mirror != nil {
|
||||||
|
sts, rErr := mirror.GetGlobalMirroringStatus(ctx)
|
||||||
|
if rErr != nil {
|
||||||
|
return status.Error(codes.Internal, rErr.Error())
|
||||||
|
}
|
||||||
|
localStatus, rErr := sts.GetLocalSiteStatus()
|
||||||
|
if rErr != nil {
|
||||||
|
log.ErrorLog(ctx, "failed to get local status for volume group%s: %w", name, rErr)
|
||||||
|
return status.Error(codes.Internal, rErr.Error())
|
||||||
|
}
|
||||||
|
log.DebugLog(ctx, "local status is %v and local state is %v", localStatus.IsUP(), localStatus.GetState())
|
||||||
|
if localStatus.IsUP() && localStatus.GetState() == librbd.MirrorGroupStatusStateReplaying.String() {
|
||||||
|
return vg.commonVolumeGroup.Delete(ctx)
|
||||||
|
}
|
||||||
|
log.ErrorLog(ctx,
|
||||||
|
"secondary group status is up=%t and state=%s",
|
||||||
|
localStatus.IsUP(),
|
||||||
|
localStatus.GetState())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = librbd.GroupRemove(ioctx, name)
|
err = librbd.GroupRemove(ioctx, name)
|
||||||
if err != nil && !errors.Is(err, rados.ErrNotFound) {
|
if err != nil && !errors.Is(err, rados.ErrNotFound) {
|
||||||
return fmt.Errorf("failed to remove volume group %q: %w", vg, err)
|
return fmt.Errorf("failed to remove volume group %q: %w", vg, err)
|
||||||
@ -252,12 +289,14 @@ func (vg *volumeGroup) AddVolume(ctx context.Context, vol types.Volume) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vg *volumeGroup) RemoveVolume(ctx context.Context, vol types.Volume) error {
|
func (vg *volumeGroup) RemoveVolume(ctx context.Context, vol types.Volume, removeImageFromGroup bool) error {
|
||||||
// volume was already removed from the group
|
// volume was already removed from the group
|
||||||
if len(vg.volumes) == 0 {
|
if len(vg.volumes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if removeImageFromGroup {
|
||||||
|
log.DebugLog(ctx, "removing image %v from group %v", vol, vg)
|
||||||
err := vol.RemoveFromGroup(ctx, vg)
|
err := vol.RemoveFromGroup(ctx, vg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, librbd.ErrNotExist) {
|
if errors.Is(err, librbd.ErrNotExist) {
|
||||||
@ -266,7 +305,7 @@ func (vg *volumeGroup) RemoveVolume(ctx context.Context, vol types.Volume) error
|
|||||||
|
|
||||||
return fmt.Errorf("failed to remove volume %q from volume group %q: %w", vol, vg, err)
|
return fmt.Errorf("failed to remove volume %q from volume group %q: %w", vol, vg, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// toRemove contain the ID of the volume that is removed from the group
|
// toRemove contain the ID of the volume that is removed from the group
|
||||||
toRemove, err := vol.GetID(ctx)
|
toRemove, err := vol.GetID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -72,7 +72,7 @@ func (cs *ControllerServer) CreateVolumeGroupSnapshot(
|
|||||||
for _, volume := range volumes {
|
for _, volume := range volumes {
|
||||||
if vg != nil {
|
if vg != nil {
|
||||||
// 'normal' cleanup, remove all images from the group
|
// 'normal' cleanup, remove all images from the group
|
||||||
vgErr := vg.RemoveVolume(ctx, volume)
|
vgErr := vg.RemoveVolume(ctx, volume, true)
|
||||||
if vgErr != nil {
|
if vgErr != nil {
|
||||||
log.ErrorLog(
|
log.ErrorLog(
|
||||||
ctx,
|
ctx,
|
||||||
@ -91,7 +91,7 @@ func (cs *ControllerServer) CreateVolumeGroupSnapshot(
|
|||||||
// the VG should always be deleted, volumes can only belong to a single VG
|
// the VG should always be deleted, volumes can only belong to a single VG
|
||||||
log.DebugLog(ctx, "removing temporary volume group %q", vg)
|
log.DebugLog(ctx, "removing temporary volume group %q", vg)
|
||||||
|
|
||||||
vgErr := vg.Delete(ctx)
|
vgErr := vg.Delete(ctx, nil, nil)
|
||||||
if vgErr != nil {
|
if vgErr != nil {
|
||||||
log.ErrorLog(ctx, "failed to remove temporary volume group %q: %v", vg, vgErr)
|
log.ErrorLog(ctx, "failed to remove temporary volume group %q: %v", vg, vgErr)
|
||||||
}
|
}
|
||||||
|
@ -58,13 +58,13 @@ type VolumeGroup interface {
|
|||||||
Create(ctx context.Context) error
|
Create(ctx context.Context) error
|
||||||
|
|
||||||
// Delete removes the VolumeGroup from the backend storage.
|
// Delete removes the VolumeGroup from the backend storage.
|
||||||
Delete(ctx context.Context) error
|
Delete(ctx context.Context, vgMirrorInfo MirrorInfo, mirror Mirror) error
|
||||||
|
|
||||||
// AddVolume adds the Volume to the VolumeGroup.
|
// AddVolume adds the Volume to the VolumeGroup.
|
||||||
AddVolume(ctx context.Context, volume Volume) error
|
AddVolume(ctx context.Context, volume Volume) error
|
||||||
|
|
||||||
// RemoveVolume removes the Volume from the VolumeGroup.
|
// RemoveVolume removes the Volume from the VolumeGroup.
|
||||||
RemoveVolume(ctx context.Context, volume Volume) error
|
RemoveVolume(ctx context.Context, volume Volume, removeImageFromGroup bool) error
|
||||||
|
|
||||||
// ListVolumes returns a slice with all Volumes in the VolumeGroup.
|
// ListVolumes returns a slice with all Volumes in the VolumeGroup.
|
||||||
ListVolumes(ctx context.Context) ([]Volume, error)
|
ListVolumes(ctx context.Context) ([]Volume, error)
|
||||||
|
Loading…
Reference in New Issue
Block a user