Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

Commit

Permalink
Merge pull request #69 from BetaXOi/output-qmp-err
Browse files Browse the repository at this point in the history
qmp: Output error detail when execute QMP command failed
  • Loading branch information
Mark Ryan committed Dec 4, 2018
2 parents d31bc8d + dab4cf1 commit 908b6aa
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 2 deletions.
29 changes: 27 additions & 2 deletions qemu/qmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (q *QMP) finaliseCommandWithResponse(cmdEl *list.Element, cmdQueue *list.Li
if succeeded {
cmd.res <- qmpResult{response: response}
} else {
cmd.res <- qmpResult{err: fmt.Errorf("QMP command failed")}
cmd.res <- qmpResult{err: fmt.Errorf("QMP command failed: %v", response)}
}
}
if cmdQueue.Len() > 0 {
Expand All @@ -325,6 +325,23 @@ func (q *QMP) finaliseCommand(cmdEl *list.Element, cmdQueue *list.List, succeede
q.finaliseCommandWithResponse(cmdEl, cmdQueue, succeeded, nil)
}

func (q *QMP) errorDesc(errorData interface{}) (string, error) {
// convert error to json
data, err := json.Marshal(errorData)
if err != nil {
return "", fmt.Errorf("Unable to extract error information: %v", err)
}

// see: https://github.com/qemu/qemu/blob/stable-2.12/qapi/qmp-dispatch.c#L125
var qmpErr map[string]string
// convert json to qmpError
if err = json.Unmarshal(data, &qmpErr); err != nil {
return "", fmt.Errorf("Unable to convert json to qmpError: %v", err)
}

return qmpErr["desc"], nil
}

func (q *QMP) processQMPInput(line []byte, cmdQueue *list.List) {
var vmData map[string]interface{}
err := json.Unmarshal(line, &vmData)
Expand All @@ -339,7 +356,7 @@ func (q *QMP) processQMPInput(line []byte, cmdQueue *list.List) {
}

response, succeeded := vmData["return"]
_, failed := vmData["error"]
errData, failed := vmData["error"]

if !succeeded && !failed {
return
Expand All @@ -353,6 +370,14 @@ func (q *QMP) processQMPInput(line []byte, cmdQueue *list.List) {
}
cmd := cmdEl.Value.(*qmpCommand)
if failed || cmd.filter == nil {
if errData != nil {
desc, err := q.errorDesc(errData)
if err != nil {
q.cfg.Logger.Infof("Get error description failed: %v", err)
} else {
response = desc
}
}
q.finaliseCommandWithResponse(cmdEl, cmdQueue, succeeded, response)
} else {
cmd.resultReceived = true
Expand Down
83 changes: 83 additions & 0 deletions qemu/qmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1403,3 +1403,86 @@ func TestExecuteBalloon(t *testing.T) {
q.Shutdown()
<-disconnectedCh
}

func TestErrorDesc(t *testing.T) {
errDesc := "Somthing err messages"
errData := map[string]string{
"class": "GenericError",
"desc": errDesc,
}

connectedCh := make(chan *QMPVersion)
disconnectedCh := make(chan struct{})
buf := newQMPTestCommandBuffer(t)
cfg := QMPConfig{Logger: qmpTestLogger{}}
q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh)
checkVersion(t, connectedCh)

desc, err := q.errorDesc(errData)
if err != nil {
t.Fatalf("Unexpected error '%v'", err)
}
if desc != errDesc {
t.Fatalf("expected '%v'\n got '%v'", errDesc, desc)
}

q.Shutdown()
<-disconnectedCh
}

func TestExecCommandFailed(t *testing.T) {
errDesc := "unable to map backing store for guest RAM: Cannot allocate memory"
errData := map[string]string{
"class": "GenericError",
"desc": errDesc,
}

connectedCh := make(chan *QMPVersion)
disconnectedCh := make(chan struct{})
buf := newQMPTestCommandBuffer(t)
buf.AddCommand("object-add", nil, "error", errData)
cfg := QMPConfig{Logger: qmpTestLogger{}}
q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh)
checkVersion(t, connectedCh)

_, err := q.executeCommandWithResponse(context.Background(), "object-add", nil, nil, nil)
if err == nil {
t.Fatalf("expected error but got nil")
}

expectedString := "QMP command failed: " + errDesc
if err.Error() != expectedString {
t.Fatalf("expected '%v' but got '%v'", expectedString, err)
}

q.Shutdown()
<-disconnectedCh
}

func TestExecCommandFailedWithInnerError(t *testing.T) {
errData := map[string]string{
"class": "GenericError",
"descFieldInvalid": "Invalid",
}

connectedCh := make(chan *QMPVersion)
disconnectedCh := make(chan struct{})
buf := newQMPTestCommandBuffer(t)
buf.AddCommand("object-add", nil, "error", errData)
cfg := QMPConfig{Logger: qmpTestLogger{}}
q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh)
checkVersion(t, connectedCh)

_, err := q.executeCommandWithResponse(context.Background(), "object-add", nil, nil, nil)
if err == nil {
t.Fatalf("expected error but got nil")
}

expectedString := "QMP command failed: "
if err.Error() != expectedString {
t.Fatalf("expected '%v' but got '%v'", expectedString, err)
}

q.Shutdown()
<-disconnectedCh
}

0 comments on commit 908b6aa

Please sign in to comment.