[#1223] lens/tui: Add metabase schema
Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
parent
919b9e3370
commit
3e976a8259
18 changed files with 1372 additions and 1 deletions
43
cmd/frostfs-lens/internal/schema/common/format.go
Normal file
43
cmd/frostfs-lens/internal/schema/common/format.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
type FormatOptions struct {
|
||||
Color tcell.Color
|
||||
|
||||
Bold,
|
||||
Italic,
|
||||
Underline,
|
||||
StrikeThrough bool
|
||||
}
|
||||
|
||||
func Format(s string, opts FormatOptions) string {
|
||||
var boldTag, italicTag, underlineTag, strikeThroughTag string
|
||||
|
||||
switch {
|
||||
case opts.Bold:
|
||||
boldTag = "b"
|
||||
case opts.Italic:
|
||||
italicTag = "i"
|
||||
case opts.Underline:
|
||||
underlineTag = "u"
|
||||
case opts.StrikeThrough:
|
||||
strikeThroughTag = "s"
|
||||
}
|
||||
|
||||
attrs := fmt.Sprintf(
|
||||
"%s%s%s%s", boldTag, italicTag, underlineTag, strikeThroughTag,
|
||||
)
|
||||
color := strconv.FormatInt(int64(opts.Color.Hex()), 16)
|
||||
|
||||
return fmt.Sprintf("[#%06s::%s]%s[-::-]", color, attrs, s)
|
||||
}
|
||||
|
||||
func FormatSimple(s string, c tcell.Color) string {
|
||||
return Format(s, FormatOptions{Color: c})
|
||||
}
|
29
cmd/frostfs-lens/internal/schema/common/raw.go
Normal file
29
cmd/frostfs-lens/internal/schema/common/raw.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/mr-tron/base58"
|
||||
)
|
||||
|
||||
type RawEntry struct {
|
||||
key, value []byte
|
||||
}
|
||||
|
||||
var RawParser Parser = rawParser
|
||||
|
||||
func rawParser(key, value []byte) (SchemaEntry, Parser, error) {
|
||||
return &RawEntry{key: key, value: value}, rawParser, nil
|
||||
}
|
||||
|
||||
func (r *RawEntry) String() string {
|
||||
return FormatSimple(base58.Encode(r.key), tcell.ColorRed)
|
||||
}
|
||||
|
||||
func (r *RawEntry) DetailedString() string {
|
||||
return spew.Sdump(r)
|
||||
}
|
||||
|
||||
func (r *RawEntry) Filter(string, any) FilterResult {
|
||||
return No
|
||||
}
|
81
cmd/frostfs-lens/internal/schema/common/schema.go
Normal file
81
cmd/frostfs-lens/internal/schema/common/schema.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type FilterResult byte
|
||||
|
||||
const (
|
||||
No FilterResult = iota
|
||||
Maybe
|
||||
Yes
|
||||
)
|
||||
|
||||
func IfThenElse(condition bool, onSuccess, onFailure FilterResult) FilterResult {
|
||||
var res FilterResult
|
||||
if condition {
|
||||
res = onSuccess
|
||||
} else {
|
||||
res = onFailure
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type SchemaEntry interface {
|
||||
String() string
|
||||
DetailedString() string
|
||||
Filter(typ string, val any) FilterResult
|
||||
}
|
||||
|
||||
type (
|
||||
Parser func(key, value []byte) (SchemaEntry, Parser, error)
|
||||
FallbackParser func(key, value []byte) (SchemaEntry, Parser)
|
||||
)
|
||||
|
||||
func Any(parsers ...Parser) Parser {
|
||||
return func(key, value []byte) (SchemaEntry, Parser, error) {
|
||||
var errs error
|
||||
for _, parser := range parsers {
|
||||
ret, next, err := parser(key, value)
|
||||
if err == nil {
|
||||
return ret, next, nil
|
||||
}
|
||||
errs = errors.Join(errs, err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("no parser succeeded: %w", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func WithFallback(parser Parser, fallback FallbackParser) Parser {
|
||||
if parser == nil {
|
||||
return fallback.ToParser()
|
||||
}
|
||||
return func(key, value []byte) (SchemaEntry, Parser, error) {
|
||||
entry, next, err := parser(key, value)
|
||||
if err == nil {
|
||||
return entry, WithFallback(next, fallback), nil
|
||||
}
|
||||
return fallback.ToParser()(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (fp FallbackParser) ToParser() Parser {
|
||||
return func(key, value []byte) (SchemaEntry, Parser, error) {
|
||||
entry, next := fp(key, value)
|
||||
return entry, next, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p Parser) ToFallbackParser() FallbackParser {
|
||||
return func(key, value []byte) (SchemaEntry, Parser) {
|
||||
entry, next, err := p(key, value)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf(
|
||||
"couldn't use that parser as a fallback parser, it returned an error: %w", err,
|
||||
))
|
||||
}
|
||||
return entry, next
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package buckets
|
||||
|
||||
import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func (b *PrefixBucket) DetailedString() string {
|
||||
return spew.Sdump(*b)
|
||||
}
|
||||
|
||||
func (b *PrefixContainerBucket) DetailedString() string {
|
||||
return spew.Sdump(*b)
|
||||
}
|
||||
|
||||
func (b *UserBucket) DetailedString() string {
|
||||
return spew.Sdump(*b)
|
||||
}
|
||||
|
||||
func (b *ContainerBucket) DetailedString() string {
|
||||
return spew.Sdump(*b)
|
||||
}
|
||||
|
||||
func (b *UserAttributeKeyBucket) DetailedString() string {
|
||||
return spew.Sdump(*b)
|
||||
}
|
||||
|
||||
func (b *UserAttributeValueBucket) DetailedString() string {
|
||||
return spew.Sdump(*b)
|
||||
}
|
81
cmd/frostfs-lens/internal/schema/metabase/buckets/filter.go
Normal file
81
cmd/frostfs-lens/internal/schema/metabase/buckets/filter.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package buckets
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
)
|
||||
|
||||
func (b *PrefixBucket) Filter(typ string, _ any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
return b.resolvers.cidResolver(false)
|
||||
case "oid":
|
||||
return b.resolvers.oidResolver(false)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (b *PrefixContainerBucket) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
id := val.(cid.ID)
|
||||
return b.resolvers.cidResolver(b.id.Equals(id))
|
||||
case "oid":
|
||||
return b.resolvers.oidResolver(false)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (b *UserBucket) Filter(typ string, _ any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
return b.resolvers.cidResolver(false)
|
||||
case "oid":
|
||||
return b.resolvers.oidResolver(false)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (b *ContainerBucket) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
id := val.(cid.ID)
|
||||
return b.resolvers.cidResolver(b.id.Equals(id))
|
||||
case "oid":
|
||||
return b.resolvers.oidResolver(false)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (b *UserAttributeKeyBucket) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
id := val.(cid.ID)
|
||||
return common.IfThenElse(b.id.Equals(id), common.Yes, common.No)
|
||||
case "oid":
|
||||
return common.Maybe
|
||||
case "key":
|
||||
key := val.(string)
|
||||
return common.IfThenElse(b.key == key, common.Yes, common.No)
|
||||
case "value":
|
||||
return common.Maybe
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (b *UserAttributeValueBucket) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
return common.Maybe
|
||||
case "value":
|
||||
value := val.(string)
|
||||
return common.IfThenElse(b.value == value, common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
111
cmd/frostfs-lens/internal/schema/metabase/buckets/parsers.go
Normal file
111
cmd/frostfs-lens/internal/schema/metabase/buckets/parsers.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package buckets
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/metabase/records"
|
||||
)
|
||||
|
||||
var (
|
||||
GraveyardParser = NewPrefixBucketParser(Graveyard, records.GraveyardRecordParser, Resolvers{
|
||||
cidResolver: LenientResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
GarbageParser = NewPrefixBucketParser(Garbage, records.GarbageRecordParser, Resolvers{
|
||||
cidResolver: LenientResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
ContainerVolumeParser = NewPrefixBucketParser(ContainerVolume, records.ContainerVolumeRecordParser, Resolvers{
|
||||
cidResolver: LenientResolver,
|
||||
oidResolver: StrictResolver,
|
||||
})
|
||||
|
||||
LockedParser = NewPrefixBucketParser(
|
||||
Locked,
|
||||
NewContainerBucketParser(
|
||||
records.LockedRecordParser,
|
||||
Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
},
|
||||
),
|
||||
Resolvers{
|
||||
cidResolver: LenientResolver,
|
||||
oidResolver: LenientResolver,
|
||||
},
|
||||
)
|
||||
|
||||
ShardInfoParser = NewPrefixBucketParser(ShardInfo, records.ShardInfoRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: StrictResolver,
|
||||
})
|
||||
|
||||
PrimaryParser = NewPrefixContainerBucketParser(Primary, records.ObjectRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
LockersParser = NewPrefixContainerBucketParser(Lockers, records.ObjectRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
TombstoneParser = NewPrefixContainerBucketParser(Tombstone, records.ObjectRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
SmallParser = NewPrefixContainerBucketParser(Small, records.SmallRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
RootParser = NewPrefixContainerBucketParser(Root, records.RootRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
OwnerParser = NewPrefixContainerBucketParser(
|
||||
Owner,
|
||||
NewUserBucketParser(
|
||||
records.OwnerRecordParser,
|
||||
Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
},
|
||||
),
|
||||
Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
},
|
||||
)
|
||||
|
||||
UserAttributeParser = NewUserAttributeKeyBucketParser(
|
||||
NewUserAttributeValueBucketParser(records.UserAttributeRecordParser),
|
||||
)
|
||||
|
||||
PayloadHashParser = NewPrefixContainerBucketParser(PayloadHash, records.PayloadHashRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: StrictResolver,
|
||||
})
|
||||
|
||||
ParentParser = NewPrefixContainerBucketParser(Parent, records.ParentRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
|
||||
SplitParser = NewPrefixContainerBucketParser(Split, records.SplitRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: StrictResolver,
|
||||
})
|
||||
|
||||
ContainerCountersParser = NewPrefixBucketParser(ContainerCounters, records.ContainerCountersRecordParser, Resolvers{
|
||||
cidResolver: LenientResolver,
|
||||
oidResolver: StrictResolver,
|
||||
})
|
||||
|
||||
ECInfoParser = NewPrefixContainerBucketParser(ECInfo, records.ECInfoRecordParser, Resolvers{
|
||||
cidResolver: StrictResolver,
|
||||
oidResolver: LenientResolver,
|
||||
})
|
||||
)
|
53
cmd/frostfs-lens/internal/schema/metabase/buckets/prefix.go
Normal file
53
cmd/frostfs-lens/internal/schema/metabase/buckets/prefix.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package buckets
|
||||
|
||||
type Prefix byte
|
||||
|
||||
const (
|
||||
Graveyard Prefix = iota
|
||||
Garbage
|
||||
ToMoveIt
|
||||
ContainerVolume
|
||||
Locked
|
||||
ShardInfo
|
||||
Primary
|
||||
Lockers
|
||||
_
|
||||
Tombstone
|
||||
Small
|
||||
Root
|
||||
Owner
|
||||
UserAttribute
|
||||
PayloadHash
|
||||
Parent
|
||||
Split
|
||||
ContainerCounters
|
||||
ECInfo
|
||||
)
|
||||
|
||||
var x = map[Prefix]string{
|
||||
Graveyard: "Graveyard",
|
||||
Garbage: "Garbage",
|
||||
ToMoveIt: "To Move It",
|
||||
ContainerVolume: "Container Volume",
|
||||
Locked: "Locked",
|
||||
ShardInfo: "Shard Info",
|
||||
Primary: "Primary",
|
||||
Lockers: "Lockers",
|
||||
Tombstone: "Tombstone",
|
||||
Small: "Small",
|
||||
Root: "Root",
|
||||
Owner: "Owner",
|
||||
UserAttribute: "User Attribute",
|
||||
PayloadHash: "Payload Hash",
|
||||
Parent: "Parent",
|
||||
Split: "Split",
|
||||
ContainerCounters: "Container Counters",
|
||||
ECInfo: "EC Info",
|
||||
}
|
||||
|
||||
func (p Prefix) String() string {
|
||||
if s, ok := x[p]; ok {
|
||||
return s
|
||||
}
|
||||
return "Unknown Prefix"
|
||||
}
|
48
cmd/frostfs-lens/internal/schema/metabase/buckets/string.go
Normal file
48
cmd/frostfs-lens/internal/schema/metabase/buckets/string.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package buckets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
func (b *PrefixBucket) String() string {
|
||||
return common.FormatSimple(
|
||||
fmt.Sprintf("(%2d %-18s)", b.prefix, b.prefix), tcell.ColorLime,
|
||||
)
|
||||
}
|
||||
|
||||
func (b *PrefixContainerBucket) String() string {
|
||||
return fmt.Sprintf(
|
||||
"%s CID %s",
|
||||
common.FormatSimple(
|
||||
fmt.Sprintf("(%2d %-18s)", b.prefix, b.prefix), tcell.ColorLime,
|
||||
),
|
||||
common.FormatSimple(b.id.String(), tcell.ColorAqua),
|
||||
)
|
||||
}
|
||||
|
||||
func (b *UserBucket) String() string {
|
||||
return "UID " + common.FormatSimple(b.id.String(), tcell.ColorAqua)
|
||||
}
|
||||
|
||||
func (b *ContainerBucket) String() string {
|
||||
return "CID " + common.FormatSimple(b.id.String(), tcell.ColorAqua)
|
||||
}
|
||||
|
||||
func (b *UserAttributeKeyBucket) String() string {
|
||||
return fmt.Sprintf("%s CID %s ATTR-KEY %s",
|
||||
common.FormatSimple(
|
||||
fmt.Sprintf("(%2d %-18s)", b.prefix, b.prefix), tcell.ColorLime,
|
||||
),
|
||||
common.FormatSimple(
|
||||
fmt.Sprintf("%-44s", b.id), tcell.ColorAqua,
|
||||
),
|
||||
common.FormatSimple(b.key, tcell.ColorAqua),
|
||||
)
|
||||
}
|
||||
|
||||
func (b *UserAttributeValueBucket) String() string {
|
||||
return "ATTR-VALUE " + common.FormatSimple(b.value, tcell.ColorAqua)
|
||||
}
|
166
cmd/frostfs-lens/internal/schema/metabase/buckets/types.go
Normal file
166
cmd/frostfs-lens/internal/schema/metabase/buckets/types.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
package buckets
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/mr-tron/base58"
|
||||
)
|
||||
|
||||
type (
|
||||
PrefixBucket struct {
|
||||
prefix Prefix
|
||||
resolvers Resolvers
|
||||
}
|
||||
|
||||
PrefixContainerBucket struct {
|
||||
prefix Prefix
|
||||
id cid.ID
|
||||
resolvers Resolvers
|
||||
}
|
||||
|
||||
ContainerBucket struct {
|
||||
id cid.ID
|
||||
resolvers Resolvers
|
||||
}
|
||||
|
||||
UserBucket struct {
|
||||
id user.ID
|
||||
resolvers Resolvers
|
||||
}
|
||||
|
||||
UserAttributeKeyBucket struct {
|
||||
prefix Prefix
|
||||
id cid.ID
|
||||
key string
|
||||
}
|
||||
|
||||
UserAttributeValueBucket struct {
|
||||
value string
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
FilterResolver = func(result bool) common.FilterResult
|
||||
|
||||
Resolvers struct {
|
||||
cidResolver FilterResolver
|
||||
oidResolver FilterResolver
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
StrictResolver = func(x bool) common.FilterResult { return common.IfThenElse(x, common.Yes, common.No) }
|
||||
LenientResolver = func(x bool) common.FilterResult { return common.IfThenElse(x, common.Yes, common.Maybe) }
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotBucket = errors.New("not a bucket")
|
||||
ErrInvalidKeyLength = errors.New("invalid key length")
|
||||
ErrInvalidValueLength = errors.New("invalid value length")
|
||||
ErrInvalidPrefix = errors.New("invalid prefix")
|
||||
)
|
||||
|
||||
func NewPrefixBucketParser(prefix Prefix, next common.Parser, resolvers Resolvers) common.Parser {
|
||||
return func(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if value != nil {
|
||||
return nil, nil, ErrNotBucket
|
||||
}
|
||||
if len(key) != 1 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var b PrefixBucket
|
||||
if b.prefix = Prefix(key[0]); b.prefix != prefix {
|
||||
return nil, nil, ErrInvalidPrefix
|
||||
}
|
||||
b.resolvers = resolvers
|
||||
return &b, next, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrefixContainerBucketParser(prefix Prefix, next common.Parser, resolvers Resolvers) common.Parser {
|
||||
return func(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if value != nil {
|
||||
return nil, nil, ErrNotBucket
|
||||
}
|
||||
if len(key) != 33 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var b PrefixContainerBucket
|
||||
if b.prefix = Prefix(key[0]); b.prefix != prefix {
|
||||
return nil, nil, ErrInvalidPrefix
|
||||
}
|
||||
if err := b.id.Decode(key[1:]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
b.resolvers = resolvers
|
||||
return &b, next, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserBucketParser(next common.Parser, resolvers Resolvers) common.Parser {
|
||||
return func(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if value != nil {
|
||||
return nil, nil, ErrNotBucket
|
||||
}
|
||||
var b UserBucket
|
||||
if err := b.id.DecodeString(base58.Encode(key)); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
b.resolvers = resolvers
|
||||
return &b, next, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewContainerBucketParser(next common.Parser, resolvers Resolvers) common.Parser {
|
||||
return func(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if value != nil {
|
||||
return nil, nil, ErrNotBucket
|
||||
}
|
||||
if len(key) != 32 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var b ContainerBucket
|
||||
if err := b.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
b.resolvers = resolvers
|
||||
return &b, next, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserAttributeKeyBucketParser(next common.Parser) common.Parser {
|
||||
return func(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if value != nil {
|
||||
return nil, nil, ErrNotBucket
|
||||
}
|
||||
if len(key) < 34 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var b UserAttributeKeyBucket
|
||||
if b.prefix = Prefix(key[0]); b.prefix != UserAttribute {
|
||||
return nil, nil, ErrInvalidPrefix
|
||||
}
|
||||
if err := b.id.Decode(key[1:33]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
b.key = string(key[33:])
|
||||
return &b, next, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserAttributeValueBucketParser(next common.Parser) common.Parser {
|
||||
return func(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if value != nil {
|
||||
return nil, nil, ErrNotBucket
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var b UserAttributeValueBucket
|
||||
b.value = string(key)
|
||||
return &b, next, nil
|
||||
}
|
||||
}
|
29
cmd/frostfs-lens/internal/schema/metabase/parser.go
Normal file
29
cmd/frostfs-lens/internal/schema/metabase/parser.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package metabase
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/metabase/buckets"
|
||||
)
|
||||
|
||||
var MetabaseParser = common.WithFallback(
|
||||
common.Any(
|
||||
buckets.GraveyardParser,
|
||||
buckets.GarbageParser,
|
||||
buckets.ContainerVolumeParser,
|
||||
buckets.LockedParser,
|
||||
buckets.ShardInfoParser,
|
||||
buckets.PrimaryParser,
|
||||
buckets.LockersParser,
|
||||
buckets.TombstoneParser,
|
||||
buckets.SmallParser,
|
||||
buckets.RootParser,
|
||||
buckets.OwnerParser,
|
||||
buckets.UserAttributeParser,
|
||||
buckets.PayloadHashParser,
|
||||
buckets.ParentParser,
|
||||
buckets.SplitParser,
|
||||
buckets.ContainerCountersParser,
|
||||
buckets.ECInfoParser,
|
||||
),
|
||||
common.RawParser.ToFallbackParser(),
|
||||
)
|
|
@ -0,0 +1,65 @@
|
|||
package records
|
||||
|
||||
import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func (r *GraveyardRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *GarbageRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *ContainerVolumeRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *LockedRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *ShardInfoRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *ObjectRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *SmallRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *RootRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *OwnerRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *UserAttributeRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *PayloadHashRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *ParentRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *SplitRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *ContainerCountersRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
||||
|
||||
func (r *ECInfoRecord) DetailedString() string {
|
||||
return spew.Sdump(*r)
|
||||
}
|
145
cmd/frostfs-lens/internal/schema/metabase/records/filter.go
Normal file
145
cmd/frostfs-lens/internal/schema/metabase/records/filter.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package records
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
)
|
||||
|
||||
func (r *GraveyardRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
id := val.(cid.ID)
|
||||
return common.IfThenElse(r.object.Container().Equals(id), common.Yes, common.No)
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.object.Object().Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GarbageRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
id := val.(cid.ID)
|
||||
return common.IfThenElse(r.addr.Container().Equals(id), common.Yes, common.No)
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.addr.Object().Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ContainerVolumeRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
id := val.(cid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ShardInfoRecord) Filter(string, any) common.FilterResult {
|
||||
return common.No
|
||||
}
|
||||
|
||||
func (r *LockedRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ObjectRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SmallRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RootRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *OwnerRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *UserAttributeRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PayloadHashRecord) Filter(string, any) common.FilterResult {
|
||||
return common.No
|
||||
}
|
||||
|
||||
func (r *ParentRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.parent.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SplitRecord) Filter(string, any) common.FilterResult {
|
||||
return common.No
|
||||
}
|
||||
|
||||
func (r *ContainerCountersRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "cid":
|
||||
id := val.(cid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ECInfoRecord) Filter(typ string, val any) common.FilterResult {
|
||||
switch typ {
|
||||
case "oid":
|
||||
id := val.(oid.ID)
|
||||
return common.IfThenElse(r.id.Equals(id), common.Yes, common.No)
|
||||
default:
|
||||
return common.No
|
||||
}
|
||||
}
|
251
cmd/frostfs-lens/internal/schema/metabase/records/parsers.go
Normal file
251
cmd/frostfs-lens/internal/schema/metabase/records/parsers.go
Normal file
|
@ -0,0 +1,251 @@
|
|||
package records
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidKeyLength = errors.New("invalid key length")
|
||||
ErrInvalidValueLength = errors.New("invalid value length")
|
||||
ErrInvalidPrefix = errors.New("invalid prefix")
|
||||
)
|
||||
|
||||
func GraveyardRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if len(key) != 64 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
if len(value) != 64 {
|
||||
return nil, nil, ErrInvalidValueLength
|
||||
}
|
||||
var (
|
||||
cnr cid.ID
|
||||
obj oid.ID
|
||||
r GraveyardRecord
|
||||
)
|
||||
|
||||
_ = cnr.Decode(key[:32])
|
||||
_ = obj.Decode(key[32:])
|
||||
|
||||
r.object.SetContainer(cnr)
|
||||
r.object.SetObject(obj)
|
||||
|
||||
_ = cnr.Decode(value[:32])
|
||||
_ = obj.Decode(value[32:])
|
||||
|
||||
r.tombstone.SetContainer(cnr)
|
||||
r.tombstone.SetObject(obj)
|
||||
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func GarbageRecordParser(key, _ []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if len(key) != 64 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var (
|
||||
cnr cid.ID
|
||||
obj oid.ID
|
||||
r GarbageRecord
|
||||
)
|
||||
|
||||
_ = cnr.Decode(key[:32])
|
||||
_ = obj.Decode(key[32:])
|
||||
|
||||
r.addr.SetContainer(cnr)
|
||||
r.addr.SetObject(obj)
|
||||
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func ContainerVolumeRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if len(key) != 32 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
if len(value) != 8 {
|
||||
return nil, nil, ErrInvalidValueLength
|
||||
}
|
||||
var r ContainerVolumeRecord
|
||||
|
||||
_ = r.id.Decode(key)
|
||||
r.volume = binary.LittleEndian.Uint64(value)
|
||||
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func LockedRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var (
|
||||
r LockedRecord
|
||||
err error
|
||||
)
|
||||
|
||||
if err := r.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if r.ids, err = DecodeOIDs(value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func ShardInfoRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if len(key) == 0 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
|
||||
var r ShardInfoRecord
|
||||
if string(key) == "id" {
|
||||
r.label = string(key)
|
||||
r.value = shard.ID(value).String()
|
||||
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
if len(value) != 8 {
|
||||
return nil, nil, ErrInvalidValueLength
|
||||
}
|
||||
r.label = string(key)
|
||||
r.value = strconv.FormatUint(binary.LittleEndian.Uint64(value), 10)
|
||||
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func ObjectRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if len(key) != 32 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var r ObjectRecord
|
||||
|
||||
_ = r.id.Decode(key)
|
||||
if err := r.object.Unmarshal(value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func SmallRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var r SmallRecord
|
||||
if err := r.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(value) != 0 {
|
||||
x := string(value)
|
||||
r.storageID = &x
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func RootRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var r RootRecord
|
||||
if err := r.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(value) == 0 {
|
||||
return &r, nil, nil
|
||||
}
|
||||
r.info = &objectSDK.SplitInfo{}
|
||||
if err := r.info.Unmarshal(value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func OwnerRecordParser(key, _ []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var r OwnerRecord
|
||||
if err := r.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func UserAttributeRecordParser(key, _ []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var r UserAttributeRecord
|
||||
if err := r.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func PayloadHashRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if len(key) != 32 {
|
||||
return nil, nil, ErrInvalidKeyLength
|
||||
}
|
||||
var (
|
||||
err error
|
||||
r PayloadHashRecord
|
||||
)
|
||||
|
||||
r.checksum.SetSHA256([32]byte(key))
|
||||
if r.ids, err = DecodeOIDs(value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func ParentRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var (
|
||||
r ParentRecord
|
||||
err error
|
||||
)
|
||||
if err = r.parent.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if r.ids, err = DecodeOIDs(value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func SplitRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var (
|
||||
err error
|
||||
r SplitRecord
|
||||
)
|
||||
if err = r.id.UnmarshalBinary(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if r.ids, err = DecodeOIDs(value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func ContainerCountersRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
if len(value) != 24 {
|
||||
return nil, nil, ErrInvalidValueLength
|
||||
}
|
||||
|
||||
var r ContainerCountersRecord
|
||||
if err := r.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.logical = binary.LittleEndian.Uint64(value[:8])
|
||||
r.physical = binary.LittleEndian.Uint64(value[8:16])
|
||||
r.user = binary.LittleEndian.Uint64(value[16:24])
|
||||
|
||||
return &r, nil, nil
|
||||
}
|
||||
|
||||
func ECInfoRecordParser(key, value []byte) (common.SchemaEntry, common.Parser, error) {
|
||||
var (
|
||||
r ECInfoRecord
|
||||
err error
|
||||
)
|
||||
|
||||
if err := r.id.Decode(key); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if r.ids, err = DecodeOIDs(value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &r, nil, nil
|
||||
}
|
134
cmd/frostfs-lens/internal/schema/metabase/records/string.go
Normal file
134
cmd/frostfs-lens/internal/schema/metabase/records/string.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package records
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
func (r *GraveyardRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Object CID %s OID %s | Tombstone CID %s OID %s",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.object.Container()), tcell.ColorAqua),
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.object.Object()), tcell.ColorAqua),
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.tombstone.Container()), tcell.ColorAqua),
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.tombstone.Object()), tcell.ColorAqua),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *GarbageRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"CID %-44s OID %-44s",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.addr.Container()), tcell.ColorAqua),
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.addr.Object()), tcell.ColorAqua),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *ContainerVolumeRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"CID %-44s %c %d",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.id), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
r.volume,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *LockedRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Locker OID %s %c Locked [%d]OID {...}",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.id), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
len(r.ids),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *ShardInfoRecord) String() string {
|
||||
return fmt.Sprintf("%-13s | %s", r.label, r.value)
|
||||
}
|
||||
|
||||
func (r *ObjectRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"OID %s %c Object {...}",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.id), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *SmallRecord) String() string {
|
||||
s := fmt.Sprintf(
|
||||
"OID %s %c",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.id), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
)
|
||||
if r.storageID != nil {
|
||||
s = fmt.Sprintf("%s %s", s, *r.storageID)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *RootRecord) String() string {
|
||||
s := fmt.Sprintf(
|
||||
"Root OID %s %c",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.id), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
)
|
||||
if r.info != nil {
|
||||
s += " Split info {...}"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *OwnerRecord) String() string {
|
||||
return "OID " + common.FormatSimple(r.id.String(), tcell.ColorAqua)
|
||||
}
|
||||
|
||||
func (r *UserAttributeRecord) String() string {
|
||||
return "OID " + common.FormatSimple(r.id.String(), tcell.ColorAqua)
|
||||
}
|
||||
|
||||
func (r *PayloadHashRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Checksum %s %c [%d]OID {...}",
|
||||
common.FormatSimple(r.checksum.String(), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
len(r.ids),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *ParentRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Parent OID %s %c [%d]OID {...}",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.parent), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
len(r.ids),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *SplitRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Split ID %s %c [%d]OID {...}",
|
||||
common.FormatSimple(r.id.String(), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
len(r.ids),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *ContainerCountersRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"CID %s %c logical %d, physical %d, user %d",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.id), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
r.logical, r.physical, r.user,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *ECInfoRecord) String() string {
|
||||
return fmt.Sprintf(
|
||||
"OID %s %c [%d]OID {...}",
|
||||
common.FormatSimple(fmt.Sprintf("%-44s", r.id), tcell.ColorAqua),
|
||||
tview.Borders.Vertical,
|
||||
len(r.ids),
|
||||
)
|
||||
}
|
82
cmd/frostfs-lens/internal/schema/metabase/records/types.go
Normal file
82
cmd/frostfs-lens/internal/schema/metabase/records/types.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package records
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type (
|
||||
GraveyardRecord struct {
|
||||
object, tombstone oid.Address
|
||||
}
|
||||
|
||||
GarbageRecord struct {
|
||||
addr oid.Address
|
||||
}
|
||||
|
||||
ContainerVolumeRecord struct {
|
||||
id cid.ID
|
||||
volume uint64
|
||||
}
|
||||
|
||||
LockedRecord struct {
|
||||
id oid.ID
|
||||
ids []oid.ID
|
||||
}
|
||||
|
||||
ShardInfoRecord struct {
|
||||
label string
|
||||
value string
|
||||
}
|
||||
|
||||
ObjectRecord struct {
|
||||
id oid.ID
|
||||
object objectSDK.Object
|
||||
}
|
||||
|
||||
SmallRecord struct {
|
||||
id oid.ID
|
||||
storageID *string // optional
|
||||
}
|
||||
|
||||
RootRecord struct {
|
||||
id oid.ID
|
||||
info *objectSDK.SplitInfo // optional
|
||||
}
|
||||
|
||||
OwnerRecord struct {
|
||||
id oid.ID
|
||||
}
|
||||
|
||||
UserAttributeRecord struct {
|
||||
id oid.ID
|
||||
}
|
||||
|
||||
PayloadHashRecord struct {
|
||||
checksum checksum.Checksum
|
||||
ids []oid.ID
|
||||
}
|
||||
|
||||
ParentRecord struct {
|
||||
parent oid.ID
|
||||
ids []oid.ID
|
||||
}
|
||||
|
||||
SplitRecord struct {
|
||||
id uuid.UUID
|
||||
ids []oid.ID
|
||||
}
|
||||
|
||||
ContainerCountersRecord struct {
|
||||
id cid.ID
|
||||
logical, physical, user uint64
|
||||
}
|
||||
|
||||
ECInfoRecord struct {
|
||||
id oid.ID
|
||||
ids []oid.ID
|
||||
}
|
||||
)
|
21
cmd/frostfs-lens/internal/schema/metabase/records/util.go
Normal file
21
cmd/frostfs-lens/internal/schema/metabase/records/util.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package records
|
||||
|
||||
import (
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
func DecodeOIDs(data []byte) ([]oid.ID, error) {
|
||||
r := io.NewBinReaderFromBuf(data)
|
||||
|
||||
size := r.ReadVarUint()
|
||||
oids := make([]oid.ID, size)
|
||||
|
||||
var i uint64
|
||||
for ; i < size; i++ {
|
||||
if err := oids[i].Decode(r.ReadVarBytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return oids, nil
|
||||
}
|
5
go.mod
5
go.mod
|
@ -17,7 +17,9 @@ require (
|
|||
github.com/VictoriaMetrics/easyproto v0.1.4
|
||||
github.com/cheggaaa/pb v1.0.29
|
||||
github.com/chzyer/readline v1.5.1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
|
||||
github.com/gdamore/tcell/v2 v2.7.4
|
||||
github.com/go-pkgz/expirable-cache/v3 v3.0.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
|
@ -65,10 +67,10 @@ require (
|
|||
github.com/consensys/bavard v0.1.13 // indirect
|
||||
github.com/consensys/gnark-crypto v0.12.2-0.20231222162921-eb75782795d2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
|
@ -85,6 +87,7 @@ require (
|
|||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/klauspost/reedsolomon v1.12.1 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
|
|
BIN
go.sum
BIN
go.sum
Binary file not shown.
Loading…
Reference in a new issue