diff --git a/qemu/qemu.go b/qemu/qemu.go index bd883f881b..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 @@ -1557,6 +1557,9 @@ type Config struct { // PidFile is the -pidfile parameter PidFile string + // LogFile is the -D parameter + LogFile string + qemuParams []string } @@ -1755,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() { @@ -1880,6 +1871,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 +1904,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..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: @@ -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) { @@ -712,7 +734,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 +744,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 { @@ -984,7 +1009,7 @@ func TestBadMemoryKnobs(t *testing.T) { c = &Config{ Knobs: Knobs{ - HugePages: true, + MemShared: true, }, } c.appendMemoryKnobs() @@ -1001,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) { 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..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,40 @@ 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 +} + +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", err) + } + if reflect.DeepEqual(info, statusInfo) == false { + t.Fatalf("Expected %v equals to %v", info, statusInfo) } q.Shutdown() <-disconnectedCh