diff --git a/tools-v2/README.md b/tools-v2/README.md index f275e5455a..7c69f592c2 100644 --- a/tools-v2/README.md +++ b/tools-v2/README.md @@ -101,6 +101,8 @@ A tool for CurveFS & CurveBs. - [stop snapshot](#stop-snapshot) - [recover](#recover) - [recover volume](#recover-volume) + - [export](#export) + - [export target](#export-target) - [Comparison of old and new commands](#comparison-of-old-and-new-commands) - [curve fs](#curve-fs) - [curve bs](#curve-bs) @@ -2038,6 +2040,72 @@ Output: | 0171a33b-17b7-4215-9f00-6d8de2686f77 | testsnap1 | success | +--------------------------------------+--------------+---------+ ``` +#### export + +##### export target + +export services and clients in the bs cluster + +Usage: + +```shell +curve bs export target +``` + +Output: + +```shell +[ + { + "label": { + "job": "etcd" + }, + "targets": [ + "172.21.0.13:23790", + "172.21.0.13:23791", + "172.21.0.13:23792" + ] + }, + { + "label": { + "job": "mds" + }, + "targets": [ + "172.21.0.13:6700", + "172.21.0.13:6701", + "172.21.0.13:6702" + ] + }, + { + "label": { + "job": "chunkserver" + }, + "targets": [ + "172.21.0.13:8200", + "172.21.0.13:8202", + "172.21.0.13:8201" + ] + }, + { + "label": { + "job": "snapshotcloneserver" + }, + "targets": [ + "172.21.0.13:5550", + "172.21.0.13:5551", + "172.21.0.13:5552" + ] + }, + { + "label": { + "job": "client" + }, + "targets": [ + "172.21.0.13:9000" + ] + } +] +``` #### recover diff --git a/tools-v2/internal/error/error.go b/tools-v2/internal/error/error.go index 381db50a67..cd12a09716 100644 --- a/tools-v2/internal/error/error.go +++ b/tools-v2/internal/error/error.go @@ -503,6 +503,13 @@ var ( ErrBsRecoverFile = func() *CmdError { return NewInternalCmdError(81, "recover file fail, err: %s") } + ErrTargetCluster = func() *CmdError { + return NewInternalCmdError(82, "get cluster target error: %s") + } + ErrTargetClient = func() *CmdError { + return NewInternalCmdError(83, "get client target error: %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/target.go b/tools-v2/internal/utils/target.go new file mode 100644 index 0000000000..c4a873cc0d --- /dev/null +++ b/tools-v2/internal/utils/target.go @@ -0,0 +1,35 @@ +/* + * 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-12-26 + * Author: wsehjk + */ +package cobrautil +const ( + LabelKey = "label" + ClientTarget = "client" + EtcdTarget = "etcd" + MdsTarget = "mds" + ChunkServerTarget = "chunkserver" + SnapshotTarget = "snapshot" + SnapshotServerTarget = "snapshotcloneserver" + TargetKey = "targets" + JobKey = "job" + OutputFile = "target.json" + TargetPathFlag = "path" +) \ No newline at end of file diff --git a/tools-v2/pkg/cli/command/curvebs/bs.go b/tools-v2/pkg/cli/command/curvebs/bs.go index c82ca69528..783cbdbae0 100644 --- a/tools-v2/pkg/cli/command/curvebs/bs.go +++ b/tools-v2/pkg/cli/command/curvebs/bs.go @@ -36,6 +36,7 @@ import ( "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/stop" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/update" + "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/export" ) type CurveBsCommand struct { @@ -56,6 +57,7 @@ func (bsCmd *CurveBsCommand) AddSubCommands() { check.NewCheckCommand(), snapshot.NewSnapshotCommand(), stop.NewStopCommand(), + export.NewExportCommand(), ) } diff --git a/tools-v2/pkg/cli/command/curvebs/export/export.go b/tools-v2/pkg/cli/command/curvebs/export/export.go new file mode 100644 index 0000000000..b8f337a8aa --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/export/export.go @@ -0,0 +1,48 @@ +/* +* 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-12-11 +* Author: wsehjk + */ + +package export + +import ( + basecmd "github.com/opencurve/curve/tools-v2/pkg/cli/command" + target "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/export/target" + "github.com/spf13/cobra" +) +type ExportCommand struct { + basecmd.MidCurveCmd +} + +var _ basecmd.MidCurveCmdFunc = (*ExportCommand)(nil) // check interface + +func (eCmd *ExportCommand) AddSubCommands() { + eCmd.Cmd.AddCommand( + target.NewTargetCommand(), + ) +} +func NewExportCommand() *cobra.Command{ + eCmd := &ExportCommand { + basecmd.MidCurveCmd{ + Use: "export", + Short: "export the target in curvebs", + }, + } + return basecmd.NewMidCurveCli(&eCmd.MidCurveCmd, eCmd) +} \ No newline at end of file diff --git a/tools-v2/pkg/cli/command/curvebs/export/target/target.go b/tools-v2/pkg/cli/command/curvebs/export/target/target.go new file mode 100644 index 0000000000..8052c1d9dd --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/export/target/target.go @@ -0,0 +1,137 @@ +/* +* 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: curve +* Created Date: 2023-12-11 +* Author: wsehjk + */ + +package target + +import ( + "encoding/json" + "fmt" + "os" + + 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/status/client" + "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/status/cluster" + "github.com/opencurve/curve/tools-v2/pkg/output" + "github.com/spf13/cobra" +) + +const ( + targetExample = "$curve bs export target" +) +type ResultStruct map[string]interface{} +type targetcommand struct { + basecmd.FinalCurveCmd +} +// check interface targetcommand implement FinalCurveCmdFunc +var _ basecmd.FinalCurveCmdFunc = (*targetcommand)(nil) + +func ExtractClusterResult(clusterResult map[string]interface{}, service string) ResultStruct{ + result := ResultStruct{} + arrs := clusterResult[service].([]map[string]string) + result[cobrautil.LabelKey] = map[string]string{ + cobrautil.JobKey : service, + } + if service == cobrautil.SnapshotTarget { + result[cobrautil.LabelKey] = map[string]string{ + cobrautil.JobKey : cobrautil.SnapshotServerTarget, + } + } + result[cobrautil.TargetKey] = make([]string, 0) + for _, arr := range arrs { + if addr, ok := arr["addr"]; ok { // see the output of curve bs status cluster + result[cobrautil.TargetKey] = append(result[cobrautil.TargetKey].([]string), addr) + } + if addr, ok := arr["internalAddr"]; ok { + result[cobrautil.TargetKey] = append(result[cobrautil.TargetKey].([]string), addr) + } + } + return result +} +func NewTargetCommand() (cmd *cobra.Command) { + tCmd := &targetcommand{ + FinalCurveCmd: basecmd.FinalCurveCmd { + Use: "target", + Short: "export all the target in cluster and used by monitor", + Example: targetExample, + }, + } + return basecmd.NewFinalCurveCli(&tCmd.FinalCurveCmd, tCmd) +} + +func (tCmd *targetcommand)Init(cmd *cobra.Command, args []string) error { + return nil +} +func (tCmd *targetcommand)RunCommand(cmd *cobra.Command, args []string) error { + // get cluster target + clusterResult, err := cluster.GetClusterStatus() + if err != nil { + retErr := cmderror.ErrTargetCluster() + retErr.Format(err.Error()) + return retErr.ToError() + } + tCmd.Result = []ResultStruct{} + tCmd.Result = append(tCmd.Result.([]ResultStruct), ExtractClusterResult(clusterResult.(map[string]interface{}), cobrautil.EtcdTarget)) + tCmd.Result = append(tCmd.Result.([]ResultStruct), ExtractClusterResult(clusterResult.(map[string]interface{}), cobrautil.MdsTarget)) + tCmd.Result = append(tCmd.Result.([]ResultStruct), ExtractClusterResult(clusterResult.(map[string]interface{}), cobrautil.ChunkServerTarget)) + tCmd.Result = append(tCmd.Result.([]ResultStruct), ExtractClusterResult(clusterResult.(map[string]interface{}), cobrautil.SnapshotTarget)) + + // get client target + clientResult, err := client.GetClientStatus() + if err != nil { + retErr := cmderror.ErrTargetClient() + retErr.Format(err.Error()) + return retErr.ToError() + } + clientStruct := ResultStruct{} + clientStruct[cobrautil.LabelKey] = map[string]string{ + cobrautil.JobKey : cobrautil.ClientTarget, + } + clientStruct[cobrautil.TargetKey] = make([]string, 0) + for _, result := range clientResult.([]map[string]string) { + clientStruct[cobrautil.TargetKey] = append(clientStruct[cobrautil.TargetKey].([]string), result["addr"]) + } + tCmd.Result = append(tCmd.Result.([]ResultStruct), clientStruct) + return nil +} + +func (tCmd *targetcommand)Print(cmd *cobra.Command, args []string) error { + output, err := json.MarshalIndent(tCmd.Result, "", " ") + if err != nil { + return err + } + path := tCmd.Cmd.Flag(cobrautil.TargetPathFlag).Value.String() + if err = os.WriteFile(path + "/" + cobrautil.OutputFile, output, 0666); err != nil { + return err + } + fmt.Println(string(output)) + return nil +} +// result in plain format string +func (tCmd *targetcommand)ResultPlainOutput() error { + return output.FinalCmdOutputPlain(&tCmd.FinalCurveCmd) +} + +// target final command has no flag +func (tCmd *targetcommand)AddFlags() { + tCmd.Cmd.Flags().String(cobrautil.TargetPathFlag, ".", "specify the path of target.json") +} \ No newline at end of file diff --git a/tools-v2/pkg/cli/command/curvebs/status/client/client.go b/tools-v2/pkg/cli/command/curvebs/status/client/client.go index e056090224..7299ecbdaa 100644 --- a/tools-v2/pkg/cli/command/curvebs/status/client/client.go +++ b/tools-v2/pkg/cli/command/curvebs/status/client/client.go @@ -25,6 +25,7 @@ package client import ( "strconv" "strings" + "fmt" cmderror "github.com/opencurve/curve/tools-v2/internal/error" cobrautil "github.com/opencurve/curve/tools-v2/internal/utils" @@ -238,3 +239,13 @@ func NewStatusClientCommand() *ClientCommand { basecmd.NewFinalCurveCli(&clientCmd.FinalCurveCmd, clientCmd) return clientCmd } + +func GetClientStatus() (interface{}, error){ + clientCommand := NewStatusClientCommand() + clientCommand.Cmd.SetArgs([]string{ + fmt.Sprintf("--%s", config.FORMAT), config.FORMAT_NOOUT,}) + clientCommand.Cmd.SilenceErrors = true + err := clientCommand.Cmd.Execute() + + return clientCommand.Result, err +} \ No newline at end of file diff --git a/tools-v2/pkg/cli/command/curvebs/status/cluster/cluster.go b/tools-v2/pkg/cli/command/curvebs/status/cluster/cluster.go index 9d951a9067..80f61aaad6 100644 --- a/tools-v2/pkg/cli/command/curvebs/status/cluster/cluster.go +++ b/tools-v2/pkg/cli/command/curvebs/status/cluster/cluster.go @@ -67,6 +67,10 @@ const ( ) func NewClusterCommand() *cobra.Command { + return NewStatusClusterCommand().Cmd +} + +func NewStatusClusterCommand() *ClusterCommand { cCmd := &ClusterCommand{ FinalCurveCmd: basecmd.FinalCurveCmd{ Use: "cluster", @@ -75,7 +79,7 @@ func NewClusterCommand() *cobra.Command { }, } basecmd.NewFinalCurveCli(&cCmd.FinalCurveCmd, cCmd) - return cCmd.Cmd + return cCmd } func (cCmd *ClusterCommand) AddFlags() { @@ -245,3 +249,13 @@ func (cCmd *ClusterCommand) buildTableCopyset(results []map[string]string) { row = append(row, results[0][cobrautil.ROW_UNHEALTHY_RATIO]) cCmd.tableCopyset.Append(row) } + +func GetClusterStatus() (interface{}, error){ + clusterCommand := NewStatusClusterCommand() + // cluster command no output + clusterCommand.Cmd.SetArgs([]string{ + fmt.Sprintf("--%s", config.FORMAT), config.FORMAT_NOOUT,}) + clusterCommand.Cmd.SilenceErrors = true + err := clusterCommand.Cmd.Execute() + return clusterCommand.Result, err +} \ No newline at end of file