Skip to content

Commit

Permalink
node: Announce capacity via the attributes
Browse files Browse the repository at this point in the history
It is done automatically (as a sum of existing space on every file system
available on the configured shards) but can be overwritten if `Capacity`
attribute is provided via the application configuration.
Closes #602.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
  • Loading branch information
carpawell committed Aug 14, 2023
1 parent 21e9d2a commit bf32f1d
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 10 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Changelog for NeoFS Node
- Stored payload metric per container (#2116)
- Stored payload metric per shard (#2023)
- Histogram metrics for RPC and engine operations (#2351)
- SN's version is announced via the attributes automatically but can be overwritten explicitly (#2455)
- SN's version and capacity is announced via the attributes automatically but can be overwritten explicitly (#2455, #602)

### Fixed
- `neo-go` RPC connection loss handling (#1337)
Expand Down
45 changes: 37 additions & 8 deletions cmd/neofs-node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"os/signal"
"path/filepath"
"strconv"
"strings"
"sync"
atomicstd "sync/atomic"
Expand Down Expand Up @@ -532,16 +533,17 @@ var persistateSideChainLastBlockKey = []byte("side_chain_last_processed_block")
func initCfg(appCfg *config.Config) *cfg {
c := &cfg{}

// attaching version to the node's attributes; do not
// move it anywhere below reading the other attributes
// since a user should be able to overwrite it.
writeAppVersion(c)

err := c.readConfig(appCfg)
if err != nil {
panic(fmt.Errorf("config reading: %w", err))
}

// filling system attributes; do not move it anywhere
// below applying the other attributes since a user
// should be able to overwrite it.
err = writeSystemAttributes(c)
fatalOnErr(err)

key := nodeconfig.Key(appCfg)

var netAddr network.AddressGroup
Expand Down Expand Up @@ -969,8 +971,35 @@ func (c *cfg) configWatcher(ctx context.Context) {
}
}

// writeAppVersion writes app version as defined at compilation
// step to the node's attributes.
func writeAppVersion(c *cfg) {
// writeSystemAttributes writes app version as defined at compilation
// step to the node's attributes and an aggregated disk capacity
// according to the all space on the all configured disks.
func writeSystemAttributes(c *cfg) error {
// `Version` attribute

c.cfgNodeInfo.localInfo.SetAttribute("Version", misc.Version)

// `Capacity` attribute

var paths []string
for _, sh := range c.applicationConfiguration.EngineCfg.shards {
for _, storage := range sh.subStorages {
path := storage.path
paths = append(paths, path)

err := util.MkdirAllX(path, storage.perm)
if err != nil {
return fmt.Errorf("can not create (ensure it exists) dir by '%s' path: %w", path, err)
}
}
}

total, err := totalBytes(paths)
if err != nil {
return fmt.Errorf("calculating capacity on every shard: %w", err)
}

c.cfgNodeInfo.localInfo.SetAttribute("Capacity", strconv.FormatUint(total/(1<<30), 10))

return nil
}
31 changes: 31 additions & 0 deletions cmd/neofs-node/fs_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//go:build !windows

package main

import (
"fmt"

"golang.org/x/sys/unix"
)

func totalBytes(paths []string) (uint64, error) {
var stat unix.Statfs_t
var total uint64 // bytes
fsIDMap := make(map[unix.Fsid]struct{})

for _, path := range paths {
err := unix.Statfs(path, &stat)
if err != nil {
return 0, fmt.Errorf("get FS stat by '%s' path: %w", path, err)
}

if _, handled := fsIDMap[stat.Fsid]; handled {
continue
}

total += stat.Blocks * uint64(stat.Bsize)
fsIDMap[stat.Fsid] = struct{}{}
}

return total, nil
}
37 changes: 37 additions & 0 deletions cmd/neofs-node/fs_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//go:build windows

package main

import (
"fmt"
"path/filepath"

"golang.org/x/sys/windows"
)

func totalBytes(paths []string) (uint64, error) {
var total uint64 // bytes
mDisks := make(map[string]struct{}, len(paths))

for _, path := range paths {
var availB uint64
var totalB uint64
var freeTotalB uint64
var pathP = windows.StringToUTF16Ptr(path)

err := windows.GetDiskFreeSpaceEx(pathP, &availB, &totalB, &freeTotalB)
if err != nil {
return 0, fmt.Errorf("get disk stat by '%s' path: %w", path, err)
}

disk := filepath.VolumeName(path)
if _, handled := mDisks[disk]; handled {
continue
}

total += totalB
mDisks[disk] = struct{}{}
}

return total, nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require (
go.etcd.io/bbolt v1.3.7
go.uber.org/atomic v1.10.0
go.uber.org/zap v1.24.0
golang.org/x/sys v0.8.0
golang.org/x/term v0.5.0
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
Expand Down Expand Up @@ -97,7 +98,6 @@ require (
golang.org/x/mod v0.6.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.2.0 // indirect
Expand Down

0 comments on commit bf32f1d

Please sign in to comment.