Skip to content

Commit a76dd6e

Browse files
committed
Add experimental memory command for balloons
Make it possible to set target memory size, for an instance. This way it should be possible to return memory to the host. Signed-off-by: Anders F Björklund <anders.f.bjorklund@gmail.com>
1 parent 9e7171a commit a76dd6e

File tree

14 files changed

+232
-0
lines changed

14 files changed

+232
-0
lines changed

cmd/limactl/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func newApp() *cobra.Command {
203203
newNetworkCommand(),
204204
newCloneCommand(),
205205
newRenameCommand(),
206+
newMemoryCommand(),
206207
)
207208
addPluginCommands(rootCmd)
208209

cmd/limactl/memory.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"fmt"
8+
"strconv"
9+
10+
"github.com/sirupsen/logrus"
11+
"github.com/spf13/cobra"
12+
13+
"github.com/lima-vm/lima/v2/pkg/memory"
14+
"github.com/lima-vm/lima/v2/pkg/store"
15+
)
16+
17+
func newMemoryCommand() *cobra.Command {
18+
memoryCmd := &cobra.Command{
19+
Use: "memory",
20+
Short: "Manage instance memory",
21+
PersistentPreRun: func(*cobra.Command, []string) {
22+
logrus.Warn("`limactl memory` is experimental")
23+
},
24+
GroupID: advancedCommand,
25+
}
26+
memoryCmd.AddCommand(newMemoryGetCommand())
27+
memoryCmd.AddCommand(newMemorySetCommand())
28+
29+
return memoryCmd
30+
}
31+
32+
func newMemoryGetCommand() *cobra.Command {
33+
getCmd := &cobra.Command{
34+
Use: "get INSTANCE",
35+
Short: "Get current memory",
36+
Long: "Get the currently used total memory of an instance, in MiB",
37+
Args: cobra.MinimumNArgs(1),
38+
RunE: memoryGetAction,
39+
ValidArgsFunction: memoryBashComplete,
40+
}
41+
42+
return getCmd
43+
}
44+
45+
func memoryGetAction(cmd *cobra.Command, args []string) error {
46+
ctx := cmd.Context()
47+
instName := args[0]
48+
49+
inst, err := store.Inspect(ctx, instName)
50+
if err != nil {
51+
return err
52+
}
53+
54+
_ = inst
55+
mem := 0 // TODO: implement
56+
fmt.Fprintf(cmd.OutOrStdout(), "%d\n", mem>>20)
57+
return nil
58+
}
59+
60+
func newMemorySetCommand() *cobra.Command {
61+
setCmd := &cobra.Command{
62+
Use: "set INSTANCE memory AMOUNT",
63+
Short: "Set target memory",
64+
Long: "Set the target total memory of an instance, in MiB",
65+
Args: cobra.MinimumNArgs(2),
66+
RunE: memorySetAction,
67+
ValidArgsFunction: memoryBashComplete,
68+
}
69+
70+
return setCmd
71+
}
72+
73+
func memorySetAction(cmd *cobra.Command, args []string) error {
74+
ctx := cmd.Context()
75+
instName := args[0]
76+
meg, err := strconv.Atoi(args[1])
77+
if err != nil {
78+
return err
79+
}
80+
81+
inst, err := store.Inspect(ctx, instName)
82+
if err != nil {
83+
return err
84+
}
85+
86+
return memory.SetTarget(ctx, inst, int64(meg)<<20)
87+
}
88+
89+
func memoryBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
90+
return bashCompleteInstanceNames(cmd)
91+
}

pkg/driver/driver.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ type Driver interface {
8888
FillConfig(ctx context.Context, cfg *limatype.LimaYAML, filePath string) error
8989

9090
SSHAddress(ctx context.Context) (string, error)
91+
92+
SetTargetMemory(memory int64) error
9193
}
9294

