rbd: enabe journal based mirroring

Journal-based RADOS block device mirroring ensures point-in-time
consistent replicas of all changes to an image, including reads and
writes, block device resizing, snapshots, clones, and flattening.

Journaling-based mirroring records all modifications to an image in the
order in which they occur. This ensures that a crash-consistent mirror
of an image is available.

Mirroring when configured in journal mode, mirroring will
utilize the RBD journaling image feature to replicate the image
contents. If the RBD journaling image feature is not yet enabled on the
image, it will be automatically enabled.

Fixes: #2018
Co-authored-by: Madhu Rajanna <madhupr007@gmail.com>
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
This commit is contained in:
Prasanna Kumar Kalever 2021-11-26 12:08:21 +05:30 committed by mergify[bot]
parent ab76459e87
commit e7d8834149
2 changed files with 29 additions and 8 deletions

View File

@ -43,6 +43,9 @@ const (
// imageMirrorModeSnapshot uses snapshots to propagate RBD images between // imageMirrorModeSnapshot uses snapshots to propagate RBD images between
// ceph clusters. // ceph clusters.
imageMirrorModeSnapshot imageMirroringMode = "snapshot" imageMirrorModeSnapshot imageMirroringMode = "snapshot"
// imageMirrorModeJournal uses journaling to propagate RBD images between
// ceph clusters.
imageMirrorModeJournal imageMirroringMode = "journal"
) )
const ( const (
@ -123,6 +126,8 @@ func getMirroringMode(ctx context.Context, parameters map[string]string) (librbd
switch imageMirroringMode(val) { switch imageMirroringMode(val) {
case imageMirrorModeSnapshot: case imageMirrorModeSnapshot:
mirroringMode = librbd.ImageMirrorModeSnapshot mirroringMode = librbd.ImageMirrorModeSnapshot
case imageMirrorModeJournal:
mirroringMode = librbd.ImageMirrorModeJournal
default: default:
return mirroringMode, status.Errorf(codes.InvalidArgument, "%s %s not supported", imageMirroringKey, val) return mirroringMode, status.Errorf(codes.InvalidArgument, "%s %s not supported", imageMirroringKey, val)
} }
@ -133,12 +138,24 @@ func getMirroringMode(ctx context.Context, parameters map[string]string) (librbd
// validateSchedulingDetails gets the mirroring mode and scheduling details from the // validateSchedulingDetails gets the mirroring mode and scheduling details from the
// input GRPC request parameters and validates the scheduling is only supported // input GRPC request parameters and validates the scheduling is only supported
// for snapshot mirroring mode. // for snapshot mirroring mode.
func validateSchedulingDetails(parameters map[string]string) error { func validateSchedulingDetails(ctx context.Context, parameters map[string]string) error {
var err error var err error
val := parameters[imageMirroringKey] val := parameters[imageMirroringKey]
switch imageMirroringMode(val) { switch imageMirroringMode(val) {
case imageMirrorModeJournal:
// journal mirror mode does not require scheduling parameters
if _, ok := parameters[schedulingIntervalKey]; ok {
log.WarningLog(ctx, "%s parameter cannot be used with %s mirror mode, ignoring it",
schedulingIntervalKey, string(imageMirrorModeJournal))
}
if _, ok := parameters[schedulingStartTimeKey]; ok {
log.WarningLog(ctx, "%s parameter cannot be used with %s mirror mode, ignoring it",
schedulingStartTimeKey, string(imageMirrorModeJournal))
}
return nil
case imageMirrorModeSnapshot: case imageMirrorModeSnapshot:
// If mirroring mode is not set in parameters, we are defaulting mirroring // If mirroring mode is not set in parameters, we are defaulting mirroring
// mode to snapshot. Discard empty mirroring mode from validation as it is // mode to snapshot. Discard empty mirroring mode from validation as it is
@ -206,7 +223,7 @@ func (rs *ReplicationServer) EnableVolumeReplication(ctx context.Context,
} }
defer cr.DeleteCredentials() defer cr.DeleteCredentials()
err = validateSchedulingDetails(req.GetParameters()) err = validateSchedulingDetails(ctx, req.GetParameters())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -325,10 +342,12 @@ func tickleMirroringOnDummyImage(rbdVol *rbdVolume, mirroringMode librbd.ImageMi
return err return err
} }
if mirroringMode == librbd.ImageMirrorModeSnapshot {
err = dummyVol.addSnapshotScheduling(admin.Interval("1m"), admin.NoStartTime) err = dummyVol.addSnapshotScheduling(admin.Interval("1m"), admin.NoStartTime)
if err != nil { if err != nil {
return err return err
} }
}
return nil return nil
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package rbd package rbd
import ( import (
"context"
"reflect" "reflect"
"testing" "testing"
@ -73,6 +74,7 @@ func TestValidateSchedulingInterval(t *testing.T) {
func TestValidateSchedulingDetails(t *testing.T) { func TestValidateSchedulingDetails(t *testing.T) {
t.Parallel() t.Parallel()
ctx := context.TODO()
tests := []struct { tests := []struct {
name string name string
parameters map[string]string parameters map[string]string
@ -98,10 +100,10 @@ func TestValidateSchedulingDetails(t *testing.T) {
{ {
"when mirroring mode is journal", "when mirroring mode is journal",
map[string]string{ map[string]string{
imageMirroringKey: "journal", imageMirroringKey: string(imageMirrorModeJournal),
schedulingIntervalKey: "1h", schedulingIntervalKey: "1h",
}, },
true, false,
}, },
{ {
"when startTime is specified without interval", "when startTime is specified without interval",
@ -136,7 +138,7 @@ func TestValidateSchedulingDetails(t *testing.T) {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel() t.Parallel()
err := validateSchedulingDetails(tt.parameters) err := validateSchedulingDetails(ctx, tt.parameters)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("getSchedulingDetails() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("getSchedulingDetails() error = %v, wantErr %v", err, tt.wantErr)