Skip to content

Commit b559232

Browse files
authored
feat: 工具箱增加 Swap 管理 (1Panel-dev#3047)
1 parent 0388798 commit b559232

File tree

19 files changed

+762
-40
lines changed

19 files changed

+762
-40
lines changed

backend/app/api/v1/device.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (b *BaseApi) LoadDeviceConf(c *gin.Context) {
7373
// @Success 200
7474
// @Security ApiKeyAuth
7575
// @Router /toolbox/device/update/byconf [post]
76-
func (b *BaseApi) UpdateDevicByFile(c *gin.Context) {
76+
func (b *BaseApi) UpdateDeviceByFile(c *gin.Context) {
7777
var req dto.UpdateByNameAndFile
7878
if err := helper.CheckBindAndValidate(&req, c); err != nil {
7979
return
@@ -138,7 +138,7 @@ func (b *BaseApi) UpdateDeviceHost(c *gin.Context) {
138138
// @Success 200
139139
// @Security ApiKeyAuth
140140
// @Router /toolbox/device/update/passwd [post]
141-
func (b *BaseApi) UpdateDevicPasswd(c *gin.Context) {
141+
func (b *BaseApi) UpdateDevicePasswd(c *gin.Context) {
142142
var req dto.ChangePasswd
143143
if err := helper.CheckBindAndValidate(&req, c); err != nil {
144144
return
@@ -159,6 +159,28 @@ func (b *BaseApi) UpdateDevicPasswd(c *gin.Context) {
159159
helper.SuccessWithData(c, nil)
160160
}
161161

162+
// @Tags Device
163+
// @Summary Update device swap
164+
// @Description 修改系统 Swap
165+
// @Accept json
166+
// @Param request body dto.SwapHelper true "request"
167+
// @Success 200
168+
// @Security ApiKeyAuth
169+
// @Router /toolbox/device/update/swap [post]
170+
// @x-panel-log {"bodyKeys":["operate","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机 swap [path]","formatEN":"[operate] device swap [path]"}
171+
func (b *BaseApi) UpdateDeviceSwap(c *gin.Context) {
172+
var req dto.SwapHelper
173+
if err := helper.CheckBindAndValidate(&req, c); err != nil {
174+
return
175+
}
176+
if err := deviceService.UpdateSwap(req); err != nil {
177+
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
178+
return
179+
}
180+
181+
helper.SuccessWithData(c, nil)
182+
}
183+
162184
// @Tags Device
163185
// @Summary Check device DNS conf
164186
// @Description 检查系统 DNS 配置可用性

backend/app/dto/device.go

+14
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,27 @@ type DeviceBaseInfo struct {
88
LocalTime string `json:"localTime"`
99
Ntp string `json:"ntp"`
1010
User string `json:"user"`
11+
12+
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
13+
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
14+
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
15+
16+
SwapDetails []SwapHelper `json:"swapDetails"`
1117
}
1218

1319
type HostHelper struct {
1420
IP string `json:"ip"`
1521
Host string `json:"host"`
1622
}
1723

24+
type SwapHelper struct {
25+
Path string `json:"path" validate:"required"`
26+
Size uint64 `json:"size"`
27+
Used string `json:"used"`
28+
29+
IsNew bool `json:"isNew"`
30+
}
31+
1832
type TimeZoneOptions struct {
1933
From string `json:"from"`
2034
Zones []string `json:"zones"`

backend/app/service/device.go

+107-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"fmt"
77
"net"
88
"os"
9+
"path"
10+
"strconv"
911
"strings"
1012
"time"
1113

@@ -14,10 +16,12 @@ import (
1416
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
1517
"github.com/1Panel-dev/1Panel/backend/utils/common"
1618
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
19+
"github.com/shirou/gopsutil/v3/mem"
1720
)
1821

1922
const defaultDNSPath = "/etc/resolv.conf"
2023
const defaultHostPath = "/etc/hosts"
24+
const defaultFstab = "/etc/fstab"
2125

2226
type DeviceService struct{}
2327

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

55+
swapInfo, _ := mem.SwapMemory()
56+
baseInfo.SwapMemoryTotal = swapInfo.Total
57+
baseInfo.SwapMemoryAvailable = swapInfo.Free
58+
baseInfo.SwapMemoryUsed = swapInfo.Used
59+
if baseInfo.SwapMemoryTotal != 0 {
60+
baseInfo.SwapDetails = loadSwap()
61+
}
62+
5063
return baseInfo, nil
5164
}
5265

@@ -104,11 +117,20 @@ func (u *DeviceService) Update(key, value string) error {
104117
if err != nil {
105118
return errors.New(std)
106119
}
107-
case "LocalTime":
108-
if err := settingRepo.Update("NtpSite", value); err != nil {
109-
return err
120+
case "Ntp", "LocalTime":
121+
ntpValue := value
122+
if key == "LocalTime" {
123+
ntpItem, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
124+
if err != nil {
125+
return err
126+
}
127+
ntpValue = ntpItem.Value
128+
} else {
129+
if err := settingRepo.Update("NtpSite", ntpValue); err != nil {
130+
return err
131+
}
110132
}
111-
ntime, err := ntp.GetRemoteTime(value)
133+
ntime, err := ntp.GetRemoteTime(ntpValue)
112134
if err != nil {
113135
return err
114136
}
@@ -168,6 +190,35 @@ func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
168190
return nil
169191
}
170192

193+
func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
194+
if !req.IsNew {
195+
std, err := cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
196+
if err != nil {
197+
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
198+
}
199+
}
200+
if req.Size == 0 {
201+
if req.Path == path.Join(global.CONF.System.BaseDir, ".1panel_swap") {
202+
_ = os.Remove(path.Join(global.CONF.System.BaseDir, ".1panel_swap"))
203+
}
204+
return operateSwapWithFile(true, req)
205+
}
206+
std1, err := cmd.Execf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
207+
if err != nil {
208+
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
209+
}
210+
std2, err := cmd.Execf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
211+
if err != nil {
212+
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
213+
}
214+
std3, err := cmd.Execf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
215+
if err != nil {
216+
_, _ = cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
217+
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
218+
}
219+
return operateSwapWithFile(false, req)
220+
}
221+
171222
func (u *DeviceService) LoadConf(name string) (string, error) {
172223
pathItem := ""
173224
switch name {
@@ -291,3 +342,55 @@ func loadUser() string {
291342
}
292343
return strings.ReplaceAll(std, "\n", "")
293344
}
345+
346+
func loadSwap() []dto.SwapHelper {
347+
var data []dto.SwapHelper
348+
std, err := cmd.Execf("%s swapon --show --summary", cmd.SudoHandleCmd())
349+
if err != nil {
350+
return data
351+
}
352+
lines := strings.Split(std, "\n")
353+
for index, line := range lines {
354+
if index == 0 {
355+
continue
356+
}
357+
parts := strings.Fields(line)
358+
if len(parts) < 5 {
359+
continue
360+
}
361+
sizeItem, _ := strconv.Atoi(parts[2])
362+
data = append(data, dto.SwapHelper{Path: parts[0], Size: uint64(sizeItem), Used: parts[3]})
363+
}
364+
return data
365+
}
366+
367+
func operateSwapWithFile(delete bool, req dto.SwapHelper) error {
368+
conf, err := os.ReadFile(defaultFstab)
369+
if err != nil {
370+
return fmt.Errorf("read file %s failed, err: %v", defaultFstab, err)
371+
}
372+
lines := strings.Split(string(conf), "\n")
373+
newFile := ""
374+
for _, line := range lines {
375+
if len(line) == 0 {
376+
continue
377+
}
378+
parts := strings.Fields(line)
379+
if len(parts) == 6 && parts[0] == req.Path {
380+
continue
381+
}
382+
newFile += line + "\n"
383+
}
384+
if !delete {
385+
newFile += fmt.Sprintf("%s swap swap defaults 0 0\n", req.Path)
386+
}
387+
file, err := os.OpenFile(defaultFstab, os.O_WRONLY|os.O_TRUNC, 0640)
388+
if err != nil {
389+
return err
390+
}
391+
defer file.Close()
392+
write := bufio.NewWriter(file)
393+
_, _ = write.WriteString(newFile)
394+
write.Flush()
395+
return nil
396+
}

backend/router/ro_toolbox.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ func (s *ToolboxRouter) InitToolboxRouter(Router *gin.RouterGroup) {
2020
toolboxRouter.GET("/device/zone/options", baseApi.LoadTimeOption)
2121
toolboxRouter.POST("/device/update/conf", baseApi.UpdateDeviceConf)
2222
toolboxRouter.POST("/device/update/host", baseApi.UpdateDeviceHost)
23-
toolboxRouter.POST("/device/update/passwd", baseApi.UpdateDevicPasswd)
24-
toolboxRouter.POST("/device/update/byconf", baseApi.UpdateDevicByFile)
23+
toolboxRouter.POST("/device/update/passwd", baseApi.UpdateDevicePasswd)
24+
toolboxRouter.POST("/device/update/swap", baseApi.UpdateDeviceSwap)
25+
toolboxRouter.POST("/device/update/byconf", baseApi.UpdateDeviceByFile)
2526
toolboxRouter.POST("/device/check/dns", baseApi.CheckDNS)
2627
toolboxRouter.POST("/device/conf", baseApi.LoadDeviceConf)
2728

cmd/server/docs/docs.go

+87
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)