diff --git a/tools-v2/README.md b/tools-v2/README.md index cc24dc284f..2edf49a320 100644 --- a/tools-v2/README.md +++ b/tools-v2/README.md @@ -1603,6 +1603,38 @@ Output: +------------+-----------+--------+--------+--------+---------+ ``` +##### status volume + +###### status volume snapshot + +get the snapshot status + +Usage: + +```bash +curve bs status volume snapshot --filename test --snapshotaddr 127.0.0.1:5550,127.0.0.1:5551,127.0.0.1:5552 +``` + +Output: + +```bash ++---------------+-------+ +| STATUS | COUNT | ++---------------+-------+ +| done | 10 | ++---------------+-------+ +| in-progress | 4 | ++---------------+-------+ +| deleting | 2 | ++---------------+-------+ +| errorDeleting | 0 | ++---------------+-------+ +| canceling | 0 | ++---------------+-------+ +| error | 0 | ++---------------+-------+ +``` + #### delete ##### delete peer diff --git a/tools-v2/internal/error/error.go b/tools-v2/internal/error/error.go index 7a1b71d870..b6d83f76f2 100644 --- a/tools-v2/internal/error/error.go +++ b/tools-v2/internal/error/error.go @@ -498,7 +498,10 @@ var ( return NewInternalCmdError(78, fmt.Sprintf("list snapshot fail, requestId: %s, code: %s, message: %s", requestId, code, message)) } ErrBsGetCloneRecover = func() *CmdError { - return NewInternalCmdError(73, "get clone-recover fail, err: %s") + return NewInternalCmdError(79, "get clone-recover fail, err: %s") + } + ErrBsGetSnapshotStatus = func() *CmdError { + return NewInternalCmdError(80, "get snapshot status fail, err: %s") } // http error diff --git a/tools-v2/internal/utils/row.go b/tools-v2/internal/utils/row.go index 6369aa3ed5..dde02142af 100644 --- a/tools-v2/internal/utils/row.go +++ b/tools-v2/internal/utils/row.go @@ -37,6 +37,7 @@ const ( ROW_COPYSET = "copyset" ROW_COPYSET_ID = "copysetId" ROW_COPYSET_KEY = "copysetKey" + ROW_COUNT = "count" ROW_CREATE_TIME = "createTime" ROW_CREATED = "created" ROW_CTIME = "ctime" diff --git a/tools-v2/pkg/cli/command/curvebs/status/status.go b/tools-v2/pkg/cli/command/curvebs/status/status.go index 3e4efa70ce..5a3283842e 100644 --- a/tools-v2/pkg/cli/command/curvebs/status/status.go +++ b/tools-v2/pkg/cli/command/curvebs/status/status.go @@ -31,6 +31,7 @@ import ( "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status/etcd" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status/mds" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status/snapshot" + "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status/volume" "github.com/spf13/cobra" ) @@ -49,6 +50,7 @@ func (statusCmd *StatusCommand) AddSubCommands() { chunkserver.NewChunkServerCommand(), copyset.NewCopysetCommand(), cluster.NewClusterCommand(), + volume.NewVolumeCommand(), ) } diff --git a/tools-v2/pkg/cli/command/curvebs/status/volume/snapshot/snapshot.go b/tools-v2/pkg/cli/command/curvebs/status/volume/snapshot/snapshot.go new file mode 100644 index 0000000000..ffa716be82 --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/status/volume/snapshot/snapshot.go @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Project: CurveCli + * Created Date: 2023-09-13 + * Author: saicaca + */ + +package snapshot + +import ( + "encoding/json" + "fmt" + "strconv" + "time" + + cmderror "github.com/opencurve/curve/tools-v2/internal/error" + cobrautil "github.com/opencurve/curve/tools-v2/internal/utils" + snapshotutil "github.com/opencurve/curve/tools-v2/internal/utils/snapshot" + basecmd "github.com/opencurve/curve/tools-v2/pkg/cli/command" + "github.com/opencurve/curve/tools-v2/pkg/config" + "github.com/opencurve/curve/tools-v2/pkg/output" + "github.com/spf13/cobra" +) + +const ( + LENGTH_EMPTY = 0 +) + +type SnapshotCommand struct { + basecmd.FinalCurveCmd + + snapshotAddrs []string + timeout time.Duration + user string + filename string + + errCode int + health cobrautil.ClUSTER_HEALTH_STATUS +} + +var _ basecmd.FinalCurveCmdFunc = (*SnapshotCommand)(nil) // check interface + +func NewSnapshotCommand() *cobra.Command { + return NewVolumeSnapshotCommand().Cmd +} + +func NewVolumeSnapshotCommand() *SnapshotCommand { + vsCmd := &SnapshotCommand{ + FinalCurveCmd: basecmd.FinalCurveCmd{ + Use: "snapshot", + Short: "show the snapshot status", + }, + } + basecmd.NewFinalCurveCli(&vsCmd.FinalCurveCmd, vsCmd) + return vsCmd +} + +func (sCmd *SnapshotCommand) Init(cmd *cobra.Command, args []string) error { + snapshotAddrs, err := config.GetBsSnapshotAddrSlice(sCmd.Cmd) + if err.TypeCode() != cmderror.CODE_SUCCESS || len(snapshotAddrs) == 0 { + return err.ToError() + } + sCmd.snapshotAddrs = snapshotAddrs + sCmd.filename = config.GetBsFlagString(sCmd.Cmd, config.CURVEBS_FILENAME) + sCmd.user = config.GetBsFlagString(sCmd.Cmd, config.CURVEBS_USER) + sCmd.timeout = config.GetFlagDuration(sCmd.Cmd, config.HTTPTIMEOUT) + + header := []string{cobrautil.ROW_STATUS, cobrautil.ROW_COUNT} + sCmd.SetHeader(header) + + return nil +} + +func (sCmd *SnapshotCommand) RunCommand(cmd *cobra.Command, args []string) error { + params := map[string]any{ + snapshotutil.QueryAction: snapshotutil.ActionGetFileSnapshotList, + snapshotutil.QueryUser: sCmd.user, + snapshotutil.QueryFile: sCmd.filename, + snapshotutil.QueryLimit: snapshotutil.Limit, + snapshotutil.QueryOffset: snapshotutil.Offset, + } + count := make(map[int]int) + for { + subUri := snapshotutil.NewQuerySubUri(params) + + metric := basecmd.NewMetric(sCmd.snapshotAddrs, subUri, sCmd.timeout) + result, err := basecmd.QueryMetric(metric) + if err.TypeCode() != cmderror.CODE_SUCCESS { + sCmd.Error = cmderror.ErrBsGetSnapshotStatus() + sCmd.Error.Format(fmt.Sprintf("code: %d, msg: %s", err.TypeCode(), err.ToError().Error())) + return sCmd.Error.ToError() + } + + var resp struct { + snapshotutil.Response + TotalCount int `json:"TotalCount"` + SnapShots []*snapshotutil.SnapshotInfo `json:"SnapShots"` + } + if err := json.Unmarshal([]byte(result), &resp); err != nil { + sCmd.Error = cmderror.ErrUnmarshalJson() + sCmd.Error.Format(err.Error()) + return sCmd.Error.ToError() + } + if resp.Code != snapshotutil.ResultSuccess { + sCmd.Error = cmderror.ErrBsGetSnapshotStatus() + sCmd.Error.Format(fmt.Sprintf("code: %s, msg: %s", resp.Code, resp.Message)) + return sCmd.Error.ToError() + } + if len(resp.SnapShots) == LENGTH_EMPTY { + break + } + for _, s := range resp.SnapShots { + count[s.Status] += 1 + } + + params[snapshotutil.QueryOffset] = params[snapshotutil.QueryOffset].(int) + params[snapshotutil.QueryLimit].(int) + } + + rows := make([]map[string]string, 0) + for i, s := range snapshotutil.SnapshotStatus { + rows = append(rows, map[string]string{ + cobrautil.ROW_STATUS: s, + cobrautil.ROW_COUNT: strconv.Itoa(count[i]), + }) + } + list := cobrautil.ListMap2ListSortByKeys(rows, sCmd.Header, []string{}) + sCmd.TableNew.AppendBulk(list) + sCmd.Result = rows + sCmd.Error = cmderror.ErrSuccess() + + return nil +} + +func (sCmd *SnapshotCommand) Print(cmd *cobra.Command, args []string) error { + return output.FinalCmdOutput(&sCmd.FinalCurveCmd, sCmd) +} + +func (sCmd *SnapshotCommand) ResultPlainOutput() error { + return output.FinalCmdOutputPlain(&sCmd.FinalCurveCmd) +} + +func (sCmd *SnapshotCommand) AddFlags() { + config.AddBsUserOptionFlag(sCmd.Cmd) + config.AddBsSnapshotFilenameFlag(sCmd.Cmd) + config.AddBsSnapshotCloneFlagOption(sCmd.Cmd) + config.AddHttpTimeoutFlag(sCmd.Cmd) +} diff --git a/tools-v2/pkg/cli/command/curvebs/status/volume/volume.go b/tools-v2/pkg/cli/command/curvebs/status/volume/volume.go new file mode 100644 index 0000000000..6314c65282 --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/status/volume/volume.go @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Project: CurveCli + * Created Date: 2023-09-13 + * Author: saicaca + */ + +package volume + +import ( + basecmd "github.com/opencurve/curve/tools-v2/pkg/cli/command" + "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status/volume/snapshot" + "github.com/spf13/cobra" +) + +type VolumeCommand struct { + basecmd.MidCurveCmd +} + +var _ basecmd.MidCurveCmdFunc = (*VolumeCommand)(nil) // check interface + +func (volumeCmd *VolumeCommand) AddSubCommands() { + volumeCmd.Cmd.AddCommand( + snapshot.NewSnapshotCommand(), + ) +} + +func NewVolumeCommand() *cobra.Command { + volumeCmd := &VolumeCommand{ + basecmd.MidCurveCmd{ + Use: "volume", + Short: "get the status of curvebs volume", + }, + } + return basecmd.NewMidCurveCli(&volumeCmd.MidCurveCmd, volumeCmd) +} diff --git a/tools-v2/pkg/config/bs.go b/tools-v2/pkg/config/bs.go index 2eceb2b9ca..f78194e350 100644 --- a/tools-v2/pkg/config/bs.go +++ b/tools-v2/pkg/config/bs.go @@ -147,11 +147,12 @@ const ( CURVEBS_DEFAULT_SNAPSHOT_ID = "*" CURVEBS_FAILED = "failed" VIPER_CURVEBS_FAILED = "curvebs.failed" - CURVEBS_CHUNK_SIZE = "chunksize" - VIPER_CURVEBS_CHUNK_SIZE = "curvebs.chunksize" - CURVEBS_CHECK_HASH = "checkhash" - VIPER_CURVEBS_CHECK_HASH = "curvebs.checkhash" - CURVEBS_DEFAULT_CHECK_HASH = false + CURVEBS_CHUNK_SIZE = "chunksize" + VIPER_CURVEBS_CHUNK_SIZE = "curvebs.chunksize" + CURVEBS_CHECK_HASH = "checkhash" + VIPER_CURVEBS_CHECK_HASH = "curvebs.checkhash" + CURVEBS_DEFAULT_CHECK_HASH = false + CURVEBS_FILENAME = "filename" ) var ( @@ -207,8 +208,8 @@ var ( CURVEBS_TASKID: VIPER_CURVEBS_TASKID, CURVEBS_SNAPSHOT_ID: VIPER_CURVEBS_SNAPSHOT_ID, CURVEBS_FAILED: VIPER_CURVEBS_FAILED, - CURVEBS_CHUNK_SIZE: VIPER_CURVEBS_CHUNK_SIZE, - CURVEBS_CHECK_HASH: VIPER_CURVEBS_CHECK_HASH, + CURVEBS_CHUNK_SIZE: VIPER_CURVEBS_CHUNK_SIZE, + CURVEBS_CHECK_HASH: VIPER_CURVEBS_CHECK_HASH, } BSFLAG2DEFAULT = map[string]interface{}{ @@ -232,7 +233,7 @@ var ( CURVEBS_ALL: CURVEBS_DEFAULT_ALL, CURVEBS_LOGIC_POOL_ID: CURVEBS_DEFAULT_LOGIC_POOL_ID, CURVEBS_COPYSET_ID: CURVEBS_DEFAULT_COPYSET_ID, - CURVEBS_CHECK_HASH: CURVEBS_DEFAULT_CHECK_HASH, + CURVEBS_CHECK_HASH: CURVEBS_DEFAULT_CHECK_HASH, CURVEBS_SNAPSHOT_ID: CURVEBS_DEFAULT_SNAPSHOT_ID, } ) @@ -663,6 +664,10 @@ func AddBsFailedOptionFlag(cmd *cobra.Command) { AddBsBoolOptionFlag(cmd, CURVEBS_FAILED, "failed") } +func AddBsSnapshotFilenameFlag(cmd *cobra.Command) { + AddBsStringOptionFlag(cmd, CURVEBS_FILENAME, "snapshot filename") +} + func AddBsUserRequireFlag(cmd *cobra.Command) { AddBsStringRequiredFlag(cmd, CURVEBS_USER, "user name") }