Files
waf-platform/EdgeNode/internal/utils/fs/ssd.go
2026-02-04 20:27:13 +08:00

185 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package fsutils
import (
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
)
// StorageType 存储类型
type StorageType int
const (
StorageTypeUnknown StorageType = 0
StorageTypeSSD StorageType = 1
StorageTypeHDD StorageType = 2
)
var (
ssdCache = map[string]StorageType{} // path => StorageType
ssdCacheLock sync.RWMutex
)
// IsSSD 检测指定路径是否为 SSD
// 方法:
// 1. 检查 /sys/block/*/queue/rotationalLinux
// 2. 使用 lsblk 命令Linux
// 3. 检查设备名称nvme, ssd 等关键词)
func IsSSD(path string) (bool, error) {
storageType, err := DetectStorageType(path)
if err != nil {
return false, err
}
return storageType == StorageTypeSSD, nil
}
// DetectStorageType 检测存储类型
func DetectStorageType(path string) (StorageType, error) {
// 检查缓存
ssdCacheLock.RLock()
if cachedType, ok := ssdCache[path]; ok {
ssdCacheLock.RUnlock()
return cachedType, nil
}
ssdCacheLock.RUnlock()
// 获取绝对路径
absPath, err := filepath.Abs(path)
if err != nil {
return StorageTypeUnknown, err
}
// 获取设备路径
devicePath, err := getDevicePath(absPath)
if err != nil {
return StorageTypeUnknown, err
}
// 方法 1检查 /sys/block/*/queue/rotationalLinux
if isSSD, err := checkRotational(devicePath); err == nil {
storageType := StorageTypeHDD
if isSSD {
storageType = StorageTypeSSD
}
ssdCacheLock.Lock()
ssdCache[path] = storageType
ssdCacheLock.Unlock()
return storageType, nil
}
// 方法 2使用 lsblk 命令Linux
if isSSD, err := checkWithLsblk(devicePath); err == nil {
storageType := StorageTypeHDD
if isSSD {
storageType = StorageTypeSSD
}
ssdCacheLock.Lock()
ssdCache[path] = storageType
ssdCacheLock.Unlock()
return storageType, nil
}
// 方法 3检查设备名称nvme, ssd 等关键词)
if isSSD := checkDeviceName(devicePath); isSSD {
ssdCacheLock.Lock()
ssdCache[path] = StorageTypeSSD
ssdCacheLock.Unlock()
return StorageTypeSSD, nil
}
// 默认返回未知
ssdCacheLock.Lock()
ssdCache[path] = StorageTypeUnknown
ssdCacheLock.Unlock()
return StorageTypeUnknown, nil
}
// getDevicePath 获取设备路径
func getDevicePath(path string) (string, error) {
// 从路径提取设备名(简化实现)
// 实际应该通过 stat 获取设备信息
// 这里使用路径的第一级目录作为设备标识
parts := strings.Split(strings.Trim(path, "/"), "/")
if len(parts) > 0 {
return parts[0], nil
}
return "", os.ErrNotExist
}
// checkRotational 检查 /sys/block/*/queue/rotational
func checkRotational(devicePath string) (bool, error) {
// 提取设备名(如 sda, nvme0n1
deviceName := extractDeviceName(devicePath)
if len(deviceName) == 0 {
return false, os.ErrNotExist
}
// 检查 /sys/block/{device}/queue/rotational
rotationalPath := "/sys/block/" + deviceName + "/queue/rotational"
data, err := os.ReadFile(rotationalPath)
if err != nil {
return false, err
}
// 0 表示 SSD1 表示 HDD
value := strings.TrimSpace(string(data))
return value == "0", nil
}
// checkWithLsblk 使用 lsblk 命令检查
func checkWithLsblk(devicePath string) (bool, error) {
cmd := exec.Command("lsblk", "-d", "-o", "name,rota", "-n")
output, err := cmd.Output()
if err != nil {
return false, err
}
lines := strings.Split(string(output), "\n")
deviceName := extractDeviceName(devicePath)
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) >= 2 && fields[0] == deviceName {
// rota=0 表示 SSDrota=1 表示 HDD
return fields[1] == "0", nil
}
}
return false, os.ErrNotExist
}
// checkDeviceName 检查设备名称
func checkDeviceName(devicePath string) bool {
deviceName := strings.ToLower(extractDeviceName(devicePath))
// 检查常见 SSD 关键词
ssdKeywords := []string{"nvme", "ssd", "flash"}
for _, keyword := range ssdKeywords {
if strings.Contains(deviceName, keyword) {
return true
}
}
return false
}
// extractDeviceName 从路径提取设备名
func extractDeviceName(path string) string {
// 简化实现:从路径中提取可能的设备名
// 实际应该通过更可靠的方法获取
parts := strings.Split(path, "/")
for _, part := range parts {
if len(part) > 0 && (strings.HasPrefix(part, "sd") ||
strings.HasPrefix(part, "nvme") ||
strings.HasPrefix(part, "hd") ||
strings.HasPrefix(part, "vd")) {
return part
}
}
return ""
}