qmp: add "query-cpus" support

Add "query-cpus" and "query-cpus-fast" to query CPU information from qemu

Signed-off-by: Wei Zhang <zhangwei555@huawei.com>
This commit is contained in:
Wei Zhang 2018-09-19 10:09:46 +08:00
parent 9905ae92c5
commit 1130aab85e
2 changed files with 145 additions and 1 deletions

View File

@ -144,7 +144,7 @@ type QMPVersion struct {
Capabilities []string
}
// CPUProperties contains the properties to be used for hotplugging a CPU instance
// CPUProperties contains the properties of a CPU instance
type CPUProperties struct {
Node int `json:"node-id"`
Socket int `json:"socket-id"`
@ -178,6 +178,28 @@ type MemoryDevices struct {
Type string `json:"type"`
}
// CPUInfo represents information about each virtual CPU
type CPUInfo struct {
CPU int `json:"CPU"`
Current bool `json:"current"`
Halted bool `json:"halted"`
QomPath string `json:"qom_path"`
Arch string `json:"arch"`
Pc int `json:"pc"`
ThreadID int `json:"thread_id"`
Props CPUProperties `json:"props"`
}
// CPUInfoFast represents information about each virtual CPU
type CPUInfoFast struct {
CPUIndex int `json:"cpu-index"`
QomPath string `json:"qom-path"`
Arch string `json:"arch"`
ThreadID int `json:"thread-id"`
Target string `json:"target"`
Props CPUProperties `json:"props"`
}
func (q *QMP) readLoop(fromVMCh chan<- []byte) {
scanner := bufio.NewScanner(q.conn)
for scanner.Scan() {
@ -1033,6 +1055,54 @@ func (q *QMP) ExecQueryMemoryDevices(ctx context.Context) ([]MemoryDevices, erro
return memoryDevices, nil
}
// ExecQueryCpus returns a slice with the list of `CpuInfo`
// Since qemu 2.12, we have `query-cpus-fast` as a better choice in production
// we can still choose `ExecQueryCpus` for compatibility though not recommended.
func (q *QMP) ExecQueryCpus(ctx context.Context) ([]CPUInfo, error) {
response, err := q.executeCommandWithResponse(ctx, "query-cpus", nil, nil, nil)
if err != nil {
return nil, err
}
// convert response to json
data, err := json.Marshal(response)
if err != nil {
return nil, fmt.Errorf("Unable to extract memory devices information: %v", err)
}
var cpuInfo []CPUInfo
// convert json to []CPUInfo
if err = json.Unmarshal(data, &cpuInfo); err != nil {
return nil, fmt.Errorf("unable to convert json to CPUInfo: %v", err)
}
return cpuInfo, nil
}
// ExecQueryCpusFast returns a slice with the list of `CpuInfoFast`
// This is introduced since 2.12, it does not incur a performance penalty and
// should be used in production instead of query-cpus.
func (q *QMP) ExecQueryCpusFast(ctx context.Context) ([]CPUInfoFast, error) {
response, err := q.executeCommandWithResponse(ctx, "query-cpus-fast", nil, nil, nil)
if err != nil {
return nil, err
}
// convert response to json
data, err := json.Marshal(response)
if err != nil {
return nil, fmt.Errorf("Unable to extract memory devices information: %v", err)
}
var cpuInfoFast []CPUInfoFast
// convert json to []CPUInfoFast
if err = json.Unmarshal(data, &cpuInfoFast); err != nil {
return nil, fmt.Errorf("unable to convert json to CPUInfoFast: %v", err)
}
return cpuInfoFast, 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{}{

View File

@ -1096,6 +1096,80 @@ func TestQMPExecuteQueryMemoryDevices(t *testing.T) {
<-disconnectedCh
}
// Checks that cpus are listed correctly
func TestQMPExecuteQueryCpus(t *testing.T) {
connectedCh := make(chan *QMPVersion)
disconnectedCh := make(chan struct{})
buf := newQMPTestCommandBuffer(t)
cpuInfo := CPUInfo{
CPU: 1,
Current: false,
Halted: false,
Arch: "x86_64",
QomPath: "/tmp/testQom",
Pc: 123456,
ThreadID: 123457,
Props: CPUProperties{
Node: 0,
Socket: 1,
Core: 1,
Thread: 1966,
},
}
buf.AddCommand("query-cpus", nil, "return", []interface{}{cpuInfo})
cfg := QMPConfig{Logger: qmpTestLogger{}}
q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh)
checkVersion(t, connectedCh)
cpus, err := q.ExecQueryCpus(context.Background())
if err != nil {
t.Fatalf("Unexpected error: %v\n", 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)
}
q.Shutdown()
<-disconnectedCh
}
// Checks that cpus are listed correctly
func TestQMPExecuteQueryCpusFast(t *testing.T) {
connectedCh := make(chan *QMPVersion)
disconnectedCh := make(chan struct{})
buf := newQMPTestCommandBuffer(t)
cpuInfoFast := CPUInfoFast{
CPUIndex: 1,
Arch: "x86",
Target: "x86_64",
QomPath: "/tmp/testQom",
ThreadID: 123457,
Props: CPUProperties{
Node: 0,
Socket: 1,
Core: 1,
Thread: 1966,
},
}
buf.AddCommand("query-cpus-fast", nil, "return", []interface{}{cpuInfoFast})
cfg := QMPConfig{Logger: qmpTestLogger{}}
q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh)
checkVersion(t, connectedCh)
cpus, err := q.ExecQueryCpusFast(context.Background())
if err != nil {
t.Fatalf("Unexpected error: %v\n", 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)
}
q.Shutdown()
<-disconnectedCh
}
// Checks that migrate capabilities can be set
func TestExecSetMigrationCaps(t *testing.T) {
connectedCh := make(chan *QMPVersion)