From 1fec20be8354ecd1af7ac71d9e6bcadde16afd61 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Sun, 13 Sep 2020 11:45:29 -0400 Subject: [PATCH] cephfs admin: add SubVolumeSnapshotInfo function for octopus Add the new to octopus call for subvolume snapshot info. Two fields appear to return strings "yes"/"no", so could possibly have been converted to booleans. In the chance that other string values get returned and simplicity I left them as strings. If we need to, we can add convenience funcs to return bools later. Signed-off-by: John Mulligan --- cephfs/admin/subvolume_octopus.go | 38 +++++++++++ cephfs/admin/subvolume_octopus_test.go | 92 ++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 cephfs/admin/subvolume_octopus.go create mode 100644 cephfs/admin/subvolume_octopus_test.go diff --git a/cephfs/admin/subvolume_octopus.go b/cephfs/admin/subvolume_octopus.go new file mode 100644 index 0000000..615712b --- /dev/null +++ b/cephfs/admin/subvolume_octopus.go @@ -0,0 +1,38 @@ +// +build octopus + +package admin + +// SubVolumeSnapshotInfo reports various informational values about a subvolume. +type SubVolumeSnapshotInfo struct { + CreatedAt TimeStamp `json:"created_at"` + DataPool string `json:"data_pool"` + HasPendingClones string `json:"has_pending_clones"` + Protected string `json:"protected"` + Size ByteCount `json:"size"` +} + +func parseSubVolumeSnapshotInfo(r []byte, s string, err error) (*SubVolumeSnapshotInfo, error) { + var info SubVolumeSnapshotInfo + if err := unmarshalResponseJSON(r, s, err, &info); err != nil { + return nil, err + } + return &info, nil +} + +// SubVolumeSnapshotInfo returns information about the specified subvolume snapshot. +// +// Similar To: +// ceph fs subvolume snapshot info --group-name= +func (fsa *FSAdmin) SubVolumeSnapshotInfo(volume, group, subvolume, name string) (*SubVolumeSnapshotInfo, error) { + m := map[string]string{ + "prefix": "fs subvolume snapshot info", + "vol_name": volume, + "sub_name": subvolume, + "snap_name": name, + "format": "json", + } + if group != NoGroup { + m["group_name"] = group + } + return parseSubVolumeSnapshotInfo(fsa.marshalMgrCommand(m)) +} diff --git a/cephfs/admin/subvolume_octopus_test.go b/cephfs/admin/subvolume_octopus_test.go new file mode 100644 index 0000000..163cbbb --- /dev/null +++ b/cephfs/admin/subvolume_octopus_test.go @@ -0,0 +1,92 @@ +// +build octopus + +package admin + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +var sampleSubVolumeSnapshoInfo1 = []byte(` +{ + "created_at": "2020-09-11 17:40:12.035792", + "data_pool": "cephfs_data", + "has_pending_clones": "no", + "protected": "yes", + "size": 0 +} +`) + +func TestParseSubVolumeSnapshotInfo(t *testing.T) { + t.Run("error", func(t *testing.T) { + _, err := parseSubVolumeSnapshotInfo(nil, "", errors.New("flub")) + assert.Error(t, err) + assert.Equal(t, "flub", err.Error()) + }) + t.Run("statusSet", func(t *testing.T) { + _, err := parseSubVolumeSnapshotInfo(nil, "unexpected!", nil) + assert.Error(t, err) + }) + t.Run("badJSON", func(t *testing.T) { + _, err := parseSubVolumeSnapshotInfo([]byte("_XxXxX"), "", nil) + assert.Error(t, err) + }) + t.Run("ok", func(t *testing.T) { + info, err := parseSubVolumeSnapshotInfo(sampleSubVolumeSnapshoInfo1, "", nil) + assert.NoError(t, err) + if assert.NotNil(t, info) { + assert.Equal(t, "cephfs_data", info.DataPool) + assert.EqualValues(t, 0, info.Size) + assert.Equal(t, 2020, info.CreatedAt.Year()) + assert.Equal(t, "yes", info.Protected) + assert.Equal(t, "no", info.HasPendingClones) + } + }) +} + +func TestSubVolumeSnapshotInfo(t *testing.T) { + fsa := getFSAdmin(t) + volume := "cephfs" + group := "20000leagues" + subname := "poulp" + snapname1 := "t1" + snapname2 := "nope" + + 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, snapname1) + assert.NoError(t, err) + defer func() { + err := fsa.RemoveSubVolumeSnapshot(volume, group, subname, snapname1) + assert.NoError(t, err) + }() + + sinfo, err := fsa.SubVolumeSnapshotInfo(volume, group, subname, snapname1) + assert.NoError(t, err) + assert.NotNil(t, sinfo) + assert.EqualValues(t, 0, sinfo.Size) + assert.Equal(t, "cephfs_data", sinfo.DataPool) + assert.GreaterOrEqual(t, 2020, sinfo.CreatedAt.Year()) + + sinfo, err = fsa.SubVolumeSnapshotInfo(volume, group, subname, snapname2) + assert.Error(t, err) + assert.Nil(t, sinfo) +}