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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -68,6 +69,14 @@ type DomainEvent struct {
|
|||||||
Details []byte
|
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.
|
// Connect establishes communication with the libvirt server.
|
||||||
// The underlying libvirt socket connection must be previously established.
|
// The underlying libvirt socket connection must be previously established.
|
||||||
func (l *Libvirt) Connect() error {
|
func (l *Libvirt) Connect() error {
|
||||||
@ -225,10 +234,16 @@ func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := <-resp
|
res := <-resp
|
||||||
|
// check for libvirt errors
|
||||||
if res.Status != StatusOK {
|
if res.Status != StatusOK {
|
||||||
return nil, decodeError(res.Payload)
|
return nil, decodeError(res.Payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for QEMU process errors
|
||||||
|
if err = getQEMUError(res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
r := bytes.NewReader(res.Payload)
|
r := bytes.NewReader(res.Payload)
|
||||||
dec := xdr.NewDecoder(r)
|
dec := xdr.NewDecoder(r)
|
||||||
data, _, err := dec.DecodeFixedOpaque(int32(r.Len()))
|
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
|
// drop QMP control characters from start of line, and drop
|
||||||
// any trailing NULL characters from the end
|
// 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.
|
// Version returns the version of the libvirt daemon.
|
||||||
@ -308,6 +323,29 @@ func (l *Libvirt) lookup(name string) (*Domain, error) {
|
|||||||
return &d, nil
|
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.
|
// New configures a new Libvirt RPC connection.
|
||||||
func New(conn net.Conn) *Libvirt {
|
func New(conn net.Conn) *Libvirt {
|
||||||
l := &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) {
|
func TestVersion(t *testing.T) {
|
||||||
conn := libvirttest.New()
|
conn := libvirttest.New()
|
||||||
l := New(conn)
|
l := New(conn)
|
||||||
|
@ -120,6 +120,32 @@ var testRunReply = []byte{
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
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{
|
var testDomainsReply = []byte{
|
||||||
0x00, 0x00, 0x00, 0x6c, // length
|
0x00, 0x00, 0x00, 0x6c, // length
|
||||||
0x20, 0x00, 0x80, 0x86, // program
|
0x20, 0x00, 0x80, 0x86, // program
|
||||||
@ -171,6 +197,7 @@ var testVersionReply = []byte{
|
|||||||
type MockLibvirt struct {
|
type MockLibvirt struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
Test net.Conn
|
Test net.Conn
|
||||||
|
Fail bool
|
||||||
serial uint32
|
serial uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,9 +260,13 @@ func (m *MockLibvirt) handleQEMU(procedure uint32, conn net.Conn) {
|
|||||||
case constants.QEMUConnectDomainMonitorEventDeregister:
|
case constants.QEMUConnectDomainMonitorEventDeregister:
|
||||||
conn.Write(m.reply(testDeregisterEvent))
|
conn.Write(m.reply(testDeregisterEvent))
|
||||||
case constants.QEMUDomainMonitor:
|
case constants.QEMUDomainMonitor:
|
||||||
|
if m.Fail {
|
||||||
|
conn.Write(m.reply(testRunReplyFail))
|
||||||
|
} else {
|
||||||
conn.Write(m.reply(testRunReply))
|
conn.Write(m.reply(testRunReply))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// reply automatically injects the correct serial
|
// reply automatically injects the correct serial
|
||||||
// number into the provided response buffer.
|
// number into the provided response buffer.
|
||||||
|
Loading…
Reference in New Issue
Block a user