diff --git a/cephfs/admin/clone.go b/cephfs/admin/clone.go index d8281359c..9a00402fe 100644 --- a/cephfs/admin/clone.go +++ b/cephfs/admin/clone.go @@ -81,10 +81,18 @@ type CloneSource struct { Snapshot string `json:"snapshot"` } +// CloneProgressReport contains the progress report of a subvolume clone. +type CloneProgressReport struct { + PercentageCloned string `json:"percentage cloned"` + AmountCloned string `json:"amount cloned"` + FilesCloned string `json:"files cloned"` +} + // CloneStatus reports on the status of a subvolume clone. type CloneStatus struct { - State CloneState `json:"state"` - Source CloneSource `json:"source"` + State CloneState `json:"state"` + Source CloneSource `json:"source"` + ProgressReport CloneProgressReport `json:"progress_report"` // failure can be obtained through .GetFailure() failure *CloneFailure diff --git a/cephfs/admin/clone_progress_report_test.go b/cephfs/admin/clone_progress_report_test.go new file mode 100644 index 000000000..16d8e6b07 --- /dev/null +++ b/cephfs/admin/clone_progress_report_test.go @@ -0,0 +1,76 @@ +//go:build main + +package admin + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestCloneProgress(t *testing.T) { + fsa := getFSAdmin(t) + volume := "cephfs" + group := "Park" + subname := "Jurrasic" + snapname := "dinodna1" + clonename := "babydino" + + err := fsa.CreateSubVolumeGroup(volume, group, nil) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolumeGroup(volume, group) + assert.NoError(t, err) + }() + + svopts := &SubVolumeOptions{ + Mode: 0750, + Size: 20 * gibiByte, + } + err = fsa.CreateSubVolume(volume, group, subname, svopts) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolume(volume, group, subname) + assert.NoError(t, err) + }() + + err = fsa.CreateSubVolumeSnapshot(volume, group, subname, snapname) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolumeSnapshot(volume, group, subname, snapname) + assert.NoError(t, err) + }() + + err = fsa.CloneSubVolumeSnapshot( + volume, group, subname, snapname, clonename, + &CloneOptions{TargetGroup: group}) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolume(volume, group, clonename) + assert.NoError(t, err) + }() + + wasInProgress := false + for done := false; !done; { + status, err := fsa.CloneStatus(volume, group, clonename) + assert.NoError(t, err) + assert.NotNil(t, status) + switch status.State { + case ClonePending: + case CloneInProgress: + wasInProgress = true + assert.NotNil(t, status.ProgressReport.PercentageCloned) + assert.NotNil(t, status.ProgressReport.AmountCloned) + assert.NotNil(t, status.ProgressReport.FilesCloned) + case CloneComplete: + done = true + case CloneFailed: + t.Fatal("clone failed") + default: + t.Fatalf("invalid clone status: %q", status.State) + } + time.Sleep(5 * time.Millisecond) + } + assert.Equal(t, wasInProgress, true) +} diff --git a/cephfs/admin/clone_progress_report_unsupported_test.go b/cephfs/admin/clone_progress_report_unsupported_test.go new file mode 100644 index 000000000..42bad912f --- /dev/null +++ b/cephfs/admin/clone_progress_report_unsupported_test.go @@ -0,0 +1,76 @@ +//go:build !main + +package admin + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestCloneProgress(t *testing.T) { + fsa := getFSAdmin(t) + volume := "cephfs" + group := "Park" + subname := "Jurrasic" + snapname := "dinodna1" + clonename := "babydino" + + err := fsa.CreateSubVolumeGroup(volume, group, nil) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolumeGroup(volume, group) + assert.NoError(t, err) + }() + + svopts := &SubVolumeOptions{ + Mode: 0750, + Size: 20 * gibiByte, + } + err = fsa.CreateSubVolume(volume, group, subname, svopts) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolume(volume, group, subname) + assert.NoError(t, err) + }() + + err = fsa.CreateSubVolumeSnapshot(volume, group, subname, snapname) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolumeSnapshot(volume, group, subname, snapname) + assert.NoError(t, err) + }() + + err = fsa.CloneSubVolumeSnapshot( + volume, group, subname, snapname, clonename, + &CloneOptions{TargetGroup: group}) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolume(volume, group, clonename) + assert.NoError(t, err) + }() + + wasInProgress := false + for done := false; !done; { + status, err := fsa.CloneStatus(volume, group, clonename) + assert.NoError(t, err) + assert.NotNil(t, status) + switch status.State { + case ClonePending: + case CloneInProgress: + wasInProgress = true + assert.Empty(t, status.ProgressReport.PercentageCloned) + assert.Empty(t, status.ProgressReport.AmountCloned) + assert.Empty(t, status.ProgressReport.FilesCloned) + case CloneComplete: + done = true + case CloneFailed: + t.Fatal("clone failed") + default: + t.Fatalf("invalid clone status: %q", status.State) + } + time.Sleep(5 * time.Millisecond) + } + assert.Equal(t, wasInProgress, true) +}