lens: Add ability to view raw data in metabase
#1246
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
|
||||||
|
}
|
135
cmd/frostfs-lens/internal/schema/metabase/records/string.go
Normal file
135
cmd/frostfs-lens/internal/schema/metabase/records/string.go
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
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 %c 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),
|
||||||
|
tview.Borders.Vertical,
|
||||||
|
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 %c %s", r.label, tview.Borders.Vertical, 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
|
||||||
|
}
|
||||||
|
)
|
20
cmd/frostfs-lens/internal/schema/metabase/records/util.go
Normal file
20
cmd/frostfs-lens/internal/schema/metabase/records/util.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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)
|
||||||
|
|
||||||
|
for i := uint64(0); 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/VictoriaMetrics/easyproto v0.1.4
|
||||||
github.com/cheggaaa/pb v1.0.29
|
github.com/cheggaaa/pb v1.0.29
|
||||||
github.com/chzyer/readline v1.5.1
|
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/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/go-pkgz/expirable-cache/v3 v3.0.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
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/bavard v0.1.13 // indirect
|
||||||
github.com/consensys/gnark-crypto v0.12.2-0.20231222162921-eb75782795d2 // 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/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/davidmz/go-pageant v1.0.2 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.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-fed/httpsig v1.1.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // 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/josharian/intern v1.0.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.12.1 // 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/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.1 // 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