Files
aiops/agent/collector/disk.go
T
attacker 8240a88a84 feat(k8s): M14 Namespace 管理看板(资源配额 + 命名空间 CRUD)
- 后端: ListNamespacesDetailed/CreateNamespace/DeleteNamespace/ListResourceQuotas/ListLimitRanges
- 后端: GetNamespacesDetail handler (6 API calls aggregate, not N×3)
- 后端: CreateNamespaceHandler + DeleteNamespaceHandler (system NS protection)
- 前端: NamespaceManagementPage.tsx (~280行) — 表格+概览卡片+创建/删除弹框
- 路由: GET/POST/DELETE /:clusterCode/namespaces + GET /namespace/:ns/detail
- 验证: 9/9 通过 (urllib), TS 零错误, Vite 构建成功
2026-06-24 23:46:52 +08:00

144 lines
3.5 KiB
Go

// Package collector 提供服务器各项系统指标的采集能力。
// 本文件实现对所有已挂载的真实文件系统执行 statfs,采集磁盘使用情况。
// 使用 golang.org/x/sys/unix 的 Statfs,兼容 Linux 与 macOS。
//
// 署名 Logan.Li
package collector
import (
"bufio"
"fmt"
"os"
"runtime"
"strings"
"golang.org/x/sys/unix"
"ezops/agent/model"
)
// DiskCollector 磁盘使用率采集器
type DiskCollector struct{}
// NewDiskCollector 创建磁盘采集器实例
func NewDiskCollector() *DiskCollector {
return &DiskCollector{}
}
// mount 描述一个挂载点信息
type mount struct {
device string
mountpoint string
fstype string
}
// realFilesystems 允许采集的"真实"文件系统类型白名单,
// 用于排除 tmpfs/proc/sysfs 等伪文件系统,避免产生噪音数据。
var realFilesystems = map[string]bool{
"ext2": true, "ext3": true, "ext4": true,
"xfs": true, "btrfs": true, "zfs": true,
"apfs": true, "hfs": true, "exfat": true,
"ntfs": true, "vfat": true, "fat32": true,
"f2fs": true, "jfs": true, "reiserfs": true,
"ufs": true,
}
// Collect 采集所有真实文件系统挂载点的磁盘使用情况
func (d *DiskCollector) Collect() ([]model.DiskStat, error) {
mounts, err := d.getMounts()
if err != nil {
return nil, fmt.Errorf("获取挂载点失败: %w", err)
}
disks := make([]model.DiskStat, 0, len(mounts))
seen := make(map[string]bool) // 按挂载点去重
for _, m := range mounts {
if !realFilesystems[m.fstype] {
continue
}
if seen[m.mountpoint] {
continue
}
seen[m.mountpoint] = true
var fs unix.Statfs_t
if err := unix.Statfs(m.mountpoint, &fs); err != nil {
// 单个挂载点采集失败不影响其余挂载点
continue
}
bsize := uint64(fs.Bsize)
total := fs.Blocks * bsize
free := fs.Bfree * bsize
if total == 0 {
continue
}
used := total - free
// inode 使用率
var inodePct float64
if fs.Files > 0 {
inodePct = float64(fs.Files-fs.Ffree) / float64(fs.Files) * 100.0
if inodePct < 0 {
inodePct = 0
}
}
disks = append(disks, model.DiskStat{
Mount: m.mountpoint,
Device: m.device,
TotalGB: bytesToGB(total),
UsedGB: bytesToGB(used),
UsagePercent: float64(used) / float64(total) * 100.0,
InodeUsagePct: inodePct,
})
}
return disks, nil
}
// getMounts 获取挂载点列表。
// - Linux: 解析 /proc/mounts
// - macOS(开发环境): 返回根分区
func (d *DiskCollector) getMounts() ([]mount, error) {
if runtime.GOOS != "linux" {
// 开发环境(macOS)下提供根分区以便本地调试
return []mount{{device: "/dev/disk1", mountpoint: "/", fstype: "apfs"}}, nil
}
f, err := os.Open("/proc/mounts")
if err != nil {
return nil, err
}
defer f.Close()
var mounts []mount
scanner := bufio.NewScanner(f)
// 挂载点路径可能较长,扩大缓冲区
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
for scanner.Scan() {
// 形如: /dev/sda1 / ext4 rw,relatime 0 0
fields := strings.Fields(scanner.Text())
if len(fields) < 3 {
continue
}
mounts = append(mounts, mount{
device: fields[0],
mountpoint: unescapeMount(fields[1]),
fstype: fields[2],
})
}
return mounts, scanner.Err()
}
// unescapeMount 还原 /proc/mounts 中被转义的挂载点路径(如 \040 -> 空格)
func unescapeMount(s string) string {
return strings.ReplaceAll(s, "\\040", " ")
}
// bytesToGB 字节数转 GB
func bytesToGB(b uint64) float64 {
const gb = 1024.0 * 1024.0 * 1024.0
return float64(b) / gb
}