diff --git a/agent/app/service/disk.go b/agent/app/service/disk.go
index 19abf39ac..7a75479f0 100644
--- a/agent/app/service/disk.go
+++ b/agent/app/service/disk.go
@@ -1,17 +1,12 @@
package service
import (
- "bufio"
- "encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"os"
- "os/exec"
- "path/filepath"
- "strconv"
"strings"
"time"
@@ -33,127 +28,24 @@ func NewIDiskService() IDiskService {
}
func (s *DiskService) GetCompleteDiskInfo() (*response.CompleteDiskInfo, error) {
- disksWithPartitions, unpartitionedDisks, err := getAllDisksInfo()
+ output, err := cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err != nil {
- return nil, err
- }
- var systemDisk *response.DiskInfo
- var totalCapacity int64
- var filteredDisksWithPartitions []response.DiskInfo
-
- for i, disk := range disksWithPartitions {
- if disk.IsSystem {
- if systemDisk == nil {
- systemDisk = &disksWithPartitions[i]
- }
- } else {
- filteredDisksWithPartitions = append(filteredDisksWithPartitions, disk)
- }
+ return nil, fmt.Errorf("failed to execute lsblk command: %v", err)
}
- completeDiskInfo := &response.CompleteDiskInfo{
- Disks: filteredDisksWithPartitions,
- UnpartitionedDisks: unpartitionedDisks,
- SystemDisk: systemDisk,
- TotalDisks: len(filteredDisksWithPartitions) + len(unpartitionedDisks),
- TotalCapacity: totalCapacity,
- }
- return completeDiskInfo, nil
-}
-
-func getDiskType(rota bool, tran string) string {
- if tran == "" {
- return ""
- }
- if !rota {
- return "SSD"
- }
- return "HDD"
-}
-
-func getAllDisksInfo() ([]response.DiskInfo, []response.DiskBasicInfo, error) {
- var disksWithPartitions []response.DiskInfo
- var unpartitionedDisks []response.DiskBasicInfo
-
- output, err := cmd.RunDefaultWithStdoutBashC("lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
+ diskInfos, err := parseLsblkOutput(output)
if err != nil {
- return nil, nil, err
+ return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
}
- var lsblkOutput dto.LsblkOutput
- if err = json.Unmarshal([]byte(output), &lsblkOutput); err != nil {
- return nil, nil, err
- }
-
- for _, device := range lsblkOutput.BlockDevices {
- if device.Type != "disk" {
- continue
- }
-
- devicePath := "/dev/" + device.Name
- model := ""
- isPhysical := false
-
- if device.Tran != "" {
- isPhysical = true
- if device.Model != nil {
- model = *device.Model
- }
- }
-
- hasPartitions := len(device.Children) > 0
-
- if hasPartitions {
- disk := response.DiskInfo{
- DiskBasicInfo: response.DiskBasicInfo{
- Device: devicePath,
- Size: device.Size,
- DiskType: getDiskType(device.Rota, device.Tran),
- IsRemovable: isRemovableDisk(devicePath),
- IsSystem: false,
- },
- Partitions: []response.DiskBasicInfo{},
- }
-
- if isPhysical {
- disk.Serial = device.Serial
- disk.Model = model
- }
-
- for _, partition := range device.Children {
- partitionInfo := processPartition(partition)
- if partitionInfo != nil {
- disk.Partitions = append(disk.Partitions, *partitionInfo)
- if partitionInfo.IsSystem {
- disk.IsSystem = true
- }
- }
- }
-
- if len(disk.Partitions) > 0 {
- disksWithPartitions = append(disksWithPartitions, disk)
- }
- } else {
- if isSystemDiskByDevice(device) {
- continue
- }
-
- unpartitionedDisk := response.DiskBasicInfo{
- Device: devicePath,
- Size: device.Size,
- Model: model,
- Serial: device.Serial,
- DiskType: getDiskType(device.Rota, device.Tran),
- IsRemovable: isRemovableDisk(devicePath),
- }
- unpartitionedDisks = append(unpartitionedDisks, unpartitionedDisk)
- }
- }
-
- return disksWithPartitions, unpartitionedDisks, nil
+ result := organizeDiskInfo(diskInfos)
+ return &result, nil
}
func (s *DiskService) PartitionDisk(req request.DiskPartitionRequest) (string, error) {
+ if !strings.HasPrefix("/dev", req.Device) {
+ req.Device = "/dev/" + req.Device
+ }
if !deviceExists(req.Device) {
return "", buserr.WithName("DeviceNotFound", req.Device)
}
@@ -250,305 +142,3 @@ func (s *DiskService) UnmountDisk(req request.DiskUnmountRequest) error {
}
return nil
}
-
-func formatDisk(req dto.DiskFormatRequest) error {
- var mkfsCmd *exec.Cmd
-
- switch req.Filesystem {
- case "ext4":
- mkfsCmd = exec.Command("mkfs.ext4", "-F", req.Device)
- case "xfs":
- if !cmd.Which("mkfs.xfs") {
- return buserr.New("XfsNotFound")
- }
- mkfsCmd = exec.Command("mkfs.xfs", "-f", req.Device)
- default:
- return fmt.Errorf("unsupport type: %s", req.Filesystem)
- }
- if err := mkfsCmd.Run(); err != nil {
- return err
- }
- return nil
-}
-
-func parseDiskInfoLinux(fields []string) (response.DiskInfo, error) {
- device := fields[0]
- filesystem := fields[1]
- sizeStr := fields[2]
- usedStr := fields[3]
- availStr := fields[4]
- usePercentStr := fields[5]
- mountPoint := fields[6]
-
- usePercent := 0
- if strings.HasSuffix(usePercentStr, "%") {
- if percent, err := strconv.Atoi(strings.TrimSuffix(usePercentStr, "%")); err == nil {
- usePercent = percent
- }
- }
-
- return response.DiskInfo{
- DiskBasicInfo: response.DiskBasicInfo{
- Device: device,
- Filesystem: filesystem,
- Size: sizeStr,
- Used: usedStr,
- Avail: availStr,
- UsePercent: usePercent,
- MountPoint: mountPoint,
- },
- }, nil
-}
-
-func deviceExists(device string) bool {
- _, err := os.Stat(device)
- return err == nil
-}
-
-func isDeviceMounted(device string) bool {
- file, err := os.Open("/proc/mounts")
- if err != nil {
- return false
- }
- defer file.Close()
-
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- fields := strings.Fields(scanner.Text())
- if len(fields) >= 2 && fields[0] == device {
- return true
- }
- }
- return false
-}
-
-func isPointMounted(mountPoint string) bool {
- file, err := os.Open("/proc/mounts")
- if err != nil {
- return false
- }
- defer file.Close()
-
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- fields := strings.Fields(scanner.Text())
- if len(fields) >= 2 && fields[1] == mountPoint {
- return true
- }
- }
- return false
-}
-
-func addToFstabWithOptions(device, mountPoint, filesystem, options string) error {
- if filesystem == "" {
- fsType, err := getFilesystemType(device)
- if err != nil {
- filesystem = "auto"
- } else {
- filesystem = fsType
- }
- }
-
- if options == "" {
- options = "defaults"
- }
-
- entry := fmt.Sprintf("%s %s %s %s 0 2\n", device, mountPoint, filesystem, options)
-
- file, err := os.OpenFile("/etc/fstab", os.O_APPEND|os.O_WRONLY, 0644)
- if err != nil {
- return err
- }
- defer file.Close()
-
- _, err = file.WriteString(entry)
- return err
-}
-
-func removeFromFstab(mountPoint string) error {
- file, err := os.Open("/etc/fstab")
- if err != nil {
- return err
- }
- defer file.Close()
-
- var lines []string
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- line := scanner.Text()
- fields := strings.Fields(line)
- if len(fields) >= 2 && fields[1] == mountPoint {
- continue
- }
- lines = append(lines, line)
- }
-
- return os.WriteFile("/etc/fstab", []byte(strings.Join(lines, "\n")+"\n"), 0644)
-}
-
-func getFilesystemType(device string) (string, error) {
- output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("blkid -o value -s TYPE %s", device))
- if err != nil {
- return "", err
- }
- return strings.TrimSpace(output), nil
-}
-
-func isSystemDisk(mountPoint string) bool {
- systemMountPoints := []string{
- "/",
- "/boot",
- "/boot/efi",
- "/usr",
- "/var",
- "/home",
- }
-
- for _, sysMount := range systemMountPoints {
- if mountPoint == sysMount {
- return true
- }
- }
-
- return false
-}
-
-func isSystemDiskByDevice(device dto.LsblkDevice) bool {
- for _, child := range device.Children {
- if child.Type == "part" {
- if child.MountPoint != nil && isSystemDisk(*child.MountPoint) {
- return true
- }
- for _, lvmChild := range child.Children {
- if lvmChild.Type == "lvm" && lvmChild.MountPoint != nil {
- if isSystemDisk(*lvmChild.MountPoint) {
- return true
- }
- }
- }
- }
- }
- return false
-}
-
-func isRemovableDisk(device string) bool {
- deviceName := strings.TrimPrefix(device, "/dev/")
- for i := len(deviceName) - 1; i >= 0; i-- {
- if deviceName[i] < '0' || deviceName[i] > '9' {
- deviceName = deviceName[:i+1]
- break
- }
- }
-
- removablePath := filepath.Join("/sys/block", deviceName, "removable")
- if data, err := os.ReadFile(removablePath); err == nil {
- removable := strings.TrimSpace(string(data))
- return removable == "1"
- }
-
- return false
-}
-
-func processPartition(partition dto.LsblkDevice) *response.DiskBasicInfo {
- if partition.Type != "part" {
- return nil
- }
-
- devicePath := "/dev/" + partition.Name
-
- var mountPoint, filesystem string
- if partition.MountPoint != nil {
- mountPoint = *partition.MountPoint
- }
- if partition.FsType != nil {
- filesystem = *partition.FsType
- }
-
- var (
- actualMountPoint, actualFilesystem string
- size, used, avail string
- usePercent int
- isMounted bool
- isSystem bool
- )
-
- if len(partition.Children) > 0 {
- for _, child := range partition.Children {
- if child.Type == "lvm" {
- lvmDevicePath := "/dev/mapper/" + child.Name
-
- if child.MountPoint != nil {
- actualMountPoint = *child.MountPoint
- isMounted = true
- }
- if child.FsType != nil {
- actualFilesystem = *child.FsType
- }
-
- if actualMountPoint != "" {
- diskUsage, err := getDiskUsageInfo(lvmDevicePath)
- if err == nil {
- used = diskUsage.Used
- avail = diskUsage.Avail
- size = diskUsage.Size
- usePercent = diskUsage.UsePercent
- }
- }
-
- isSystem = isSystemDisk(actualMountPoint)
- break
- }
- }
- } else {
- actualMountPoint = mountPoint
- actualFilesystem = filesystem
- isMounted = mountPoint != ""
-
- if actualMountPoint != "" {
- diskUsage, err := getDiskUsageInfo(devicePath)
- if err == nil {
- used = diskUsage.Used
- avail = diskUsage.Avail
- size = diskUsage.Size
- usePercent = diskUsage.UsePercent
- }
- }
-
- isSystem = isSystemDisk(actualMountPoint)
- }
-
- if size == "" {
- size = partition.Size
- }
-
- return &response.DiskBasicInfo{
- Device: devicePath,
- Size: size,
- Used: used,
- Avail: avail,
- UsePercent: usePercent,
- MountPoint: actualMountPoint,
- Filesystem: actualFilesystem,
- IsMounted: isMounted,
- IsSystem: isSystem,
- }
-}
-
-func getDiskUsageInfo(device string) (response.DiskInfo, error) {
- output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("df -hT -P %s", device))
- if err != nil {
- return response.DiskInfo{}, err
- }
-
- lines := strings.Split(output, "\n")
- if len(lines) < 2 {
- return response.DiskInfo{}, err
- }
-
- fields := strings.Fields(lines[1])
- if len(fields) < 7 {
- return response.DiskInfo{}, err
- }
-
- return parseDiskInfoLinux(fields)
-}
diff --git a/agent/app/service/disk_utils.go b/agent/app/service/disk_utils.go
new file mode 100644
index 000000000..1399629e8
--- /dev/null
+++ b/agent/app/service/disk_utils.go
@@ -0,0 +1,433 @@
+package service
+
+import (
+ "bufio"
+ "fmt"
+ "github.com/1Panel-dev/1Panel/agent/app/dto"
+ "github.com/1Panel-dev/1Panel/agent/app/dto/response"
+ "github.com/1Panel-dev/1Panel/agent/buserr"
+ "github.com/1Panel-dev/1Panel/agent/utils/cmd"
+ "os"
+ "os/exec"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskInfo {
+ var result response.CompleteDiskInfo
+ var systemDisk *response.DiskInfo
+ diskMap := make(map[string]*response.DiskInfo)
+ partitions := make(map[string][]response.DiskBasicInfo)
+
+ for _, info := range diskInfos {
+ isPartition := isPartitionDevice(info.Device)
+
+ if isPartition {
+ parentDevice := getParentDevice(info.Device)
+ partitions[parentDevice] = append(partitions[parentDevice], info)
+ } else {
+ disk := &response.DiskInfo{
+ DiskBasicInfo: info,
+ Partitions: []response.DiskBasicInfo{},
+ }
+ diskMap[info.Device] = disk
+ }
+ }
+
+ for parentDevice, partList := range partitions {
+ if disk, exists := diskMap[parentDevice]; exists {
+ for index, part := range partList {
+ part.Device = fmt.Sprintf("/dev/%s", part.Device)
+ if part.IsSystem {
+ disk.IsSystem = true
+ }
+ partList[index] = part
+ }
+ disk.Partitions = partList
+ }
+ }
+
+ var totalCapacity int64
+ for _, disk := range diskMap {
+ capacity := parseSizeToBytes(disk.Size)
+ totalCapacity += capacity
+
+ if disk.IsSystem {
+ systemDisk = disk
+ } else if len(disk.Partitions) == 0 {
+ result.UnpartitionedDisks = append(result.UnpartitionedDisks, disk.DiskBasicInfo)
+ } else {
+ result.Disks = append(result.Disks, *disk)
+ }
+ }
+
+ result.SystemDisk = systemDisk
+ result.TotalDisks = len(diskMap)
+ result.TotalCapacity = totalCapacity
+
+ return result
+}
+
+func parseLsblkOutput(output string) ([]response.DiskBasicInfo, error) {
+ lines := strings.Split(strings.TrimSpace(output), "\n")
+ if len(lines) == 0 {
+ return nil, fmt.Errorf("invalid lsblk output")
+ }
+
+ var diskInfos []response.DiskBasicInfo
+ lvmMap := make(map[string]response.DiskBasicInfo)
+ var pendingDevices []map[string]string
+
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+
+ fields := parseKeyValuePairs(line)
+
+ name, ok := fields["NAME"]
+ if !ok {
+ continue
+ }
+
+ if strings.HasPrefix(name, "loop") ||
+ strings.HasPrefix(name, "dm-") {
+ continue
+ }
+
+ diskType := fields["TYPE"]
+ mountPoint := fields["MOUNTPOINT"]
+ fsType := fields["FSTYPE"]
+ size := fields["SIZE"]
+
+ if diskType == "lvm" {
+ total, used, avail, usePercent, _ := getDiskUsageInfo("/dev/mapper/" + name)
+ if total != "" {
+ size = total
+ }
+
+ lvmInfo := response.DiskBasicInfo{
+ Device: name,
+ Size: size,
+ Model: fields["MODEL"],
+ DiskType: "LVM",
+ IsRemovable: false,
+ IsSystem: isSystemDisk(mountPoint),
+ Filesystem: fsType,
+ Used: used,
+ Avail: avail,
+ UsePercent: usePercent,
+ MountPoint: mountPoint,
+ IsMounted: mountPoint != "" && mountPoint != "-",
+ Serial: fields["SERIAL"],
+ }
+ lvmMap[name] = lvmInfo
+ } else if diskType == "disk" || diskType == "part" {
+ pendingDevices = append(pendingDevices, fields)
+ }
+ }
+
+ for _, fields := range pendingDevices {
+ name := fields["NAME"]
+ size := fields["SIZE"]
+ mountPoint := fields["MOUNTPOINT"]
+ fsType := fields["FSTYPE"]
+ model := fields["MODEL"]
+ serial := fields["SERIAL"]
+ tran := fields["TRAN"]
+ rota := fields["ROTA"]
+
+ totalSize, used, avail, usePercent, _ := getDiskUsageInfo("/dev/" + name)
+ if totalSize != "" {
+ size = totalSize
+ }
+ actualMountPoint := mountPoint
+ actualFsType := fsType
+ actualUsed := used
+ actualAvail := avail
+ actualUsePercent := usePercent
+ isMounted := mountPoint != "" && mountPoint != "-"
+ isSystemPartition := isSystemDisk(mountPoint)
+
+ if fsType == "LVM2_member" {
+ for _, lvmInfo := range lvmMap {
+ if lvmInfo.IsMounted {
+ actualMountPoint = lvmInfo.MountPoint
+ actualFsType = lvmInfo.Filesystem
+ actualUsed = lvmInfo.Used
+ size = lvmInfo.Size
+ actualAvail = lvmInfo.Avail
+ actualUsePercent = lvmInfo.UsePercent
+ isMounted = true
+ isSystemPartition = lvmInfo.IsSystem
+ break
+ }
+ }
+ }
+
+ diskInfo := response.DiskBasicInfo{
+ Device: name,
+ Size: size,
+ Model: model,
+ DiskType: getDiskType(rota),
+ IsRemovable: tran == "usb",
+ IsSystem: isSystemPartition,
+ Filesystem: actualFsType,
+ Used: actualUsed,
+ Avail: actualAvail,
+ UsePercent: actualUsePercent,
+ MountPoint: actualMountPoint,
+ IsMounted: isMounted,
+ Serial: serial,
+ }
+
+ diskInfos = append(diskInfos, diskInfo)
+ }
+
+ return diskInfos, nil
+}
+
+func getDiskType(rota string) string {
+ if rota == "0" {
+ return "SSD"
+ } else if rota == "1" {
+ return "HDD"
+ }
+ return "Unknown"
+}
+
+var kvRe = regexp.MustCompile(`([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`)
+
+func parseKeyValuePairs(line string) map[string]string {
+ fields := make(map[string]string)
+
+ matches := kvRe.FindAllStringSubmatch(line, -1)
+ for _, m := range matches {
+ key := m[1]
+ raw := m[2]
+
+ val := raw
+ if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
+ if unq, err := strconv.Unquote(val); err == nil {
+ val = unq
+ } else {
+ val = val[1 : len(val)-1]
+ }
+ }
+ fields[key] = val
+ }
+ return fields
+}
+
+func isPartitionDevice(device string) bool {
+ if strings.Contains(device, "nvme") {
+ return strings.Contains(device, "p") &&
+ strings.ContainsAny(device[strings.LastIndex(device, "p")+1:], "0123456789")
+ } else if strings.HasPrefix(device, "sd") || strings.HasPrefix(device, "hd") {
+ return len(device) > 3 &&
+ strings.ContainsAny(device[len(device)-1:], "0123456789")
+ } else if strings.HasPrefix(device, "vd") {
+ return len(device) > 3 &&
+ strings.ContainsAny(device[len(device)-1:], "0123456789")
+ }
+
+ return false
+}
+
+func getParentDevice(device string) string {
+ if strings.Contains(device, "nvme") {
+ if idx := strings.LastIndex(device, "p"); idx != -1 {
+ return device[:idx]
+ }
+ } else {
+ return strings.TrimRight(device, "0123456789")
+ }
+
+ return device
+}
+
+func getDiskUsageInfo(device string) (size, used, avail string, usePercent int, err error) {
+ output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("df -h %s | tail -1", device))
+ if err != nil {
+ return "", "", "", 0, nil
+ }
+
+ fields := strings.Fields(output)
+ if len(fields) >= 5 {
+ size = fields[1]
+ used = fields[2]
+ avail = fields[3]
+ usePercentStr := strings.TrimSuffix(fields[4], "%")
+ usePercent, _ = strconv.Atoi(usePercentStr)
+ }
+
+ return size, used, avail, usePercent, nil
+}
+
+func formatDisk(req dto.DiskFormatRequest) error {
+ var mkfsCmd *exec.Cmd
+
+ switch req.Filesystem {
+ case "ext4":
+ mkfsCmd = exec.Command("mkfs.ext4", "-F", req.Device)
+ case "xfs":
+ if !cmd.Which("mkfs.xfs") {
+ return buserr.New("XfsNotFound")
+ }
+ mkfsCmd = exec.Command("mkfs.xfs", "-f", req.Device)
+ default:
+ return fmt.Errorf("unsupport type: %s", req.Filesystem)
+ }
+ if err := mkfsCmd.Run(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func deviceExists(device string) bool {
+ _, err := os.Stat(device)
+ return err == nil
+}
+
+func isDeviceMounted(device string) bool {
+ file, err := os.Open("/proc/mounts")
+ if err != nil {
+ return false
+ }
+ defer file.Close()
+
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+ if len(fields) >= 2 && fields[0] == device {
+ return true
+ }
+ }
+ return false
+}
+
+func isPointMounted(mountPoint string) bool {
+ file, err := os.Open("/proc/mounts")
+ if err != nil {
+ return false
+ }
+ defer file.Close()
+
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+ if len(fields) >= 2 && fields[1] == mountPoint {
+ return true
+ }
+ }
+ return false
+}
+
+func addToFstabWithOptions(device, mountPoint, filesystem, options string) error {
+ if filesystem == "" {
+ fsType, err := getFilesystemType(device)
+ if err != nil {
+ filesystem = "auto"
+ } else {
+ filesystem = fsType
+ }
+ }
+
+ if options == "" {
+ options = "defaults"
+ }
+
+ entry := fmt.Sprintf("%s %s %s %s 0 2\n", device, mountPoint, filesystem, options)
+
+ file, err := os.OpenFile("/etc/fstab", os.O_APPEND|os.O_WRONLY, 0644)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ _, err = file.WriteString(entry)
+ return err
+}
+
+func removeFromFstab(mountPoint string) error {
+ file, err := os.Open("/etc/fstab")
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ var lines []string
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+ fields := strings.Fields(line)
+ if len(fields) >= 2 && fields[1] == mountPoint {
+ continue
+ }
+ lines = append(lines, line)
+ }
+
+ return os.WriteFile("/etc/fstab", []byte(strings.Join(lines, "\n")+"\n"), 0644)
+}
+
+func getFilesystemType(device string) (string, error) {
+ output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("blkid -o value -s TYPE %s", device))
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(output), nil
+}
+
+func isSystemDisk(mountPoint string) bool {
+ systemMountPoints := []string{
+ "/",
+ "/boot",
+ "/boot/efi",
+ "/usr",
+ "/var",
+ "/home",
+ }
+
+ for _, sysMount := range systemMountPoints {
+ if mountPoint == sysMount {
+ return true
+ }
+ }
+
+ return false
+}
+
+func parseSizeToBytes(sizeStr string) int64 {
+ if sizeStr == "" || sizeStr == "-" {
+ return 0
+ }
+
+ sizeStr = strings.TrimSpace(sizeStr)
+ if len(sizeStr) < 2 {
+ return 0
+ }
+
+ unit := strings.ToUpper(sizeStr[len(sizeStr)-1:])
+ valueStr := sizeStr[:len(sizeStr)-1]
+
+ value, err := strconv.ParseFloat(valueStr, 64)
+ if err != nil {
+ return 0
+ }
+
+ switch unit {
+ case "K":
+ return int64(value * 1024)
+ case "M":
+ return int64(value * 1024 * 1024)
+ case "G":
+ return int64(value * 1024 * 1024 * 1024)
+ case "T":
+ return int64(value * 1024 * 1024 * 1024 * 1024)
+ default:
+ val, _ := strconv.ParseInt(sizeStr, 10, 64)
+ return val
+ }
+}
diff --git a/core/constant/common.go b/core/constant/common.go
index 9c1862071..723faf25e 100644
--- a/core/constant/common.go
+++ b/core/constant/common.go
@@ -98,6 +98,7 @@ var WebUrlMap = map[string]struct{}{
"/hosts/ssh/ssh": {},
"/hosts/ssh/log": {},
"/hosts/ssh/session": {},
+ "/hosts/disk": {},
"/terminal": {},
diff --git a/frontend/src/views/host/disk-management/components/disk-card.vue b/frontend/src/views/host/disk-management/components/disk-card.vue
index b4fb95533..c063c3de7 100644
--- a/frontend/src/views/host/disk-management/components/disk-card.vue
+++ b/frontend/src/views/host/disk-management/components/disk-card.vue
@@ -30,7 +30,7 @@
{{ diskInfo.model }}