Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

neofs-lens: storage status new command #2591

Merged
merged 1 commit into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
roman-khimov marked this conversation as resolved.
Show resolved Hide resolved
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) {
roman-khimov marked this conversation as resolved.
Show resolved Hide resolved
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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so in the worst scenario, could it print LOCKED AVAILABLE AVAILABLE GC MARKED?

Copy link
Contributor Author

@AliceInHunterland AliceInHunterland Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could be such a situation when an object is inBucket and inGraveyardWithKey at the same time?

if so it could be LOCKED AVAILABLE AVAILABLE GC MARKED

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"AVAILABLE AVAILABLE" is better be avoided, you can use some different terms.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would expect printing statuses if they are interested, e.g.: a regular available object -- shard ID, if locked, another line in the print about it, if removed but locked -- one more line. if not found at all -- just "not found" line or smth like that

it could be such a situation when an object is inBucket

depends on what bucket we are talking about. but an object can be placed in a shard, be removed but be locked so LOCKED AVAILABLE AVAILABLE GC MARKED seems reachable (but not clear)

}
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really unobvious what this slice means without looking into the implementation.

}

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

roman-khimov marked this conversation as resolved.
Show resolved Hide resolved
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
Loading