Skip to content

Commit

Permalink
feat: 工具箱增加 Swap 管理 (1Panel-dev#3047)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssongliu authored Nov 27, 2023
1 parent 75773d2 commit 0d27469
Show file tree
Hide file tree
Showing 19 changed files with 762 additions and 40 deletions.
26 changes: 24 additions & 2 deletions backend/app/api/v1/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (b *BaseApi) LoadDeviceConf(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/byconf [post]
func (b *BaseApi) UpdateDevicByFile(c *gin.Context) {
func (b *BaseApi) UpdateDeviceByFile(c *gin.Context) {
var req dto.UpdateByNameAndFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
Expand Down Expand Up @@ -138,7 +138,7 @@ func (b *BaseApi) UpdateDeviceHost(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/passwd [post]
func (b *BaseApi) UpdateDevicPasswd(c *gin.Context) {
func (b *BaseApi) UpdateDevicePasswd(c *gin.Context) {
var req dto.ChangePasswd
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
Expand All @@ -159,6 +159,28 @@ func (b *BaseApi) UpdateDevicPasswd(c *gin.Context) {
helper.SuccessWithData(c, nil)
}

// @Tags Device
// @Summary Update device swap
// @Description 修改系统 Swap
// @Accept json
// @Param request body dto.SwapHelper true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/swap [post]
// @x-panel-log {"bodyKeys":["operate","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机 swap [path]","formatEN":"[operate] device swap [path]"}
func (b *BaseApi) UpdateDeviceSwap(c *gin.Context) {
var req dto.SwapHelper
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.UpdateSwap(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}

helper.SuccessWithData(c, nil)
}

// @Tags Device
// @Summary Check device DNS conf
// @Description 检查系统 DNS 配置可用性
Expand Down
14 changes: 14 additions & 0 deletions backend/app/dto/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,27 @@ type DeviceBaseInfo struct {
LocalTime string `json:"localTime"`
Ntp string `json:"ntp"`
User string `json:"user"`

SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`

SwapDetails []SwapHelper `json:"swapDetails"`
}

type HostHelper struct {
IP string `json:"ip"`
Host string `json:"host"`
}

type SwapHelper struct {
Path string `json:"path" validate:"required"`
Size uint64 `json:"size"`
Used string `json:"used"`

IsNew bool `json:"isNew"`
}

type TimeZoneOptions struct {
From string `json:"from"`
Zones []string `json:"zones"`
Expand Down
111 changes: 107 additions & 4 deletions backend/app/service/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"net"
"os"
"path"
"strconv"
"strings"
"time"

Expand All @@ -14,10 +16,12 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/shirou/gopsutil/v3/mem"
)

const defaultDNSPath = "/etc/resolv.conf"
const defaultHostPath = "/etc/hosts"
const defaultFstab = "/etc/fstab"

type DeviceService struct{}

Expand All @@ -26,6 +30,7 @@ type IDeviceService interface {
Update(key, value string) error
UpdateHosts(req []dto.HostHelper) error
UpdatePasswd(req dto.ChangePasswd) error
UpdateSwap(req dto.SwapHelper) error
UpdateByConf(req dto.UpdateByNameAndFile) error
LoadTimeZone() ([]string, error)
CheckDNS(key, value string) (bool, error)
Expand All @@ -47,6 +52,14 @@ func (u *DeviceService) LoadBaseInfo() (dto.DeviceBaseInfo, error) {
ntp, _ := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
baseInfo.Ntp = ntp.Value

swapInfo, _ := mem.SwapMemory()
baseInfo.SwapMemoryTotal = swapInfo.Total
baseInfo.SwapMemoryAvailable = swapInfo.Free
baseInfo.SwapMemoryUsed = swapInfo.Used
if baseInfo.SwapMemoryTotal != 0 {
baseInfo.SwapDetails = loadSwap()
}

return baseInfo, nil
}

Expand Down Expand Up @@ -104,11 +117,20 @@ func (u *DeviceService) Update(key, value string) error {
if err != nil {
return errors.New(std)
}
case "LocalTime":
if err := settingRepo.Update("NtpSite", value); err != nil {
return err
case "Ntp", "LocalTime":
ntpValue := value
if key == "LocalTime" {
ntpItem, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err != nil {
return err
}
ntpValue = ntpItem.Value
} else {
if err := settingRepo.Update("NtpSite", ntpValue); err != nil {
return err
}
}
ntime, err := ntp.GetRemoteTime(value)
ntime, err := ntp.GetRemoteTime(ntpValue)
if err != nil {
return err
}
Expand Down Expand Up @@ -168,6 +190,35 @@ func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
return nil
}

func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
if !req.IsNew {
std, err := cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
}
}
if req.Size == 0 {
if req.Path == path.Join(global.CONF.System.BaseDir, ".1panel_swap") {
_ = os.Remove(path.Join(global.CONF.System.BaseDir, ".1panel_swap"))
}
return operateSwapWithFile(true, req)
}
std1, err := cmd.Execf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
}
std2, err := cmd.Execf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
}
std3, err := cmd.Execf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
_, _ = cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
}
return operateSwapWithFile(false, req)
}

func (u *DeviceService) LoadConf(name string) (string, error) {
pathItem := ""
switch name {
Expand Down Expand Up @@ -291,3 +342,55 @@ func loadUser() string {
}
return strings.ReplaceAll(std, "\n", "")
}

func loadSwap() []dto.SwapHelper {
var data []dto.SwapHelper
std, err := cmd.Execf("%s swapon --show --summary", cmd.SudoHandleCmd())
if err != nil {
return data
}
lines := strings.Split(std, "\n")
for index, line := range lines {
if index == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
sizeItem, _ := strconv.Atoi(parts[2])
data = append(data, dto.SwapHelper{Path: parts[0], Size: uint64(sizeItem), Used: parts[3]})
}
return data
}

func operateSwapWithFile(delete bool, req dto.SwapHelper) error {
conf, err := os.ReadFile(defaultFstab)
if err != nil {
return fmt.Errorf("read file %s failed, err: %v", defaultFstab, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) == 6 && parts[0] == req.Path {
continue
}
newFile += line + "\n"
}
if !delete {
newFile += fmt.Sprintf("%s swap swap defaults 0 0\n", req.Path)
}
file, err := os.OpenFile(defaultFstab, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
5 changes: 3 additions & 2 deletions backend/router/ro_toolbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ func (s *ToolboxRouter) InitToolboxRouter(Router *gin.RouterGroup) {
toolboxRouter.GET("/device/zone/options", baseApi.LoadTimeOption)
toolboxRouter.POST("/device/update/conf", baseApi.UpdateDeviceConf)
toolboxRouter.POST("/device/update/host", baseApi.UpdateDeviceHost)
toolboxRouter.POST("/device/update/passwd", baseApi.UpdateDevicPasswd)
toolboxRouter.POST("/device/update/byconf", baseApi.UpdateDevicByFile)
toolboxRouter.POST("/device/update/passwd", baseApi.UpdateDevicePasswd)
toolboxRouter.POST("/device/update/swap", baseApi.UpdateDeviceSwap)
toolboxRouter.POST("/device/update/byconf", baseApi.UpdateDeviceByFile)
toolboxRouter.POST("/device/check/dns", baseApi.CheckDNS)
toolboxRouter.POST("/device/conf", baseApi.LoadDeviceConf)

Expand Down
87 changes: 87 additions & 0 deletions cmd/server/docs/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0d27469

Please sign in to comment.