diff --git a/qemu/qemu.go b/qemu/qemu.go index 5fbfb81a57..a518da52c4 100644 --- a/qemu/qemu.go +++ b/qemu/qemu.go @@ -88,6 +88,9 @@ const ( // VirtioRng is the paravirtualized RNG device driver. VirtioRng DeviceDriver = "virtio-rng" + + // VirtioBalloon is the memory balloon device driver. + VirtioBalloon DeviceDriver = "virtio-balloon" ) // ObjectType is a string representing a qemu object type. @@ -1078,6 +1081,51 @@ func (v RngDevice) QemuParams(_ *Config) []string { return qemuParams } +// BalloonDevice represents a memory balloon device. +type BalloonDevice struct { + DeflateOnOOM bool + DisableModern bool + ID string +} + +// QemuParams returns the qemu parameters built out of the BalloonDevice. +func (b BalloonDevice) QemuParams(_ *Config) []string { + var qemuParams []string + var deviceParams []string + + deviceParams = append(deviceParams, string(VirtioBalloon)) + + if b.ID != "" { + deviceParams = append(deviceParams, "id="+b.ID) + } + + if b.DeflateOnOOM { + deviceParams = append(deviceParams, "deflate-on-oom=on") + } else { + deviceParams = append(deviceParams, "deflate-on-oom=off") + } + + if b.DisableModern { + deviceParams = append(deviceParams, "disable-modern=on") + } else { + deviceParams = append(deviceParams, "disable-modern=off") + } + + qemuParams = append(qemuParams, "-device") + qemuParams = append(qemuParams, strings.Join(deviceParams, ",")) + + return qemuParams +} + +// Valid returns true if the balloonDevice structure is valid and complete. +func (b BalloonDevice) Valid() bool { + if b.ID == "" { + return false + } + + return true +} + // RTCBaseType is the qemu RTC base time type. type RTCBaseType string diff --git a/qemu/qemu_test.go b/qemu/qemu_test.go index ea568ca0c6..e1d9fb341b 100644 --- a/qemu/qemu_test.go +++ b/qemu/qemu_test.go @@ -404,6 +404,45 @@ func TestVirtioRngValid(t *testing.T) { } +func TestAppendVirtioBalloon(t *testing.T) { + balloonDevice := BalloonDevice{ + ID: "balloon", + } + + var deviceString = "-device " + string(VirtioBalloon) + deviceString += ",id=" + balloonDevice.ID + + var OnDeflateOnOMM = ",deflate-on-oom=on" + var OffDeflateOnOMM = ",deflate-on-oom=off" + + var OnDisableModern = ",disable-modern=on" + var OffDisableModern = ",disable-modern=off" + + testAppend(balloonDevice, deviceString+OffDeflateOnOMM+OffDisableModern, t) + + balloonDevice.DeflateOnOOM = true + testAppend(balloonDevice, deviceString+OnDeflateOnOMM+OffDisableModern, t) + + balloonDevice.DisableModern = true + testAppend(balloonDevice, deviceString+OnDeflateOnOMM+OnDisableModern, t) + +} + +func TestVirtioBalloonValid(t *testing.T) { + balloon := BalloonDevice{ + ID: "", + } + + if balloon.Valid() { + t.Fatalf("balloon should be not valid when ID is empty") + } + + balloon.ID = "balloon0" + if !balloon.Valid() { + t.Fatalf("balloon should be valid") + } +} + var deviceSCSIControllerStr = "-device virtio-scsi-pci,id=foo" var deviceSCSIControllerBusAddrStr = "-device virtio-scsi-pci,id=foo,bus=pci.0,addr=00:04.0,disable-modern=true,iothread=iothread1" diff --git a/qemu/qmp.go b/qemu/qmp.go index 58cd76709c..aac906f440 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -1178,6 +1178,15 @@ func (q *QMP) ExecHotplugMemory(ctx context.Context, qomtype, id, mempath string return err } +// ExecuteBalloon sets the size of the balloon, hence updates the memory +// allocated for the VM. +func (q *QMP) ExecuteBalloon(ctx context.Context, bytes uint64) error { + args := map[string]interface{}{ + "value": bytes, + } + return q.executeCommand(ctx, "balloon", args, nil) +} + // ExecutePCIVSockAdd adds a vhost-vsock-pci bus func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID, vhostfd, addr, bus string, disableModern bool) error { args := map[string]interface{}{ diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index 6a70d02c62..b7c201f02e 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -1345,3 +1345,21 @@ func TestExecuteQueryMigration(t *testing.T) { q.Shutdown() <-disconnectedCh } + +// Checks balloon +func TestExecuteBalloon(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("balloon", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + err := q.ExecuteBalloon(context.Background(), 1073741824) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +}