Merge pull request #4 from digitalocean/qemu-errors
check for QEMU response errors
This commit is contained in:
commit
a8acb825b1
40
libvirt.go
40
libvirt.go
@ -19,6 +19,7 @@ package libvirt
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -68,6 +69,14 @@ type DomainEvent struct {
|
||||
Details []byte
|
||||
}
|
||||
|
||||
// qemuError represents a QEMU process error.
|
||||
type qemuError struct {
|
||||
Error struct {
|
||||
Class string `json:"class"`
|
||||
Description string `json:"desc"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// Connect establishes communication with the libvirt server.
|
||||
// The underlying libvirt socket connection must be previously established.
|
||||
func (l *Libvirt) Connect() error {
|
||||
@ -225,10 +234,16 @@ func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
res := <-resp
|
||||
// check for libvirt errors
|
||||
if res.Status != StatusOK {
|
||||
return nil, decodeError(res.Payload)
|
||||
}
|
||||
|
||||
// check for QEMU process errors
|
||||
if err = getQEMUError(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := bytes.NewReader(res.Payload)
|
||||
dec := xdr.NewDecoder(r)
|
||||
data, _, err := dec.DecodeFixedOpaque(int32(r.Len()))
|
||||
@ -238,7 +253,7 @@ func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) {
|
||||
|
||||
// drop QMP control characters from start of line, and drop
|
||||
// any trailing NULL characters from the end
|
||||
return bytes.TrimRight(data[4:], "\x00"), err
|
||||
return bytes.TrimRight(data[4:], "\x00"), nil
|
||||
}
|
||||
|
||||
// Version returns the version of the libvirt daemon.
|
||||
@ -308,6 +323,29 @@ func (l *Libvirt) lookup(name string) (*Domain, error) {
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// getQEMUError checks the provided response for QEMU process errors.
|
||||
// If an error is found, it is extracted an returned, otherwise nil.
|
||||
func getQEMUError(r response) error {
|
||||
pl := bytes.NewReader(r.Payload)
|
||||
dec := xdr.NewDecoder(pl)
|
||||
|
||||
s, _, err := dec.DecodeString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var e qemuError
|
||||
if err = json.Unmarshal([]byte(s), &e); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Error.Description != "" {
|
||||
return errors.New(e.Error.Description)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New configures a new Libvirt RPC connection.
|
||||
func New(conn net.Conn) *Libvirt {
|
||||
l := &Libvirt{
|
||||
|
@ -148,6 +148,17 @@ func TestRun(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunFail(t *testing.T) {
|
||||
conn := libvirttest.New()
|
||||
conn.Fail = true
|
||||
l := New(conn)
|
||||
|
||||
_, err := l.Run("test", []byte(`{"drive-foo"}`))
|
||||
if err == nil {
|
||||
t.Error("expected qemu error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
conn := libvirttest.New()
|
||||
l := New(conn)
|
||||
|
@ -120,6 +120,32 @@ var testRunReply = []byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
var testRunReplyFail = []byte{
|
||||
0x00, 0x00, 0x00, 0x8c, // length
|
||||
0x20, 0x00, 0x80, 0x87, // program
|
||||
0x00, 0x00, 0x00, 0x01, // version
|
||||
0x00, 0x00, 0x00, 0x01, // procedure
|
||||
0x00, 0x00, 0x00, 0x01, // type
|
||||
0x00, 0x00, 0x00, 0x0a, // serial
|
||||
0x00, 0x00, 0x00, 0x00, // status
|
||||
|
||||
// {"id":"libvirt-68","error":{"class":"CommandNotFound","desc":"The command drive-foo has not been found"}}`
|
||||
0x00, 0x00, 0x00, 0x69, 0x7b, 0x22, 0x69, 0x64,
|
||||
0x22, 0x3a, 0x22, 0x6c, 0x69, 0x62, 0x76, 0x69,
|
||||
0x72, 0x74, 0x2d, 0x36, 0x38, 0x22, 0x2c, 0x22,
|
||||
0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x3a, 0x7b,
|
||||
0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a,
|
||||
0x22, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
|
||||
0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64,
|
||||
0x22, 0x2c, 0x22, 0x64, 0x65, 0x73, 0x63, 0x22,
|
||||
0x3a, 0x22, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x72,
|
||||
0x69, 0x76, 0x65, 0x2d, 0x66, 0x6f, 0x6f, 0x20,
|
||||
0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20,
|
||||
0x62, 0x65, 0x65, 0x6e, 0x20, 0x66, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x22, 0x7d, 0x7d, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
var testDomainsReply = []byte{
|
||||
0x00, 0x00, 0x00, 0x6c, // length
|
||||
0x20, 0x00, 0x80, 0x86, // program
|
||||
@ -171,6 +197,7 @@ var testVersionReply = []byte{
|
||||
type MockLibvirt struct {
|
||||
net.Conn
|
||||
Test net.Conn
|
||||
Fail bool
|
||||
serial uint32
|
||||
}
|
||||
|
||||
@ -233,8 +260,12 @@ func (m *MockLibvirt) handleQEMU(procedure uint32, conn net.Conn) {
|
||||
case constants.QEMUConnectDomainMonitorEventDeregister:
|
||||
conn.Write(m.reply(testDeregisterEvent))
|
||||
case constants.QEMUDomainMonitor:
|
||||
if m.Fail {
|
||||
conn.Write(m.reply(testRunReplyFail))
|
||||
} else {
|
||||
conn.Write(m.reply(testRunReply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reply automatically injects the correct serial
|
||||
|
Loading…
Reference in New Issue
Block a user