From 30bfcaaa6d7e9fb92346554c57f1af443a44c8fe Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Tue, 13 Aug 2019 01:42:29 -0700 Subject: [PATCH 1/4] qemu: add debug logfile When LogFile is specified, output debug log there. Signed-off-by: Peng Tao --- qemu/qemu.go | 11 +++++++++++ qemu/qemu_test.go | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/qemu/qemu.go b/qemu/qemu.go index bd883f881b..e1394831e6 100644 --- a/qemu/qemu.go +++ b/qemu/qemu.go @@ -1557,6 +1557,9 @@ type Config struct { // PidFile is the -pidfile parameter PidFile string + // LogFile is the -D parameter + LogFile string + qemuParams []string } @@ -1880,6 +1883,13 @@ func (config *Config) appendPidFile() { } } +func (config *Config) appendLogFile() { + if config.LogFile != "" { + config.qemuParams = append(config.qemuParams, "-D") + config.qemuParams = append(config.qemuParams, config.LogFile) + } +} + // LaunchQemu can be used to launch a new qemu instance. // // The Config parameter contains a set of qemu parameters and settings. @@ -1906,6 +1916,7 @@ func LaunchQemu(config Config, logger QMPLog) (string, error) { config.appendIOThreads() config.appendIncoming() config.appendPidFile() + config.appendLogFile() if err := config.appendCPUs(); err != nil { return "", err diff --git a/qemu/qemu_test.go b/qemu/qemu_test.go index 5307c2d9bf..577660d783 100644 --- a/qemu/qemu_test.go +++ b/qemu/qemu_test.go @@ -712,7 +712,8 @@ func TestAppendQMPSocketServer(t *testing.T) { } var pidfile = "/run/vc/vm/iamsandboxid/pidfile" -var qemuString = "-name cc-qemu -cpu host -uuid " + agentUUID + " -pidfile " + pidfile +var logfile = "/run/vc/vm/iamsandboxid/logfile" +var qemuString = "-name cc-qemu -cpu host -uuid " + agentUUID + " -pidfile " + pidfile + " -D " + logfile func TestAppendStrings(t *testing.T) { config := Config{ @@ -721,12 +722,14 @@ func TestAppendStrings(t *testing.T) { UUID: agentUUID, CPUModel: "host", PidFile: pidfile, + LogFile: logfile, } config.appendName() config.appendCPUModel() config.appendUUID() config.appendPidFile() + config.appendLogFile() result := strings.Join(config.qemuParams, " ") if result != qemuString { From 234e0edfd72da840b759bcf5b6b65cb7b05952fd Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Tue, 13 Aug 2019 01:58:51 -0700 Subject: [PATCH 2/4] qemu: fix memory prealloc handling Memory preallocation is just a property of different memory backends. We should treat it similar to memory sharing property. Also rename FileBackedMemShared to MemShared as it is just another memory backend property that works with different memory backends not just file backed memory. Signed-off-by: Peng Tao --- qemu/qemu.go | 70 +++++++++++++------------------- qemu/qemu_test.go | 101 +++++++++++++++++++++++++--------------------- 2 files changed, 84 insertions(+), 87 deletions(-) diff --git a/qemu/qemu.go b/qemu/qemu.go index e1394831e6..4cb18d10ad 100644 --- a/qemu/qemu.go +++ b/qemu/qemu.go @@ -1456,8 +1456,8 @@ type Knobs struct { // be set. FileBackedMem bool - // FileBackedMemShared will set the FileBackedMem device as shared. - FileBackedMemShared bool + // MemShared will set the memory device as shared. + MemShared bool // Mlock will control locking of memory // Only active when Realtime is set to true @@ -1758,46 +1758,34 @@ func (config *Config) appendKernel() { } func (config *Config) appendMemoryKnobs() { - if config.Knobs.HugePages { - if config.Memory.Size != "" { - dimmName := "dimm1" - objMemParam := "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=/dev/hugepages,share=on,prealloc=on" - numaMemParam := "node,memdev=" + dimmName - - config.qemuParams = append(config.qemuParams, "-object") - config.qemuParams = append(config.qemuParams, objMemParam) - - config.qemuParams = append(config.qemuParams, "-numa") - config.qemuParams = append(config.qemuParams, numaMemParam) - } - } else if config.Knobs.MemPrealloc { - if config.Memory.Size != "" { - dimmName := "dimm1" - objMemParam := "memory-backend-ram,id=" + dimmName + ",size=" + config.Memory.Size + ",prealloc=on" - numaMemParam := "node,memdev=" + dimmName - - config.qemuParams = append(config.qemuParams, "-object") - config.qemuParams = append(config.qemuParams, objMemParam) - - config.qemuParams = append(config.qemuParams, "-numa") - config.qemuParams = append(config.qemuParams, numaMemParam) - } - } else if config.Knobs.FileBackedMem { - if config.Memory.Size != "" && config.Memory.Path != "" { - dimmName := "dimm1" - objMemParam := "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=" + config.Memory.Path - if config.Knobs.FileBackedMemShared { - objMemParam += ",share=on" - } - numaMemParam := "node,memdev=" + dimmName - - config.qemuParams = append(config.qemuParams, "-object") - config.qemuParams = append(config.qemuParams, objMemParam) - - config.qemuParams = append(config.qemuParams, "-numa") - config.qemuParams = append(config.qemuParams, numaMemParam) - } + if config.Memory.Size == "" { + return } + + var objMemParam, numaMemParam string + dimmName := "dimm1" + if config.Knobs.HugePages { + objMemParam = "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=/dev/hugepages" + numaMemParam = "node,memdev=" + dimmName + } else if config.Knobs.FileBackedMem && config.Memory.Path != "" { + objMemParam = "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=" + config.Memory.Path + numaMemParam = "node,memdev=" + dimmName + } else { + objMemParam = "memory-backend-ram,id=" + dimmName + ",size=" + config.Memory.Size + numaMemParam = "node,memdev=" + dimmName + } + + if config.Knobs.MemShared { + objMemParam += ",share=on" + } + if config.Knobs.MemPrealloc { + objMemParam += ",prealloc=on" + } + config.qemuParams = append(config.qemuParams, "-object") + config.qemuParams = append(config.qemuParams, objMemParam) + + config.qemuParams = append(config.qemuParams, "-numa") + config.qemuParams = append(config.qemuParams, numaMemParam) } func (config *Config) appendKnobs() { diff --git a/qemu/qemu_test.go b/qemu/qemu_test.go index 577660d783..bc527ab6bc 100644 --- a/qemu/qemu_test.go +++ b/qemu/qemu_test.go @@ -488,16 +488,16 @@ func TestAppendEmptyDevice(t *testing.T) { func TestAppendKnobsAllTrue(t *testing.T) { var knobsString = "-no-user-config -nodefaults -nographic -daemonize -realtime mlock=on -S" knobs := Knobs{ - NoUserConfig: true, - NoDefaults: true, - NoGraphic: true, - Daemonize: true, - MemPrealloc: true, - FileBackedMem: true, - FileBackedMemShared: true, - Realtime: true, - Mlock: true, - Stopped: true, + NoUserConfig: true, + NoDefaults: true, + NoGraphic: true, + Daemonize: true, + MemPrealloc: true, + FileBackedMem: true, + MemShared: true, + Realtime: true, + Mlock: true, + Stopped: true, } testAppend(knobs, knobsString, t) @@ -506,15 +506,15 @@ func TestAppendKnobsAllTrue(t *testing.T) { func TestAppendKnobsAllFalse(t *testing.T) { var knobsString = "-realtime mlock=off" knobs := Knobs{ - NoUserConfig: false, - NoDefaults: false, - NoGraphic: false, - MemPrealloc: false, - FileBackedMem: false, - FileBackedMemShared: false, - Realtime: false, - Mlock: false, - Stopped: false, + NoUserConfig: false, + NoDefaults: false, + NoGraphic: false, + MemPrealloc: false, + FileBackedMem: false, + MemShared: false, + Realtime: false, + Mlock: false, + Stopped: false, } testAppend(knobs, knobsString, t) @@ -533,10 +533,10 @@ func TestAppendMemoryHugePages(t *testing.T) { testConfigAppend(conf, conf.Memory, memString, t) knobs := Knobs{ - HugePages: true, - MemPrealloc: true, - FileBackedMem: true, - FileBackedMemShared: true, + HugePages: true, + MemPrealloc: true, + FileBackedMem: true, + MemShared: true, } knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=/dev/hugepages,share=on,prealloc=on -numa node,memdev=dimm1" mlockFalseString := "-realtime mlock=off" @@ -557,17 +557,16 @@ func TestAppendMemoryMemPrealloc(t *testing.T) { testConfigAppend(conf, conf.Memory, memString, t) knobs := Knobs{ - MemPrealloc: true, - FileBackedMem: true, - FileBackedMemShared: true, + MemPrealloc: true, + MemShared: true, } - knobsString := "-object memory-backend-ram,id=dimm1,size=1G,prealloc=on -numa node,memdev=dimm1" + knobsString := "-object memory-backend-ram,id=dimm1,size=1G,share=on,prealloc=on -numa node,memdev=dimm1" mlockFalseString := "-realtime mlock=off" testConfigAppend(conf, knobs, memString+" "+knobsString+" "+mlockFalseString, t) } -func TestAppendMemoryFileBackedMemShared(t *testing.T) { +func TestAppendMemoryMemShared(t *testing.T) { conf := &Config{ Memory: Memory{ Size: "1G", @@ -580,8 +579,8 @@ func TestAppendMemoryFileBackedMemShared(t *testing.T) { testConfigAppend(conf, conf.Memory, memString, t) knobs := Knobs{ - FileBackedMem: true, - FileBackedMemShared: true, + FileBackedMem: true, + MemShared: true, } knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=foobar,share=on -numa node,memdev=dimm1" mlockFalseString := "-realtime mlock=off" @@ -602,8 +601,8 @@ func TestAppendMemoryFileBackedMem(t *testing.T) { testConfigAppend(conf, conf.Memory, memString, t) knobs := Knobs{ - FileBackedMem: true, - FileBackedMemShared: false, + FileBackedMem: true, + MemShared: false, } knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=foobar -numa node,memdev=dimm1" mlockFalseString := "-realtime mlock=off" @@ -611,6 +610,29 @@ func TestAppendMemoryFileBackedMem(t *testing.T) { testConfigAppend(conf, knobs, memString+" "+knobsString+" "+mlockFalseString, t) } +func TestAppendMemoryFileBackedMemPrealloc(t *testing.T) { + conf := &Config{ + Memory: Memory{ + Size: "1G", + Slots: 8, + MaxMem: "3G", + Path: "foobar", + }, + } + memString := "-m 1G,slots=8,maxmem=3G" + testConfigAppend(conf, conf.Memory, memString, t) + + knobs := Knobs{ + FileBackedMem: true, + MemShared: true, + MemPrealloc: true, + } + knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=foobar,share=on,prealloc=on -numa node,memdev=dimm1" + mlockFalseString := "-realtime mlock=off" + + testConfigAppend(conf, knobs, memString+" "+knobsString+" "+mlockFalseString, t) +} + var kernelString = "-kernel /opt/vmlinux.container -initrd /opt/initrd.container -append root=/dev/pmem0p1 rootflags=dax,data=ordered,errors=remount-ro rw rootfstype=ext4 tsc=reliable" func TestAppendKernel(t *testing.T) { @@ -987,7 +1009,7 @@ func TestBadMemoryKnobs(t *testing.T) { c = &Config{ Knobs: Knobs{ - HugePages: true, + MemShared: true, }, } c.appendMemoryKnobs() @@ -1004,19 +1026,6 @@ func TestBadMemoryKnobs(t *testing.T) { if len(c.qemuParams) != 0 { t.Errorf("Expected empty qemuParams, found %s", c.qemuParams) } - - c = &Config{ - Knobs: Knobs{ - FileBackedMem: true, - }, - Memory: Memory{ - Size: "1024", - }, - } - c.appendMemoryKnobs() - if len(c.qemuParams) != 0 { - t.Errorf("Expected empty qemuParams, found %s", c.qemuParams) - } } func TestBadKnobs(t *testing.T) { From 73555a409cbc8c3f612a0ac8e36294befb21da48 Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Tue, 13 Aug 2019 02:06:46 -0700 Subject: [PATCH 3/4] qmp: add query-status API So that caller can find out guest status via qmp. Signed-off-by: Peng Tao --- qemu/qmp.go | 27 +++++++++++++++++++++++++++ qemu/qmp_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/qemu/qmp.go b/qemu/qmp.go index 186f3fadd1..473ce4afaa 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -251,6 +251,13 @@ type SchemaInfo struct { Name string `json:"name"` } +// StatusInfo represents guest running status +type StatusInfo struct { + Running bool `json:"running"` + SingleStep bool `json:"singlestep"` + Status string `json:"status"` +} + func (q *QMP) readLoop(fromVMCh chan<- []byte) { scanner := bufio.NewScanner(q.conn) if q.cfg.MaxCapacity > 0 { @@ -1545,3 +1552,23 @@ func (q *QMP) ExecQueryQmpSchema(ctx context.Context) ([]SchemaInfo, error) { return schemaInfo, nil } + +// ExecuteQueryStatus queries guest status +func (q *QMP) ExecuteQueryStatus(ctx context.Context) (StatusInfo, error) { + response, err := q.executeCommandWithResponse(ctx, "query-status", nil, nil, nil) + if err != nil { + return StatusInfo{}, err + } + + data, err := json.Marshal(response) + if err != nil { + return StatusInfo{}, fmt.Errorf("unable to extract migrate status information: %v", err) + } + + var status StatusInfo + if err = json.Unmarshal(data, &status); err != nil { + return StatusInfo{}, fmt.Errorf("unable to convert migrate status information: %v", err) + } + + return status, nil +} diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index 067665371f..bfad9d0dd6 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -1623,3 +1623,30 @@ func TestQMPExecQueryQmpSchema(t *testing.T) { q.Shutdown() <-disconnectedCh } + +func TestQMPExecQueryQmpStatus(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + statusInfo := StatusInfo{ + Running: true, + SingleStep: false, + Status: "running", + } + buf.AddCommand("query-status", nil, "return", statusInfo) + cfg := QMPConfig{ + Logger: qmpTestLogger{}, + MaxCapacity: 1024, + } + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + info, err := q.ExecuteQueryStatus(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %v\n", err) + } + if reflect.DeepEqual(info, statusInfo) == false { + t.Fatalf("Expected %v equals to %v\n", info, statusInfo) + } + q.Shutdown() + <-disconnectedCh +} From 164bd8cd22d30ea07be6d480cc57d03bc0c44dce Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Tue, 13 Aug 2019 23:36:07 -0700 Subject: [PATCH 4/4] test/fmt: drop extra newlines They are unneeded. Signed-off-by: Peng Tao --- qemu/qemu_test.go | 2 +- qemu/qmp_test.go | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/qemu/qemu_test.go b/qemu/qemu_test.go index bc527ab6bc..7caba015e9 100644 --- a/qemu/qemu_test.go +++ b/qemu/qemu_test.go @@ -64,7 +64,7 @@ func testConfigAppend(config *Config, structure interface{}, expected string, t case SMP: config.SMP = s if err := config.appendCPUs(); err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } case QMPSocket: diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index bfad9d0dd6..c5722765ab 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -1110,13 +1110,13 @@ func TestQMPExecuteQueryHotpluggableCPUs(t *testing.T) { checkVersion(t, connectedCh) hotCPUs, err := q.ExecuteQueryHotpluggableCPUs(context.Background()) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } if len(hotCPUs) != 1 { t.Fatalf("Expected hot CPUs length equals to 1\n") } if reflect.DeepEqual(hotCPUs[0], hotCPU) == false { - t.Fatalf("Expected %v equals to %v\n", hotCPUs[0], hotCPU) + t.Fatalf("Expected %v equals to %v", hotCPUs[0], hotCPU) } q.Shutdown() <-disconnectedCh @@ -1146,13 +1146,13 @@ func TestQMPExecuteQueryMemoryDevices(t *testing.T) { checkVersion(t, connectedCh) memDevices, err := q.ExecQueryMemoryDevices(context.Background()) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } if len(memDevices) != 1 { t.Fatalf("Expected memory devices length equals to 1\n") } if reflect.DeepEqual(memDevices[0], memoryDevices) == false { - t.Fatalf("Expected %v equals to %v\n", memDevices[0], memoryDevices) + t.Fatalf("Expected %v equals to %v", memDevices[0], memoryDevices) } q.Shutdown() <-disconnectedCh @@ -1184,13 +1184,13 @@ func TestQMPExecuteQueryCpus(t *testing.T) { checkVersion(t, connectedCh) cpus, err := q.ExecQueryCpus(context.Background()) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } if len(cpus) != 1 { t.Fatalf("Expected memory devices length equals to 1\n") } if reflect.DeepEqual(cpus[0], cpuInfo) == false { - t.Fatalf("Expected %v equals to %v\n", cpus[0], cpuInfo) + t.Fatalf("Expected %v equals to %v", cpus[0], cpuInfo) } q.Shutdown() <-disconnectedCh @@ -1220,13 +1220,13 @@ func TestQMPExecuteQueryCpusFast(t *testing.T) { checkVersion(t, connectedCh) cpus, err := q.ExecQueryCpusFast(context.Background()) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } if len(cpus) != 1 { t.Fatalf("Expected memory devices length equals to 1\n") } if reflect.DeepEqual(cpus[0], cpuInfoFast) == false { - t.Fatalf("Expected %v equals to %v\n", cpus[0], cpuInfoFast) + t.Fatalf("Expected %v equals to %v", cpus[0], cpuInfoFast) } q.Shutdown() <-disconnectedCh @@ -1249,7 +1249,7 @@ func TestExecSetMigrationCaps(t *testing.T) { } err := q.ExecSetMigrationCaps(context.Background(), caps) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } q.Shutdown() <-disconnectedCh @@ -1266,7 +1266,7 @@ func TestExecSetMigrateArguments(t *testing.T) { checkVersion(t, connectedCh) err := q.ExecSetMigrateArguments(context.Background(), "exec:foobar") if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } q.Shutdown() <-disconnectedCh @@ -1284,7 +1284,7 @@ func TestExecHotplugMemory(t *testing.T) { checkVersion(t, connectedCh) err := q.ExecHotplugMemory(context.Background(), "memory-backend-ram", "mem0", "", 128, true) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } q.Shutdown() <-disconnectedCh @@ -1419,7 +1419,7 @@ func TestExecuteQueryMigration(t *testing.T) { t.Fatalf("Unexpected error %v", err) } if !reflect.DeepEqual(s, status) { - t.Fatalf("expected %v\n got %v", status, s) + t.Fatalf("expected %v got %v", status, s) } q.Shutdown() <-disconnectedCh @@ -1538,7 +1538,7 @@ func TestExecuteNVDIMMDeviceAdd(t *testing.T) { checkVersion(t, connectedCh) err := q.ExecuteNVDIMMDeviceAdd(context.Background(), "nvdimm0", "/dev/rbd0", 1024) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } q.Shutdown() <-disconnectedCh @@ -1612,13 +1612,13 @@ func TestQMPExecQueryQmpSchema(t *testing.T) { checkVersion(t, connectedCh) info, err := q.ExecQueryQmpSchema(context.Background()) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } if len(schemaInfo) != 2 { t.Fatalf("Expected schema infos length equals to 2\n") } if reflect.DeepEqual(info, schemaInfo) == false { - t.Fatalf("Expected %v equals to %v\n", info, schemaInfo) + t.Fatalf("Expected %v equals to %v", info, schemaInfo) } q.Shutdown() <-disconnectedCh @@ -1642,10 +1642,10 @@ func TestQMPExecQueryQmpStatus(t *testing.T) { checkVersion(t, connectedCh) info, err := q.ExecuteQueryStatus(context.Background()) if err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: %v", err) } if reflect.DeepEqual(info, statusInfo) == false { - t.Fatalf("Expected %v equals to %v\n", info, statusInfo) + t.Fatalf("Expected %v equals to %v", info, statusInfo) } q.Shutdown() <-disconnectedCh