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 }} - {{ $t('disk.model') }}: + {{ $t('disk.serial') }}: {{ diskInfo.serial }}
@@ -118,10 +118,14 @@ const mount = (diskInfo: Host.DiskInfo) => { }; const unmount = (diskInfo: Host.DiskInfo) => { - ElMessageBox.confirm(i18n.global.t('disk.unmountHelper', [diskInfo.device]), i18n.global.t('disk.unmount'), { - confirmButtonText: i18n.global.t('commons.button.confirm'), - cancelButtonText: i18n.global.t('commons.button.cancel'), - }).then(async () => { + ElMessageBox.confirm( + i18n.global.t('disk.unmountHelper', [diskInfo.device.split('/').pop()]), + i18n.global.t('disk.unmount'), + { + confirmButtonText: i18n.global.t('commons.button.confirm'), + cancelButtonText: i18n.global.t('commons.button.cancel'), + }, + ).then(async () => { unmountDisk({ mountPoint: diskInfo.mountPoint, }).then(() => { diff --git a/frontend/src/views/host/disk-management/partition/index.vue b/frontend/src/views/host/disk-management/partition/index.vue index 288430320..bccbe5274 100644 --- a/frontend/src/views/host/disk-management/partition/index.vue +++ b/frontend/src/views/host/disk-management/partition/index.vue @@ -2,7 +2,7 @@ @@ -109,6 +109,7 @@ const submit = async () => { const handleClose = () => { open.value = false; + loading.value = false; emit('search'); };