diff --git a/qmp.go b/qmp.go index 0e08e4d261..3727b8478e 100644 --- a/qmp.go +++ b/qmp.go @@ -140,6 +140,22 @@ type QMPVersion struct { Capabilities []string } +// CPUProperties contains the properties to be used for hotplugging a CPU instance +type CPUProperties struct { + Node int `json:"node-id"` + Socket int `json:"socket-id"` + Core int `json:"core-id"` + Thread int `json:"thread-id"` +} + +// HotpluggableCPU represents a hotpluggable CPU +type HotpluggableCPU struct { + Type string `json:"type"` + VcpusCount int `json:"vcpus-count"` + Properties CPUProperties `json:"props"` + QOMPath string `json:"qom-path"` +} + func (q *QMP) readLoop(fromVMCh chan<- []byte) { scanner := bufio.NewScanner(q.conn) for scanner.Scan() { @@ -751,3 +767,25 @@ func (q *QMP) ExecuteCPUDeviceAdd(ctx context.Context, driver, cpuID, socketID, } return q.executeCommand(ctx, "device_add", args, nil) } + +// ExecuteQueryHotpluggableCPUs returns a slice with the list of hotpluggable CPUs +func (q *QMP) ExecuteQueryHotpluggableCPUs(ctx context.Context) ([]HotpluggableCPU, error) { + response, err := q.executeCommandWithResponse(ctx, "query-hotpluggable-cpus", 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 CPU information: %v", err) + } + + var cpus []HotpluggableCPU + // convert json to []HotpluggableCPU + if err = json.Unmarshal(data, &cpus); err != nil { + return nil, fmt.Errorf("Unable to convert json to hotpluggable CPU: %v", err) + } + + return cpus, nil +} diff --git a/qmp_test.go b/qmp_test.go index 7e83f4f939..21b5e968de 100644 --- a/qmp_test.go +++ b/qmp_test.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "log" + "reflect" "sync" "testing" "time" @@ -75,7 +76,7 @@ type qmpTestEvent struct { type qmpTestResult struct { result string - data map[string]interface{} + data interface{} } type qmpTestCommandBuffer struct { @@ -132,11 +133,8 @@ func (b *qmpTestCommandBuffer) startEventLoop(wg *sync.WaitGroup) { } func (b *qmpTestCommandBuffer) AddCommand(name string, args map[string]interface{}, - result string, data map[string]interface{}) { + result string, data interface{}) { b.cmds = append(b.cmds, qmpTestCommand{name, args}) - if data == nil { - data = make(map[string]interface{}) - } b.results = append(b.results, qmpTestResult{result, data}) } @@ -832,3 +830,37 @@ func TestQMPCPUDeviceAdd(t *testing.T) { q.Shutdown() <-disconnectedCh } + +// Checks that hotpluggable CPUs are listed correctly +func TestQMPExecuteQueryHotpluggableCPUs(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + hotCPU := HotpluggableCPU{ + Type: "host-x86", + VcpusCount: 5, + Properties: CPUProperties{ + Node: 1, + Socket: 3, + Core: 2, + Thread: 4, + }, + QOMPath: "/abc/123/rgb", + } + buf.AddCommand("query-hotpluggable-cpus", nil, "return", []interface{}{hotCPU}) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + hotCPUs, err := q.ExecuteQueryHotpluggableCPUs(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %v\n", 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) + } + q.Shutdown() + <-disconnectedCh +}