Skip to content

Commit

Permalink
neofs-lens: storage status new command
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
AliceInHunterland committed Oct 11, 2023
1 parent 1d7bd26 commit 2549f5d
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Changelog for NeoFS Node
### Added
- Policer's setting to the SN's application configuration (#2600)
- Support of verified domains for the storage nodes (#2280)
- `neofs-lens storage status` CLI command (#2550)

### Fixed
- `neofs-cli netmap netinfo` documentation (#2555)
Expand Down
33 changes: 33 additions & 0 deletions cmd/neofs-lens/internal/printers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -67,3 +69,34 @@ 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) {
for _, shard := range status.Shards {
if len(shard.Shard.Blob.Substorages) != 0 {
cmd.Printf("Shard ID:\t%s\n", shard.ID)
for i, subblobs := range shard.Shard.Blob.Substorages {
cmd.Printf("\tBlobstor substorage %d\n", i)
cmd.Printf("\t\tStorage type:\t%s\n", subblobs.Type)
cmd.Printf("\t\tStorage Path:\t%s\n", subblobs.Path)
if subblobs.Error != nil {
cmd.Printf("\t\tStorage Error:\t%s\n", subblobs.Error)
}
}

cmd.Printf("\tMetabase\n")
cmd.Printf("\t\tMetabase storage ID:\t%s\n", shard.Shard.Metabase.StorageID)
cmd.Printf("\t\tMetabase path:\t%s\n", shard.Shard.Metabase.Path)
cmd.Printf("\t\tMetabase object status:\t%s\n", strings.Join(shard.Shard.Metabase.State, " "))
if shard.Shard.Metabase.Error != nil {
cmd.Printf("\t\tMetabase object error:\t%v\n", shard.Shard.Metabase.Error)
}
if shard.Shard.Writecache.PathDB != "" || shard.Shard.Writecache.PathFSTree != "" {
cmd.Printf("\tWritecache\n")
cmd.Printf("\t\tWritecache DB path:\t%s\n", shard.Shard.Writecache.PathDB)
cmd.Printf("\t\tWritecache FSTree path:\t%s\n", shard.Shard.Writecache.PathFSTree)
}
cmd.Println()
}
}
}
4 changes: 3 additions & 1 deletion cmd/neofs-lens/internal/storage/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ var Root = &cobra.Command{

func init() {
Root.AddCommand(
storageInspectObjCMD, storageGetObjCMD,
storageInspectObjCMD,
storageGetObjCMD,
storageStatusObjCMD,
)
}

Expand Down
34 changes: 34 additions & 0 deletions cmd/neofs-lens/internal/storage/status.go
Original file line number Diff line number Diff line change
@@ -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)
}
43 changes: 43 additions & 0 deletions pkg/local_object_storage/blobstor/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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"
)

// ObjectSubstorageStatus represents the status of the object in the Blob
// storage, containing the type and path of the storage and an error if it
// occurred.
type ObjectSubstorageStatus struct {
Type string
Path string
Error error
}

// ObjectStatus represents the status of the object in the Blob storage.
type ObjectStatus struct {
Substorages []ObjectSubstorageStatus
}

// ObjectStatus returns the status of the object in the Blob storage. It contains
// status of the object in all blob substorages.
func (b *BlobStor) ObjectStatus(address oid.Address) (ObjectStatus, error) {
b.modeMtx.RLock()
defer b.modeMtx.RUnlock()
res := ObjectStatus{
Substorages: []ObjectSubstorageStatus{},
}
prm := common.GetPrm{
Address: address,
}
for i := range b.storage {
_, err := b.storage[i].Storage.Get(prm)
if err == nil {
res.Substorages = append(res.Substorages, ObjectSubstorageStatus{
Type: b.storage[i].Storage.Type(),
Path: b.storage[i].Storage.Path(),
})
}
}
return res, nil
}
37 changes: 37 additions & 0 deletions pkg/local_object_storage/engine/status.go
Original file line number Diff line number Diff line change
@@ -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"
)

