Merge pull request #105 from bergwolf/interaction

improve qemu interaction
This commit is contained in:
Julio Montes 2019-08-14 08:01:15 -05:00 committed by GitHub
commit e6644f4a25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 169 additions and 104 deletions

View File

@ -1456,8 +1456,8 @@ type Knobs struct {
// be set. // be set.
FileBackedMem bool FileBackedMem bool
// FileBackedMemShared will set the FileBackedMem device as shared. // MemShared will set the memory device as shared.
FileBackedMemShared bool MemShared bool
// Mlock will control locking of memory // Mlock will control locking of memory
// Only active when Realtime is set to true // Only active when Realtime is set to true
@ -1557,6 +1557,9 @@ type Config struct {
// PidFile is the -pidfile parameter // PidFile is the -pidfile parameter
PidFile string PidFile string
// LogFile is the -D parameter
LogFile string
qemuParams []string qemuParams []string
} }
@ -1755,46 +1758,34 @@ func (config *Config) appendKernel() {
} }
func (config *Config) appendMemoryKnobs() { func (config *Config) appendMemoryKnobs() {
if config.Memory.Size == "" {
return
}
var objMemParam, numaMemParam string
dimmName := "dimm1"
if config.Knobs.HugePages { if config.Knobs.HugePages {
if config.Memory.Size != "" { objMemParam = "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=/dev/hugepages"
dimmName := "dimm1" numaMemParam = "node,memdev=" + dimmName
objMemParam := "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=/dev/hugepages,share=on,prealloc=on" } else if config.Knobs.FileBackedMem && config.Memory.Path != "" {
numaMemParam := "node,memdev=" + dimmName objMemParam = "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=" + config.Memory.Path
numaMemParam = "node,memdev=" + dimmName
config.qemuParams = append(config.qemuParams, "-object") } else {
config.qemuParams = append(config.qemuParams, objMemParam) objMemParam = "memory-backend-ram,id=" + dimmName + ",size=" + config.Memory.Size
numaMemParam = "node,memdev=" + dimmName
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") if config.Knobs.MemShared {
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" objMemParam += ",share=on"
} }
numaMemParam := "node,memdev=" + dimmName if config.Knobs.MemPrealloc {
objMemParam += ",prealloc=on"
}
config.qemuParams = append(config.qemuParams, "-object") config.qemuParams = append(config.qemuParams, "-object")
config.qemuParams = append(config.qemuParams, objMemParam) config.qemuParams = append(config.qemuParams, objMemParam)
config.qemuParams = append(config.qemuParams, "-numa") config.qemuParams = append(config.qemuParams, "-numa")
config.qemuParams = append(config.qemuParams, numaMemParam) config.qemuParams = append(config.qemuParams, numaMemParam)
}
}
} }
func (config *Config) appendKnobs() { 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. // LaunchQemu can be used to launch a new qemu instance.
// //
// The Config parameter contains a set of qemu parameters and settings. // 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.appendIOThreads()
config.appendIncoming() config.appendIncoming()
config.appendPidFile() config.appendPidFile()
config.appendLogFile()
if err := config.appendCPUs(); err != nil { if err := config.appendCPUs(); err != nil {
return "", err return "", err

View File

@ -64,7 +64,7 @@ func testConfigAppend(config *Config, structure interface{}, expected string, t
case SMP: case SMP:
config.SMP = s config.SMP = s
if err := config.appendCPUs(); err != nil { if err := config.appendCPUs(); err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
case QMPSocket: case QMPSocket:
@ -494,7 +494,7 @@ func TestAppendKnobsAllTrue(t *testing.T) {
Daemonize: true, Daemonize: true,
MemPrealloc: true, MemPrealloc: true,
FileBackedMem: true, FileBackedMem: true,
FileBackedMemShared: true, MemShared: true,
Realtime: true, Realtime: true,
Mlock: true, Mlock: true,
Stopped: true, Stopped: true,
@ -511,7 +511,7 @@ func TestAppendKnobsAllFalse(t *testing.T) {
NoGraphic: false, NoGraphic: false,
MemPrealloc: false, MemPrealloc: false,
FileBackedMem: false, FileBackedMem: false,
FileBackedMemShared: false, MemShared: false,
Realtime: false, Realtime: false,
Mlock: false, Mlock: false,
Stopped: false, Stopped: false,
@ -536,7 +536,7 @@ func TestAppendMemoryHugePages(t *testing.T) {
HugePages: true, HugePages: true,
MemPrealloc: true, MemPrealloc: true,
FileBackedMem: true, FileBackedMem: true,
FileBackedMemShared: true, MemShared: true,
} }
knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=/dev/hugepages,share=on,prealloc=on -numa node,memdev=dimm1" 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" mlockFalseString := "-realtime mlock=off"
@ -558,16 +558,15 @@ func TestAppendMemoryMemPrealloc(t *testing.T) {
knobs := Knobs{ knobs := Knobs{
MemPrealloc: true, MemPrealloc: true,
FileBackedMem: true, MemShared: true,
FileBackedMemShared: 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" mlockFalseString := "-realtime mlock=off"
testConfigAppend(conf, knobs, memString+" "+knobsString+" "+mlockFalseString, t) testConfigAppend(conf, knobs, memString+" "+knobsString+" "+mlockFalseString, t)
} }
func TestAppendMemoryFileBackedMemShared(t *testing.T) { func TestAppendMemoryMemShared(t *testing.T) {
conf := &Config{ conf := &Config{
Memory: Memory{ Memory: Memory{
Size: "1G", Size: "1G",
@ -581,7 +580,7 @@ func TestAppendMemoryFileBackedMemShared(t *testing.T) {
knobs := Knobs{ knobs := Knobs{
FileBackedMem: true, FileBackedMem: true,
FileBackedMemShared: true, MemShared: true,
} }
knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=foobar,share=on -numa node,memdev=dimm1" knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=foobar,share=on -numa node,memdev=dimm1"
mlockFalseString := "-realtime mlock=off" mlockFalseString := "-realtime mlock=off"
@ -603,7 +602,7 @@ func TestAppendMemoryFileBackedMem(t *testing.T) {
knobs := Knobs{ knobs := Knobs{
FileBackedMem: true, FileBackedMem: true,
FileBackedMemShared: false, MemShared: false,
} }
knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=foobar -numa node,memdev=dimm1" knobsString := "-object memory-backend-file,id=dimm1,size=1G,mem-path=foobar -numa node,memdev=dimm1"
mlockFalseString := "-realtime mlock=off" mlockFalseString := "-realtime mlock=off"
@ -611,6 +610,29 @@ func TestAppendMemoryFileBackedMem(t *testing.T) {
testConfigAppend(conf, knobs, memString+" "+knobsString+" "+mlockFalseString, 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" 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) { func TestAppendKernel(t *testing.T) {
@ -712,7 +734,8 @@ func TestAppendQMPSocketServer(t *testing.T) {
} }
var pidfile = "/run/vc/vm/iamsandboxid/pidfile" 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) { func TestAppendStrings(t *testing.T) {
config := Config{ config := Config{
@ -721,12 +744,14 @@ func TestAppendStrings(t *testing.T) {
UUID: agentUUID, UUID: agentUUID,
CPUModel: "host", CPUModel: "host",
PidFile: pidfile, PidFile: pidfile,
LogFile: logfile,
} }
config.appendName() config.appendName()
config.appendCPUModel() config.appendCPUModel()
config.appendUUID() config.appendUUID()
config.appendPidFile() config.appendPidFile()
config.appendLogFile()
result := strings.Join(config.qemuParams, " ") result := strings.Join(config.qemuParams, " ")
if result != qemuString { if result != qemuString {
@ -984,7 +1009,7 @@ func TestBadMemoryKnobs(t *testing.T) {
c = &Config{ c = &Config{
Knobs: Knobs{ Knobs: Knobs{
HugePages: true, MemShared: true,
}, },
} }
c.appendMemoryKnobs() c.appendMemoryKnobs()
@ -1001,19 +1026,6 @@ func TestBadMemoryKnobs(t *testing.T) {
if len(c.qemuParams) != 0 { if len(c.qemuParams) != 0 {
t.Errorf("Expected empty qemuParams, found %s", c.qemuParams) 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) { func TestBadKnobs(t *testing.T) {

View File

@ -251,6 +251,13 @@ type SchemaInfo struct {
Name string `json:"name"` 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) { func (q *QMP) readLoop(fromVMCh chan<- []byte) {
scanner := bufio.NewScanner(q.conn) scanner := bufio.NewScanner(q.conn)
if q.cfg.MaxCapacity > 0 { if q.cfg.MaxCapacity > 0 {
@ -1545,3 +1552,23 @@ func (q *QMP) ExecQueryQmpSchema(ctx context.Context) ([]SchemaInfo, error) {
return schemaInfo, nil 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
}

View File

@ -1110,13 +1110,13 @@ func TestQMPExecuteQueryHotpluggableCPUs(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
hotCPUs, err := q.ExecuteQueryHotpluggableCPUs(context.Background()) hotCPUs, err := q.ExecuteQueryHotpluggableCPUs(context.Background())
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
if len(hotCPUs) != 1 { if len(hotCPUs) != 1 {
t.Fatalf("Expected hot CPUs length equals to 1\n") t.Fatalf("Expected hot CPUs length equals to 1\n")
} }
if reflect.DeepEqual(hotCPUs[0], hotCPU) == false { 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() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1146,13 +1146,13 @@ func TestQMPExecuteQueryMemoryDevices(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
memDevices, err := q.ExecQueryMemoryDevices(context.Background()) memDevices, err := q.ExecQueryMemoryDevices(context.Background())
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
if len(memDevices) != 1 { if len(memDevices) != 1 {
t.Fatalf("Expected memory devices length equals to 1\n") t.Fatalf("Expected memory devices length equals to 1\n")
} }
if reflect.DeepEqual(memDevices[0], memoryDevices) == false { 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() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1184,13 +1184,13 @@ func TestQMPExecuteQueryCpus(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
cpus, err := q.ExecQueryCpus(context.Background()) cpus, err := q.ExecQueryCpus(context.Background())
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
if len(cpus) != 1 { if len(cpus) != 1 {
t.Fatalf("Expected memory devices length equals to 1\n") t.Fatalf("Expected memory devices length equals to 1\n")
} }
if reflect.DeepEqual(cpus[0], cpuInfo) == false { 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() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1220,13 +1220,13 @@ func TestQMPExecuteQueryCpusFast(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
cpus, err := q.ExecQueryCpusFast(context.Background()) cpus, err := q.ExecQueryCpusFast(context.Background())
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
if len(cpus) != 1 { if len(cpus) != 1 {
t.Fatalf("Expected memory devices length equals to 1\n") t.Fatalf("Expected memory devices length equals to 1\n")
} }
if reflect.DeepEqual(cpus[0], cpuInfoFast) == false { 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() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1249,7 +1249,7 @@ func TestExecSetMigrationCaps(t *testing.T) {
} }
err := q.ExecSetMigrationCaps(context.Background(), caps) err := q.ExecSetMigrationCaps(context.Background(), caps)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
q.Shutdown() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1266,7 +1266,7 @@ func TestExecSetMigrateArguments(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
err := q.ExecSetMigrateArguments(context.Background(), "exec:foobar") err := q.ExecSetMigrateArguments(context.Background(), "exec:foobar")
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
q.Shutdown() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1284,7 +1284,7 @@ func TestExecHotplugMemory(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
err := q.ExecHotplugMemory(context.Background(), "memory-backend-ram", "mem0", "", 128, true) err := q.ExecHotplugMemory(context.Background(), "memory-backend-ram", "mem0", "", 128, true)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
q.Shutdown() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1419,7 +1419,7 @@ func TestExecuteQueryMigration(t *testing.T) {
t.Fatalf("Unexpected error %v", err) t.Fatalf("Unexpected error %v", err)
} }
if !reflect.DeepEqual(s, status) { if !reflect.DeepEqual(s, status) {
t.Fatalf("expected %v\n got %v", status, s) t.Fatalf("expected %v got %v", status, s)
} }
q.Shutdown() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1538,7 +1538,7 @@ func TestExecuteNVDIMMDeviceAdd(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
err := q.ExecuteNVDIMMDeviceAdd(context.Background(), "nvdimm0", "/dev/rbd0", 1024) err := q.ExecuteNVDIMMDeviceAdd(context.Background(), "nvdimm0", "/dev/rbd0", 1024)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
q.Shutdown() q.Shutdown()
<-disconnectedCh <-disconnectedCh
@ -1612,13 +1612,40 @@ func TestQMPExecQueryQmpSchema(t *testing.T) {
checkVersion(t, connectedCh) checkVersion(t, connectedCh)
info, err := q.ExecQueryQmpSchema(context.Background()) info, err := q.ExecQueryQmpSchema(context.Background())
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v\n", err) t.Fatalf("Unexpected error: %v", err)
} }
if len(schemaInfo) != 2 { if len(schemaInfo) != 2 {
t.Fatalf("Expected schema infos length equals to 2\n") t.Fatalf("Expected schema infos length equals to 2\n")
} }
if reflect.DeepEqual(info, schemaInfo) == false { 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() q.Shutdown()
<-disconnectedCh <-disconnectedCh