[#1704] Add command `container nodes` to output list of nodes for container, grouped by replica (#1704)

Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
remotes/fyrchik/neofs-adm-fix-commands
Anton Nikiforov 2022-10-05 16:52:46 +03:00 committed by fyrchik
parent 6557f5d249
commit 5e493b7f1c
10 changed files with 157 additions and 64 deletions

View File

@ -15,6 +15,7 @@ Changelog for NeoFS Node
- `wallet-address` flag in `neofs-adm morph refill-gas` command (#1820) - `wallet-address` flag in `neofs-adm morph refill-gas` command (#1820)
- Validate policy before container creation (#1704) - Validate policy before container creation (#1704)
- `--timeout` flag in `neofs-cli` subcommands (#1837) - `--timeout` flag in `neofs-cli` subcommands (#1837)
- `container nodes` command to output list of nodes for container, grouped by replica (#1704)
### Changed ### Changed
- Allow to evacuate shard data with `EvacuateShard` control RPC (#1800) - Allow to evacuate shard data with `EvacuateShard` control RPC (#1800)

View File

@ -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)
}
}

View File

@ -96,7 +96,7 @@ func initContainerDeleteCmd() {
flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage) flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
flags.StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage) 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.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") flags.BoolP(commonflags.ForceFlag, commonflags.ForceFlagShorthand, false, "do not check whether container contains locks and remove immediately")
} }

View File

@ -2,6 +2,7 @@ package container
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"encoding/json" "encoding/json"
"os" "os"
@ -15,6 +16,14 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const (
cidFlag = "cid"
cidFlagUsage = "container ID"
fromFlag = "from"
fromFlagUsage = "path to file with encoded container"
)
var ( var (
containerID string containerID string
containerPathFrom string containerPathFrom string
@ -27,28 +36,7 @@ var getContainerInfoCmd = &cobra.Command{
Short: "Get container field info", Short: "Get container field info",
Long: `Get container field info`, Long: `Get container field info`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var cnr container.Container cnr, _ := getContainer(cmd)
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()
}
prettyPrintContainer(cmd, cnr, containerJSON) prettyPrintContainer(cmd, cnr, containerJSON)
@ -76,9 +64,9 @@ func initContainerInfoCmd() {
flags := getContainerInfoCmd.Flags() 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(&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") 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() 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
}

View File

@ -57,7 +57,7 @@ func initContainerGetEACLCmd() {
flags := getExtendedACLCmd.Flags() 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.StringVar(&containerPathTo, "to", "", "path to dump encoded container (default: binary encoded)")
flags.BoolVar(&containerJSON, commonflags.JSON, false, "encode EACL table in json format") flags.BoolVar(&containerJSON, commonflags.JSON, false, "encode EACL table in json format")
} }

View File

@ -89,7 +89,7 @@ func initContainerListObjectsCmd() {
flags := listContainerObjectsCmd.Flags() flags := listContainerObjectsCmd.Flags()
flags.StringVar(&containerID, "cid", "", "container ID") flags.StringVar(&containerID, cidFlag, "", cidFlagUsage)
flags.BoolVar(&flagVarListObjectsPrintAttr, flagListObjectPrintAttr, false, flags.BoolVar(&flagVarListObjectsPrintAttr, flagListObjectPrintAttr, false,
"request and print user attributes of each object", "request and print user attributes of each object",
) )

View File

@ -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")
}

View File

@ -27,6 +27,7 @@ func init() {
getContainerInfoCmd, getContainerInfoCmd,
getExtendedACLCmd, getExtendedACLCmd,
setExtendedACLCmd, setExtendedACLCmd,
containerNodesCmd,
} }
Cmd.AddCommand(containerChildCommand...) Cmd.AddCommand(containerChildCommand...)
@ -38,6 +39,7 @@ func init() {
initContainerInfoCmd() initContainerInfoCmd()
initContainerGetEACLCmd() initContainerGetEACLCmd()
initContainerSetEACLCmd() initContainerSetEACLCmd()
initContainerNodesCmd()
for _, containerCommand := range containerChildCommand { for _, containerCommand := range containerChildCommand {
commonflags.InitAPI(containerCommand) commonflags.InitAPI(containerCommand)

View File

@ -103,7 +103,7 @@ func initContainerSetEACLCmd() {
commonflags.Init(setExtendedACLCmd) commonflags.Init(setExtendedACLCmd)
flags := setExtendedACLCmd.Flags() 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.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(&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") flags.BoolVar(&flagVarsSetEACL.noPreCheck, "no-precheck", false, "do not pre-check the extensibility of the container ACL")

View File

@ -1,13 +1,10 @@
package netmap package netmap
import ( import (
"encoding/hex"
internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client" 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/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags" "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-node/cmd/neofs-cli/internal/key"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -25,7 +22,7 @@ var snapshotCmd = &cobra.Command{
res, err := internalclient.NetMapSnapshot(prm) res, err := internalclient.NetMapSnapshot(prm)
common.ExitOnErr(cmd, "rpc error: %w", err) 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.Init(snapshotCmd)
commonflags.InitAPI(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)
})
}
}