From 5bceb590fd05800abf63b6113c9935986c0854a2 Mon Sep 17 00:00:00 2001 From: Humble Chirammal Date: Tue, 4 Aug 2020 09:44:56 +0530 Subject: [PATCH] cephfs: add snap reserve/unreserve and snap exist functionalities Signed-off-by: Humble Chirammal --- internal/cephfs/fsjournal.go | 129 +++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/internal/cephfs/fsjournal.go b/internal/cephfs/fsjournal.go index 5ac021f37..7ebe8509c 100644 --- a/internal/cephfs/fsjournal.go +++ b/internal/cephfs/fsjournal.go @@ -246,3 +246,132 @@ func reserveVol(ctx context.Context, volOptions *volumeOptions, secret map[strin return &vid, nil } + +// reserveSnap is a helper routine to request a UUID reservation for the CSI SnapName and, +// to generate the snapshot identifier for the reserved UUID. +func reserveSnap(ctx context.Context, volOptions *volumeOptions, parentSubVolName string, snap *cephfsSnapshot, cr *util.Credentials) (*snapshotIdentifier, error) { + var ( + vid snapshotIdentifier + imageUUID string + err error + ) + + j, err := snapJournal.Connect(volOptions.Monitors, cr) + if err != nil { + return nil, err + } + defer j.Destroy() + + imageUUID, vid.FsSnapshotName, err = j.ReserveName( + ctx, volOptions.MetadataPool, util.InvalidPoolID, + volOptions.MetadataPool, util.InvalidPoolID, snap.RequestName, + snap.NamePrefix, parentSubVolName, "") + if err != nil { + return nil, err + } + + // generate the snapshot ID to return to the CO system + vid.SnapshotID, err = util.GenerateVolID(ctx, volOptions.Monitors, cr, volOptions.FscID, + "", volOptions.ClusterID, imageUUID, volIDVersion) + if err != nil { + return nil, err + } + + util.DebugLog(ctx, "Generated Snapshot ID (%s) for request name (%s)", + vid.SnapshotID, snap.RequestName) + + return &vid, nil +} + +// undoSnapReservation is a helper routine to undo a name reservation for a CSI SnapshotName. +func undoSnapReservation(ctx context.Context, volOptions *volumeOptions, vid snapshotIdentifier, snapName string, cr *util.Credentials) error { + j, err := snapJournal.Connect(volOptions.Monitors, cr) + if err != nil { + return err + } + defer j.Destroy() + + err = j.UndoReservation(ctx, volOptions.MetadataPool, + volOptions.MetadataPool, vid.FsSnapshotName, snapName) + return err +} + +/* +checkSnapExists checks to determine if passed in RequestName in volOptions exists on the backend. + +**NOTE:** These functions manipulate the rados omaps that hold information regarding +volume names as requested by the CSI drivers. Hence, these need to be invoked only when the +respective CSI driver generated volume name based locks are held, as otherwise racy +access to these omaps may end up leaving them in an inconsistent state. + +These functions also cleanup omap reservations that are stale. I.e when omap entries exist and +backing subvolumes are missing, or one of the omaps exist and the next is missing. This is +because, the order of omap creation and deletion are inverse of each other, and protected by the +request name lock, and hence any stale omaps are leftovers from incomplete transactions and are +hence safe to garbage collect. +*/ +func checkSnapExists( + ctx context.Context, + volOptions *volumeOptions, + parentSubVolName string, + snap *cephfsSnapshot, + cr *util.Credentials) (*snapshotIdentifier, *snapshotInfo, error) { + j, err := snapJournal.Connect(volOptions.Monitors, cr) + if err != nil { + return nil, nil, err + } + defer j.Destroy() + + snapData, err := j.CheckReservation( + ctx, volOptions.MetadataPool, snap.RequestName, snap.NamePrefix, parentSubVolName, "") + if err != nil { + return nil, nil, err + } + if snapData == nil { + return nil, nil, nil + } + sid := &snapshotIdentifier{} + snapUUID := snapData.ImageUUID + snapID := snapData.ImageAttributes.ImageName + sid.FsSnapshotName = snapData.ImageAttributes.ImageName + snapInfo, err := getSnapshotInfo(ctx, volOptions, cr, volumeID(snapID), volumeID(parentSubVolName)) + if err != nil { + if errors.Is(err, ErrSnapNotFound) { + err = j.UndoReservation(ctx, volOptions.MetadataPool, + volOptions.MetadataPool, snapID, snap.RequestName) + return nil, nil, err + } + return nil, nil, err + } + + defer func() { + if err != nil { + err = deleteSnapshot(ctx, volOptions, cr, volumeID(snapID), volumeID(parentSubVolName)) + if err != nil { + klog.Errorf(util.Log(ctx, "failed to delete snapshot %s: %v"), snapID, err) + return + } + err = j.UndoReservation(ctx, volOptions.MetadataPool, + volOptions.MetadataPool, snapID, snap.RequestName) + if err != nil { + klog.Errorf(util.Log(ctx, "removing reservation failed for snapshot %s: %v"), snapID, err) + } + } + }() + tm, err := parseTime(ctx, snapInfo.CreatedAt) + if err != nil { + return nil, nil, err + } + + sid.CreationTime = tm + // found a snapshot already available, process and return it! + sid.SnapshotID, err = util.GenerateVolID(ctx, volOptions.Monitors, cr, volOptions.FscID, + "", volOptions.ClusterID, snapUUID, volIDVersion) + if err != nil { + return nil, nil, err + } + util.DebugLog(ctx, "Found existing snapshot (%s) with subvolume name (%s) for request (%s)", + snapData.ImageAttributes.RequestName, parentSubVolName, sid.FsSnapshotName) + + return sid, &snapInfo, nil +}