forked from TrueCloudLab/frostfs-node
Add Inner Ring code
This commit is contained in:
parent
dadfd90dcd
commit
b7b5079934
400 changed files with 11420 additions and 8690 deletions
37
pkg/core/netmap/epoch/marshal.go
Normal file
37
pkg/core/netmap/epoch/marshal.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Size is a size of Epoch
|
||||
// in a binary form.
|
||||
const Size = 8
|
||||
|
||||
// Marshal encodes Epoch into a
|
||||
// binary form and returns the result.
|
||||
//
|
||||
// Result slice has Size length.
|
||||
func Marshal(e Epoch) []byte {
|
||||
d := make([]byte, Size)
|
||||
|
||||
binary.BigEndian.PutUint64(d, ToUint64(e))
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals Epoch from
|
||||
// a binary representation.
|
||||
//
|
||||
// If buffer size is insufficient,
|
||||
// io.ErrUnexpectedEOF is returned.
|
||||
func (e *Epoch) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < Size {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
*e = FromUint64(binary.BigEndian.Uint64(data))
|
||||
|
||||
return nil
|
||||
}
|
20
pkg/core/netmap/epoch/marshal_test.go
Normal file
20
pkg/core/netmap/epoch/marshal_test.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEpochMarshal(t *testing.T) {
|
||||
e := FromUint64(1)
|
||||
e2 := new(Epoch)
|
||||
|
||||
require.NoError(t,
|
||||
e2.UnmarshalBinary(
|
||||
Marshal(e),
|
||||
),
|
||||
)
|
||||
|
||||
require.True(t, EQ(e, *e2))
|
||||
}
|
12
pkg/core/netmap/epoch/math.go
Normal file
12
pkg/core/netmap/epoch/math.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package epoch
|
||||
|
||||
// Sum returns the result of
|
||||
// summing up two Epoch.
|
||||
//
|
||||
// Function defines a binary
|
||||
// operation of summing two Epoch.
|
||||
// Try to avoid using operator
|
||||
// "+" for better portability.
|
||||
func Sum(a, b Epoch) Epoch {
|
||||
return FromUint64(ToUint64(a) + ToUint64(b))
|
||||
}
|
28
pkg/core/netmap/epoch/math_test.go
Normal file
28
pkg/core/netmap/epoch/math_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEpochMath(t *testing.T) {
|
||||
items := []struct {
|
||||
mathFn func(Epoch, Epoch) Epoch
|
||||
|
||||
a, b, c uint64
|
||||
}{
|
||||
{
|
||||
mathFn: Sum, a: 1, b: 2, c: 3},
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
require.Equal(t,
|
||||
item.mathFn(
|
||||
FromUint64(item.a),
|
||||
FromUint64(item.b),
|
||||
),
|
||||
FromUint64(item.c),
|
||||
)
|
||||
}
|
||||
}
|
55
pkg/core/netmap/epoch/relation.go
Normal file
55
pkg/core/netmap/epoch/relation.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package epoch
|
||||
|
||||
// EQ reports whether e and e2 are the same Epoch.
|
||||
//
|
||||
// Function defines the relation of equality
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "==" operator for better portability.
|
||||
func EQ(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) == ToUint64(e2)
|
||||
}
|
||||
|
||||
// NE reports whether e1 and e2 are the different Epoch.
|
||||
//
|
||||
// Method defines the relation of inequality
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "!=" operator for better portability.
|
||||
func NE(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) != ToUint64(e2)
|
||||
}
|
||||
|
||||
// LT reports whether e1 is less Epoch than e2.
|
||||
//
|
||||
// Method defines the "less than" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "<" operator for better portability.
|
||||
func LT(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) < ToUint64(e2)
|
||||
}
|
||||
|
||||
// GT reports whether e1 is greater Epoch than e2.
|
||||
//
|
||||
// Method defines the "greater than" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// ">" operator for better portability.
|
||||
func GT(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) > ToUint64(e2)
|
||||
}
|
||||
|
||||
// LE reports whether e1 is less or equal Epoch than e2.
|
||||
//
|
||||
// Method defines the "less or equal" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "<=" operator for better portability.
|
||||
func LE(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) <= ToUint64(e2)
|
||||
}
|
||||
|
||||
// GE reports whether e1 is greater or equal Epoch than e2.
|
||||
//
|
||||
// Method defines the "greater or equal" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// ">=" operator for better portability.
|
||||
func GE(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) >= ToUint64(e2)
|
||||
}
|
40
pkg/core/netmap/epoch/relation_test.go
Normal file
40
pkg/core/netmap/epoch/relation_test.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEpochRelations(t *testing.T) {
|
||||
items := []struct {
|
||||
relFn func(Epoch, Epoch) bool
|
||||
|
||||
base, ok, fail uint64
|
||||
}{
|
||||
{relFn: EQ, base: 1, ok: 1, fail: 2},
|
||||
{relFn: NE, base: 1, ok: 2, fail: 1},
|
||||
{relFn: LT, base: 1, ok: 2, fail: 0},
|
||||
{relFn: GT, base: 1, ok: 0, fail: 2},
|
||||
{relFn: LE, base: 1, ok: 1, fail: 0},
|
||||
{relFn: LE, base: 1, ok: 2, fail: 0},
|
||||
{relFn: GE, base: 1, ok: 0, fail: 2},
|
||||
{relFn: GE, base: 1, ok: 1, fail: 2},
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
require.True(t,
|
||||
item.relFn(
|
||||
FromUint64(item.base),
|
||||
FromUint64(item.ok),
|
||||
),
|
||||
)
|
||||
|
||||
require.False(t,
|
||||
item.relFn(
|
||||
FromUint64(item.base),
|
||||
FromUint64(item.fail),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
23
pkg/core/netmap/epoch/type.go
Normal file
23
pkg/core/netmap/epoch/type.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package epoch
|
||||
|
||||
// Epoch represents the
|
||||
// number of NeoFS epoch.
|
||||
type Epoch uint64
|
||||
|
||||
// FromUint64 converts builtin
|
||||
// uint64 value to Epoch.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func FromUint64(e uint64) Epoch {
|
||||
return Epoch(e)
|
||||
}
|
||||
|
||||
// ToUint64 converts Epoch value
|
||||
// to builtin uint64.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func ToUint64(e Epoch) uint64 {
|
||||
return uint64(e)
|
||||
}
|
119
pkg/core/netmap/netmap.go
Normal file
119
pkg/core/netmap/netmap.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
package netmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/node"
|
||||
"github.com/nspcc-dev/netmap"
|
||||
)
|
||||
|
||||
// Info represent node information.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/netmap/node.Info.
|
||||
type Info = node.Info
|
||||
|
||||
// Bucket represents NeoFS network map as a graph.
|
||||
//
|
||||
// If is a type alias of
|
||||
// github.com/nspcc-dev/netmap.Bucket.
|
||||
type Bucket = netmap.Bucket
|
||||
|
||||
// NetMap represents NeoFS network map
|
||||
// with concurrent access support.
|
||||
type NetMap struct {
|
||||
mtx *sync.RWMutex
|
||||
|
||||
root *Bucket
|
||||
|
||||
items []Info
|
||||
}
|
||||
|
||||
// New creates and initializes a new NetMap.
|
||||
//
|
||||
// Using the NetMap that has been created with new(NetMap)
|
||||
// expression (or just declaring a NetMap variable) is unsafe
|
||||
// and can lead to panic.
|
||||
func New() *NetMap {
|
||||
return &NetMap{
|
||||
mtx: new(sync.RWMutex),
|
||||
root: new(Bucket),
|
||||
}
|
||||
}
|
||||
|
||||
// Root returns the root bucket of the network map.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the network map.
|
||||
func (n NetMap) Root() *Bucket {
|
||||
n.mtx.RLock()
|
||||
defer n.mtx.RUnlock()
|
||||
|
||||
return n.root
|
||||
}
|
||||
|
||||
// SetRoot sets the root bucket of the network map.
|
||||
//
|
||||
// Subsequent changing the source bucket
|
||||
// is unsafe and affects the network map.
|
||||
func (n *NetMap) SetRoot(v *Bucket) {
|
||||
n.mtx.Lock()
|
||||
n.root = v
|
||||
n.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Nodes returns node list of the network map.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the network map.
|
||||
func (n NetMap) Nodes() []Info {
|
||||
n.mtx.RLock()
|
||||
defer n.mtx.RUnlock()
|
||||
|
||||
return n.items
|
||||
}
|
||||
|
||||
// SetNodes sets node list of the network map.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is unsafe and affects the network map.
|
||||
func (n *NetMap) SetNodes(v []Info) {
|
||||
n.mtx.Lock()
|
||||
n.items = v
|
||||
n.mtx.Unlock()
|
||||
}
|
||||
|
||||
// AddNode adds node information to the network map
|
||||
//
|
||||
// If node with provided information is already presented
|
||||
// in network map, nothing happens,
|
||||
func (n *NetMap) AddNode(nodeInfo Info) error {
|
||||
n.mtx.Lock()
|
||||
defer n.mtx.Unlock()
|
||||
|
||||
num := -1
|
||||
|
||||
// looking for existed node info item
|
||||
for i := range n.items {
|
||||
if bytes.Equal(
|
||||
n.items[i].PublicKey(),
|
||||
nodeInfo.PublicKey(),
|
||||
) {
|
||||
num = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// add node if it does not exist
|
||||
if num < 0 {
|
||||
n.items = append(n.items, nodeInfo)
|
||||
num = len(n.items) - 1
|
||||
}
|
||||
|
||||
return n.root.AddStrawNode(netmap.Node{
|
||||
N: uint32(num),
|
||||
C: n.items[num].Capacity(),
|
||||
P: n.items[num].Price(),
|
||||
}, nodeInfo.Options()...)
|
||||
}
|
39
pkg/core/netmap/netmap_test.go
Normal file
39
pkg/core/netmap/netmap_test.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package netmap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNetMap_Nodes(t *testing.T) {
|
||||
nm := New()
|
||||
|
||||
info1 := Info{}
|
||||
info1.SetPublicKey([]byte{1, 2, 3})
|
||||
|
||||
info2 := Info{}
|
||||
info2.SetPublicKey([]byte{4, 5, 6})
|
||||
|
||||
nodes := []Info{
|
||||
info1,
|
||||
info2,
|
||||
}
|
||||
|
||||
nm.SetNodes(nodes)
|
||||
|
||||
require.Equal(t, nodes, nm.Nodes())
|
||||
}
|
||||
|
||||
func TestNetMap_Root(t *testing.T) {
|
||||
nm := New()
|
||||
|
||||
bucket := &Bucket{
|
||||
Key: "key",
|
||||
Value: "value",
|
||||
}
|
||||
|
||||
nm.SetRoot(bucket)
|
||||
|
||||
require.Equal(t, bucket, nm.Root())
|
||||
}
|
154
pkg/core/netmap/node/info.go
Normal file
154
pkg/core/netmap/node/info.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Info represents the information
|
||||
// about NeoFS storage node.
|
||||
type Info struct {
|
||||
address string // net address
|
||||
|
||||
key []byte // public key
|
||||
|
||||
opts []string // options
|
||||
|
||||
status Status // status bits
|
||||
}
|
||||
|
||||
// ErrNilInfo is returned by functions that expect
|
||||
// a non-nil Info pointer, but received nil.
|
||||
var ErrNilInfo = errors.New("node info is nil")
|
||||
|
||||
// Address returns node network address.
|
||||
//
|
||||
// Address format is dictated by
|
||||
// application architecture.
|
||||
func (i Info) Address() string {
|
||||
return i.address
|
||||
}
|
||||
|
||||
// SetAddress sets node network address.
|
||||
func (i *Info) SetAddress(v string) {
|
||||
i.address = v
|
||||
}
|
||||
|
||||
// Status returns the node status.
|
||||
func (i Info) Status() Status {
|
||||
return i.status
|
||||
}
|
||||
|
||||
// SetStatus sets the node status.
|
||||
func (i *Info) SetStatus(v Status) {
|
||||
i.status = v
|
||||
}
|
||||
|
||||
// PublicKey returns node public key in
|
||||
// a binary format.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the node info. In order to
|
||||
// prevent state mutations, use
|
||||
// CopyPublicKey.
|
||||
//
|
||||
// Key format is dictated by
|
||||
// application architecture.
|
||||
func (i Info) PublicKey() []byte {
|
||||
return i.key
|
||||
}
|
||||
|
||||
// CopyPublicKey returns the copy of
|
||||
// node public key.
|
||||
//
|
||||
// Changing the result is safe and
|
||||
// does not affect the node info.
|
||||
func CopyPublicKey(i Info) []byte {
|
||||
res := make([]byte, len(i.key))
|
||||
|
||||
copy(res, i.key)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SetPublicKey sets node public key
|
||||
// in a binary format.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is unsafe and affects node info.
|
||||
// In order to prevent state mutations,
|
||||
// use SetPublicKeyCopy.
|
||||
func (i *Info) SetPublicKey(v []byte) {
|
||||
i.key = v
|
||||
}
|
||||
|
||||
// SetPublicKeyCopy copies public key and
|
||||
// sets the copy as node public key.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is safe and does not affect node info.
|
||||
//
|
||||
// Returns ErrNilInfo on nil node info.
|
||||
func SetPublicKeyCopy(i *Info, key []byte) error {
|
||||
if i == nil {
|
||||
return ErrNilInfo
|
||||
}
|
||||
|
||||
i.key = make([]byte, len(key))
|
||||
|
||||
copy(i.key, key)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options returns node option list.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the node info. In order to
|
||||
// prevent state mutations, use
|
||||
// CopyOptions.
|
||||
//
|
||||
// Option format is dictated by
|
||||
// application architecture.
|
||||
func (i Info) Options() []string {
|
||||
return i.opts
|
||||
}
|
||||
|
||||
// CopyOptions returns the copy of
|
||||
// node options list.
|
||||
//
|
||||
// Changing the result is safe and
|
||||
// does not affect the node info.
|
||||
func CopyOptions(i Info) []string {
|
||||
res := make([]string, len(i.opts))
|
||||
|
||||
copy(res, i.opts)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SetOptions sets node option list.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is unsafe and affects node info.
|
||||
// In order to prevent state mutations,
|
||||
// use SetOptionsCopy.
|
||||
func (i *Info) SetOptions(v []string) {
|
||||
i.opts = v
|
||||
}
|
||||
|
||||
// SetOptionsCopy copies option list and sets
|
||||
// the copy as node options list.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is safe and does not affect node info.
|
||||
//
|
||||
// SetOptionsCopy does nothing if Info is nil.
|
||||
func SetOptionsCopy(i *Info, opts []string) {
|
||||
if i == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.opts = make([]string, len(opts))
|
||||
|
||||
copy(i.opts, opts)
|
||||
}
|
133
pkg/core/netmap/node/info_test.go
Normal file
133
pkg/core/netmap/node/info_test.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInfo_Address(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
addr := "address"
|
||||
i.SetAddress(addr)
|
||||
|
||||
require.Equal(t, addr, i.Address())
|
||||
}
|
||||
|
||||
func TestInfo_Status(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
st := StatusFromUint64(1)
|
||||
i.SetStatus(st)
|
||||
|
||||
require.Equal(t, st, i.Status())
|
||||
}
|
||||
|
||||
func TestInfo_PublicKey(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
key := []byte{1, 2, 3}
|
||||
i.SetPublicKey(key)
|
||||
|
||||
require.Equal(t, key, i.PublicKey())
|
||||
}
|
||||
|
||||
func TestCopyPublicKey(t *testing.T) {
|
||||
i := Info{}
|
||||
|
||||
// set initial node key
|
||||
initKey := []byte{1, 2, 3}
|
||||
i.SetPublicKey(initKey)
|
||||
|
||||
// get node key copy
|
||||
keyCopy := CopyPublicKey(i)
|
||||
|
||||
// change the copy
|
||||
keyCopy[0]++
|
||||
|
||||
// check that node key has not changed
|
||||
require.Equal(t, initKey, i.PublicKey())
|
||||
}
|
||||
|
||||
func TestSetPublicKeyCopy(t *testing.T) {
|
||||
require.EqualError(t,
|
||||
SetPublicKeyCopy(nil, nil),
|
||||
ErrNilInfo.Error(),
|
||||
)
|
||||
|
||||
i := new(Info)
|
||||
|
||||
// create source key
|
||||
srcKey := []byte{1, 2, 3}
|
||||
|
||||
// copy and set node key
|
||||
require.NoError(t, SetPublicKeyCopy(i, srcKey))
|
||||
|
||||
// get node key
|
||||
nodeKey := i.PublicKey()
|
||||
|
||||
// change the source key
|
||||
srcKey[0]++
|
||||
|
||||
// check that node key has not changed
|
||||
require.Equal(t, nodeKey, i.PublicKey())
|
||||
}
|
||||
|
||||
func TestInfo_Options(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
opts := []string{
|
||||
"opt1",
|
||||
"opt2",
|
||||
}
|
||||
i.SetOptions(opts)
|
||||
|
||||
require.Equal(t, opts, i.Options())
|
||||
}
|
||||
|
||||
func TestCopyOptions(t *testing.T) {
|
||||
i := Info{}
|
||||
|
||||
// set initial node options
|
||||
initOpts := []string{
|
||||
"opt1",
|
||||
"opt2",
|
||||
}
|
||||
i.SetOptions(initOpts)
|
||||
|
||||
// get node options copy
|
||||
optsCopy := CopyOptions(i)
|
||||
|
||||
// change the copy
|
||||
optsCopy[0] = "some other opt"
|
||||
|
||||
// check that node options have not changed
|
||||
require.Equal(t, initOpts, i.Options())
|
||||
}
|
||||
|
||||
func TestSetOptionsCopy(t *testing.T) {
|
||||
require.NotPanics(t, func() {
|
||||
SetOptionsCopy(nil, nil)
|
||||
})
|
||||
|
||||
i := new(Info)
|
||||
|
||||
// create source options
|
||||
srcOpts := []string{
|
||||
"opt1",
|
||||
"opt2",
|
||||
}
|
||||
|
||||
// copy and set node options
|
||||
SetOptionsCopy(i, srcOpts)
|
||||
|
||||
// get node options
|
||||
nodeOpts := i.Options()
|
||||
|
||||
// change the source options
|
||||
srcOpts[0] = "some other opt"
|
||||
|
||||
// check that node options have not changed
|
||||
require.Equal(t, nodeOpts, i.Options())
|
||||
}
|
46
pkg/core/netmap/node/options.go
Normal file
46
pkg/core/netmap/node/options.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/object"
|
||||
)
|
||||
|
||||
const optionPrice = "/Price:"
|
||||
|
||||
const optionCapacity = "/Capacity:"
|
||||
|
||||
// Price parses node options and returns the price in 1e-8*GAS/Megabyte per month.
|
||||
//
|
||||
// User sets the price in GAS/Terabyte per month.
|
||||
func (i Info) Price() uint64 {
|
||||
for j := range i.opts {
|
||||
if strings.HasPrefix(i.opts[j], optionPrice) {
|
||||
n, err := strconv.ParseFloat(i.opts[j][len(optionPrice):], 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint64(n*1e8) / uint64(object.UnitsMB) // UnitsMB == megabytes in 1 terabyte
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Capacity parses node options and returns the capacity .
|
||||
func (i Info) Capacity() uint64 {
|
||||
for j := range i.opts {
|
||||
if strings.HasPrefix(i.opts[j], optionCapacity) {
|
||||
n, err := strconv.ParseUint(i.opts[j][len(optionCapacity):], 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
24
pkg/core/netmap/node/options_test.go
Normal file
24
pkg/core/netmap/node/options_test.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/object"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInfo_Price(t *testing.T) {
|
||||
var info Info
|
||||
|
||||
// too small value
|
||||
info.opts = []string{"/Price:0.01048575"}
|
||||
require.Equal(t, uint64(0), info.Price())
|
||||
|
||||
// min value
|
||||
info.opts = []string{"/Price:0.01048576"}
|
||||
require.Equal(t, uint64(1), info.Price())
|
||||
|
||||
// big value
|
||||
info.opts = []string{"/Price:1000000000.666"}
|
||||
require.Equal(t, uint64(1000000000.666*1e8/object.UnitsMB), info.Price())
|
||||
}
|
63
pkg/core/netmap/node/status.go
Normal file
63
pkg/core/netmap/node/status.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package node
|
||||
|
||||
// Status represents a node
|
||||
// status bits.
|
||||
type Status uint64
|
||||
|
||||
const leftBitPos = 64
|
||||
|
||||
const (
|
||||
bitFullStorage = 1
|
||||
)
|
||||
|
||||
// returns true if n-th left bit is set (starting at 0).
|
||||
func isLeftBitSet(value Status, n uint8) bool {
|
||||
bitMask := Status(1 << (leftBitPos - n))
|
||||
return bitMask != 0 && value&bitMask == bitMask
|
||||
}
|
||||
|
||||
// sets n-th left bit (starting at 0).
|
||||
func setLeftBit(value *Status, n uint8) {
|
||||
*value |= Status(1 << (leftBitPos - n))
|
||||
}
|
||||
|
||||
// resets n-th left bit (starting at 0).
|
||||
func resetLeftBit(value *Status, n uint8) {
|
||||
*value &= ^Status(1 << (leftBitPos - n))
|
||||
}
|
||||
|
||||
// Full returns true if node is in Full status.
|
||||
//
|
||||
// Full status marks node has enough space
|
||||
// for storing users objects.
|
||||
func (n Status) Full() bool {
|
||||
return isLeftBitSet(n, bitFullStorage)
|
||||
}
|
||||
|
||||
// SetFull sets Full status of node.
|
||||
func (n *Status) SetFull() {
|
||||
setLeftBit(n, bitFullStorage)
|
||||
}
|
||||
|
||||
// ResetFull resets Full status of node.
|
||||
func (n *Status) ResetFull() {
|
||||
resetLeftBit(n, bitFullStorage)
|
||||
}
|
||||
|
||||
// StatusFromUint64 converts builtin
|
||||
// uint64 value to Status.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func StatusFromUint64(v uint64) Status {
|
||||
return Status(v)
|
||||
}
|
||||
|
||||
// StatusToUint64 converts Status value
|
||||
// to builtin uint64.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func StatusToUint64(s Status) uint64 {
|
||||
return uint64(s)
|
||||
}
|
17
pkg/core/netmap/node/status_test.go
Normal file
17
pkg/core/netmap/node/status_test.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatus_Full(t *testing.T) {
|
||||
st := new(Status)
|
||||
|
||||
st.SetFull()
|
||||
require.True(t, st.Full())
|
||||
|
||||
st.ResetFull()
|
||||
require.False(t, st.Full())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue