From 6163fa2fa1017d0faf71ec11e86eff2804757941 Mon Sep 17 00:00:00 2001 From: Ekaterina Pavlova Date: Mon, 25 Sep 2023 19:57:17 +0400 Subject: [PATCH] neofs-lens: storage status new command This command prints out information about storage and its components, where object is located. It can be useful for object inspection and storage health check. Refs: #2550. Signed-off-by: Ekaterina Pavlova --- CHANGELOG.md | 3 + cmd/neofs-lens/internal/printers.go | 28 ++++++++ cmd/neofs-lens/internal/storage/root.go | 1 + cmd/neofs-lens/internal/storage/status.go | 34 +++++++++ pkg/local_object_storage/blobstor/status.go | 35 ++++++++++ pkg/local_object_storage/engine/status.go | 37 ++++++++++ pkg/local_object_storage/metabase/status.go | 69 +++++++++++++++++++ pkg/local_object_storage/shard/status.go | 33 +++++++++ pkg/local_object_storage/writecache/status.go | 41 +++++++++++ .../writecache/writecache.go | 1 + 10 files changed, 282 insertions(+) create mode 100644 cmd/neofs-lens/internal/storage/status.go create mode 100644 pkg/local_object_storage/blobstor/status.go create mode 100644 pkg/local_object_storage/engine/status.go create mode 100644 pkg/local_object_storage/metabase/status.go create mode 100644 pkg/local_object_storage/shard/status.go create mode 100644 pkg/local_object_storage/writecache/status.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 659bd01b848..dd6e57fa42c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ Changelog for NeoFS Node ## [Unreleased] +## Added +- `neofs-lens storage status` CLI command (#2550) + ### Fixed - `neofs-cli netmap netinfo` documentation (#2555) - `GETRANGEHASH` to a node without an object produced `GETRANGE` or `GET` requests (#2541) diff --git a/cmd/neofs-lens/internal/printers.go b/cmd/neofs-lens/internal/printers.go index 853cc8156af..c83bcbbec58 100644 --- a/cmd/neofs-lens/internal/printers.go +++ b/cmd/neofs-lens/internal/printers.go @@ -2,7 +2,9 @@ package common import ( "os" + "strings" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/engine" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" @@ -67,3 +69,29 @@ func WriteObjectToFile(cmd *cobra.Command, path string, data []byte, payloadOnly } cmd.Printf("\nSaved object to '%s' file\n", path) } + +// PrintStorageObjectStatus prints object status . +func PrintStorageObjectStatus(cmd *cobra.Command, status engine.ObjectStatus) { + //cmd.Println("Object status: ", status) + for _, shard := range status.Shards { + cmd.Println("Shard ID: ", shard.ID) + for i, subblobs := range shard.Shard.Blob.Substorages { + cmd.Println(" Substorage ", i) + cmd.Println(" Blob Type: ", subblobs.Type) + cmd.Println(" Blob Path: ", subblobs.Path) + cmd.Println(" Blob Error: ", subblobs.Error) + } + cmd.Println(" Metabase") + cmd.Println(" Metabase storage ID: ", shard.Shard.Metabase.StorageID) + cmd.Println(" Metabase path: ", shard.Shard.Metabase.Path) + cmd.Println(" Metabase object status: ", strings.Join(shard.Shard.Metabase.State, " ")) + cmd.Println(" Metabase object error: ", shard.Shard.Metabase.Error) + + cmd.Println(" Writecache") + cmd.Println(" Writecache DB path: ", shard.Shard.Writecache.PathDB) + cmd.Println(" Writecache DB error: ", shard.Shard.Writecache.ErrorDB) + cmd.Println(" Writecache FSTree path: ", shard.Shard.Writecache.PathFSTree) + cmd.Println(" Writecache FSTree error: ", shard.Shard.Writecache.ErrorFSTree) + } + +} diff --git a/cmd/neofs-lens/internal/storage/root.go b/cmd/neofs-lens/internal/storage/root.go index 208394757a2..99045aa2ad6 100644 --- a/cmd/neofs-lens/internal/storage/root.go +++ b/cmd/neofs-lens/internal/storage/root.go @@ -45,6 +45,7 @@ var Root = &cobra.Command{ func init() { Root.AddCommand( storageInspectObjCMD, + storageStatusObjCMD, ) } diff --git a/cmd/neofs-lens/internal/storage/status.go b/cmd/neofs-lens/internal/storage/status.go new file mode 100644 index 00000000000..cf5fb06abe3 --- /dev/null +++ b/cmd/neofs-lens/internal/storage/status.go @@ -0,0 +1,34 @@ +package storage + +import ( + common "github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + "github.com/spf13/cobra" +) + +var storageStatusObjCMD = &cobra.Command{ + Use: "status", + Short: "Get object from the NeoFS node's storage snapshot", + Long: "Get object from the NeoFS node's storage snapshot", + Args: cobra.NoArgs, + Run: statusObject, +} + +func init() { + common.AddAddressFlag(storageStatusObjCMD, &vAddress) + common.AddConfigFileFlag(storageStatusObjCMD, &vConfig) +} + +func statusObject(cmd *cobra.Command, _ []string) { + var addr oid.Address + + err := addr.DecodeString(vAddress) + common.ExitOnErr(cmd, common.Errf("invalid address argument: %w", err)) + + storage := openEngine(cmd) + defer storage.Close() + status, err := storage.ObjectStatus(addr) + common.ExitOnErr(cmd, common.Errf("could not fetch object: %w", err)) + + common.PrintStorageObjectStatus(cmd, status) +} diff --git a/pkg/local_object_storage/blobstor/status.go b/pkg/local_object_storage/blobstor/status.go new file mode 100644 index 00000000000..3cd73a99705 --- /dev/null +++ b/pkg/local_object_storage/blobstor/status.go @@ -0,0 +1,35 @@ +package blobstor + +import ( + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/common" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" +) + +type ObjectSubstorageStatus struct { + Type string + Path string + Error error +} +type ObjectStatus struct { + //State ObjectState // LOCKED, INHUMED, GARBAGE, etc. + Substorages []ObjectSubstorageStatus +} + +func (b *BlobStor) ObjectStatus(address oid.Address) (ObjectStatus, error) { + b.modeMtx.RLock() + defer b.modeMtx.RUnlock() + res := ObjectStatus{ + Substorages: make([]ObjectSubstorageStatus, len(b.storage)), + } + prm := common.GetPrm{ + Address: address, + } + for i := range b.storage { + _, err := b.storage[i].Storage.Get(prm) + res.Substorages[i].Error = err + res.Substorages[i].Type = b.storage[i].Storage.Type() + res.Substorages[i].Path = b.storage[i].Storage.Path() + } + return res, nil + +} diff --git a/pkg/local_object_storage/engine/status.go b/pkg/local_object_storage/engine/status.go new file mode 100644 index 00000000000..2e6be6e0852 --- /dev/null +++ b/pkg/local_object_storage/engine/status.go @@ -0,0 +1,37 @@ +package engine + +import ( + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" +) + +type ObjectShardStatus struct { + ID string + Shard shard.ObjectStatus +} + +type ObjectStatus struct { + Shards []ObjectShardStatus +} + +func (e *StorageEngine) ObjectStatus(address oid.Address) (ObjectStatus, error) { + var res ObjectStatus + var err error + + e.iterateOverSortedShards(address, func(_ int, sh hashedShard) (stop bool) { + var shardStatus shard.ObjectStatus + shardStatus, err = sh.ObjectStatus(address) + id := *sh.ID() + res.Shards = append(res.Shards, ObjectShardStatus{ + ID: id.String(), + Shard: shardStatus, + }) + + if err != nil { + return true + } + return false + }) + + return res, err +} diff --git a/pkg/local_object_storage/metabase/status.go b/pkg/local_object_storage/metabase/status.go new file mode 100644 index 00000000000..6bc80a5d0dc --- /dev/null +++ b/pkg/local_object_storage/metabase/status.go @@ -0,0 +1,69 @@ +package meta + +import ( + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobovnicza" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + "go.etcd.io/bbolt" +) + +type ObjectStatus struct { + State []string + Path string + StorageID string + Error error +} + +func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) { + db.modeMtx.RLock() + defer db.modeMtx.RUnlock() + + var res ObjectStatus + var err error + + if db.mode.NoMetabase() { + res.Error = ErrDegradedMode + return res, ErrDegradedMode + } + + res.Path = db.boltDB.Path() + + storageID := StorageIDPrm{} + storageID.SetAddress(address) + resStorageID, err := db.StorageID(storageID) + if id := resStorageID.StorageID(); id != nil { + res.StorageID = blobovnicza.NewIDFromBytes(id).String() + } + + err = db.boltDB.View(func(tx *bbolt.Tx) error { + oID := address.Object() + cID := address.Container() + objKey := objectKey(address.Object(), make([]byte, objectKeySize)) + key := make([]byte, bucketKeySize) + + if objectLocked(tx, cID, oID) { + res.State = append(res.State, "LOCKED") + } + if inBucket(tx, primaryBucketName(cID, key), objKey) || inBucket(tx, parentBucketName(cID, key), objKey) { + res.State = append(res.State, "AVAILABLE") + } + + graveyardBkt := tx.Bucket(graveyardBucketName) + garbageBkt := tx.Bucket(garbageBucketName) + addrKey := addressKey(address, make([]byte, addressKeySize)) + + removedStatus := inGraveyardWithKey(addrKey, graveyardBkt, garbageBkt) + if removedStatus != 0 && objectLocked(tx, cID, oID) { + res.State = append(res.State, "AVAILABLE") + } + if removedStatus == 1 { + res.State = append(res.State, "GC MARKED") + } else if removedStatus == 2 { + res.State = append(res.State, "IN GRAVEYARD") + } else { + res.State = append(res.State, "NOT IN GRAVEYARD") + } + return err + }) + res.Error = err + return res, err +} diff --git a/pkg/local_object_storage/shard/status.go b/pkg/local_object_storage/shard/status.go new file mode 100644 index 00000000000..bd233f51960 --- /dev/null +++ b/pkg/local_object_storage/shard/status.go @@ -0,0 +1,33 @@ +package shard + +import ( + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor" + meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/writecache" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" +) + +type ObjectStatus struct { + Blob blobstor.ObjectStatus + Metabase meta.ObjectStatus + Writecache writecache.ObjectStatus + Errors []error +} + +func (s *Shard) ObjectStatus(address oid.Address) (ObjectStatus, error) { + var res ObjectStatus + var err error + res.Blob, err = s.blobStor.ObjectStatus(address) + res.Errors = append(res.Errors, err) + res.Metabase, err = s.metaBase.ObjectStatus(address) + res.Errors = append(res.Errors, err) + if s.hasWriteCache() { + res.Writecache, err = s.writeCache.ObjectStatus(address) + res.Errors = append(res.Errors, err) + } + if err != nil { + //TODO add context + return res, err + } + return res, err +} diff --git a/pkg/local_object_storage/writecache/status.go b/pkg/local_object_storage/writecache/status.go new file mode 100644 index 00000000000..e956ef7ca24 --- /dev/null +++ b/pkg/local_object_storage/writecache/status.go @@ -0,0 +1,41 @@ +package writecache + +import ( + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/common" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + "github.com/pkg/errors" + "go.etcd.io/bbolt" +) + +type ObjectStatus struct { + PathDB string + PathFSTree string + ErrorDB error + ErrorFSTree error +} + +func (c *cache) ObjectStatus(address oid.Address) (ObjectStatus, error) { + saddr := address.EncodeToString() + var value []byte + var res ObjectStatus + + err := c.db.View(func(tx *bbolt.Tx) error { + res.PathDB = c.db.Path() + b := tx.Bucket(defaultBucket) + if b == nil { + res.ErrorDB = errors.New("bucket not found") + } else { + value = b.Get([]byte(saddr)) + if value == nil { + res.ErrorDB = errors.New("object not found") + } + } + return nil + }) + _, err = c.fsTree.Get(common.GetPrm{Address: address}) + res.PathFSTree = c.fsTree.Path() + if err != nil { + res.ErrorFSTree = errors.New("object not found") + } + return res, nil +} diff --git a/pkg/local_object_storage/writecache/writecache.go b/pkg/local_object_storage/writecache/writecache.go index 7d88d024551..9bd9977fdea 100644 --- a/pkg/local_object_storage/writecache/writecache.go +++ b/pkg/local_object_storage/writecache/writecache.go @@ -39,6 +39,7 @@ type Cache interface { Init() error Open(readOnly bool) error Close() error + ObjectStatus(address oid.Address) (ObjectStatus, error) } type cache struct {