// ObjectShardStatus contains the status of the object in the Shard and Shard ID.
type ObjectShardStatus struct {
ID string
Shard shard.ObjectStatus
}

// ObjectStatus represents the status of the object in the StorageEngine.
type ObjectStatus struct {
Shards []ObjectShardStatus
}

// ObjectStatus returns the status of the object in the StorageEngine. It contains status of the object in all shards.
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()
if err == nil {
res.Shards = append(res.Shards, ObjectShardStatus{
ID: id.String(),
Shard: shardStatus,
})
}
return err != nil
})
return res, err
}
65 changes: 65 additions & 0 deletions pkg/local_object_storage/metabase/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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"
)

// ObjectStatus represents the status of the object in the Metabase.
type ObjectStatus struct {
State []string
Path string
StorageID string
Error error
}

// ObjectStatus returns the status of the object in the Metabase. It contains state, path and storageID.
func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) {
db.modeMtx.RLock()
defer db.modeMtx.RUnlock()
var res ObjectStatus
if db.mode.NoMetabase() {
return res, nil
}

storageID := StorageIDPrm{}
storageID.SetAddress(address)
resStorageID, err := db.StorageID(storageID)
if id := resStorageID.StorageID(); id != nil {
res.StorageID = blobovnicza.NewIDFromBytes(id).String()
} else {
return res, nil
}

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

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) || inBucket(tx, primaryBucketName(cID, key), objKey) || inBucket(tx, parentBucketName(cID, key), objKey) {
res.State = append(res.State, "AVAILABLE")
}
if removedStatus == 1 {
res.State = append(res.State, "GC MARKED")
}
if removedStatus == 2 {
res.State = append(res.State, "IN GRAVEYARD")
}
return err
})
res.Path = db.boltDB.Path()
res.Error = err
return res, err
}
37 changes: 37 additions & 0 deletions pkg/local_object_storage/shard/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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"
)

// ObjectStatus represents the status of an object in a storage system. It contains
// information about the object's status in various sub-components such as Blob storage,
// Metabase, and Writecache. Additionally, it includes a slice of errors that may have
// occurred at the object level.
type ObjectStatus struct {
Blob blobstor.ObjectStatus
Metabase meta.ObjectStatus
Writecache writecache.ObjectStatus
Errors []error
}

// ObjectStatus returns the status of the object in the Shard. It contains status
// of the object in Blob storage, Metabase and Writecache.
func (s *Shard) ObjectStatus(address oid.Address) (ObjectStatus, error) {
var res ObjectStatus
var err error
res.Blob, err = s.blobStor.ObjectStatus(address)
if len(res.Blob.Substorages) != 0 {
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)
}
}
return res, nil
}
37 changes: 37 additions & 0 deletions pkg/local_object_storage/writecache/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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"
"go.etcd.io/bbolt"
)

// ObjectStatus represents the status of the object in the Writecache.
type ObjectStatus struct {
PathDB string
PathFSTree string
}

// ObjectStatus returns the status of the object in the Writecache. It contains path to the DB and path to the FSTree.
func (c *cache) ObjectStatus(address oid.Address) (ObjectStatus, error) {
saddr := address.EncodeToString()
var value []byte
var res ObjectStatus
var err error

err = c.db.View(func(tx *bbolt.Tx) error {
b := tx.Bucket(defaultBucket)
if b != nil {
value = b.Get([]byte(saddr))
if value != nil {
res.PathDB = c.db.Path()
}
}
return nil
})
_, err = c.fsTree.Get(common.GetPrm{Address: address})
if err == nil {
res.PathFSTree = c.fsTree.Path()
}
return res, err
}
1 change: 1 addition & 0 deletions pkg/local_object_storage/writecache/writecache.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Cache interface {
Init() error
Open(readOnly bool) error
Close() error
ObjectStatus(address oid.Address) (ObjectStatus, error)
}

type cache struct {
Expand Down

0 comments on commit 2549f5d

Please sign in to comment.