Files
seaweedfs/weed/admin/dash/tier_stats_test.go
Chris LuandGitHub 97a155d14d admin: show capacity per storage tier and stop counting remote-tiered bytes as local disk usage (#10766)
* admin: show capacity per storage tier and stop counting remote-tiered bytes as local disk usage

A remote-tiered volume reports its cloud object's size, so summing volume
sizes inflated the dashboard's used-vs-capacity numbers (the local .dat is
gone after volume.tier.move). Split the accounting: DiskUsage now only
counts bytes on local disks, with the cloud bytes surfaced separately per
server and per remote storage name.

The dashboard gains a Storage Tiers table breaking volumes and EC shards
down by tier (each local disk type plus each remote storage), using the
per-disk-type statfs numbers already in the VolumeList response. The
volumes page badges remote-tiered volumes with their storage name, and
the EC shards page fills in real per-shard sizes instead of hardcoding 0.

* admin: review fixes for the tier capacity display

- A disk that predates disk_total_bytes now contributes its logical
  bytes to the tier's DiskUsed, so a tier mixing old and new volume
  servers doesn't underreport usage; the usage bar always reflects the
  displayed Disk Used value (the DataSize fallback in UsagePercent is
  gone, and the percent math is overflow-safe).
- getTopologyViaGRPC defaults a zero VolumeSizeLimitMb to 30000 MB like
  GetClusterVolumeServers, keeping slot-based capacities consistent.
- The dashboard volume-servers column reads Usage / Capacity to match
  its cell content, and the hdd disk-type default is shared between the
  volumes-page badge and countUniqueDiskTypes.
2026-08-15 12:35:16 -07:00

92 lines
2.8 KiB
Go

package dash
import (
"reflect"
"testing"
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
)
func TestCollectTierStats(t *testing.T) {
topo := &master_pb.TopologyInfo{
DataCenterInfos: []*master_pb.DataCenterInfo{{
Id: "dc1",
RackInfos: []*master_pb.RackInfo{{
Id: "rack1",
DataNodeInfos: []*master_pb.DataNodeInfo{
{
Id: "node1:8080",
DiskInfos: map[string]*master_pb.DiskInfo{
"": {
Type: "",
MaxVolumeCount: 10,
DiskTotalBytes: 1_000_000,
DiskFreeBytes: 400_000,
VolumeInfos: []*master_pb.VolumeInformationMessage{
{Id: 1, Size: 1000},
{Id: 2, Size: 5000, RemoteStorageName: "s3.backup", RemoteStorageKey: "/2.dat"},
},
EcShardInfos: []*master_pb.VolumeEcShardInformationMessage{
{Id: 7, EcIndexBits: 0b111, ShardSizes: []int64{10, 20, 30}},
},
},
},
},
{
Id: "node2:8080",
DiskInfos: map[string]*master_pb.DiskInfo{
"ssd": {
Type: "ssd",
MaxVolumeCount: 5,
// No statfs numbers: capacity falls back to
// slots and usage to logical bytes.
VolumeInfos: []*master_pb.VolumeInformationMessage{
{Id: 3, Size: 2000, DiskType: "ssd"},
{Id: 4, Size: 7000, DiskType: "ssd", RemoteStorageName: "s3.backup", RemoteStorageKey: "/4.dat"},
{Id: 5, Size: 100, DiskType: "ssd", RemoteStorageName: "gcs.archive", RemoteStorageKey: "/5.dat"},
},
},
},
},
{
// An old server without statfs numbers sharing the
// hdd tier with node1: its logical bytes must still
// count toward the tier's DiskUsed.
Id: "node3:8080",
DiskInfos: map[string]*master_pb.DiskInfo{
"": {
Type: "",
MaxVolumeCount: 2,
VolumeInfos: []*master_pb.VolumeInformationMessage{
{Id: 6, Size: 800},
},
},
},
},
},
}},
}},
}
got := CollectTierStats(topo, 30)
want := []TierStats{
{Name: "hdd", VolumeCount: 2, EcShardCount: 3, DataSize: 1860, DiskUsed: 600_800, DiskCapacity: 1_000_000 + 2*30*1024*1024, MaxVolumes: 12},
{Name: "ssd", VolumeCount: 1, DataSize: 2000, DiskUsed: 2000, DiskCapacity: 5 * 30 * 1024 * 1024, MaxVolumes: 5},
{Name: "gcs.archive", IsRemote: true, VolumeCount: 1, DataSize: 100},
{Name: "s3.backup", IsRemote: true, VolumeCount: 2, DataSize: 12000},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("CollectTierStats mismatch:\n got: %+v\nwant: %+v", got, want)
}
}
func TestCollectTierStatsEmpty(t *testing.T) {
if got := CollectTierStats(nil, 30); got != nil {
t.Errorf("expected nil for nil topology, got %+v", got)
}
if got := CollectTierStats(&master_pb.TopologyInfo{}, 30); len(got) != 0 {
t.Errorf("expected no tiers for empty topology, got %+v", got)
}
}