Skip to content

Commit

Permalink
neofs-lens: storage status new command (#2591)
Browse files Browse the repository at this point in the history
Closes #2550.
  • Loading branch information
roman-khimov authored Oct 12, 2023
2 parents 1d7bd26 + 4c1ab5e commit 41e17ce
Show file tree
Hide file tree
Showing 10 changed files with 294 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
}
40 changes: 40 additions & 0 deletions pkg/local_object_storage/writecache/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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"
)

// 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

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 errors.New("value not found")
})
if err != nil {
return res, err
}
_, 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 41e17ce

Please sign in to comment.