Skip to content

Commit

Permalink
neofs-adm: Add container estimations inspector
Browse files Browse the repository at this point in the history
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
  • Loading branch information
carpawell committed Apr 23, 2024
1 parent a32a930 commit 40e1ab0
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog for NeoFS Node
## [Unreleased]

### Added
- Container estimations inspector to neofs-adm (#2826)

### Fixed

Expand Down
140 changes: 140 additions & 0 deletions cmd/neofs-adm/internal/modules/morph/estimation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package morph

import (
"crypto/sha256"
"fmt"

"github.com/google/uuid"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var estimationsCmd = &cobra.Command{
Use: "estimations",
Short: "See container estimations reported by storage nodes",
PreRun: func(cmd *cobra.Command, _ []string) {
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
},
RunE: estimationsFunc,
Args: cobra.NoArgs,
}

func estimationsFunc(cmd *cobra.Command, _ []string) error {
cIDString, err := cmd.Flags().GetString(estimationsContainerFlag)
if err != nil {
panic(fmt.Errorf("reading %s flag: %w", estimationsContainerFlag, err))
}

var cID cid.ID
if err := cID.DecodeString(cIDString); err != nil {
return fmt.Errorf("invalid container ID: %w", err)
}

c, err := getN3Client(viper.GetViper())
if err != nil {
return fmt.Errorf("can't create N3 client: %w", err)
}

inv := invoker.New(c, nil)

nnsState, err := c.GetContractStateByID(1)
if err != nil {
return err
}

epoch, err := cmd.Flags().GetInt64(estimationsEpochFlag)
if err != nil {
panic(fmt.Errorf("reading %s flag: %w", estimationsEpochFlag, err))
}

if epoch <= 0 {
netmapHash, err := nnsResolveHash(inv, nnsState.Hash, netmapContract+".neofs")
if err != nil {
return fmt.Errorf("netmap contract hash resolution: %w", err)
}

epochFromContract, err := unwrap.Int64(inv.Call(netmapHash, "epoch"))
if err != nil {
return fmt.Errorf("reading epoch: %w", err)
}

epoch = epochFromContract + epoch // epoch is negative here
}

cnrHash, err := nnsResolveHash(inv, nnsState.Hash, containerContract+".neofs")
if err != nil {
return fmt.Errorf("container contract hash resolution: %w", err)
}

cIDBytes := make([]byte, sha256.Size)
cID.Encode(cIDBytes)

sID, iter, err := unwrap.SessionIterator(inv.Call(cnrHash, "iterateContainerSizes", epoch, cIDBytes))
if err != nil {
return fmt.Errorf("iterator expansion: %w", err)
}

defer func() {
if (sID != uuid.UUID{}) {
_ = inv.TerminateSession(sID)
}
}()

ee, err := parseEstimations(inv, sID, iter)
if err != nil {
return fmt.Errorf("parsing estimations read from contract: %w", err)
}

if len(ee) == 0 {
cmd.Printf("No estimations found for %s container in %d epoch\n", cID, epoch)
return nil
}

printEstimations(cmd, epoch, ee)

return nil
}

func parseEstimations(inv *invoker.Invoker, sID uuid.UUID, iter result.Iterator) ([]container.Estimation, error) {
items := make([]stackitem.Item, 0)

for {
ii, err := inv.TraverseIterator(sID, &iter, config.DefaultMaxIteratorResultItems)
if err != nil {
return nil, fmt.Errorf("iterator traversal; session: %s, error: %w", sID, err)
}

if len(ii) == 0 {
break
}

items = append(items, ii...)
}

ee := make([]container.Estimation, len(items))
for i := range items {
err := ee[i].FromStackItem(items[i])
if err != nil {
return nil, fmt.Errorf("parsing stack item: %w", err)
}
}

return ee, nil
}

func printEstimations(cmd *cobra.Command, epoch int64, ee []container.Estimation) {
cmd.Printf("Estimations for %d epoch:\n", epoch)

for _, estimation := range ee {
reporterString := base58.Encode(estimation.Reporter)
cmd.Printf("Container size: %d, reporter's key (base58 encoding): %s\n", estimation.Size, reporterString)
}
}
16 changes: 16 additions & 0 deletions cmd/neofs-adm/internal/modules/morph/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const (
neoAddressesFlag = "neo-addresses"
publicKeysFlag = "public-keys"
walletFlag = "wallet"
estimationsEpochFlag = "epoch"
estimationsContainerFlag = "cid"
)

var (
Expand Down Expand Up @@ -438,6 +440,20 @@ func init() {
RootCmd.AddCommand(netmapCandidatesCmd)
netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")

RootCmd.AddCommand(estimationsCmd)
ff := estimationsCmd.Flags()
ff.Int64(estimationsEpochFlag, 0, "Epoch for estimations, `0` for current, negative for relative epochs")
estimationsCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
err := cobra.MarkFlagRequired(ff, endpointFlag)
if err != nil {
panic(fmt.Errorf("failed to mark required %s flag: %w", endpointFlag, err))
}
ff.String(estimationsContainerFlag, "", "Inspected container, base58 encoded")
err = cobra.MarkFlagRequired(ff, estimationsContainerFlag)
if err != nil {
panic(fmt.Errorf("failed to mark required %s flag: %w", estimationsContainerFlag, err))
}

cmd := verifiedNodesDomainAccessListCmd
fs := cmd.Flags()
fs.StringP(endpointFlag, "r", "", "NeoFS Sidechain RPC endpoint")
Expand Down

0 comments on commit 40e1ab0

Please sign in to comment.