Repository: ceph/go-ceph
Pull request #455 (2021-02-01 16:54:20)

cephfs: add state field to SubVolumeInfo structure.
Signed-off-by: Mudit Agarwal <muagarwa@redhat.com>

Fixes: #453 

## Checklist
- [ ] Added tests for features and functional changes
- [ ] Public functions and types are documented
- [ ] Standard formatting is applied to Go code



Patch:
diff --git a/cephfs/admin/subvolume.go b/cephfs/admin/subvolume.go
index ac18983..69fb4f9 100644
--- a/cephfs/admin/subvolume.go
+++ b/cephfs/admin/subvolume.go
@@ -197,23 +197,48 @@ const (
 	SnapshotRetentionFeature = Feature("snapshot-retention")
 )
 
+// SubVolumeState is used to define constant value for the state of
+// a subvolume.
+type SubVolumeState string
+
+const (
+	// StateUnset indicates a subvolume without any state.
+	StateUnset = SubVolumeState("")
+	// StateInit indicates that the subvolume is in initializing state.
+	StateInit = SubVolumeState("init")
+	// StatePending indicates that the subvolume is in pending state.
+	StatePending = SubVolumeState("pending")
+	// StateInProgress indicates that the subvolume is in in-progress state.
+	StateInProgress = SubVolumeState("in-progress")
+	// StateFailed indicates that the subvolume is in failed state.
+	StateFailed = SubVolumeState("failed")
+	// StateComplete indicates that the subvolume is in complete state.
+	StateComplete = SubVolumeState("complete")
+	// StateCanceled indicates that the subvolume is in canceled state.
+	StateCanceled = SubVolumeState("canceled")
+	// StateSnapRetained indicates that the subvolume is in
+	// snapshot-retained state.
+	StateSnapRetained = SubVolumeState("snapshot-retained")
+)
+
 // SubVolumeInfo reports various informational values about a subvolume.
 type SubVolumeInfo struct {
-	Type          string    `json:"type"`
-	Path          string    `json:"path"`
-	Uid           int       `json:"uid"`
-	Gid           int       `json:"gid"`
-	Mode          int       `json:"mode"`
-	BytesPercent  string    `json:"bytes_pcent"`
-	BytesUsed     ByteCount `json:"bytes_used"`
-	BytesQuota    QuotaSize `json:"-"`
-	DataPool      string    `json:"data_pool"`
-	PoolNamespace string    `json:"pool_namespace"`
-	Atime         TimeStamp `json:"atime"`
-	Mtime         TimeStamp `json:"mtime"`
-	Ctime         TimeStamp `json:"ctime"`
-	CreatedAt     TimeStamp `json:"created_at"`
-	Features      []Feature `json:"features"`
+	Type          string         `json:"type"`
+	Path          string         `json:"path"`
+	State         SubVolumeState `json:"state"`
+	Uid           int            `json:"uid"`
+	Gid           int            `json:"gid"`
+	Mode          int            `json:"mode"`
+	BytesPercent  string         `json:"bytes_pcent"`
+	BytesUsed     ByteCount      `json:"bytes_used"`
+	BytesQuota    QuotaSize      `json:"-"`
+	DataPool      string         `json:"data_pool"`
+	PoolNamespace string         `json:"pool_namespace"`
+	Atime         TimeStamp      `json:"atime"`
+	Mtime         TimeStamp      `json:"mtime"`
+	Ctime         TimeStamp      `json:"ctime"`
+	CreatedAt     TimeStamp      `json:"created_at"`
+	Features      []Feature      `json:"features"`
 }
 
 type subVolumeInfoWrapper struct {


Test patch:
diff --git a/cephfs/admin/subvolume_test.go b/cephfs/admin/subvolume_test.go
index 94fe19a..78cdc94 100644
--- a/cephfs/admin/subvolume_test.go
+++ b/cephfs/admin/subvolume_test.go
@@ -192,8 +192,11 @@ func TestRemoveSubVolume(t *testing.T) {
 		assert.NoError(t, err)
 
 		delay()
-		_, err = fsa.SubVolumeInfo(volume, NoGroup, subname)
+		subInfo, err := fsa.SubVolumeInfo(volume, NoGroup, subname)
 		assert.NoError(t, err)
+		// If the subvolume is deleted with snapshots retained, then
+		// it must have snapshot-retained state.
+		assert.Equal(t, subInfo.State, StateSnapRetained)
 
 		err = fsa.RemoveSubVolumeSnapshot(volume, NoGroup, subname, snapname)
 		assert.NoError(t, err)
@@ -361,6 +364,34 @@ var sampleSubVolumeInfo3 = []byte(`
 }
 `)
 
+var sampleSubVolumeInfo4 = []byte(`
+{
+	"atime": "2020-10-02 13:48:17",
+	"bytes_pcent": "undefined",
+	"bytes_quota": "infinite",
+	"bytes_used": 0,
+	"created_at": "2020-10-02 13:48:17",
+	"ctime": "2020-10-02 13:48:17",
+	"data_pool": "cephfs_data",
+	"features": [
+		"snapshot-clone",
+		"snapshot-autoprotect",
+		"snapshot-retention"
+	],
+	"gid": 0,
+	"mode": 16877,
+	"mon_addrs": [
+		"127.0.0.1:6789"
+	],
+	"mtime": "2020-10-02 13:48:17",
+	"path": "/volumes/igotta/boogie/0302e067-e7fb-4d9b-8388-aae46164d8b0",
+	"pool_namespace": "",
+	"state": "complete",
+	"type": "subvolume",
+	"uid": 0
+}
+`)
+
 var badSampleSubVolumeInfo1 = []byte(`
 {
     "bytes_quota": "fishy",
@@ -430,6 +461,18 @@ func TestParseSubVolumeInfo(t *testing.T) {
 			assert.Contains(t, info.Features, SnapshotAutoprotectFeature)
 		}
 	})
+	t.Run("ok4", func(t *testing.T) {
+		info, err := parseSubVolumeInfo(R(sampleSubVolumeInfo4, "", nil))
+		assert.NoError(t, err)
+		if assert.NotNil(t, info) {
+			assert.Equal(t,
+				"/volumes/igotta/boogie/0302e067-e7fb-4d9b-8388-aae46164d8b0",
+				info.Path)
+			assert.Equal(t, 0, info.Uid)
+			assert.Contains(t, info.Features, SnapshotRetentionFeature)
+			assert.Equal(t, info.State, StateComplete)
+		}
+	})
 	t.Run("invalidBytesQuotaValue", func(t *testing.T) {
 		info, err := parseSubVolumeInfo(R(badSampleSubVolumeInfo1, "", nil))
 		assert.Error(t, err)
@@ -473,6 +516,16 @@ func TestSubVolumeInfo(t *testing.T) {
 	assert.Equal(t, 20*gibiByte, vinfo.BytesQuota)
 	assert.Equal(t, 040750, vinfo.Mode)
 	assert.Equal(t, time.Now().Year(), vinfo.Ctime.Year())
+	// state field was added with snapshot retention feature.
+	canRetain := false
+	for _, f := range vinfo.Features {
+		if f == SnapshotRetentionFeature {
+			canRetain = true
+		}
+	}
+	if canRetain {
+		assert.Equal(t, vinfo.State, StateComplete)
+	}
 }
 
 func TestSubVolumeSnapshots(t *testing.T) {