diff --git a/tools-v2/README.md b/tools-v2/README.md index b9fea277f8..a0342f973e 100644 --- a/tools-v2/README.md +++ b/tools-v2/README.md @@ -1561,6 +1561,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 a8d91a9cc9..931abebfdb 100644 --- a/tools-v2/internal/error/error.go +++ b/tools-v2/internal/error/error.go @@ -493,6 +493,9 @@ var ( ErrBsGetChunkHash = func() *CmdError { return NewInternalCmdError(77, "get chunk hash fail, err: %s") } + ErrBsGetSnapshotStatus = func() *CmdError { + return NewInternalCmdError(78, "get snapshot status fail, err: %s") + } // http error ErrHttpUnreadableResult = func() *CmdError { diff --git a/tools-v2/internal/utils/row.go b/tools-v2/internal/utils/row.go index 33590c75f8..9e60327715 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/internal/utils/snapshot.go b/tools-v2/internal/utils/snapshot.go index 358c7b4c44..ba36ad8fb2 100644 --- a/tools-v2/internal/utils/snapshot.go +++ b/tools-v2/internal/utils/snapshot.go @@ -42,6 +42,7 @@ const ( QueryOffset = "Offset" QueryStatus = "Status" QueryType = "Type" + QueryFile = "File" ActionClone = "Clone" ActionRecover = "Recover" @@ -64,7 +65,7 @@ func NewSnapshotQuerySubUri(params map[string]any) string { values.Add(QueryVersion, Version) for key, value := range params { if value != "" { - values.Add(key, fmt.Sprintf("%s", value)) + values.Add(key, fmt.Sprintf("%v", value)) } } 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..0687f558d3 --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/status/volume/snapshot/snapshot.go @@ -0,0 +1,204 @@ +/* + * 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" + cmderror "github.com/opencurve/curve/tools-v2/internal/error" + cobrautil "github.com/opencurve/curve/tools-v2/internal/utils" + 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" + "strconv" + "time" +) + +type QueryResponse struct { + Code string `json:"Code"` + Message string `json:"Message"` + RequestId string `json:"RequestId"` + TotalCount int `json:"TotalCount"` + Snapshots []*SnapshotInfoData `json:"Snapshots,omitempty"` +} + +type SnapshotInfoData struct { + UUID string `json:"UUID"` + User string `json:"User"` + FileName string `json:"FileName"` + SnapshotName string `json:"SnapshotName"` + SeqNum uint64 `json:"SeqNum"` + ChunkSize uint32 `json:"ChunkSize"` + SegmentSize uint64 `json:"SegmentSize"` + FileLength uint64 `json:"FileLength"` + Time uint64 `json:"Time"` + Status int32 `json:"Status"` + StripeUnit *uint64 `json:"StripeUnit,omitempty"` + StripeCount *uint64 `json:"StripeCount,omitempty"` + Poolset *string `json:"Poolset,omitempty"` +} + +const ( + SNAPSHOT_QUERY_LIMIT = 100 + SNAPSHOT_QUERY_INITIAL_OFFSET = 0 + SNAPSHOT_QUERY_CODE_SUCCESS = "0" + LENGTH_EMPTY = 0 +) + +const ( + STATUS_DONE = iota + STATUS_IN_PROGRESS + STATUS_DELETING + STATUS_ERROR_DELETING + STATUS_CANCELING + STATUS_ERROR +) + +var statusList = []int{ + STATUS_DONE, + STATUS_IN_PROGRESS, + STATUS_DELETING, + STATUS_ERROR_DELETING, + STATUS_CANCELING, + STATUS_ERROR, +} + +var statusName = map[int]string{ + STATUS_DONE: "done", + STATUS_IN_PROGRESS: "in-progress", + STATUS_DELETING: "deleting", + STATUS_ERROR_DELETING: "errorDeleting", + STATUS_CANCELING: "canceling", + STATUS_ERROR: "error", +} + +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{ + cobrautil.QueryAction: cobrautil.ActionGetFileSnapshotList, + cobrautil.QueryUser: sCmd.user, + cobrautil.QueryFile: sCmd.filename, + cobrautil.QueryLimit: SNAPSHOT_QUERY_LIMIT, + cobrautil.QueryOffset: SNAPSHOT_QUERY_INITIAL_OFFSET, + } + count := make(map[int]int) + for { + subUri := cobrautil.NewSnapshotQuerySubUri(params) + + metric := basecmd.NewMetric(sCmd.snapshotAddrs, subUri, sCmd.timeout) + result, err := basecmd.QueryMetric(metric) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + + var resp QueryResponse + if err := json.Unmarshal([]byte(result), &resp); err != nil { + return err + } + if resp.Code != SNAPSHOT_QUERY_CODE_SUCCESS { + return fmt.Errorf("get clone list fail, error code: %s", resp.Code) + } + if len(resp.Snapshots) == LENGTH_EMPTY { + break + } + for _, s := range resp.Snapshots { + count[int(s.Status)] += 1 + } + + params[cobrautil.QueryOffset] = params[cobrautil.QueryOffset].(int) + params[cobrautil.QueryLimit].(int) + } + + rows := make([]map[string]string, 0) + for _, s := range statusList { + rows = append(rows, map[string]string{ + cobrautil.ROW_STATUS: statusName[s], + cobrautil.ROW_COUNT: strconv.Itoa(count[s]), + }) + } + 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 08b80dd179..df68402278 100644 --- a/tools-v2/pkg/config/bs.go +++ b/tools-v2/pkg/config/bs.go @@ -144,11 +144,12 @@ const ( VIPER_CURVEBS_TASKID = "curvebs.taskid" 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 ( @@ -203,8 +204,8 @@ var ( CURVEBS_DEST: VIPER_CURVEBS_DEST, CURVEBS_TASKID: VIPER_CURVEBS_TASKID, 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{}{ @@ -228,7 +229,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, } ) @@ -658,6 +659,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") }