diff --git a/tools-v2/README.md b/tools-v2/README.md index a6cbc5936c..f275e5455a 100644 --- a/tools-v2/README.md +++ b/tools-v2/README.md @@ -99,6 +99,8 @@ A tool for CurveFS & CurveBs. - [snapshot copyset](#snapshot-copyset) - [stop](#stop) - [stop snapshot](#stop-snapshot) + - [recover](#recover) + - [recover volume](#recover-volume) - [Comparison of old and new commands](#comparison-of-old-and-new-commands) - [curve fs](#curve-fs) - [curve bs](#curve-bs) @@ -2037,6 +2039,27 @@ Output: +--------------------------------------+--------------+---------+ ``` +#### recover + +##### recover volume + +recover volume from recycleBin + +Usage: +```shell +curve bs recover volume --path /test/path --user root +``` + +Output: +``` ++-----------+---------+ +| FILENAME | RESULT | ++-----------+---------+ +| test/path | success | ++-----------+---------+ +``` + + ## Comparison of old and new commands ### curve fs diff --git a/tools-v2/internal/error/error.go b/tools-v2/internal/error/error.go index aea8ad92d3..381db50a67 100644 --- a/tools-v2/internal/error/error.go +++ b/tools-v2/internal/error/error.go @@ -500,7 +500,9 @@ var ( ErrInvalidMetaServerAddr = func() *CmdError { return NewInternalCmdError(80, "invalid metaserver external addr: %s") } - + ErrBsRecoverFile = func() *CmdError { + return NewInternalCmdError(81, "recover file 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/pkg/cli/command/curvebs/recover/recover.go b/tools-v2/pkg/cli/command/curvebs/recover/recover.go new file mode 100644 index 0000000000..61f749e5ea --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/recover/recover.go @@ -0,0 +1,50 @@ +/* + * 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-11-15 + * Author: CrystalAnalyst + */ +package recover + +import ( + basecmd "github.com/opencurve/curve/tools-v2/pkg/cli/command" + volume "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/recover/volume" + "github.com/spf13/cobra" +) + +type RecoverCommand struct { + basecmd.MidCurveCmd +} + +var _ basecmd.MidCurveCmdFunc = (*RecoverCommand)(nil) + +func (rCmd *RecoverCommand) AddSubCommands() { + rCmd.Cmd.AddCommand( + volume.NewVolumeCommand(), + ) +} + +func NewRecoverCommand() *cobra.Command { + rCmd := &RecoverCommand{ + basecmd.MidCurveCmd{ + Use: "recover", + Short: "recover resources in curvebs cluster", + }, + } + return basecmd.NewMidCurveCli(&rCmd.MidCurveCmd, rCmd) +} diff --git a/tools-v2/pkg/cli/command/curvebs/recover/volume/volume.go b/tools-v2/pkg/cli/command/curvebs/recover/volume/volume.go new file mode 100644 index 0000000000..2051d79edf --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/recover/volume/volume.go @@ -0,0 +1,175 @@ +package volume + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "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/config" + "github.com/opencurve/curve/tools-v2/pkg/output" + "github.com/opencurve/curve/tools-v2/proto/proto/nameserver2" +) + +const ( + RecoverExample = `curve bs recover volume --path /curvebs-volume-path + --user username [--password password] [--fileid fileid]` +) + +func NewVolumeCommand() *cobra.Command { + return NewRecoverFileCommand().Cmd +} + +func NewRecoverFileCommand() *RecoverFileCommand { + RecoverFileCommand := &RecoverFileCommand{ + FinalCurveCmd: basecmd.FinalCurveCmd{ + Use: "volume", + Short: "recover volume in curvebs", + Example: RecoverExample, + }, + } + basecmd.NewFinalCurveCli(&RecoverFileCommand.FinalCurveCmd, RecoverFileCommand) + return RecoverFileCommand +} + +type RecoverCertainFileRPC struct { + Info *basecmd.Rpc + Request *nameserver2.RecoverFileRequest + mdsClient nameserver2.CurveFSServiceClient +} + +var _ basecmd.RpcFunc = (*RecoverCertainFileRPC)(nil) + +func (gRpc *RecoverCertainFileRPC) NewRpcClient(cc grpc.ClientConnInterface) { + gRpc.mdsClient = nameserver2.NewCurveFSServiceClient(cc) +} + +func (gRpc *RecoverCertainFileRPC) Stub_Func(ctx context.Context) (interface{}, error) { + return gRpc.mdsClient.RecoverFile(ctx, gRpc.Request) +} + +type RecoverFileCommand struct { + basecmd.FinalCurveCmd + Rpc *RecoverCertainFileRPC + Response *nameserver2.RecoverFileResponse +} + +var _ basecmd.FinalCurveCmdFunc = (*RecoverFileCommand)(nil) + +func (rCmd *RecoverFileCommand) Init(cmd *cobra.Command, args []string) error { + mdsAddrs, err := config.GetBsMdsAddrSlice(rCmd.Cmd) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + + //get the default timeout and retrytimes + timeout := config.GetFlagDuration(rCmd.Cmd, config.RPCTIMEOUT) + retrytimes := config.GetFlagInt32(rCmd.Cmd, config.RPCRETRYTIMES) + + //get the params needed from commandline + path := config.GetBsFlagString(rCmd.Cmd, config.CURVEBS_PATH) + username := config.GetBsFlagString(rCmd.Cmd, config.CURVEBS_USER) + password := config.GetBsFlagString(rCmd.Cmd, config.CURVEBS_PASSWORD) + fileId := config.GetBsFlagUint64(rCmd.Cmd, config.CURVEBS_FILE_ID) + date, errDat := cobrautil.GetTimeofDayUs() + if errDat.TypeCode() != cmderror.CODE_SUCCESS { + return errDat.ToError() + } + + //add basic info + recoverRequest := nameserver2.RecoverFileRequest{ + FileName: &path, + Owner: &username, + FileId: &fileId, + Date: &date, + } + + //add signature + if username == viper.GetString(config.VIPER_CURVEBS_USER) && len(password) != 0 { + strSig := cobrautil.GetString2Signature(date, username) + sig := cobrautil.CalcString2Signature(strSig, password) + recoverRequest.Signature = &sig + } + rCmd.Rpc = &RecoverCertainFileRPC{ + Info: basecmd.NewRpc(mdsAddrs, timeout, retrytimes, "recoverFile"), + Request: &recoverRequest, + } + + //set headers + header := []string{ + cobrautil.ROW_FILE_NAME, + cobrautil.ROW_RESULT, + } + rCmd.SetHeader(header) + out := make(map[string]string) + out[cobrautil.ROW_FILE_NAME] = *recoverRequest.FileName + list := cobrautil.Map2List(out, []string{cobrautil.ROW_FILE_NAME}) + rCmd.TableNew.Append(list) + return nil +} + +func (rCmd *RecoverFileCommand) AddFlags() { + config.AddRpcTimeoutFlag(rCmd.Cmd) + config.AddRpcRetryTimesFlag(rCmd.Cmd) + config.AddBsMdsFlagOption(rCmd.Cmd) + + //file path and user name is required. + config.AddBsPathRequiredFlag(rCmd.Cmd) + config.AddBsUserRequireFlag(rCmd.Cmd) + + //password and fileID is optional. + config.AddBsPasswordOptionFlag(rCmd.Cmd) + config.AddBsFileIdOptionFlag(rCmd.Cmd) +} + +func (rCmd *RecoverFileCommand) RunCommand(cmd *cobra.Command, args []string) error { + result, err := basecmd.GetRpcResponse(rCmd.Rpc.Info, rCmd.Rpc) + if err.TypeCode() != cmderror.CODE_SUCCESS { + rCmd.Error = err + rCmd.Result = result + return err.ToError() + } + rCmd.Response = result.(*nameserver2.RecoverFileResponse) + if rCmd.Response.GetStatusCode() != nameserver2.StatusCode_kOK { + rCmd.Error = cmderror.ErrBsRecoverFile() + rCmd.Result = result + return rCmd.Error.ToError() + } + out := make(map[string]string) + out[cobrautil.ROW_RESULT] = cobrautil.ROW_VALUE_SUCCESS + list := cobrautil.Map2List(out, []string{cobrautil.ROW_RESULT}) + rCmd.TableNew.Append(list) + rCmd.Result, rCmd.Error = result, cmderror.Success() + return nil +} + +func (rCmd *RecoverFileCommand) Print(cmd *cobra.Command, args []string) error { + return output.FinalCmdOutput(&rCmd.FinalCurveCmd, rCmd) +} + +func (rCmd *RecoverFileCommand) ResultPlainOutput() error { + return output.FinalCmdOutputPlain(&rCmd.FinalCurveCmd) +} + +func RecoverFile(caller *cobra.Command) (*nameserver2.RecoverFileResponse, *cmderror.CmdError) { + rCmd := NewRecoverFileCommand() + config.AlignFlagsValue(caller, rCmd.Cmd, []string{ + config.RPCRETRYTIMES, config.RPCTIMEOUT, config.CURVEBS_MDSADDR, + config.CURVEBS_PATH, config.CURVEBS_USER, + config.CURVEBS_PASSWORD, config.CURVEBS_FILE_ID, + }) + rCmd.Cmd.SilenceErrors = true + rCmd.Cmd.SilenceUsage = true + rCmd.Cmd.SetArgs([]string{"--format", config.FORMAT_NOOUT}) + err := rCmd.Cmd.Execute() + if err != nil { + retErr := cmderror.ErrBsRecoverFile() + retErr.Format(err.Error()) + return rCmd.Response, retErr + } + return rCmd.Response, cmderror.Success() +} diff --git a/tools-v2/pkg/config/bs.go b/tools-v2/pkg/config/bs.go index 97eb3b8a67..4fad5d2306 100644 --- a/tools-v2/pkg/config/bs.go +++ b/tools-v2/pkg/config/bs.go @@ -156,6 +156,8 @@ const ( VIPER_CURVEBS_FILENAME = "curvebs.filename" CURVEBS_SNAPSHOTNAME = "snapshotname" VIPER_CURVEBS_SNAPSHOTNAME = "curvebs.snapshotname" + CURVEBS_FILE_ID = "fileId" + VIPER_CURVEBS_FILE_ID = "curvebs.fileId" ) var ( @@ -557,6 +559,10 @@ func AddBsPathRequiredFlag(cmd *cobra.Command) { AddBsStringRequiredFlag(cmd, CURVEBS_PATH, "file path") } +func AddBsUserRequireFlag(cmd *cobra.Command) { + AddBsStringRequiredFlag(cmd, CURVEBS_USER, "user name") +} + func AddBsLogicalPoolIdRequiredFlag(cmd *cobra.Command) { AddBsUint32RequiredFlag(cmd, CURVEBS_LOGIC_POOL_ID, "logical pool id") } @@ -693,6 +699,10 @@ func AddBsSnapshotNameRequiredFlag(cmd *cobra.Command) { AddBsStringRequiredFlag(cmd, CURVEBS_SNAPSHOTNAME, "snapshot name") } +func AddBsFileIdOptionFlag(cmd *cobra.Command) { + AddBsUint64OptionFlag(cmd, CURVEBS_FILE_ID, "recover fileId") +} + // get stingslice flag func GetBsFlagStringSlice(cmd *cobra.Command, flagName string) []string { var value []string @@ -876,3 +886,7 @@ func GetBsChunkServerId(cmd *cobra.Command) []uint32 { } return chunkserveridSlice } + +func GetBsFileId(cmd *cobra.Command) uint64 { + return GetBsFlagUint64(cmd, CURVEBS_FILE_ID) +}