From 755481f8c243b2d464a861f984ff5640ef32e16f Mon Sep 17 00:00:00 2001 From: Manish Date: Thu, 8 Feb 2024 08:58:38 +0530 Subject: [PATCH] cephfs/admin: API for quiesceing io on a cephfs cephfs has new quiesce feature for cephfs volume. This change add the feature to go-ceph. Signed-off-by: Manish --- cephfs/admin/fs_quiesce.go | 135 +++++++++++++++++++++++++++ cephfs/admin/fs_quiesce_reef_test.go | 25 +++++ cephfs/admin/fs_quiesce_test.go | 51 ++++++++++ docs/api-status.json | 9 +- docs/api-status.md | 6 +- 5 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 cephfs/admin/fs_quiesce.go create mode 100644 cephfs/admin/fs_quiesce_reef_test.go create mode 100644 cephfs/admin/fs_quiesce_test.go diff --git a/cephfs/admin/fs_quiesce.go b/cephfs/admin/fs_quiesce.go new file mode 100644 index 0000000..3c93dcc --- /dev/null +++ b/cephfs/admin/fs_quiesce.go @@ -0,0 +1,135 @@ +//go:build ceph_preview + +package admin + +import "fmt" + +// fixedPointFloat is a custom type that implements the MarshalJSON interface. +// This is used to format float64 values to two decimal places. +// By default these get converted to integers in the JSON output and +// fail the command. +type fixedPointFloat float64 + +// MarshalJSON provides a custom implementation for the JSON marshalling +// of fixedPointFloat. It formats the float to two decimal places. +func (fpf fixedPointFloat) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%.2f", float64(fpf))), nil +} + +// fSQuiesceFields is the internal type used to create JSON for ceph. +// See FSQuiesceOptions for the type that users of the library +// interact with. +type fSQuiesceFields struct { + Prefix string `json:"prefix"` + VolName string `json:"vol_name"` + GroupName string `json:"group_name,omitempty"` + Members []string `json:"members,omitempty"` + SetId string `json:"set_id,omitempty"` + Timeout fixedPointFloat `json:"timeout,omitempty"` + Expiration fixedPointFloat `json:"expiration,omitempty"` + AwaitFor fixedPointFloat `json:"await_for,omitempty"` + Await bool `json:"await,omitempty"` + IfVersion int `json:"if_version,omitempty"` + Include bool `json:"include,omitempty"` + Exclude bool `json:"exclude,omitempty"` + Reset bool `json:"reset,omitempty"` + Release bool `json:"release,omitempty"` + Query bool `json:"query,omitempty"` + All bool `json:"all,omitempty"` + Cancel bool `json:"cancel,omitempty"` +} + +// FSQuiesceOptions are used to specify optional, non-identifying, values +// to be used when quiescing a cephfs volume. +type FSQuiesceOptions struct { + Timeout float64 + Expiration float64 + AwaitFor float64 + Await bool + IfVersion int + Include bool + Exclude bool + Reset bool + Release bool + Query bool + All bool + Cancel bool +} + +// toFields is used to convert the FSQuiesceOptions to the internal +// fSQuiesceFields type. +func (o *FSQuiesceOptions) toFields(volume, group string, subvolumes []string, setId string) *fSQuiesceFields { + return &fSQuiesceFields{ + Prefix: "fs quiesce", + VolName: volume, + GroupName: group, + Members: subvolumes, + SetId: setId, + Timeout: fixedPointFloat(o.Timeout), + Expiration: fixedPointFloat(o.Expiration), + AwaitFor: fixedPointFloat(o.AwaitFor), + Await: o.Await, + IfVersion: o.IfVersion, + Include: o.Include, + Exclude: o.Exclude, + Reset: o.Reset, + Release: o.Release, + Query: o.Query, + All: o.All, + Cancel: o.Cancel, + } +} + +// QuiesceState is used to report the state of a quiesced fs volume. +type QuiesceState struct { + Name string `json:"name"` + Age float64 `json:"age"` +} + +// QuiesceInfoMember is used to report the state of a quiesced fs volume. +// This is part of sets members object array in the json. +type QuiesceInfoMember struct { + Excluded bool `json:"excluded"` + State QuiesceState `json:"state"` +} + +// QuiesceInfo reports various informational values about a quiesced volume. +// This is returned as sets object array in the json. +type QuiesceInfo struct { + Version int `json:"version"` + AgeRef float64 `json:"age_ref"` + State QuiesceState `json:"state"` + Timeout float64 `json:"timeout"` + Expiration float64 `json:"expiration"` + Members map[string]QuiesceInfoMember `json:"members"` +} + +// FSQuiesceInfo reports various informational values about quiesced volumes. +type FSQuiesceInfo struct { + Epoch int `json:"epoch"` + SetVersion int `json:"set_version"` + Sets map[string]QuiesceInfo `json:"sets"` +} + +// parseFSQuiesceInfo is used to parse the response from the quiesce command. It returns a FSQuiesceInfo object. +func parseFSQuiesceInfo(res response) (*FSQuiesceInfo, error) { + var info FSQuiesceInfo + if err := res.NoStatus().Unmarshal(&info).End(); err != nil { + return nil, err + } + return &info, nil +} + +// FSQuiesce will quiesce the specified subvolumes in a volume. +// Quiescing a fs will prevent new writes to the subvolumes. +// Similar To: +// +// ceph fs quiesce +func (fsa *FSAdmin) FSQuiesce(volume, group string, subvolumes []string, setId string, o *FSQuiesceOptions) (*FSQuiesceInfo, error) { + if o == nil { + o = &FSQuiesceOptions{} + } + f := o.toFields(volume, group, subvolumes, setId) + + return parseFSQuiesceInfo(fsa.marshalMgrCommand(f)) +} diff --git a/cephfs/admin/fs_quiesce_reef_test.go b/cephfs/admin/fs_quiesce_reef_test.go new file mode 100644 index 0000000..5a1075c --- /dev/null +++ b/cephfs/admin/fs_quiesce_reef_test.go @@ -0,0 +1,25 @@ +//go:build (nautilus || octopus || pacific || quincy || reef || squid || ceph_pre_squid) && ceph_preview + +package admin + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFSQuiesce(t *testing.T) { + fsa := getFSAdmin(t) + volume := "cephfs" + group := NoGroup + fsa.CreateSubVolume(volume, group, "quiesceMe", nil) + defer func() { + err := fsa.RemoveSubVolume(volume, group, "quiesceMe") + assert.NoError(t, err) + }() + ret, err := fsa.FSQuiesce(volume, group, []string{"quiesceMe"}, "", nil) + assert.Nil(t, ret) + var notImplemented NotImplementedError + assert.True(t, errors.As(err, ¬Implemented)) +} diff --git a/cephfs/admin/fs_quiesce_test.go b/cephfs/admin/fs_quiesce_test.go new file mode 100644 index 0000000..db2d94d --- /dev/null +++ b/cephfs/admin/fs_quiesce_test.go @@ -0,0 +1,51 @@ +//go:build !(nautilus || octopus || pacific || quincy || reef || squid || ceph_pre_squid) && ceph_preview + +package admin + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFSQuiesce(t *testing.T) { + fsa := getFSAdmin(t) + volume := "cephfs" + group := NoGroup + subvol := "quiesceMe" + fsa.CreateSubVolume(volume, group, subvol, nil) + defer func() { + err := fsa.RemoveSubVolume(volume, group, subvol) + assert.NoError(t, err) + }() + ret, err := fsa.FSQuiesce(volume, group, []string{subvol}, "", nil) + assert.NoError(t, err) + require.NoError(t, err) + for _, val := range ret.Sets { + assert.Equal(t, 0.0, val.Timeout) + } + o := &FSQuiesceOptions{} + o.Timeout = 10.7 + ret, err = fsa.FSQuiesce(volume, group, []string{subvol}, "", o) + assert.NoError(t, err) + for _, val := range ret.Sets { + assert.Equal(t, 10.7, val.Timeout) + } + + o.Expiration = 15.2 + ret, err = fsa.FSQuiesce(volume, group, []string{subvol}, "", o) + assert.NoError(t, err) + for _, val := range ret.Sets { + assert.Equal(t, 15.2, val.Expiration) + assert.Equal(t, 10.7, val.Timeout) + } + + o.Expiration = 15 + ret, err = fsa.FSQuiesce(volume, group, []string{subvol}, "", o) + assert.NoError(t, err) + for _, val := range ret.Sets { + assert.Equal(t, 15.0, val.Expiration) + assert.Equal(t, 10.7, val.Timeout) + } +} diff --git a/docs/api-status.json b/docs/api-status.json index 1e2e4db..7f1da83 100644 --- a/docs/api-status.json +++ b/docs/api-status.json @@ -614,7 +614,14 @@ } ], "deprecated_api": [], - "preview_api": [] + "preview_api": [ + { + "name": "FSAdmin.FSQuiesce", + "comment": "FSQuiesce will quiesce the specified subvolumes in a volume.\nQuiescing a fs will prevent new writes to the subvolumes.\nSimilar To:\n\nceph fs quiesce \n", + "added_in_version": "$NEXT_RELEASE", + "expected_stable_version": "$NEXT_RELEASE_STABLE" + } + ] }, "rados": { "stable_api": [ diff --git a/docs/api-status.md b/docs/api-status.md index c46e929..70292bc 100644 --- a/docs/api-status.md +++ b/docs/api-status.md @@ -8,7 +8,11 @@ No Preview/Deprecated APIs found. All APIs are considered stable. ## Package: cephfs/admin -No Preview/Deprecated APIs found. All APIs are considered stable. +### Preview APIs + +Name | Added in Version | Expected Stable Version | +---- | ---------------- | ----------------------- | +FSAdmin.FSQuiesce | $NEXT_RELEASE | $NEXT_RELEASE_STABLE | ## Package: rados