diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f3d19d208..9c76f05316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,9 @@ minor release, the component will be purged, so be prepared (see `Updating` sect - 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) - New storage component for small objects named Peapod (#2453) - New `blobovnicza-to-peapod` tool providing blobovnicza-to-peapod data migration (#2453) +- 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) diff --git a/cmd/neofs-node/config.go b/cmd/neofs-node/config.go index 74d143f6f7..db6cb28448 100644 --- a/cmd/neofs-node/config.go +++ b/cmd/neofs-node/config.go @@ -542,16 +542,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 @@ -986,8 +987,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.SetCapacity(total / (1 << 30)) + + return nil } diff --git a/cmd/neofs-node/fs_unix.go b/cmd/neofs-node/fs_unix.go new file mode 100644 index 0000000000..13d203ecbc --- /dev/null +++ b/cmd/neofs-node/fs_unix.go @@ -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 +} diff --git a/cmd/neofs-node/fs_windows.go b/cmd/neofs-node/fs_windows.go new file mode 100644 index 0000000000..6cb1a32d02 --- /dev/null +++ b/cmd/neofs-node/fs_windows.go @@ -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 + diskMap := make(map[string]struct{}, len(paths)) + + for _, path := range paths { + disk := filepath.VolumeName(path) + if _, handled := diskMap[disk]; handled { + continue + } + + 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) + } + + total += totalB + diskMap[disk] = struct{}{} + } + + return total, nil +} diff --git a/go.mod b/go.mod index 559f30009a..c76e5e0de6 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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