From 5e6eec6a1aaeee4e3f2c333c4cd641f559a5d28b Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Tue, 4 Aug 2020 17:00:16 -0400 Subject: [PATCH] cephfs admin: add functions to create, list, & delete subvolumegroups Adds functions and tests for: * CreateSubVolumeGroup * ListSubVolumeGroups * RemoveSubVolumeGroup Signed-off-by: John Mulligan --- cephfs/admin/subvolumegroup.go | 69 +++++++++++++++++++++++ cephfs/admin/subvolumegroup_test.go | 87 +++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 cephfs/admin/subvolumegroup.go create mode 100644 cephfs/admin/subvolumegroup_test.go diff --git a/cephfs/admin/subvolumegroup.go b/cephfs/admin/subvolumegroup.go new file mode 100644 index 0000000..a98d0ba --- /dev/null +++ b/cephfs/admin/subvolumegroup.go @@ -0,0 +1,69 @@ +package admin + +// this is the internal type used to create JSON for ceph. +// See SubVolumeGroupOptions for the type that users of the library +// interact with. +// note that the ceph json takes mode as a string. +type subVolumeGroupFields struct { + Prefix string `json:"prefix"` + Format string `json:"format"` + VolName string `json:"vol_name"` + GroupName string `json:"group_name"` + Uid int `json:"uid,omitempty"` + Gid int `json:"gid,omitempty"` + Mode string `json:"mode,omitempty"` + PoolLayout string `json:"pool_layout,omitempty"` +} + +// SubVolumeGroupOptions are used to specify optional, non-identifying, values +// to be used when creating a new subvolume group. +type SubVolumeGroupOptions struct { + Uid int + Gid int + Mode int + PoolLayout string +} + +func (s *SubVolumeGroupOptions) toFields(v, g string) *subVolumeGroupFields { + return &subVolumeGroupFields{ + Prefix: "fs subvolumegroup create", + Format: "json", + VolName: v, + GroupName: g, + Uid: s.Uid, + Gid: s.Gid, + Mode: modeString(s.Mode, false), + PoolLayout: s.PoolLayout, + } +} + +// CreateSubVolumeGroup sends a request to create a subvolume group in a volume. +func (fsa *FSAdmin) CreateSubVolumeGroup(volume, name string, o *SubVolumeGroupOptions) error { + if o == nil { + o = &SubVolumeGroupOptions{} + } + r, s, err := fsa.marshalMgrCommand(o.toFields(volume, name)) + return checkEmptyResponseExpected(r, s, err) +} + +// ListSubVolumeGroups returns a list of subvolume groups belonging to the +// specified volume. +func (fsa *FSAdmin) ListSubVolumeGroups(volume string) ([]string, error) { + r, s, err := fsa.marshalMgrCommand(map[string]string{ + "prefix": "fs subvolumegroup ls", + "vol_name": volume, + "format": "json", + }) + return parseListNames(r, s, err) +} + +// RemoveSubVolumeGroup will delete a subvolume group in a volume. +func (fsa *FSAdmin) RemoveSubVolumeGroup(volume, name string) error { + r, s, err := fsa.marshalMgrCommand(map[string]string{ + "prefix": "fs subvolumegroup rm", + "vol_name": volume, + "group_name": name, + "format": "json", + }) + return checkEmptyResponseExpected(r, s, err) +} diff --git a/cephfs/admin/subvolumegroup_test.go b/cephfs/admin/subvolumegroup_test.go new file mode 100644 index 0000000..bec04b2 --- /dev/null +++ b/cephfs/admin/subvolumegroup_test.go @@ -0,0 +1,87 @@ +package admin + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateSubVolumeGroup(t *testing.T) { + fsa := getFSAdmin(t) + volume := "cephfs" + created := []string{} + defer func() { + for _, name := range created { + err := fsa.RemoveSubVolumeGroup(volume, name) + assert.NoError(t, err) + } + }() + + t.Run("simple", func(t *testing.T) { + svgroup := "svg1" + err := fsa.CreateSubVolumeGroup(volume, svgroup, nil) + assert.NoError(t, err) + created = append(created, svgroup) + + lsvg, err := fsa.ListSubVolumeGroups(volume) + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(lsvg), 1) + assert.Contains(t, lsvg, svgroup) + }) + + t.Run("options1", func(t *testing.T) { + svgroup := "svg2" + err := fsa.CreateSubVolumeGroup(volume, svgroup, &SubVolumeGroupOptions{ + Mode: 0777, + }) + assert.NoError(t, err) + created = append(created, svgroup) + + lsvg, err := fsa.ListSubVolumeGroups(volume) + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(lsvg), 1) + assert.Contains(t, lsvg, svgroup) + }) + + t.Run("options2", func(t *testing.T) { + svgroup := "anotherSVG" + err := fsa.CreateSubVolumeGroup(volume, svgroup, &SubVolumeGroupOptions{ + Uid: 200, + Gid: 200, + Mode: 0771, + // TODO: test pool_layout... I think its a pool name + }) + assert.NoError(t, err) + created = append(created, svgroup) + + lsvg, err := fsa.ListSubVolumeGroups(volume) + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(lsvg), 1) + assert.Contains(t, lsvg, svgroup) + }) +} + +func TestRemoveSubVolumeGroup(t *testing.T) { + fsa := getFSAdmin(t) + volume := "cephfs" + + lsvg, err := fsa.ListSubVolumeGroups(volume) + assert.NoError(t, err) + beforeCount := len(lsvg) + + err = fsa.CreateSubVolumeGroup(volume, "deleteme1", nil) + assert.NoError(t, err) + + lsvg, err = fsa.ListSubVolumeGroups(volume) + assert.NoError(t, err) + afterCount := len(lsvg) + assert.Equal(t, beforeCount, afterCount-1) + + err = fsa.RemoveSubVolumeGroup(volume, "deleteme1") + assert.NoError(t, err) + + lsvg, err = fsa.ListSubVolumeGroups(volume) + assert.NoError(t, err) + nowCount := len(lsvg) + assert.Equal(t, beforeCount, nowCount) +}