rbd: add VolumeGroup.CreateSnapshots() implementation

When the rbd.Manager creates a VolumeGroupSnapshot, each RBD-snapshot
that is created as part of the RBD-group needs to be cloned into its own
RBD-image that will be used as a CSI Snapshot.

The VolumeGroup.CreateSnapshots() creates the RBD-group snapshot and
returns a list of the Snapshot structs.

Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
Niels de Vos 2024-10-04 11:54:11 +02:00 committed by mergify[bot]
parent 20fadf2016
commit efb7bccaea
2 changed files with 96 additions and 2 deletions

View File

@ -241,7 +241,8 @@ func (vg *volumeGroup) AddVolume(ctx context.Context, vol types.Volume) error {
err = j.AddVolumesMapping(ctx, pool, csiID.ObjectUUID, toAdd) err = j.AddVolumesMapping(ctx, pool, csiID.ObjectUUID, toAdd)
if err != nil { if err != nil {
return fmt.Errorf("failed to add mapping for volume %q to volume group id %q: %w", volID, id, err) return fmt.Errorf("failed to add mapping for volume %q to volume group id %q: %w",
volID, id, err)
} }
return nil return nil
@ -315,7 +316,8 @@ func (vg *volumeGroup) RemoveVolume(ctx context.Context, vol types.Volume) error
err = j.RemoveVolumesMapping(ctx, pool, csiID.ObjectUUID, mapping) err = j.RemoveVolumesMapping(ctx, pool, csiID.ObjectUUID, mapping)
if err != nil { if err != nil {
return fmt.Errorf("failed to remove mapping for volume %q to volume group id %q: %w", toRemove, id, err) return fmt.Errorf("failed to remove mapping for volume %q to volume group id %q: %w",
toRemove, id, err)
} }
return nil return nil
@ -324,3 +326,88 @@ func (vg *volumeGroup) RemoveVolume(ctx context.Context, vol types.Volume) error
func (vg *volumeGroup) ListVolumes(ctx context.Context) ([]types.Volume, error) { func (vg *volumeGroup) ListVolumes(ctx context.Context) ([]types.Volume, error) {
return vg.volumes, nil return vg.volumes, nil
} }
// CreateSnapshots makes consistent snapshots of all the volumes in the volume group.
func (vg *volumeGroup) CreateSnapshots(
ctx context.Context,
cr *util.Credentials,
name string,
) ([]types.Snapshot, error) {
group, err := vg.GetName(ctx)
if err != nil {
return nil, err
}
ioctx, err := vg.GetIOContext(ctx)
if err != nil {
return nil, err
}
err = librbd.GroupSnapCreate(ioctx, group, name)
if err != nil {
if !errors.Is(err, librbd.ErrExist) {
return nil, fmt.Errorf("failed to create volume group snapshot %q: %w", name, err)
}
log.DebugLog(ctx, "ignoring error while creating volume group snapshot %q: %v", vg, err)
}
defer func() {
// always remove the groups-snapshot on function exit, it is not used anymore afterwards
cleanupErr := librbd.GroupSnapRemove(ioctx, group, name)
if cleanupErr != nil {
log.ErrorLog(ctx, "failed to remove temporary volume group snapshot %q: %v",
name, cleanupErr)
}
}()
info, err := librbd.GroupSnapGetInfo(ioctx, group, name)
if err != nil {
return nil, fmt.Errorf("failed to get info for volume group snapshot %q: %w",
vg.String()+"@"+name, err)
}
snapshots := make([]types.Snapshot, len(info.Snapshots))
defer func() {
// free all created snapshot objects in case of a failure
if err == nil {
return
}
for _, snapshot := range snapshots {
snapshot.Destroy(ctx)
}
}()
// Loop though all the RBD-snapshots in the group, and find the volume
// that was used to create the snapshot. Once found, use the volume to
// create a new RBD-image from the RBD-snapshot.
for i, snap := range info.Snapshots {
for _, volume := range vg.volumes {
var volName string
volName, err = volume.GetName(ctx)
if err != nil {
return nil, fmt.Errorf(
"failed to get name for volume %q: %w", volume, err)
}
if volName != snap.Name {
// the volume isn't the snapshot-source, continue with the next one
continue
}
// yay, volume for the RBD-snapshot found!
snapName := fmt.Sprintf("%s-snap-%d", group, i)
snapshots[i], err = volume.NewSnapshotByID(ctx, cr, snapName, snap.SnapID)
if err != nil {
return nil, fmt.Errorf(
"failed to create snapshot for image %q with snapshot id %d: %w",
snap.Name, snap.SnapID, err)
}
// done, no need to try more volumes in the loop
break
}
}
return snapshots, nil
}

View File

@ -21,6 +21,8 @@ import (
"github.com/ceph/go-ceph/rados" "github.com/ceph/go-ceph/rados"
"github.com/csi-addons/spec/lib/go/volumegroup" "github.com/csi-addons/spec/lib/go/volumegroup"
"github.com/ceph/ceph-csi/internal/util"
) )
type journalledObject interface { type journalledObject interface {
@ -66,4 +68,9 @@ type VolumeGroup interface {
// 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)
// CreateSnapshots creates Snapshots of all Volume in the VolumeGroup.
// The Snapshots are crash consistent, and created as a consistency
// group.
CreateSnapshots(ctx context.Context, cr *util.Credentials, name string) ([]Snapshot, error)
} }