From 5e493b7f1c6c3bf80ec04dc13125aae983feba1e Mon Sep 17 00:00:00 2001 From: Anton Nikiforov Date: Wed, 5 Oct 2022 16:52:46 +0300 Subject: [PATCH] [#1704] Add command `container nodes` to output list of nodes for container, grouped by replica (#1704) Signed-off-by: Anton Nikiforov --- CHANGELOG.md | 1 + cmd/neofs-cli/internal/common/netmap.go | 49 +++++++++++++++ cmd/neofs-cli/modules/container/delete.go | 2 +- cmd/neofs-cli/modules/container/get.go | 62 ++++++++++++------- cmd/neofs-cli/modules/container/get_eacl.go | 2 +- .../modules/container/list_objects.go | 2 +- cmd/neofs-cli/modules/container/nodes.go | 62 +++++++++++++++++++ cmd/neofs-cli/modules/container/root.go | 2 + cmd/neofs-cli/modules/container/set_eacl.go | 2 +- cmd/neofs-cli/modules/netmap/snapshot.go | 37 +---------- 10 files changed, 157 insertions(+), 64 deletions(-) create mode 100644 cmd/neofs-cli/internal/common/netmap.go create mode 100644 cmd/neofs-cli/modules/container/nodes.go diff --git a/CHANGELOG.md b/CHANGELOG.md index f17a3c088..37079902b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Changelog for NeoFS Node - `wallet-address` flag in `neofs-adm morph refill-gas` command (#1820) - Validate policy before container creation (#1704) - `--timeout` flag in `neofs-cli` subcommands (#1837) +- `container nodes` command to output list of nodes for container, grouped by replica (#1704) ### Changed - Allow to evacuate shard data with `EvacuateShard` control RPC (#1800) diff --git a/cmd/neofs-cli/internal/common/netmap.go b/cmd/neofs-cli/internal/common/netmap.go new file mode 100644 index 000000000..2045433f4 --- /dev/null +++ b/cmd/neofs-cli/internal/common/netmap.go @@ -0,0 +1,49 @@ +package common + +import ( + "encoding/hex" + + "github.com/nspcc-dev/neofs-sdk-go/netmap" + "github.com/spf13/cobra" +) + +// PrettyPrintNodeInfo print information about network node with given indent and index. +// To avoid printing attribute list use short parameter. +func PrettyPrintNodeInfo(cmd *cobra.Command, node netmap.NodeInfo, + index int, indent string, short bool) { + var strState string + + switch { + default: + strState = "STATE_UNSUPPORTED" + case node.IsOnline(): + strState = "ONLINE" + case node.IsOffline(): + strState = "OFFLINE" + case node.IsMaintenance(): + strState = "MAINTENANCE" + } + + cmd.Printf("%sNode %d: %s %s ", indent, index+1, hex.EncodeToString(node.PublicKey()), strState) + + netmap.IterateNetworkEndpoints(node, func(endpoint string) { + cmd.Printf("%s ", endpoint) + }) + cmd.Println() + + if !short { + node.IterateAttributes(func(key, value string) { + cmd.Printf("%s\t%s: %s\n", indent, key, value) + }) + } +} + +// PrettyPrintNetMap print information about network map +func PrettyPrintNetMap(cmd *cobra.Command, nm netmap.NetMap) { + cmd.Println("Epoch:", nm.Epoch()) + + nodes := nm.Nodes() + for i := range nodes { + PrettyPrintNodeInfo(cmd, nodes[i], i, "", false) + } +} diff --git a/cmd/neofs-cli/modules/container/delete.go b/cmd/neofs-cli/modules/container/delete.go index f5abd6c87..8be62c3a2 100644 --- a/cmd/neofs-cli/modules/container/delete.go +++ b/cmd/neofs-cli/modules/container/delete.go @@ -96,7 +96,7 @@ func initContainerDeleteCmd() { flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage) flags.StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage) - flags.StringVar(&containerID, "cid", "", "container ID") + flags.StringVar(&containerID, cidFlag, "", cidFlagUsage) flags.BoolVar(&containerAwait, "await", false, "block execution until container is removed") flags.BoolP(commonflags.ForceFlag, commonflags.ForceFlagShorthand, false, "do not check whether container contains locks and remove immediately") } diff --git a/cmd/neofs-cli/modules/container/get.go b/cmd/neofs-cli/modules/container/get.go index ccdb27277..5fbcd9789 100644 --- a/cmd/neofs-cli/modules/container/get.go +++ b/cmd/neofs-cli/modules/container/get.go @@ -2,6 +2,7 @@ package container import ( "bytes" + "crypto/ecdsa" "encoding/json" "os" @@ -15,6 +16,14 @@ import ( "github.com/spf13/cobra" ) +const ( + cidFlag = "cid" + cidFlagUsage = "container ID" + + fromFlag = "from" + fromFlagUsage = "path to file with encoded container" +) + var ( containerID string containerPathFrom string @@ -27,28 +36,7 @@ var getContainerInfoCmd = &cobra.Command{ Short: "Get container field info", Long: `Get container field info`, Run: func(cmd *cobra.Command, args []string) { - var cnr container.Container - - if containerPathFrom != "" { - data, err := os.ReadFile(containerPathFrom) - common.ExitOnErr(cmd, "can't read file: %w", err) - - err = cnr.Unmarshal(data) - common.ExitOnErr(cmd, "can't unmarshal container: %w", err) - } else { - id := parseContainerID(cmd) - pk := key.GetOrGenerate(cmd) - cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC) - - var prm internalclient.GetContainerPrm - prm.SetClient(cli) - prm.SetContainer(id) - - res, err := internalclient.GetContainer(prm) - common.ExitOnErr(cmd, "rpc error: %w", err) - - cnr = res.Container() - } + cnr, _ := getContainer(cmd) prettyPrintContainer(cmd, cnr, containerJSON) @@ -76,9 +64,9 @@ func initContainerInfoCmd() { flags := getContainerInfoCmd.Flags() - flags.StringVar(&containerID, "cid", "", "container ID") + flags.StringVar(&containerID, cidFlag, "", cidFlagUsage) flags.StringVar(&containerPathTo, "to", "", "path to dump encoded container") - flags.StringVar(&containerPathFrom, "from", "", "path to file with encoded container") + flags.StringVar(&containerPathFrom, fromFlag, "", fromFlagUsage) flags.BoolVar(&containerJSON, commonflags.JSON, false, "print or dump container in JSON format") } @@ -157,3 +145,29 @@ func prettyPrintBasicACL(cmd *cobra.Command, basicACL acl.Basic) { cmd.Println() } + +func getContainer(cmd *cobra.Command) (container.Container, *ecdsa.PrivateKey) { + var cnr container.Container + var pk *ecdsa.PrivateKey + if containerPathFrom != "" { + data, err := os.ReadFile(containerPathFrom) + common.ExitOnErr(cmd, "can't read file: %w", err) + + err = cnr.Unmarshal(data) + common.ExitOnErr(cmd, "can't unmarshal container: %w", err) + } else { + id := parseContainerID(cmd) + pk = key.GetOrGenerate(cmd) + cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC) + + var prm internalclient.GetContainerPrm + prm.SetClient(cli) + prm.SetContainer(id) + + res, err := internalclient.GetContainer(prm) + common.ExitOnErr(cmd, "rpc error: %w", err) + + cnr = res.Container() + } + return cnr, pk +} diff --git a/cmd/neofs-cli/modules/container/get_eacl.go b/cmd/neofs-cli/modules/container/get_eacl.go index 46fdf71ff..81e636feb 100644 --- a/cmd/neofs-cli/modules/container/get_eacl.go +++ b/cmd/neofs-cli/modules/container/get_eacl.go @@ -57,7 +57,7 @@ func initContainerGetEACLCmd() { flags := getExtendedACLCmd.Flags() - flags.StringVar(&containerID, "cid", "", "container ID") + flags.StringVar(&containerID, cidFlag, "", cidFlagUsage) flags.StringVar(&containerPathTo, "to", "", "path to dump encoded container (default: binary encoded)") flags.BoolVar(&containerJSON, commonflags.JSON, false, "encode EACL table in json format") } diff --git a/cmd/neofs-cli/modules/container/list_objects.go b/cmd/neofs-cli/modules/container/list_objects.go index c8db5f843..5afa2c20d 100644 --- a/cmd/neofs-cli/modules/container/list_objects.go +++ b/cmd/neofs-cli/modules/container/list_objects.go @@ -89,7 +89,7 @@ func initContainerListObjectsCmd() { flags := listContainerObjectsCmd.Flags() - flags.StringVar(&containerID, "cid", "", "container ID") + flags.StringVar(&containerID, cidFlag, "", cidFlagUsage) flags.BoolVar(&flagVarListObjectsPrintAttr, flagListObjectPrintAttr, false, "request and print user attributes of each object", ) diff --git a/cmd/neofs-cli/modules/container/nodes.go b/cmd/neofs-cli/modules/container/nodes.go new file mode 100644 index 000000000..aa4bde57c --- /dev/null +++ b/cmd/neofs-cli/modules/container/nodes.go @@ -0,0 +1,62 @@ +package container + +import ( + "crypto/sha256" + + internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client" + "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common" + "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags" + "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key" + containerAPI "github.com/nspcc-dev/neofs-sdk-go/container" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + "github.com/nspcc-dev/neofs-sdk-go/netmap" + "github.com/spf13/cobra" +) + +var short bool + +var containerNodesCmd = &cobra.Command{ + Use: "nodes", + Short: "Show nodes for container", + Long: "Show nodes taking part in a container at the current epoch.", + Run: func(cmd *cobra.Command, args []string) { + var cnr, pkey = getContainer(cmd) + + if pkey == nil { + pkey = key.GetOrGenerate(cmd) + } + + cli := internalclient.GetSDKClientByFlag(cmd, pkey, commonflags.RPC) + + var prm internalclient.NetMapSnapshotPrm + prm.SetClient(cli) + + resmap, err := internalclient.NetMapSnapshot(prm) + common.ExitOnErr(cmd, "unable to get netmap snapshot", err) + + var id cid.ID + containerAPI.CalculateID(&id, cnr) + binCnr := make([]byte, sha256.Size) + id.Encode(binCnr) + + var cnrNodes [][]netmap.NodeInfo + cnrNodes, err = resmap.NetMap().ContainerNodes(cnr.PlacementPolicy(), binCnr) + common.ExitOnErr(cmd, "could not build container nodes for given container: %w", err) + + for i := range cnrNodes { + cmd.Printf("Rep %d\n", i+1) + for j := range cnrNodes[i] { + common.PrettyPrintNodeInfo(cmd, cnrNodes[i][j], j, "\t", short) + } + } + }, +} + +func initContainerNodesCmd() { + commonflags.Init(containerNodesCmd) + + flags := containerNodesCmd.Flags() + flags.StringVar(&containerID, cidFlag, "", cidFlagUsage) + flags.StringVar(&containerPathFrom, fromFlag, "", fromFlagUsage) + flags.BoolVar(&short, "short", false, "Shortens output of node info") +} diff --git a/cmd/neofs-cli/modules/container/root.go b/cmd/neofs-cli/modules/container/root.go index b04b679a3..182d0667f 100644 --- a/cmd/neofs-cli/modules/container/root.go +++ b/cmd/neofs-cli/modules/container/root.go @@ -27,6 +27,7 @@ func init() { getContainerInfoCmd, getExtendedACLCmd, setExtendedACLCmd, + containerNodesCmd, } Cmd.AddCommand(containerChildCommand...) @@ -38,6 +39,7 @@ func init() { initContainerInfoCmd() initContainerGetEACLCmd() initContainerSetEACLCmd() + initContainerNodesCmd() for _, containerCommand := range containerChildCommand { commonflags.InitAPI(containerCommand) diff --git a/cmd/neofs-cli/modules/container/set_eacl.go b/cmd/neofs-cli/modules/container/set_eacl.go index 4d7b3b6ef..a54dcf6c3 100644 --- a/cmd/neofs-cli/modules/container/set_eacl.go +++ b/cmd/neofs-cli/modules/container/set_eacl.go @@ -103,7 +103,7 @@ func initContainerSetEACLCmd() { commonflags.Init(setExtendedACLCmd) flags := setExtendedACLCmd.Flags() - flags.StringVar(&containerID, "cid", "", "container ID") + flags.StringVar(&containerID, cidFlag, "", cidFlagUsage) flags.StringVar(&flagVarsSetEACL.srcPath, "table", "", "path to file with JSON or binary encoded EACL table") flags.BoolVar(&containerAwait, "await", false, "block execution until EACL is persisted") flags.BoolVar(&flagVarsSetEACL.noPreCheck, "no-precheck", false, "do not pre-check the extensibility of the container ACL") diff --git a/cmd/neofs-cli/modules/netmap/snapshot.go b/cmd/neofs-cli/modules/netmap/snapshot.go index 9c516ef6c..0e9bf2b7b 100644 --- a/cmd/neofs-cli/modules/netmap/snapshot.go +++ b/cmd/neofs-cli/modules/netmap/snapshot.go @@ -1,13 +1,10 @@ package netmap import ( - "encoding/hex" - internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key" - "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/spf13/cobra" ) @@ -25,7 +22,7 @@ var snapshotCmd = &cobra.Command{ res, err := internalclient.NetMapSnapshot(prm) common.ExitOnErr(cmd, "rpc error: %w", err) - prettyPrintNetMap(cmd, res.NetMap()) + common.PrettyPrintNetMap(cmd, res.NetMap()) }, } @@ -33,35 +30,3 @@ func initSnapshotCmd() { commonflags.Init(snapshotCmd) commonflags.InitAPI(snapshotCmd) } - -func prettyPrintNetMap(cmd *cobra.Command, nm netmap.NetMap) { - cmd.Println("Epoch:", nm.Epoch()) - - nodes := nm.Nodes() - for i := range nodes { - var strState string - - switch { - default: - strState = "STATE_UNSUPPORTED" - case nodes[i].IsOnline(): - strState = "ONLINE" - case nodes[i].IsOffline(): - strState = "OFFLINE" - case nodes[i].IsMaintenance(): - strState = "MAINTENANCE" - } - - cmd.Printf("Node %d: %s %s ", i+1, hex.EncodeToString(nodes[i].PublicKey()), strState) - - netmap.IterateNetworkEndpoints(nodes[i], func(endpoint string) { - cmd.Printf("%s ", endpoint) - }) - - cmd.Println() - - nodes[i].IterateAttributes(func(key, value string) { - cmd.Printf("\t%s: %s\n", key, value) - }) - } -}