diff --git a/tools-v2/README.md b/tools-v2/README.md index cb8683b0b7..524d98dc1b 100644 --- a/tools-v2/README.md +++ b/tools-v2/README.md @@ -63,6 +63,8 @@ A tool for CurveFS & CurveBs. - [update throttle](#update-throttle) - [create dir](#create-dir) - [clean-recycle](#clean-recycle) + - [check](#check-1) + - [check copyset](#check-copyset) - [Comparison of old and new commands](#comparison-of-old-and-new-commands) - [curve fs](#curve-fs) - [curve bs](#curve-bs) @@ -1173,6 +1175,28 @@ Output: +---------+ ``` +### check + +#### check copyset + +check copysets health in curvebs + +Usage: + +```shell +curve bs check copyset --copysetid 1 --logicalpoolid 1 +``` + +Output: + +```shell ++------------+-----------+--------+--------+--------+---------+ +| COPYSETKEY | COPYSETID | POOLID | STATUS | LOGGAP | EXPLAIN | ++------------+-----------+--------+--------+--------+---------+ +| 4294967297 | 1 | 1 | ok | 0 | | ++------------+-----------+--------+--------+--------+---------+ +``` + ## Comparison of old and new commands ### curve fs @@ -1220,6 +1244,7 @@ Output: | reset-peer | curve bs update peer | | space | curve bs list space | | update-throttle | curve bs update throttle | +| check-copyset | curve bs check copyset | | status | | | chunkserver-status | | | client-status | | @@ -1233,7 +1258,6 @@ Output: | do-snapshot | | | do-snapshot-all | | | check-chunkserver | | -| check-copyset | | | check-server | | | check-operator | | | list-may-broken-vol | | diff --git a/tools-v2/internal/error/error.go b/tools-v2/internal/error/error.go index c7b74bb124..f106bd123c 100644 --- a/tools-v2/internal/error/error.go +++ b/tools-v2/internal/error/error.go @@ -414,7 +414,9 @@ var ( ErrBsListDir = func() *CmdError { return NewInternalCmdError(51, "list directory fail, err: %s") } - + ErrBsGetCopysetStatus = func() *CmdError { + return NewInternalCmdError(52, "get copyset status fail, err: %s") + } // http error ErrHttpUnreadableResult = func() *CmdError { return NewHttpResultCmdError(1, "http response is unreadable, the uri is: %s, the error is: %s") diff --git a/tools-v2/internal/utils/copyset.go b/tools-v2/internal/utils/copyset.go index 2a0528d8b5..d7762c88de 100644 --- a/tools-v2/internal/utils/copyset.go +++ b/tools-v2/internal/utils/copyset.go @@ -27,6 +27,8 @@ import ( cmderror "github.com/opencurve/curve/tools-v2/internal/error" "github.com/opencurve/curve/tools-v2/proto/curvefs/proto/copyset" "github.com/opencurve/curve/tools-v2/proto/curvefs/proto/heartbeat" + bscopyset "github.com/opencurve/curve/tools-v2/proto/proto/copyset" + bsheartbeat "github.com/opencurve/curve/tools-v2/proto/proto/heartbeat" ) type CopysetInfoStatus struct { @@ -34,6 +36,11 @@ type CopysetInfoStatus struct { Peer2Status map[string]*copyset.CopysetStatusResponse `json:"peer status,omitempty"` } +type BsCopysetInfoStatus struct { + Info *bsheartbeat.CopySetInfo `json:"info,omitempty"` + Peer2Status map[string]*bscopyset.CopysetStatusResponse `json:"peer status,omitempty"` +} + type COPYSET_HEALTH_STATUS int32 const ( @@ -153,6 +160,45 @@ func CheckCopySetHealth(copysetIS *CopysetInfoStatus) (COPYSET_HEALTH_STATUS, [] } } +func CheckBsCopySetHealth(copysetIS *BsCopysetInfoStatus) (COPYSET_HEALTH_STATUS, []*cmderror.CmdError) { + peers := copysetIS.Info.GetPeers() + peer2Status := copysetIS.Peer2Status + avalibalePeerNum := 0 + var errs []*cmderror.CmdError + for addr, status := range peer2Status { + if status == nil { + // peer is offline + err := cmderror.ErrOfflineCopysetPeer() + err.Format(addr) + errs = append(errs, err) + continue + } + opStatus := status.GetStatus() + state := status.GetState() + peer := status.GetPeer() + if opStatus == bscopyset.COPYSET_OP_STATUS_COPYSET_OP_STATUS_SUCCESS && CopysetState_Avaliable[state] { + avalibalePeerNum++ + } else if opStatus != bscopyset.COPYSET_OP_STATUS_COPYSET_OP_STATUS_SUCCESS { + err := cmderror.ErrBsCopysetOpStatus(opStatus, addr) + errs = append(errs, err) + } else { + err := cmderror.ErrStateCopysetPeer() + err.Format(peer.String(), CopysetState_name[state]) + errs = append(errs, err) + } + } + + n := len(peers) + switch { + case avalibalePeerNum == n: + return COPYSET_OK, errs + case avalibalePeerNum >= n/2+1: + return COPYSET_WARN, errs + default: + return COPYSET_ERROR, errs + } +} + func GetCopysetKey(poolid uint64, copysetid uint64) uint64 { return (poolid << 32) | copysetid } diff --git a/tools-v2/internal/utils/string.go b/tools-v2/internal/utils/string.go index 2140d19a95..7d21b69b92 100644 --- a/tools-v2/internal/utils/string.go +++ b/tools-v2/internal/utils/string.go @@ -190,3 +190,15 @@ func StringList2Uint64List(strList []string) ([]uint64, error) { } return retList, nil } + +func StringList2Uint32List(strList []string) ([]uint32, error) { + retList := make([]uint32, 0) + for _, str := range strList { + v, err := strconv.ParseUint(str, 10, 32) + if err != nil { + return nil, err + } + retList = append(retList, uint32(v)) + } + return retList, nil +} diff --git a/tools-v2/pkg/cli/command/curvebs/bs.go b/tools-v2/pkg/cli/command/curvebs/bs.go index 0c6f533260..38ff6fcf29 100644 --- a/tools-v2/pkg/cli/command/curvebs/bs.go +++ b/tools-v2/pkg/cli/command/curvebs/bs.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" basecmd "github.com/opencurve/curve/tools-v2/pkg/cli/command" + "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/check" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/clean_recycle" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/create" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/delete" @@ -50,6 +51,7 @@ func (bsCmd *CurveBsCommand) AddSubCommands() { create.NewCreateCmd(), update.NewUpdateCommand(), clean_recycle.NewCleanRecycleCommand(), + check.NewCheckCommand(), ) } diff --git a/tools-v2/pkg/cli/command/curvebs/check/check.go b/tools-v2/pkg/cli/command/curvebs/check/check.go new file mode 100644 index 0000000000..5b90ff6c0a --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/check/check.go @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 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-04-24 + * Author: baytan0720 + */ + +package check + +import ( + basecmd "github.com/opencurve/curve/tools-v2/pkg/cli/command" + "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/check/copyset" + "github.com/spf13/cobra" +) + +type CheckCommand struct { + basecmd.MidCurveCmd +} + +var _ basecmd.MidCurveCmdFunc = (*CheckCommand)(nil) // check interface + +func (checkCmd *CheckCommand) AddSubCommands() { + checkCmd.Cmd.AddCommand( + copyset.NewCopysetCommand(), + ) +} + +func NewCheckCommand() *cobra.Command { + checkCmd := &CheckCommand{ + basecmd.MidCurveCmd{ + Use: "check", + Short: "checkout the health of resources in curvebs", + }, + } + return basecmd.NewMidCurveCli(&checkCmd.MidCurveCmd, checkCmd) +} diff --git a/tools-v2/pkg/cli/command/curvebs/check/copyset/copyset.go b/tools-v2/pkg/cli/command/curvebs/check/copyset/copyset.go new file mode 100644 index 0000000000..30b4ad7709 --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/check/copyset/copyset.go @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2022 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-04-24 + * Author: baytan0720 + */ + +package copyset + +import ( + "fmt" + "sort" + "strings" + "sync" + "time" + + mapset "github.com/deckarep/golang-set/v2" + 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/cli/command/curvebs/query/chunk" + status "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status/copyset" + fscopyset "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvefs/check/copyset" + "github.com/opencurve/curve/tools-v2/pkg/config" + "github.com/opencurve/curve/tools-v2/pkg/output" + "github.com/opencurve/curve/tools-v2/proto/proto/common" + "github.com/opencurve/curve/tools-v2/proto/proto/copyset" + "github.com/opencurve/curve/tools-v2/proto/proto/heartbeat" + "github.com/spf13/cobra" +) + +const ( + copysetExample = `$ curve bs check copyset --copysetid 1 --logicalpoolid 1` +) + +type CopysetCommand struct { + basecmd.FinalCurveCmd + key2Copyset *map[uint64]*cobrautil.BsCopysetInfoStatus + Key2LeaderInfo *map[uint64]*fscopyset.CopysetLeaderInfo + Key2Health *map[uint64]cobrautil.ClUSTER_HEALTH_STATUS + leaderAddr mapset.Set[string] +} + +var _ basecmd.FinalCurveCmdFunc = (*CopysetCommand)(nil) // check interface + +func NewCopysetCommand() *cobra.Command { + return NewCheckCopysetCommand().Cmd +} + +func NewCheckCopysetCommand() *CopysetCommand { + copysetCmd := &CopysetCommand{ + FinalCurveCmd: basecmd.FinalCurveCmd{ + Use: "copyset", + Short: "check copysets health in curvebs", + Example: copysetExample, + }, + } + basecmd.NewFinalCurveCli(©setCmd.FinalCurveCmd, copysetCmd) + return copysetCmd +} + +func (cCmd *CopysetCommand) AddFlags() { + config.AddBsMdsFlagOption(cCmd.Cmd) + config.AddRpcRetryTimesFlag(cCmd.Cmd) + config.AddRpcTimeoutFlag(cCmd.Cmd) + + config.AddBsCopysetIdSliceRequiredFlag(cCmd.Cmd) + config.AddBsLogicalPoolIdSliceRequiredFlag(cCmd.Cmd) + config.AddBsMarginOptionFlag(cCmd.Cmd) +} + +func (cCmd *CopysetCommand) Init(cmd *cobra.Command, args []string) error { + mdsAddrs := config.GetBsFlagString(cmd, config.CURVEBS_MDSADDR) + retrytime := config.GetBsFlagInt32(cmd, config.RPCRETRYTIMES) + timeout := config.GetFlagDuration(cCmd.Cmd, config.RPCTIMEOUT) + margin := config.GetBsMargin(cCmd.Cmd) + + logicalpoolidList := config.GetBsFlagStringSlice(cCmd.Cmd, config.CURVEBS_LOGIC_POOL_ID) + copysetidList := config.GetBsFlagStringSlice(cCmd.Cmd, config.CURVEBS_COPYSET_ID) + if len(logicalpoolidList) != len(copysetidList) { + return fmt.Errorf("the number of logicpoolid and copysetid is not equal") + } + logicalpoolIds, errParse := cobrautil.StringList2Uint32List(logicalpoolidList) + if errParse != nil { + return fmt.Errorf("parse logicalpoolid%v fail", logicalpoolidList) + } + copysetIds, errParse := cobrautil.StringList2Uint32List(copysetidList) + if errParse != nil { + return fmt.Errorf("parse copysetid%v fail", copysetidList) + } + + key2Location, err := chunk.GetChunkServerListInCopySets(cCmd.Cmd) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + + for i := 0; i < len(logicalpoolIds); i++ { + logicpoolid := logicalpoolIds[i] + copysetid := copysetIds[i] + key := cobrautil.GetCopysetKey(uint64(logicpoolid), uint64(copysetid)) + + var peerAddress []string + for _, cs := range (*key2Location)[key] { + address := fmt.Sprintf("%s:%d", *cs.HostIp, *cs.Port) + peerAddress = append(peerAddress, address) + } + + cCmd.Cmd.ResetFlags() + config.AddBsMdsFlagOption(cCmd.Cmd) + config.AddRpcRetryTimesFlag(cCmd.Cmd) + config.AddRpcTimeoutFlag(cCmd.Cmd) + config.AddBSCopysetIdRequiredFlag(cCmd.Cmd) + config.AddBSLogicalPoolIdRequiredFlag(cCmd.Cmd) + config.AddBSPeersConfFlag(cCmd.Cmd) + cCmd.Cmd.ParseFlags([]string{ + fmt.Sprintf("--%s", config.CURVEBS_MDSADDR), mdsAddrs, + fmt.Sprintf("--%s", config.RPCRETRYTIMES), fmt.Sprintf("%d", retrytime), + fmt.Sprintf("--%s", config.CURVEBS_LOGIC_POOL_ID), logicalpoolidList[i], + fmt.Sprintf("--%s", config.CURVEBS_COPYSET_ID), copysetidList[i], + fmt.Sprintf("--%s", config.CURVEBS_PEERS_ADDRESS), + strings.Join(peerAddress, ","), + }) + results, err := status.GetCopysetStatus(cCmd.Cmd) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + peers := make([]*common.Peer, 0, len(results)) + peer2Status := make(map[string]*copyset.CopysetStatusResponse) + var leaderPeer *common.Peer + for _, result := range results { + if *result.Leader.Address == *result.Peer.Address { + leaderPeer = result.Peer + } + peers = append(peers, result.Peer) + peer2Status[*result.Peer.Address] = result + } + + copysetKey := cobrautil.GetCopysetKey(uint64(logicpoolid), uint64(copysetid)) + if cCmd.key2Copyset == nil { + key2copyset := make(map[uint64]*cobrautil.BsCopysetInfoStatus) + cCmd.key2Copyset = &key2copyset + } + + (*cCmd.key2Copyset)[copysetKey] = &cobrautil.BsCopysetInfoStatus{ + Info: &heartbeat.CopySetInfo{ + CopysetId: ©setid, + Peers: peers, + LeaderPeer: leaderPeer, + }, + Peer2Status: peer2Status, + } + } + config.AddMarginOptionFlag(cCmd.Cmd) + config.AddFormatFlag(cCmd.Cmd) + cCmd.Cmd.ParseFlags([]string{ + fmt.Sprintf("--%s", config.CURVEBS_MARGIN), + fmt.Sprintf("%d", margin), + }) + + header := []string{cobrautil.ROW_COPYSET_KEY, cobrautil.ROW_COPYSET_ID, + cobrautil.ROW_POOL_ID, cobrautil.ROW_STATUS, cobrautil.ROW_LOG_GAP, + cobrautil.ROW_EXPLAIN, + } + cCmd.SetHeader(header) + cCmd.TableNew.SetAutoMergeCellsByColumnIndex(cobrautil.GetIndexSlice( + cCmd.Header, []string{cobrautil.ROW_COPYSET_ID, cobrautil.ROW_POOL_ID, + cobrautil.ROW_STATUS, + }, + )) + + // update leaderAddr + cCmd.leaderAddr = mapset.NewSet[string]() + for _, cs := range *cCmd.key2Copyset { + addr, err := cobrautil.PeerAddressToAddr(cs.Info.LeaderPeer.GetAddress()) + if err.TypeCode() != cmderror.CODE_SUCCESS { + err := cmderror.ErrCopysetInfo() + err.Format(cs.Info.CopysetId) + } else { + cCmd.leaderAddr.Add(addr) + } + } + + key2LeaderInfo := make(map[uint64]*fscopyset.CopysetLeaderInfo) + cCmd.Key2LeaderInfo = &key2LeaderInfo + err = cCmd.UpdateCopysteGap(timeout) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + return nil +} + +func (cCmd *CopysetCommand) Print(cmd *cobra.Command, args []string) error { + return output.FinalCmdOutput(&cCmd.FinalCurveCmd, cCmd) +} + +func (cCmd *CopysetCommand) RunCommand(cmd *cobra.Command, args []string) error { + key2Health := make(map[uint64]cobrautil.ClUSTER_HEALTH_STATUS) + cCmd.Key2Health = &key2Health + rows := make([]map[string]string, 0) + var errs []*cmderror.CmdError + for k, v := range *cCmd.key2Copyset { + copysetHealthCount := make(map[cobrautil.COPYSET_HEALTH_STATUS]uint32) + row := make(map[string]string) + row[cobrautil.ROW_COPYSET_KEY] = fmt.Sprintf("%d", k) + poolid, copysetid := cobrautil.CopysetKey2PoolidCopysetid(k) + row[cobrautil.ROW_POOL_ID] = fmt.Sprintf("%d", poolid) + row[cobrautil.ROW_COPYSET_ID] = fmt.Sprintf("%d", copysetid) + if v == nil { + row[cobrautil.ROW_STATUS] = cobrautil.CopysetHealthStatus_Str[int32(cobrautil.COPYSET_NOTEXIST)] + } else { + status, errsCheck := cobrautil.CheckBsCopySetHealth(v) + copysetHealthCount[status]++ + row[cobrautil.ROW_STATUS] = cobrautil.CopysetHealthStatus_Str[int32(status)] + explain := "" + if status != cobrautil.COPYSET_OK { + for i, e := range errsCheck { + if i != len(errsCheck) { + explain += fmt.Sprintf("%s\n", e.Message) + } else { + explain += e.Message + } + errs = append(errs, e) + } + } + margin := config.GetMarginOptionFlag(cCmd.Cmd) + leaderInfo := (*cCmd.Key2LeaderInfo)[k] + if leaderInfo == nil { + explain = "no leader peer" + copysetHealthCount[cobrautil.COPYSET_ERROR]++ + row[cobrautil.ROW_STATUS] = cobrautil.CopysetHealthStatus_Str[int32(cobrautil.COPYSET_ERROR)] + } else if leaderInfo.Snapshot { + installSnapshot := "installing snapshot" + if len(explain) > 0 { + explain += "\n" + } + explain += installSnapshot + if row[cobrautil.ROW_STATUS] == cobrautil.COPYSET_OK_STR { + row[cobrautil.ROW_STATUS] = cobrautil.COPYSET_WARN_STR + copysetHealthCount[cobrautil.COPYSET_WARN]++ + } + } else { + gap := leaderInfo.Gap + row[cobrautil.ROW_LOG_GAP] = fmt.Sprintf("%d", gap) + if gap >= margin { + behind := fmt.Sprintf("log behind %d", gap) + if len(explain) > 0 { + explain += "\n" + } + explain += behind + if row[cobrautil.ROW_STATUS] == cobrautil.COPYSET_OK_STR { + row[cobrautil.ROW_STATUS] = cobrautil.COPYSET_WARN_STR + copysetHealthCount[cobrautil.COPYSET_WARN]++ + } + } + + } + row[cobrautil.ROW_EXPLAIN] = explain + } + if copysetHealthCount[cobrautil.COPYSET_NOTEXIST] > 0 || copysetHealthCount[cobrautil.COPYSET_ERROR] > 0 { + (*cCmd.Key2Health)[k] = cobrautil.HEALTH_ERROR + } else if copysetHealthCount[cobrautil.COPYSET_WARN] > 0 { + (*cCmd.Key2Health)[k] = cobrautil.HEALTH_WARN + } else { + (*cCmd.Key2Health)[k] = cobrautil.HEALTH_OK + } + rows = append(rows, row) + } + retErr := cmderror.MergeCmdErrorExceptSuccess(errs) + cCmd.Error = retErr + sort.Slice(rows, func(i, j int) bool { + return rows[i][cobrautil.ROW_COPYSET_KEY] < rows[j][cobrautil.ROW_COPYSET_KEY] + }) + list := cobrautil.ListMap2ListSortByKeys(rows, cCmd.Header, []string{ + cobrautil.ROW_POOL_ID, cobrautil.ROW_STATUS, cobrautil.ROW_COPYSET_ID, + }) + cCmd.TableNew.AppendBulk(list) + cCmd.Result = rows + return nil +} + +func (cCmd *CopysetCommand) ResultPlainOutput() error { + return output.FinalCmdOutputPlain(&cCmd.FinalCurveCmd) +} + +func (cCmd *CopysetCommand) UpdateCopysetGap(timeout time.Duration) *cmderror.CmdError { + var key2LeaderInfo sync.Map + size := config.MaxChannelSize() + errChan := make(chan *cmderror.CmdError, size) + count := 0 + for iter := range cCmd.leaderAddr.Iter() { + go func(addr string) { + err := fscopyset.GetLeaderCopysetGap(addr, &key2LeaderInfo, timeout) + errChan <- err + }(iter) + count++ + } + var errs []*cmderror.CmdError + for i := 0; i < count; i++ { + err := <-errChan + if err.TypeCode() != cmderror.CODE_SUCCESS { + errs = append(errs, err) + } + } + key2LeaderInfo.Range(func(key, value interface{}) bool { + (*cCmd.Key2LeaderInfo)[key.(uint64)] = value.(*fscopyset.CopysetLeaderInfo) + return true + }) + retErr := cmderror.MergeCmdErrorExceptSuccess(errs) + return retErr +} + +func (cCmd *CopysetCommand) UpdateCopysteGap(timeout time.Duration) *cmderror.CmdError { + var key2LeaderInfo sync.Map + size := config.MaxChannelSize() + errChan := make(chan *cmderror.CmdError, size) + count := 0 + for iter := range cCmd.leaderAddr.Iter() { + go func(addr string) { + err := fscopyset.GetLeaderCopysetGap(addr, &key2LeaderInfo, timeout) + errChan <- err + }(iter) + count++ + } + var errs []*cmderror.CmdError + for i := 0; i < count; i++ { + err := <-errChan + if err.TypeCode() != cmderror.CODE_SUCCESS { + errs = append(errs, err) + } + } + key2LeaderInfo.Range(func(key, value interface{}) bool { + (*cCmd.Key2LeaderInfo)[key.(uint64)] = value.(*fscopyset.CopysetLeaderInfo) + return true + }) + retErr := cmderror.MergeCmdErrorExceptSuccess(errs) + return retErr +} + +func CheckCopysets(caller *cobra.Command) (*map[uint64]cobrautil.ClUSTER_HEALTH_STATUS, *cmderror.CmdError) { + cCmd := NewCheckCopysetCommand() + cCmd.Cmd.SetArgs([]string{"--format", config.FORMAT_NOOUT}) + config.AlignFlagsValue(caller, cCmd.Cmd, []string{ + config.RPCRETRYTIMES, config.RPCTIMEOUT, config.CURVEBS_MDSADDR, + config.CURVEFS_COPYSETID, config.CURVEFS_POOLID, config.CURVEBS_MARGIN, + }) + cCmd.Cmd.SilenceErrors = true + err := cCmd.Cmd.Execute() + if err != nil { + retErr := cmderror.ErrCheckCopyset() + retErr.Format(err.Error()) + return cCmd.Key2Health, retErr + } + return cCmd.Key2Health, cCmd.Error +} diff --git a/tools-v2/pkg/cli/command/curvebs/status/copyset/get_copyset_status.go b/tools-v2/pkg/cli/command/curvebs/status/copyset/get_copyset_status.go new file mode 100644 index 0000000000..c149b3ca9c --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/status/copyset/get_copyset_status.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2022 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: tools-v2 + * Created Date: 2023-04-24 + * Author: baytan0720 + */ + +package copyset + +import ( + "context" + cmderror "github.com/opencurve/curve/tools-v2/internal/error" + 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/opencurve/curve/tools-v2/proto/proto/common" + "github.com/opencurve/curve/tools-v2/proto/proto/copyset" + "github.com/spf13/cobra" + "google.golang.org/grpc" +) + +type GetCopysetStatusRpc struct { + Info *basecmd.Rpc + Request *copyset.CopysetStatusRequest + mdsClient copyset.CopysetServiceClient +} + +var _ basecmd.RpcFunc = (*GetCopysetStatusRpc)(nil) // check interface + +func (gRpc *GetCopysetStatusRpc) NewRpcClient(cc grpc.ClientConnInterface) { + gRpc.mdsClient = copyset.NewCopysetServiceClient(cc) +} + +func (gRpc *GetCopysetStatusRpc) Stub_Func(ctx context.Context) (interface{}, error) { + return gRpc.mdsClient.GetCopysetStatus(ctx, gRpc.Request) +} + +type GetCopysetStatusCommand struct { + basecmd.FinalCurveCmd + Rpc []*GetCopysetStatusRpc + response []*copyset.CopysetStatusResponse +} + +var _ basecmd.FinalCurveCmdFunc = (*GetCopysetStatusCommand)(nil) // check interface + +func NewCopysetCommand() *cobra.Command { + return NewGetCopysetStatusCommand().Cmd +} + +func NewGetCopysetStatusCommand() *GetCopysetStatusCommand { + copysetCmd := &GetCopysetStatusCommand{ + FinalCurveCmd: basecmd.FinalCurveCmd{}, + } + basecmd.NewFinalCurveCli(©setCmd.FinalCurveCmd, copysetCmd) + return copysetCmd +} + +func (cCmd *GetCopysetStatusCommand) Init(cmd *cobra.Command, args []string) error { + timeout := config.GetFlagDuration(cCmd.Cmd, config.RPCTIMEOUT) + retrytimes := config.GetFlagInt32(cCmd.Cmd, config.RPCRETRYTIMES) + + copysetid := config.GetBsFlagUint32(cCmd.Cmd, config.CURVEBS_COPYSET_ID) + logicalpoolid := config.GetBsFlagUint32(cCmd.Cmd, config.CURVEBS_LOGIC_POOL_ID) + peersAddress := config.GetBsFlagStringSlice(cCmd.Cmd, config.CURVEBS_PEERS_ADDRESS) + queryHash := false + for _, address := range peersAddress { + rpc := &GetCopysetStatusRpc{ + Request: ©set.CopysetStatusRequest{ + CopysetId: ©setid, + LogicPoolId: &logicalpoolid, + Peer: &common.Peer{Address: &address}, + QueryHash: &queryHash, + }, + } + rpc.Info = basecmd.NewRpc([]string{address}, timeout, retrytimes, "GetCopysetStatus") + cCmd.Rpc = append(cCmd.Rpc, rpc) + } + return nil +} + +func (cCmd *GetCopysetStatusCommand) RunCommand(cmd *cobra.Command, args []string) error { + var infos []*basecmd.Rpc + var funcs []basecmd.RpcFunc + for _, rpc := range cCmd.Rpc { + infos = append(infos, rpc.Info) + funcs = append(funcs, rpc) + } + results, errs := basecmd.GetRpcListResponse(infos, funcs) + if len(errs) == len(infos) { + mergeErr := cmderror.MergeCmdErrorExceptSuccess(errs) + return mergeErr.ToError() + } + for _, result := range results { + cCmd.response = append(cCmd.response, result.(*copyset.CopysetStatusResponse)) + } + return nil +} + +func (cCmd *GetCopysetStatusCommand) Print(cmd *cobra.Command, args []string) error { + return output.FinalCmdOutput(&cCmd.FinalCurveCmd, cCmd) +} + +func (cCmd *GetCopysetStatusCommand) ResultPlainOutput() error { + return output.FinalCmdOutputPlain(&cCmd.FinalCurveCmd) +} + +func (cCmd *GetCopysetStatusCommand) AddFlags() { + config.AddBsMdsFlagOption(cCmd.Cmd) + config.AddRpcRetryTimesFlag(cCmd.Cmd) + config.AddRpcTimeoutFlag(cCmd.Cmd) + + config.AddBSCopysetIdRequiredFlag(cCmd.Cmd) + config.AddBSLogicalPoolIdRequiredFlag(cCmd.Cmd) + config.AddBSPeersConfFlag(cCmd.Cmd) +} + +func GetCopysetStatus(caller *cobra.Command) ([]*copyset.CopysetStatusResponse, *cmderror.CmdError) { + sCmd := NewGetCopysetStatusCommand() + config.AlignFlagsValue(caller, sCmd.Cmd, []string{ + config.RPCRETRYTIMES, config.RPCTIMEOUT, config.CURVEBS_MDSADDR, + config.CURVEBS_LOGIC_POOL_ID, config.CURVEBS_COPYSET_ID, config.CURVEBS_PEERS_ADDRESS, + }) + sCmd.Cmd.SilenceErrors = true + sCmd.Cmd.SilenceUsage = true + sCmd.Cmd.SetArgs([]string{"--format", config.FORMAT_NOOUT}) + err := sCmd.Cmd.Execute() + if err != nil { + retErr := cmderror.ErrBsGetCopysetStatus() + retErr.Format(err.Error()) + return nil, retErr + } + return sCmd.response, cmderror.Success() +} diff --git a/tools-v2/pkg/config/bs.go b/tools-v2/pkg/config/bs.go index 786d1bf4b6..892e358f3b 100644 --- a/tools-v2/pkg/config/bs.go +++ b/tools-v2/pkg/config/bs.go @@ -87,6 +87,9 @@ const ( CURVEBS_BURST_LENGTH = "burstlength" VIPER_CURVEBS_BURST_LENGTH = "curvebs.burstlength" CURVEBS_DEFAULT_BURST_LENGTH = uint64(10) + CURVEBS_MARGIN = "margin" + VIPER_CURVEBS_MARGIN = "curvebs.margin" + CURVEBS_DEFAULT_MARGIN = uint64(1000) ) var ( @@ -117,6 +120,7 @@ var ( CURVEBS_TYPE: VIPER_CURVEBS_TYPE, CURVE_EXPIRED_TIME: VIPER_CURVE_EXPIRED_TIME, CURVEBS_RECYCLE_PREFIX: VIPER_RECYCLE_PREFIX, + CURVEBS_MARGIN: VIPER_CURVEBS_MARGIN, } BSFLAG2DEFAULT = map[string]interface{}{ @@ -130,6 +134,7 @@ var ( CURVEBS_BURST_LENGTH: CURVEBS_DEFAULT_BURST_LENGTH, CURVEBS_PATH: CURVEBS_DEFAULT_PATH, CURVEBS_FORCE: CURVEBS_DEFAULT_FORCE, + CURVEBS_MARGIN: CURVEBS_DEFAULT_MARGIN, } ) @@ -280,6 +285,15 @@ func AddBsPathOptionFlag(cmd *cobra.Command) { AddBsStringOptionFlag(cmd, CURVEBS_PATH, "file or directory path") } +// marigin +func AddBsMarginOptionFlag(cmd *cobra.Command) { + AddUint64OptionFlag(cmd, CURVEBS_MARGIN, "the maximum gap between peers") +} + +func GetBsMargin(cmd *cobra.Command) uint64 { + return GetFlagUint64(cmd, CURVEBS_MARGIN) +} + // add flag required // add path[required] func AddBsPathRequiredFlag(cmd *cobra.Command) { @@ -295,7 +309,7 @@ func AddBSCopysetIdRequiredFlag(cmd *cobra.Command) { } func AddBsCopysetIdSliceRequiredFlag(cmd *cobra.Command) { - AddBsStringSliceRequiredFlag(cmd, CURVEBS_COPYSET_ID, "copyset ids") + AddBsStringSliceRequiredFlag(cmd, CURVEBS_COPYSET_ID, "copyset id") } func AddBsLogicalPoolIdSliceRequiredFlag(cmd *cobra.Command) {