Compare commits

..

1 Commits

Author SHA1 Message Date
montaguelhz c4174c5510 [feat] tools-v2: add snapshot copyset --all
Signed-off-by: montaguelhz <1443171175@qq.com>
2023-07-16 15:59:18 +08:00
10 changed files with 399 additions and 83 deletions

View File

@ -271,7 +271,7 @@ check umount date consistency
*** Keywords ***
init failover cluster
#clean env
clean env
destroy curvefs
clean log
deploy all servers

View File

@ -1679,6 +1679,23 @@ Output:
+-----------------------+---------+---------+
```
```bash
curve bs snapshot copyset --all
```
Output:
```
+----------------+---------+
| CHUNKSERVER | RESULT |
+----------------+---------+
| **.*.*.**:8200 | failed |
+----------------+---------+
| **.*.*.**:8201 | success |
+----------------+ +
| **.*.*.**:8202 | |
+----------------+---------+
```
## Comparison of old and new commands
### curve fs
@ -1740,10 +1757,10 @@ Output:
| curve_ops_tool clean-recycle | curve bs clean-recycle |
| curve_ops_tool copysets-status | curve bs status copyset |
| curve_ops_tool list-may-broken-vol | curve bs list may-broken-vol |
| curve_ops_tool rapid-leader-schedule | curve bs update leader-schedule | |
| curve_ops_tool rapid-leader-schedule | curve bs update leader-schedule |
| curve_ops_tool do-snapshot-all | curve bs snapshot --all |
| curve_ops_tool status | |
| curve_ops_tool check-consistency | |
| curve_ops_tool do-snapshot-all | |
| curve_ops_tool check-chunkserver | |
| curve_ops_tool check-server | |

View File

@ -469,6 +469,12 @@ var (
ErrSnapShotAddrNotConfigured = func() *CmdError {
return NewInternalCmdError(70, "get snapshotAddr fail, err: %s")
}
ErrBsGetOneSnapshotResult = func() *CmdError {
return NewInternalCmdError(71, "get one snapshot result fail, err: %s")
}
ErrBsGetAllSnapshotResult = func() *CmdError {
return NewInternalCmdError(72, "get all snapshot results fail, err: %s")
}
// http error
ErrHttpUnreadableResult = func() *CmdError {

View File

@ -32,6 +32,7 @@ const (
ROW_CHILD_LIST = "childList"
ROW_CHILD_TYPE = "childType"
ROW_CHUNK = "chunk"
ROW_CHUNKSERVER = "chunkserver"
ROW_CHUNK_SIZE = "chunkSize"
ROW_COPYSET = "copyset"
ROW_COPYSET_ID = "copysetId"

View File

@ -0,0 +1,148 @@
/*
* 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-07-11
* Author: montaguelhz
*/
package copyset
import (
"context"
"fmt"
"github.com/spf13/cobra"
"google.golang.org/grpc"
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/cli/command/curvebs/list/chunkserver"
"github.com/opencurve/curve/tools-v2/pkg/config"
"github.com/opencurve/curve/tools-v2/pkg/output"
"github.com/opencurve/curve/tools-v2/proto/proto/cli2"
)
type SnapshotAllRpc struct {
Info *basecmd.Rpc
Request *cli2.SnapshotAllRequest
Client cli2.CliService2Client
}
func (sRpc *SnapshotAllRpc) NewRpcClient(cc grpc.ClientConnInterface) {
sRpc.Client = cli2.NewCliService2Client(cc)
}
func (sRpc *SnapshotAllRpc) Stub_Func(ctx context.Context) (interface{}, error) {
return sRpc.Client.SnapshotAll(ctx, sRpc.Request)
}
type SnapshotAllCopysetCommand struct {
basecmd.FinalCurveCmd
rpcs []*SnapshotAllRpc
csAddrs []string
res []bool
}
var _ basecmd.FinalCurveCmdFunc = (*SnapshotAllCopysetCommand)(nil) // check interface
func NewSnapshotAllCopysetCommand() *SnapshotAllCopysetCommand {
sCmd := &SnapshotAllCopysetCommand{FinalCurveCmd: basecmd.FinalCurveCmd{}}
basecmd.NewFinalCurveCli(&sCmd.FinalCurveCmd, sCmd)
return sCmd
}
func NewAllCopysetCommand() *cobra.Command {
return NewSnapshotAllCopysetCommand().Cmd
}
func (sCmd *SnapshotAllCopysetCommand) AddFlags() {
config.AddRpcRetryTimesFlag(sCmd.Cmd)
config.AddRpcTimeoutFlag(sCmd.Cmd)
}
func (sCmd *SnapshotAllCopysetCommand) Init(cmd *cobra.Command, args []string) error {
timeout := config.GetFlagDuration(sCmd.Cmd, config.RPCTIMEOUT)
retryTimes := config.GetFlagInt32(sCmd.Cmd, config.RPCRETRYTIMES)
chunkserverInfos, err := chunkserver.GetChunkServerInCluster(sCmd.Cmd)
if err.TypeCode() != cmderror.CODE_SUCCESS {
return err.ToError()
}
sCmd.csAddrs = make([]string, len(chunkserverInfos))
sCmd.res = make([]bool, len(chunkserverInfos))
for i, info := range chunkserverInfos {
sCmd.csAddrs[i] = fmt.Sprintf("%s:%d", info.GetHostIp(), info.GetPort())
}
for _, addr := range sCmd.csAddrs {
rpc := &SnapshotAllRpc{
Request: &cli2.SnapshotAllRequest{},
Info: basecmd.NewRpc([]string{addr}, timeout, retryTimes, "SnapshotAll"),
}
sCmd.rpcs = append(sCmd.rpcs, rpc)
}
return nil
}
func (sCmd *SnapshotAllCopysetCommand) RunCommand(cmd *cobra.Command, args []string) error {
var infos []*basecmd.Rpc
var funcs []basecmd.RpcFunc
for _, rpc := range sCmd.rpcs {
infos = append(infos, rpc.Info)
funcs = append(funcs, rpc)
}
results, errs := basecmd.GetRpcListResponse(infos, funcs)
mergeErr := cmderror.MergeCmdErrorExceptSuccess(errs)
sCmd.Error = mergeErr
if len(errs) == len(infos) {
return mergeErr.ToError()
}
for i, result := range results {
sCmd.res[i] = (result != nil)
}
return nil
}
func (sCmd *SnapshotAllCopysetCommand) Print(cmd *cobra.Command, args []string) error {
return output.FinalCmdOutput(&sCmd.FinalCurveCmd, sCmd)
}
func (sCmd *SnapshotAllCopysetCommand) ResultPlainOutput() error {
return output.FinalCmdOutputPlain(&sCmd.FinalCurveCmd)
}
func GetAllSnapshotResult(caller *cobra.Command) ([]string, []bool, *cmderror.CmdError) {
sCmd := NewSnapshotAllCopysetCommand()
config.AlignFlagsValue(caller, sCmd.Cmd, []string{})
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.ErrBsGetAllSnapshotResult()
retErr.Format(err.Error())
return nil, nil, retErr
}
return sCmd.csAddrs, sCmd.res, cmderror.Success()
}

View File

@ -23,130 +23,120 @@
package copyset
import (
"context"
"fmt"
"github.com/spf13/cobra"
"google.golang.org/grpc"
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/delete/peer"
"github.com/opencurve/curve/tools-v2/pkg/config"
"github.com/opencurve/curve/tools-v2/pkg/output"
"github.com/opencurve/curve/tools-v2/proto/proto/cli2"
)
const (
updateExample = `$ curve bs snapshot copyset 127.0.0.0:8200:0 --logicalpoolid=1 --copysetid=1`
updateExample = `$ curve bs snapshot copyset 127.0.0.0:8200:0 --logicalpoolid=1 --copysetid=1
$ curve bs snapshot copyset --all`
)
type SnapshotRpc struct {
Info *basecmd.Rpc
Request *cli2.SnapshotRequest2
Client cli2.CliService2Client
}
func (sRpc *SnapshotRpc) NewRpcClient(cc grpc.ClientConnInterface) {
sRpc.Client = cli2.NewCliService2Client(cc)
}
func (sRpc *SnapshotRpc) Stub_Func(ctx context.Context) (interface{}, error) {
return sRpc.Client.Snapshot(ctx, sRpc.Request)
}
type SnapshotOneCommand struct {
type SnapshotCopysetCommand struct {
basecmd.FinalCurveCmd
Rpc *SnapshotRpc
Response *cli2.SnapshotResponse2
row map[string]string
needAll bool
}
var _ basecmd.FinalCurveCmdFunc = (*SnapshotOneCommand)(nil) // check interface
var _ basecmd.FinalCurveCmdFunc = (*SnapshotCopysetCommand)(nil) // check interface
// NewCommand ...
func NewSnapshotOneCommand() *cobra.Command {
peerCmd := &SnapshotOneCommand{
func NewSnapshotCopysetCommand() *cobra.Command {
sCmd := &SnapshotCopysetCommand{
FinalCurveCmd: basecmd.FinalCurveCmd{
Use: "copyset",
Short: "take snapshot for copyset",
Example: updateExample,
},
}
basecmd.NewFinalCurveCli(&peerCmd.FinalCurveCmd, peerCmd)
return peerCmd.Cmd
basecmd.NewFinalCurveCli(&sCmd.FinalCurveCmd, sCmd)
return sCmd.Cmd
}
func (sCmd *SnapshotOneCommand) AddFlags() {
func (sCmd *SnapshotCopysetCommand) AddFlags() {
config.AddRpcRetryTimesFlag(sCmd.Cmd)
config.AddRpcTimeoutFlag(sCmd.Cmd)
config.AddBsLogicalPoolIdRequiredFlag(sCmd.Cmd)
config.AddBsCopysetIdRequiredFlag(sCmd.Cmd)
config.AddBsLogicalPoolIdOptionFlag(sCmd.Cmd)
config.AddBsCopysetIdOptionFlag(sCmd.Cmd)
config.AddBsAllOptionFlag(sCmd.Cmd)
}
func (sCmd *SnapshotOneCommand) Init(cmd *cobra.Command, args []string) error {
sCmd.SetHeader([]string{cobrautil.ROW_PEER, cobrautil.ROW_COPYSET, cobrautil.ROW_RESULT})
sCmd.TableNew.SetAutoMergeCellsByColumnIndex(cobrautil.GetIndexSlice(
sCmd.Header, []string{},
))
func (sCmd *SnapshotCopysetCommand) Init(cmd *cobra.Command, args []string) error {
sCmd.needAll = config.GetBsFlagBool(sCmd.Cmd, config.CURVEBS_ALL)
timeout := config.GetFlagDuration(sCmd.Cmd, config.RPCTIMEOUT)
retryTimes := config.GetFlagInt32(sCmd.Cmd, config.RPCRETRYTIMES)
if !sCmd.needAll {
isLogicalPoolIDChanged := config.GetBsFlagChanged(sCmd.Cmd, config.CURVEBS_LOGIC_POOL_ID)
isCopysetIDChanged := config.GetBsFlagChanged(sCmd.Cmd, config.CURVEBS_COPYSET_ID)
if !isLogicalPoolIDChanged || !isCopysetIDChanged {
return fmt.Errorf("all or logicalpoolid and copysetid is required")
}
copysetID := config.GetBsFlagUint32(sCmd.Cmd, config.CURVEBS_COPYSET_ID)
logicalPoolID := config.GetBsFlagUint32(sCmd.Cmd, config.CURVEBS_LOGIC_POOL_ID)
// parse peer conf
if len(args) < 1 {
pErr := cmderror.ErrGetPeer()
pErr.Format("should specified the peer address")
return pErr.ToError()
}
snapshotPeer, err := peer.ParsePeer(args[0])
if err != nil {
return err.ToError()
}
out := make(map[string]string)
out[cobrautil.ROW_PEER] = fmt.Sprintf("%s:%d", snapshotPeer.GetAddress(), snapshotPeer.GetId())
out[cobrautil.ROW_COPYSET] = fmt.Sprintf("(%d:%d)", logicalPoolID, copysetID)
sCmd.row = out
sCmd.Rpc = &SnapshotRpc{
Info: basecmd.NewRpc([]string{snapshotPeer.GetAddress()}, timeout, retryTimes, "Snapshot"),
Request: &cli2.SnapshotRequest2{
LogicPoolId: &logicalPoolID,
CopysetId: &copysetID,
Peer: snapshotPeer,
},
sCmd.SetHeader([]string{cobrautil.ROW_PEER, cobrautil.ROW_COPYSET, cobrautil.ROW_RESULT})
sCmd.TableNew.SetAutoMergeCellsByColumnIndex(cobrautil.GetIndexSlice(
sCmd.Header, []string{},
))
} else {
sCmd.SetHeader([]string{cobrautil.ROW_CHUNKSERVER, cobrautil.ROW_RESULT})
sCmd.TableNew.SetAutoMergeCellsByColumnIndex(cobrautil.GetIndexSlice(
sCmd.Header, []string{cobrautil.ROW_RESULT},
))
}
return nil
}
func (sCmd *SnapshotOneCommand) Print(cmd *cobra.Command, args []string) error {
func (sCmd *SnapshotCopysetCommand) Print(cmd *cobra.Command, args []string) error {
return output.FinalCmdOutput(&sCmd.FinalCurveCmd, sCmd)
}
func (sCmd *SnapshotOneCommand) RunCommand(cmd *cobra.Command, args []string) error {
response, err := basecmd.GetRpcResponse(sCmd.Rpc.Info, sCmd.Rpc)
sCmd.Error = err
if err.TypeCode() != cmderror.CODE_SUCCESS {
return err.ToError()
func (sCmd *SnapshotCopysetCommand) RunCommand(cmd *cobra.Command, args []string) error {
if sCmd.needAll {
csAddrs, res, err := GetAllSnapshotResult(sCmd.Cmd)
if err.TypeCode() != cmderror.CODE_SUCCESS {
return err.ToError()
}
rows := make([]map[string]string, len(csAddrs))
for i := 0; i < len(csAddrs); i++ {
rows[i] = make(map[string]string)
rows[i][cobrautil.ROW_CHUNKSERVER] = csAddrs[i]
if res[i] {
rows[i][cobrautil.ROW_RESULT] = cobrautil.ROW_VALUE_SUCCESS
} else {
rows[i][cobrautil.ROW_RESULT] = cobrautil.ROW_VALUE_FAILED
}
}
list := cobrautil.ListMap2ListSortByKeys(rows, sCmd.Header, []string{cobrautil.ROW_RESULT})
sCmd.TableNew.AppendBulk(list)
sCmd.Result = rows
} else {
peer, copyset, err := GetOneSnapshotResult(sCmd.Cmd, args)
if err.TypeCode() != cmderror.CODE_SUCCESS {
return err.ToError()
}
row := make(map[string]string)
row[cobrautil.ROW_PEER] = peer
row[cobrautil.ROW_COPYSET] = copyset
row[cobrautil.ROW_RESULT] = cobrautil.ROW_VALUE_SUCCESS
list := cobrautil.Map2List(row, sCmd.Header)
sCmd.TableNew.Append(list)
sCmd.Result = row
}
sCmd.row[cobrautil.ROW_RESULT] = "success"
sCmd.Response = response.(*cli2.SnapshotResponse2)
list := cobrautil.Map2List(sCmd.row, sCmd.Header)
sCmd.TableNew.Append(list)
return nil
}
func (sCmd *SnapshotOneCommand) ResultPlainOutput() error {
func (sCmd *SnapshotCopysetCommand) ResultPlainOutput() error {
return output.FinalCmdOutputPlain(&sCmd.FinalCurveCmd)
}

View File

@ -0,0 +1,146 @@
/*
* 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-07-11
* Author: montaguelhz
*/
package copyset
import (
"context"
"fmt"
"github.com/spf13/cobra"
"google.golang.org/grpc"
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/cli/command/curvebs/delete/peer"
"github.com/opencurve/curve/tools-v2/pkg/config"
"github.com/opencurve/curve/tools-v2/pkg/output"
"github.com/opencurve/curve/tools-v2/proto/proto/cli2"
)
type SnapshotRpc struct {
Info *basecmd.Rpc
Request *cli2.SnapshotRequest2
Client cli2.CliService2Client
}
func (sRpc *SnapshotRpc) NewRpcClient(cc grpc.ClientConnInterface) {
sRpc.Client = cli2.NewCliService2Client(cc)
}
func (sRpc *SnapshotRpc) Stub_Func(ctx context.Context) (interface{}, error) {
return sRpc.Client.Snapshot(ctx, sRpc.Request)
}
type SnapshotOneCopysetCommand struct {
basecmd.FinalCurveCmd
Rpc *SnapshotRpc
peer string
copyset string
}
var _ basecmd.FinalCurveCmdFunc = (*SnapshotOneCopysetCommand)(nil) // check interface
// NewCommand ...
func NewSnapshotOneCopysetCommand() *SnapshotOneCopysetCommand {
sCmd := &SnapshotOneCopysetCommand{FinalCurveCmd: basecmd.FinalCurveCmd{}}
basecmd.NewFinalCurveCli(&sCmd.FinalCurveCmd, sCmd)
return sCmd
}
func NewOneCopysetCommand() *cobra.Command {
return NewSnapshotOneCopysetCommand().Cmd
}
func (sCmd *SnapshotOneCopysetCommand) AddFlags() {
config.AddRpcRetryTimesFlag(sCmd.Cmd)
config.AddRpcTimeoutFlag(sCmd.Cmd)
config.AddBsLogicalPoolIdRequiredFlag(sCmd.Cmd)
config.AddBsCopysetIdRequiredFlag(sCmd.Cmd)
}
func (sCmd *SnapshotOneCopysetCommand) Init(cmd *cobra.Command, args []string) error {
timeout := config.GetFlagDuration(sCmd.Cmd, config.RPCTIMEOUT)
retryTimes := config.GetFlagInt32(sCmd.Cmd, config.RPCRETRYTIMES)
// parse peer conf
if len(args) < 1 {
pErr := cmderror.ErrGetPeer()
pErr.Format("should specified the peer address")
return pErr.ToError()
}
snapshotPeer, err := peer.ParsePeer(args[0])
if err != nil {
return err.ToError()
}
copysetID := config.GetBsFlagUint32(sCmd.Cmd, config.CURVEBS_COPYSET_ID)
logicalPoolID := config.GetBsFlagUint32(sCmd.Cmd, config.CURVEBS_LOGIC_POOL_ID)
sCmd.peer = fmt.Sprintf("%s:%d", snapshotPeer.GetAddress(), snapshotPeer.GetId())
sCmd.copyset = fmt.Sprintf("(%d:%d)", logicalPoolID, copysetID)
sCmd.Rpc = &SnapshotRpc{
Info: basecmd.NewRpc([]string{snapshotPeer.GetAddress()}, timeout, retryTimes, "Snapshot"),
Request: &cli2.SnapshotRequest2{
LogicPoolId: &logicalPoolID,
CopysetId: &copysetID,
Peer: snapshotPeer,
},
}
return nil
}
func (sCmd *SnapshotOneCopysetCommand) RunCommand(cmd *cobra.Command, args []string) error {
_, err := basecmd.GetRpcResponse(sCmd.Rpc.Info, sCmd.Rpc)
sCmd.Error = err
if err.TypeCode() != cmderror.CODE_SUCCESS {
return err.ToError()
}
return nil
}
func (sCmd *SnapshotOneCopysetCommand) Print(cmd *cobra.Command, args []string) error {
return output.FinalCmdOutput(&sCmd.FinalCurveCmd, sCmd)
}
func (sCmd *SnapshotOneCopysetCommand) ResultPlainOutput() error {
return output.FinalCmdOutputPlain(&sCmd.FinalCurveCmd)
}
func GetOneSnapshotResult(caller *cobra.Command, args []string) (string, string, *cmderror.CmdError) {
sCmd := NewSnapshotOneCopysetCommand()
config.AlignFlagsValue(caller, sCmd.Cmd, []string{config.CURVEBS_COPYSET_ID, config.CURVEBS_LOGIC_POOL_ID})
sCmd.Cmd.SilenceErrors = true
sCmd.Cmd.SilenceUsage = true
args = append(args, []string{"--format", config.FORMAT_NOOUT}...)
sCmd.Cmd.SetArgs(args)
err := sCmd.Cmd.Execute()
if err != nil {
retErr := cmderror.ErrBsGetOneSnapshotResult()
retErr.Format(err.Error())
return "", "", retErr
}
return sCmd.peer, sCmd.copyset, cmderror.Success()
}

View File

@ -36,7 +36,7 @@ var _ basecmd.MidCurveCmdFunc = (*SnapshotCommand)(nil) // check interface
func (statusCmd *SnapshotCommand) AddSubCommands() {
statusCmd.Cmd.AddCommand(
copyset.NewSnapshotOneCommand(),
copyset.NewSnapshotCopysetCommand(),
)
}

View File

@ -106,8 +106,8 @@ func (lCmd *LeaderScheduleCommand) Init(cmd *cobra.Command, args []string) error
// if flag all not changed, it must be false
all := config.GetBsFlagBool(lCmd.Cmd, config.CURVEBS_ALL)
islogicalPoolIDChanged := config.GetBsFlagChanged(lCmd.Cmd, config.CURVEBS_LOGIC_POOL_ID)
if !all && !islogicalPoolIDChanged {
isLogicalPoolIDChanged := config.GetBsFlagChanged(lCmd.Cmd, config.CURVEBS_LOGIC_POOL_ID)
if !all && !isLogicalPoolIDChanged {
return fmt.Errorf("all or logicalpoolid is required")
}

View File

@ -57,8 +57,10 @@ const (
CURVEBS_DEFAULT_FORCE = false
CURVEBS_LOGIC_POOL_ID = "logicalpoolid"
VIPER_CURVEBS_LOGIC_POOL_ID = "curvebs.logicalpoolid"
CURVEBS_DEFAULT_LOGIC_POOL_ID = uint32(0)
CURVEBS_COPYSET_ID = "copysetid"
VIPER_CURVEBS_COPYSET_ID = "curvebs.copysetid"
CURVEBS_DEFAULT_COPYSET_ID = uint32(0)
CURVEBS_PEERS_ADDRESS = "peers"
VIPER_CURVEBS_PEERS_ADDRESS = "curvebs.peers"
CURVEBS_OFFSET = "offset"
@ -196,6 +198,8 @@ var (
CURVEBS_DRYRUN: CURVEBS_DEFAULT_DRYRUN,
CURVEBS_FIlTER: CURVEBS_DEFAULT_FILTER,
CURVEBS_ALL: CURVEBS_DEFAULT_ALL,
CURVEBS_LOGIC_POOL_ID: CURVEBS_DEFAULT_LOGIC_POOL_ID,
CURVEBS_COPYSET_ID: CURVEBS_DEFAULT_COPYSET_ID,
}
)
@ -488,6 +492,10 @@ func AddBsAllOptionFlag(cmd *cobra.Command) {
AddBsBoolOptionFlag(cmd, CURVEBS_ALL, "all")
}
func AddBsCopysetIdOptionFlag(cmd *cobra.Command) {
AddBsUint32OptionFlag(cmd, CURVEBS_COPYSET_ID, "copyset id")
}
// add flag required
// add path[required]
func AddBsPathRequiredFlag(cmd *cobra.Command) {