9395
type ConfiguredDriver struct {

pkg/driver/external/client/methods.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,7 @@ func (d *DriverClient) BootScripts() (map[string][]byte, error) {
323323
d.logger.Debugf("Boot scripts retrieved successfully: %d scripts", len(resp.Scripts))
324324
return resp.Scripts, nil
325325
}
326+
327+
func (d *DriverClient) SetTargetMemory(_ int64) error {
328+
return errors.New("unavailable")
329+
}

pkg/driver/qemu/errors.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package qemu
5+
6+
import "errors"
7+
8+
var errUnimplemented = errors.New("unimplemented")

pkg/driver/qemu/qemu_driver.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,3 +720,7 @@ func (l *LimaQemuDriver) ForwardGuestAgent() bool {
720720
// if driver is not providing, use host agent
721721
return l.vSockPort == 0 && l.virtioPort == ""
722722
}
723+
724+
func (l *LimaQemuDriver) SetTargetMemory(_ int64) error {
725+
return errUnimplemented
726+
}

pkg/driver/vz/vz_driver_darwin.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,19 @@ func (l *LimaVzDriver) ForwardGuestAgent() bool {
437437
// If driver is not providing, use host agent
438438
return l.vSockPort == 0 && l.virtioPort == ""
439439
}
440+
441+
func (l *LimaVzDriver) SetTargetMemory(memory int64) error {
442+
if l.machine == nil {
443+
return errors.New("no machine")
444+
}
445+
balloons := l.machine.MemoryBalloonDevices()
446+
if len(balloons) != 1 {
447+
return fmt.Errorf("unexpected number of devices: %d", len(balloons))
448+
}
449+
balloon := vz.AsVirtioTraditionalMemoryBalloonDevice(balloons[0])
450+
if balloon == nil {
451+
return errors.New("unexpected type of balloon")
452+
}
453+
balloon.SetTargetVirtualMachineMemorySize(uint64(memory))
454+
return nil
455+
}

pkg/driver/wsl2/wsl_driver_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,7 @@ func (l *LimaWslDriver) ForwardGuestAgent() bool {
357357
// If driver is not providing, use host agent
358358
return l.vSockPort == 0 && l.virtioPort == ""
359359
}
360+
361+
func (l *LimaWslDriver) SetTargetMemory(memory int64) error {
362+
return errUnimplemented
363+
}

pkg/hostagent/api/client/client.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"encoding/json"
1212
"fmt"
1313
"net/http"
14+
"strconv"
15+
"strings"
1416

1517
"github.com/lima-vm/lima/v2/pkg/hostagent/api"
1618
"github.com/lima-vm/lima/v2/pkg/httpclientutil"
@@ -19,6 +21,7 @@ import (
1921
type HostAgentClient interface {
2022
HTTPClient() *http.Client
2123
Info(context.Context) (*api.Info, error)
24+
SetTargetMemory(context.Context, int64) error
2225
}
2326

2427
// NewHostAgentClient creates a client.
@@ -65,3 +68,15 @@ func (c *client) Info(ctx context.Context) (*api.Info, error) {
6568
}
6669
return &info, nil
6770
}
71+
72+
func (c *client) SetTargetMemory(ctx context.Context, memory int64) error {
73+
u := fmt.Sprintf("http://%s/%s/memory", c.dummyHost, c.version)
74+
body := strconv.FormatInt(memory, 10)
75+
b := strings.NewReader(body)
76+
resp, err := httpclientutil.Put(ctx, c.HTTPClient(), u, b)
77+
if err != nil {
78+
return err
79+
}
80+
defer resp.Body.Close()
81+
return nil
82+
}

pkg/hostagent/api/server/server.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package server
66
import (
77
"context"
88
"encoding/json"
9+
"io"
910
"net/http"
11+
"strconv"
1012

1113
"github.com/lima-vm/lima/v2/pkg/hostagent"
1214
"github.com/lima-vm/lima/v2/pkg/httputil"
@@ -53,6 +55,33 @@ func (b *Backend) GetInfo(w http.ResponseWriter, r *http.Request) {
5355
_, _ = w.Write(m)
5456
}
5557

58+
// SetMemory is the handler for PUT /v1/memory.
59+
func (b *Backend) SetMemory(w http.ResponseWriter, r *http.Request) {
60+
if r.Method != http.MethodPut {
61+
w.WriteHeader(http.StatusMethodNotAllowed)
62+
return
63+
}
64+
65+
body, err := io.ReadAll(r.Body)
66+
if err != nil {
67+
b.onError(w, err, http.StatusInternalServerError)
68+
return
69+
}
70+
memory, err := strconv.ParseInt(string(body), 10, 64)
71+
if err != nil {
72+
b.onError(w, err, http.StatusInternalServerError)
73+
return
74+
}
75+
76+
err = b.Agent.SetTargetMemory(memory)
77+
if err != nil {
78+
b.onError(w, err, http.StatusInternalServerError)
79+
return
80+
}
81+
w.WriteHeader(http.StatusOK)
82+
}
83+
5684
func AddRoutes(r *http.ServeMux, b *Backend) {
5785
r.Handle("/v1/info", http.HandlerFunc(b.GetInfo))
86+
r.Handle("/v1/memory", http.HandlerFunc(b.SetMemory))
5887
}

0 commit comments

Comments
 (0)