Handle TypedParamStrings (#46)

* Handle TypedParamStrings

Add handling for TypedParamStrings, which are different from the other
TypedParam... types in that the decoded value is variable-sized.
This commit is contained in:
Geoff Hickey 2017-09-15 10:48:46 -04:00 committed by GitHub
parent c8e4b6a7b8
commit a339d0ac95
2 changed files with 146 additions and 48 deletions

View File

@ -19,7 +19,6 @@ package libvirt
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/binary"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -113,65 +112,129 @@ const (
// FlagDomainAffectConfig means affect the persistent domain state. // FlagDomainAffectConfig means affect the persistent domain state.
FlagDomainAffectConfig FlagDomainAffectConfig
// FlagTypedParamStringOkay tells the server that this client understands // FlagTypedParamStringOkay tells the server that this client understands
// TypedParameStrings. // TypedParamStrings.
FlagTypedParamStringOkay FlagTypedParamStringOkay
) )
// Consts relating to TypedParams: // Consts relating to TypedParams:
const ( const (
// TypedParamInt is a C int. // TypeParamInt is a C int.
TypedParamInt = iota + 1 TypeParamInt = iota + 1
// TypedParamUInt is a C unsigned int. // TypeParamUInt is a C unsigned int.
TypedParamUInt TypeParamUInt
// TypedParamLLong is a C long long int. // TypeParamLLong is a C long long int.
TypedParamLLong TypeParamLLong
// TypedParamULLong is a C unsigned long long int. // TypeParamULLong is a C unsigned long long int.
TypedParamULLong TypeParamULLong
// TypedParamDouble is a C double. // TypeParamDouble is a C double.
TypedParamDouble TypeParamDouble
// TypedParamBoolean is a C char. // TypeParamBoolean is a C char.
TypedParamBoolean TypeParamBoolean
// TypedParamString is a C char*. // TypeParamString is a C char*.
TypedParamString TypeParamString
// TypedParamLast is just an end-of-enum marker. // TypeParamLast is just an end-of-enum marker.
TypedParamLast TypeParamLast
) )
// TypedParam represents libvirt's virTypedParameter, which is used to pass // TypedParam represents libvirt's virTypedParameter, which is used to pass
// typed parameters to libvirt functions. The `Value` field defined as a union // typed parameters to libvirt functions. This is defined as an interface, and
// in libvirt, and given the current set of types inside it, is 8 bytes long. // implemented by TypedParam... concrete types.
type TypedParam struct { type TypedParam interface {
Field string Field() string
Type int Value() interface{}
Value [8]byte }
// TypedParamInt contains a 32-bit signed integer.
type TypedParamInt struct {
Fld string
PType int32
Padding [4]byte
Val int32
}
// Field returns the field name, a string name for the parameter.
func (tp *TypedParamInt) Field() string {
return tp.Fld
}
// Value returns the value for the typed parameter, as an empty interface.
func (tp *TypedParamInt) Value() interface{} {
return tp.Val
} }
// NewTypedParamInt returns a TypedParam encoding for an int. // NewTypedParamInt returns a TypedParam encoding for an int.
func NewTypedParamInt(name string, v int) *TypedParam { func NewTypedParamInt(name string, v int32) *TypedParamInt {
// Truncate the field name if it's longer than the limit. // Truncate the field name if it's longer than the limit.
if len(name) > constants.TypedParamFieldLength { if len(name) > constants.TypedParamFieldLength {
name = name[:constants.TypedParamFieldLength] name = name[:constants.TypedParamFieldLength]
} }
tp := TypedParam{ tp := TypedParamInt{
Field: name, Fld: name,
Type: TypedParamInt, PType: TypeParamInt,
Val: v,
} }
binary.BigEndian.PutUint32(tp.Value[:], uint32(v))
return &tp return &tp
} }
// TypedParamULongLong contains a 64-bit unsigned integer.
type TypedParamULongLong struct {
Fld string
PType int32
Val uint64
}
// Field returns the field name, a string name for the parameter.
func (tp *TypedParamULongLong) Field() string {
return tp.Fld
}
// Value returns the value for the typed parameter, as an empty interface.
func (tp *TypedParamULongLong) Value() interface{} {
return tp.Val
}
// NewTypedParamULongLong returns a TypedParam encoding for an unsigned long long. // NewTypedParamULongLong returns a TypedParam encoding for an unsigned long long.
func NewTypedParamULongLong(name string, v uint64) *TypedParam { func NewTypedParamULongLong(name string, v uint64) *TypedParamULongLong {
// Truncate the field name if it's longer than the limit. // Truncate the field name if it's longer than the limit.
if len(name) > constants.TypedParamFieldLength { if len(name) > constants.TypedParamFieldLength {
name = name[:constants.TypedParamFieldLength] name = name[:constants.TypedParamFieldLength]
} }
tp := TypedParam{ tp := TypedParamULongLong{
Field: name, Fld: name,
Type: TypedParamULLong, PType: TypeParamULLong,
Val: v,
}
return &tp
}
// TypedParamString contains a string parameter.
type TypedParamString struct {
Fld string
PType int
Val string
}
// Field returns the field name, a string name for the parameter.
func (tp *TypedParamString) Field() string {
return tp.Fld
}
// Value returns the value for the typed parameter, as an empty interface.
func (tp *TypedParamString) Value() interface{} {
return tp.Val
}
// NewTypedParamString returns a typed parameter containing the passed string.
func NewTypedParamString(name string, v string) TypedParam {
if len(name) > constants.TypedParamFieldLength {
name = name[:constants.TypedParamFieldLength]
}
tp := TypedParamString{
Fld: name,
PType: TypeParamString,
Val: v,
} }
binary.BigEndian.PutUint64(tp.Value[:], v)
return &tp return &tp
} }
@ -1295,6 +1358,7 @@ type BlockLimit struct {
// not necessarily the only possible values; different libvirt versions may add // not necessarily the only possible values; different libvirt versions may add
// or remove parameters from this list. // or remove parameters from this list.
const ( const (
QEMUBlockIOGroupName = "group_name"
QEMUBlockIOTotalBytesSec = "total_bytes_sec" QEMUBlockIOTotalBytesSec = "total_bytes_sec"
QEMUBlockIOReadBytesSec = "read_bytes_sec" QEMUBlockIOReadBytesSec = "read_bytes_sec"
QEMUBlockIOWriteBytesSec = "write_bytes_sec" QEMUBlockIOWriteBytesSec = "write_bytes_sec"
@ -1347,14 +1411,13 @@ func (l *Libvirt) SetBlockIOTune(dom string, disk string, limits ...BlockLimit)
for _, limit := range limits { for _, limit := range limits {
tp := NewTypedParamULongLong(limit.Name, limit.Value) tp := NewTypedParamULongLong(limit.Name, limit.Value)
payload.Params = append(payload.Params, *tp) payload.Params = append(payload.Params, tp)
} }
buf, err := encode(&payload) buf, err := encode(&payload)
if err != nil { if err != nil {
return err return err
} }
resp, err := l.request(constants.ProcDomainSetBlockIOTune, constants.ProgramRemote, &buf) resp, err := l.request(constants.ProcDomainSetBlockIOTune, constants.ProgramRemote, &buf)
if err != nil { if err != nil {
return err return err
@ -1403,20 +1466,49 @@ func (l *Libvirt) GetBlockIOTune(dom string, disk string) ([]BlockLimit, error)
return nil, decodeError(r.Payload) return nil, decodeError(r.Payload)
} }
dec := xdr.NewDecoder(bytes.NewReader(r.Payload)) var limits []BlockLimit
result := struct { rdr := bytes.NewReader(r.Payload)
Limits []TypedParam dec := xdr.NewDecoder(rdr)
ParamCount uint32
}{} // find out how many params were returned
_, err = dec.Decode(&result) paramCount, _, err := dec.DecodeInt()
if err != nil { if err != nil {
return nil, err return nil, err
} }
limits := make([]BlockLimit, len(result.Limits)) // now decode each of the returned TypedParams. To do this we read the field
for ix := range result.Limits { // name and type, then use the type information to decode the value.
limits[ix].Name = result.Limits[ix].Field for param := int32(0); param < paramCount; param++ {
limits[ix].Value = binary.BigEndian.Uint64(result.Limits[ix].Value[:]) // Get the field name
name, _, err := dec.DecodeString()
if err != nil {
return nil, err
}
// ...and the type
ptype, _, err := dec.DecodeInt()
if err != nil {
return nil, err
}
// Now we can read the actual value.
switch ptype {
case TypeParamULLong:
var val uint64
_, err = dec.Decode(&val)
if err != nil {
return nil, err
}
lim := BlockLimit{name, val}
limits = append(limits, lim)
case TypeParamString:
var val string
_, err = dec.Decode(&val)
if err != nil {
return nil, err
}
// This routine doesn't currently return strings. As of libvirt 3+,
// there's one string here, `group_name`.
}
} }
return limits, nil return limits, nil

View File

@ -414,7 +414,7 @@ var testSetBlockIoTuneReply = []byte{
// the result returned by an actual call to GetBlockIoTune, and then adding the // the result returned by an actual call to GetBlockIoTune, and then adding the
// standard header to the beginning. The length parameter has to be correct! // standard header to the beginning. The length parameter has to be correct!
var testGetBlockIoTuneReply = []byte{ var testGetBlockIoTuneReply = []byte{
0x00, 0x00, 0x02, 0xe0, // length 0x00, 0x00, 0x03, 0x00, // length
0x20, 0x00, 0x80, 0x86, // program 0x20, 0x00, 0x80, 0x86, // program
0x00, 0x00, 0x00, 0x01, // version 0x00, 0x00, 0x00, 0x01, // version
0x00, 0x00, 0x00, 0xfd, // procedure 0x00, 0x00, 0x00, 0xfd, // procedure
@ -422,7 +422,7 @@ var testGetBlockIoTuneReply = []byte{
0x00, 0x00, 0x00, 0x00, // serial 0x00, 0x00, 0x00, 0x00, // serial
0x00, 0x00, 0x00, 0x00, // status 0x00, 0x00, 0x00, 0x00, // status
0x0, 0x0, 0x0, 0x13, // 13 TypedParams follow 0x0, 0x0, 0x0, 0x14, // 14 TypedParams follow
0x0, 0x0, 0x0, 0xf, // field name is 15 bytes, padded to a multiple of 4 0x0, 0x0, 0x0, 0xf, // field name is 15 bytes, padded to a multiple of 4
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0,
@ -519,6 +519,12 @@ var testGetBlockIoTuneReply = []byte{
0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xa, // This is field "group_name", a string (type 7), whose value is "somename"
0x67, 0x72, 0x6F, 0x75, 0x70, 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x0, 0x0,
0x0, 0x0, 0x0, 0x7,
0x0, 0x0, 0x0, 0x8,
0x73, 0x6F, 0x6D, 0x65, 0x6E, 0x61, 0x6D, 0x65,
0x0, 0x0, 0x0, 0x0, // End of TypedParams 0x0, 0x0, 0x0, 0x0, // End of TypedParams
} }