mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-22 05:36:15 +01:00
refactor: mdraid comments and organization
also hide serial / firmware in smart details if empty, remove a few unnecessary ops, and add a few more passed state values
This commit is contained in:
@@ -1,24 +0,0 @@
|
|||||||
package agent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readStringFile(path string) string {
|
|
||||||
content, _ := readStringFileOK(path)
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
func readStringFileOK(path string) (string, bool) {
|
|
||||||
b, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(b)), true
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileExists(path string) bool {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
41
agent/fs_utils.go
Normal file
41
agent/fs_utils.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// readStringFile returns trimmed file contents or empty string on error.
|
||||||
|
func readStringFile(path string) string {
|
||||||
|
content, _ := readStringFileOK(path)
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
// readStringFileOK returns trimmed file contents and read success.
|
||||||
|
func readStringFileOK(path string) (string, bool) {
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(b)), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileExists reports whether the given path exists.
|
||||||
|
func fileExists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readUintFile parses a decimal uint64 value from a file.
|
||||||
|
func readUintFile(path string) (uint64, bool) {
|
||||||
|
raw, ok := readStringFileOK(path)
|
||||||
|
if !ok {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
parsed, err := strconv.ParseUint(raw, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return parsed, true
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
var mdraidSysfsRoot = "/sys"
|
var mdraidSysfsRoot = "/sys"
|
||||||
|
|
||||||
type mdraidHealth struct {
|
type mdraidHealth struct {
|
||||||
name string
|
|
||||||
level string
|
level string
|
||||||
arrayState string
|
arrayState string
|
||||||
degraded uint64
|
degraded uint64
|
||||||
@@ -28,6 +27,7 @@ type mdraidHealth struct {
|
|||||||
capacity uint64
|
capacity uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scanMdraidDevices discovers Linux md arrays exposed in sysfs.
|
||||||
func scanMdraidDevices() []*DeviceInfo {
|
func scanMdraidDevices() []*DeviceInfo {
|
||||||
blockDir := filepath.Join(mdraidSysfsRoot, "block")
|
blockDir := filepath.Join(mdraidSysfsRoot, "block")
|
||||||
entries, err := os.ReadDir(blockDir)
|
entries, err := os.ReadDir(blockDir)
|
||||||
@@ -58,6 +58,7 @@ func scanMdraidDevices() []*DeviceInfo {
|
|||||||
return devices
|
return devices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collectMdraidHealth reads mdraid health and stores it in SmartDataMap.
|
||||||
func (sm *SmartManager) collectMdraidHealth(deviceInfo *DeviceInfo) (bool, error) {
|
func (sm *SmartManager) collectMdraidHealth(deviceInfo *DeviceInfo) (bool, error) {
|
||||||
if deviceInfo == nil || deviceInfo.Name == "" {
|
if deviceInfo == nil || deviceInfo.Name == "" {
|
||||||
return false, nil
|
return false, nil
|
||||||
@@ -115,19 +116,16 @@ func (sm *SmartManager) collectMdraidHealth(deviceInfo *DeviceInfo) (bool, error
|
|||||||
if health.level != "" {
|
if health.level != "" {
|
||||||
data.ModelName = "Linux MD RAID (" + health.level + ")"
|
data.ModelName = "Linux MD RAID (" + health.level + ")"
|
||||||
}
|
}
|
||||||
data.SerialNumber = ""
|
|
||||||
data.FirmwareVersion = ""
|
|
||||||
data.Capacity = health.capacity
|
data.Capacity = health.capacity
|
||||||
data.Temperature = 0
|
|
||||||
data.SmartStatus = status
|
data.SmartStatus = status
|
||||||
data.DiskName = filepath.Join("/dev", base)
|
data.DiskName = filepath.Join("/dev", base)
|
||||||
data.DiskType = "mdraid"
|
data.DiskType = "mdraid"
|
||||||
data.Attributes = attrs
|
data.Attributes = attrs
|
||||||
sm.SmartDataMap[key] = data
|
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readMdraidHealth reads md array health fields from sysfs.
|
||||||
func readMdraidHealth(blockName string) (mdraidHealth, bool) {
|
func readMdraidHealth(blockName string) (mdraidHealth, bool) {
|
||||||
var out mdraidHealth
|
var out mdraidHealth
|
||||||
|
|
||||||
@@ -141,7 +139,6 @@ func readMdraidHealth(blockName string) (mdraidHealth, bool) {
|
|||||||
return out, false
|
return out, false
|
||||||
}
|
}
|
||||||
|
|
||||||
out.name = blockName
|
|
||||||
out.arrayState = arrayState
|
out.arrayState = arrayState
|
||||||
out.level = readStringFile(filepath.Join(mdDir, "level"))
|
out.level = readStringFile(filepath.Join(mdDir, "level"))
|
||||||
out.syncAction = readStringFile(filepath.Join(mdDir, "sync_action"))
|
out.syncAction = readStringFile(filepath.Join(mdDir, "sync_action"))
|
||||||
@@ -165,6 +162,7 @@ func readMdraidHealth(blockName string) (mdraidHealth, bool) {
|
|||||||
return out, true
|
return out, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mdraidSmartStatus maps md state/sync signals to a SMART-like status.
|
||||||
func mdraidSmartStatus(health mdraidHealth) string {
|
func mdraidSmartStatus(health mdraidHealth) string {
|
||||||
state := strings.ToLower(strings.TrimSpace(health.arrayState))
|
state := strings.ToLower(strings.TrimSpace(health.arrayState))
|
||||||
switch state {
|
switch state {
|
||||||
@@ -174,18 +172,18 @@ func mdraidSmartStatus(health mdraidHealth) string {
|
|||||||
if health.degraded > 0 {
|
if health.degraded > 0 {
|
||||||
return "FAILED"
|
return "FAILED"
|
||||||
}
|
}
|
||||||
|
|
||||||
switch strings.ToLower(strings.TrimSpace(health.syncAction)) {
|
switch strings.ToLower(strings.TrimSpace(health.syncAction)) {
|
||||||
case "resync", "recover", "reshape", "check", "repair":
|
case "resync", "recover", "reshape", "check", "repair":
|
||||||
return "WARNING"
|
return "WARNING"
|
||||||
}
|
}
|
||||||
|
switch state {
|
||||||
if state == "clean" || state == "active" || state == "readonly" {
|
case "clean", "active", "active-idle", "write-pending", "read-auto", "readonly":
|
||||||
return "PASSED"
|
return "PASSED"
|
||||||
}
|
}
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isMdraidBlockName matches /dev/mdN-style block device names.
|
||||||
func isMdraidBlockName(name string) bool {
|
func isMdraidBlockName(name string) bool {
|
||||||
if !strings.HasPrefix(name, "md") {
|
if !strings.HasPrefix(name, "md") {
|
||||||
return false
|
return false
|
||||||
@@ -202,18 +200,7 @@ func isMdraidBlockName(name string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func readUintFile(path string) (uint64, bool) {
|
// readMdraidBlockCapacityBytes converts block size metadata into bytes.
|
||||||
raw, ok := readStringFileOK(path)
|
|
||||||
if !ok {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
parsed, err := strconv.ParseUint(strings.TrimSpace(raw), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
return parsed, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func readMdraidBlockCapacityBytes(blockName, root string) (uint64, bool) {
|
func readMdraidBlockCapacityBytes(blockName, root string) (uint64, bool) {
|
||||||
sizePath := filepath.Join(root, "block", blockName, "size")
|
sizePath := filepath.Join(root, "block", blockName, "size")
|
||||||
lbsPath := filepath.Join(root, "block", blockName, "queue", "logical_block_size")
|
lbsPath := filepath.Join(root, "block", blockName, "queue", "logical_block_size")
|
||||||
@@ -222,15 +209,14 @@ func readMdraidBlockCapacityBytes(blockName, root string) (uint64, bool) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
sectors, err := strconv.ParseUint(strings.TrimSpace(sizeStr), 10, 64)
|
sectors, err := strconv.ParseUint(sizeStr, 10, 64)
|
||||||
if err != nil || sectors == 0 {
|
if err != nil || sectors == 0 {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
lbsStr, ok := readStringFileOK(lbsPath)
|
|
||||||
logicalBlockSize := uint64(512)
|
logicalBlockSize := uint64(512)
|
||||||
if ok {
|
if lbsStr, ok := readStringFileOK(lbsPath); ok {
|
||||||
if parsed, err := strconv.ParseUint(strings.TrimSpace(lbsStr), 10, 64); err == nil && parsed > 0 {
|
if parsed, err := strconv.ParseUint(lbsStr, 10, 64); err == nil && parsed > 0 {
|
||||||
logicalBlockSize = parsed
|
logicalBlockSize = parsed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -621,8 +621,8 @@ function DiskSheet({
|
|||||||
const deviceName = disk?.name || unknown
|
const deviceName = disk?.name || unknown
|
||||||
const model = disk?.model || unknown
|
const model = disk?.model || unknown
|
||||||
const capacity = disk?.capacity ? formatCapacity(disk.capacity) : unknown
|
const capacity = disk?.capacity ? formatCapacity(disk.capacity) : unknown
|
||||||
const serialNumber = disk?.serial || unknown
|
const serialNumber = disk?.serial
|
||||||
const firmwareVersion = disk?.firmware || unknown
|
const firmwareVersion = disk?.firmware
|
||||||
const status = disk?.state || unknown
|
const status = disk?.state || unknown
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -636,24 +636,32 @@ function DiskSheet({
|
|||||||
{model}
|
{model}
|
||||||
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
||||||
{capacity}
|
{capacity}
|
||||||
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
{serialNumber && (
|
||||||
<Tooltip>
|
<>
|
||||||
<TooltipTrigger asChild>
|
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
||||||
<span>{serialNumber}</span>
|
<Tooltip>
|
||||||
</TooltipTrigger>
|
<TooltipTrigger asChild>
|
||||||
<TooltipContent>
|
<span>{serialNumber}</span>
|
||||||
<Trans>Serial Number</Trans>
|
</TooltipTrigger>
|
||||||
</TooltipContent>
|
<TooltipContent>
|
||||||
</Tooltip>
|
<Trans>Serial Number</Trans>
|
||||||
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
</TooltipContent>
|
||||||
<Tooltip>
|
</Tooltip>
|
||||||
<TooltipTrigger asChild>
|
</>
|
||||||
<span>{firmwareVersion}</span>
|
)}
|
||||||
</TooltipTrigger>
|
{firmwareVersion && (
|
||||||
<TooltipContent>
|
<>
|
||||||
<Trans>Firmware</Trans>
|
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
||||||
</TooltipContent>
|
<Tooltip>
|
||||||
</Tooltip>
|
<TooltipTrigger asChild>
|
||||||
|
<span>{firmwareVersion}</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<Trans>Firmware</Trans>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</SheetDescription>
|
</SheetDescription>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
<div className="flex-1 overflow-hidden p-4 flex flex-col gap-4">
|
<div className="flex-1 overflow-hidden p-4 flex flex-col gap-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user