From efb7bccaea265beec646ec0fa3b0328ada9daaef Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Fri, 4 Oct 2024 11:54:11 +0200 Subject: [PATCH] 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 --- internal/rbd/group/volume_group.go | 91 +++++++++++++++++++++++++++++- internal/rbd/types/group.go | 7 +++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/internal/rbd/group/volume_group.go b/internal/rbd/group/volume_group.go index d268b1142..cdb6a8cf0 100644 --- a/internal/rbd/group/volume_group.go +++ b/internal/rbd/group/volume_group.go @@ -241,7 +241,8 @@ func (vg *volumeGroup) AddVolume(ctx context.Context, vol types.Volume) error { err = j.AddVolumesMapping(ctx, pool, csiID.ObjectUUID, toAdd) 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 @@ -315,7 +316,8 @@ func (vg *volumeGroup) RemoveVolume(ctx context.Context, vol types.Volume) error err = j.RemoveVolumesMapping(ctx, pool, csiID.ObjectUUID, mapping) 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 @@ -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) { 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 +} diff --git a/internal/rbd/types/group.go b/internal/rbd/types/group.go index 36f89e807..08183f9fb 100644 --- a/internal/rbd/types/group.go +++ b/internal/rbd/types/group.go @@ -21,6 +21,8 @@ import ( "github.com/ceph/go-ceph/rados" "github.com/csi-addons/spec/lib/go/volumegroup" + + "github.com/ceph/ceph-csi/internal/util" ) type journalledObject interface { @@ -66,4 +68,9 @@ type VolumeGroup interface { // ListVolumes returns a slice with all Volumes in the VolumeGroup. 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) }