diff --git a/qemu/qmp.go b/qemu/qmp.go index 9e37821814..37334e99e2 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -846,3 +846,38 @@ func (q *QMP) ExecSetMigrateArguments(ctx context.Context, url string) error { return q.executeCommand(ctx, "migrate", args, nil) } + +// ExecHotplugMemory adds size of MiB memory to the guest +func (q *QMP) ExecHotplugMemory(ctx context.Context, qomtype, id, mempath string, size int) error { + args := map[string]interface{}{ + "qom-type": qomtype, + "id": id, + "props": map[string]interface{}{"size": uint64(size) << 20}, + } + if mempath != "" { + args["mem-path"] = mempath + } + err := q.executeCommand(ctx, "object-add", args, nil) + if err != nil { + return err + } + + defer func() { + if err != nil { + q.cfg.Logger.Errorf("Unable to hotplug memory device: %v", err) + err = q.executeCommand(ctx, "object-del", map[string]interface{}{"id": id}, nil) + if err != nil { + q.cfg.Logger.Warningf("Unable to clean up memory object: %v", err) + } + } + }() + + args = map[string]interface{}{ + "driver": "pc-dimm", + "id": "dimm" + id, + "memdev": id, + } + err = q.executeCommand(ctx, "device_add", args, nil) + + return err +} diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index 9b471d6c63..f5b472d741 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -927,3 +927,21 @@ func TestExecSetMigrateArguments(t *testing.T) { q.Shutdown() <-disconnectedCh } + +// Checks hotplug memory +func TestExecHotplugMemory(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("object-add", nil, "return", nil) + buf.AddCommand("device_add", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + err := q.ExecHotplugMemory(context.Background(), "memory-backend-ram", "mem0", "", 128) + if err != nil { + t.Fatalf("Unexpected error: %v\n", err) + } + q.Shutdown() + <-disconnectedCh +}