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 Sep 29, 2023
1 parent 225a796 commit 8222ff4
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, #2598)
Expand Down
39 changes: 39 additions & 0 deletions cmd/neofs-lens/internal/printers.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package common

import (
"fmt"
"os"
"strings"
"text/tabwriter"

"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 +71,38 @@ 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) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)

for _, shard := range status.Shards {
if len(shard.Shard.Blob.Substorages) != 0 {
fmt.Fprintf(w, "Shard ID:\t%s\n", shard.ID)
for i, subblobs := range shard.Shard.Blob.Substorages {
fmt.Fprintf(w, "\tBlobstor substorage %d\n", i)
fmt.Fprintf(w, "\t\tStorage type:\t%s\n", subblobs.Type)
fmt.Fprintf(w, "\t\tStorage Path:\t%s\n", subblobs.Path)
if subblobs.Error != nil {
fmt.Fprintf(w, "\t\tStorage Error:\t%s\n", subblobs.Error)
}
}

fmt.Fprintln(w, "\tMetabase")
fmt.Fprintf(w, "\t\tMetabase storage ID:\t%s\n", shard.Shard.Metabase.StorageID)
fmt.Fprintf(w, "\t\tMetabase path:\t%s\n", shard.Shard.Metabase.Path)
fmt.Fprintf(w, "\t\tMetabase object status:\t%s\n", strings.Join(shard.Shard.Metabase.State, " "))
if shard.Shard.Metabase.Error != nil {
fmt.Fprintf(w, "\t\tMetabase object error:\t%v\n", shard.Shard.Metabase.Error)
}
if shard.Shard.Writecache.PathDB != "" || shard.Shard.Writecache.PathFSTree != "" {
fmt.Fprintln(w, "\tWritecache")
fmt.Fprintf(w, "\t\tWritecache DB path:\t%s\n", shard.Shard.Writecache.PathDB)
fmt.Fprintf(w, "\t\tWritecache FSTree path:\t%s\n", shard.Shard.Writecache.PathFSTree)
}
fmt.Fprintln(w)
}
}

w.Flush()
}
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)
}
38 changes: 38 additions & 0 deletions pkg/local_object_storage/blobstor/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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
}

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

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
}
34 changes: 34 additions & 0 deletions pkg/local_object_storage/engine/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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()
if err == nil {
res.Shards = append(res.Shards, ObjectShardStatus{
ID: id.String(),
Shard: shardStatus,
})
}
return err != nil
})
return res, err
}
67 changes: 67 additions & 0 deletions pkg/local_object_storage/metabase/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
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
}

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")
}
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.Path = db.boltDB.Path()
res.Error = err
return res, err
}
35 changes: 35 additions & 0 deletions pkg/local_object_storage/shard/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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
}

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
}

func (c *cache) ObjectStatus(address oid.Address) (ObjectStatus, error) {
saddr := address.EncodeToString()
var value []byte
var res ObjectStatus
var err error

_ = 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, nil
}
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 8222ff4

Please sign in to comment.