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:
parent
c8e4b6a7b8
commit
a339d0ac95
184
libvirt.go
184
libvirt.go
@ -19,7 +19,6 @@ package libvirt
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -113,65 +112,129 @@ const (
|
||||
// FlagDomainAffectConfig means affect the persistent domain state.
|
||||
FlagDomainAffectConfig
|
||||
// FlagTypedParamStringOkay tells the server that this client understands
|
||||
// TypedParameStrings.
|
||||
// TypedParamStrings.
|
||||
FlagTypedParamStringOkay
|
||||
)
|
||||
|
||||
// Consts relating to TypedParams:
|
||||
const (
|
||||
// TypedParamInt is a C int.
|
||||
TypedParamInt = iota + 1
|
||||
// TypedParamUInt is a C unsigned int.
|
||||
TypedParamUInt
|
||||
// TypedParamLLong is a C long long int.
|
||||
TypedParamLLong
|
||||
// TypedParamULLong is a C unsigned long long int.
|
||||
TypedParamULLong
|
||||
// TypedParamDouble is a C double.
|
||||
TypedParamDouble
|
||||
// TypedParamBoolean is a C char.
|
||||
TypedParamBoolean
|
||||
// TypedParamString is a C char*.
|
||||
TypedParamString
|
||||
// TypeParamInt is a C int.
|
||||
TypeParamInt = iota + 1
|
||||
// TypeParamUInt is a C unsigned int.
|
||||
TypeParamUInt
|
||||
// TypeParamLLong is a C long long int.
|
||||
TypeParamLLong
|
||||
// TypeParamULLong is a C unsigned long long int.
|
||||
TypeParamULLong
|
||||
// TypeParamDouble is a C double.
|
||||
TypeParamDouble
|
||||
// TypeParamBoolean is a C char.
|
||||
TypeParamBoolean
|
||||
// TypeParamString is a C char*.
|
||||
TypeParamString
|
||||
|
||||
// TypedParamLast is just an end-of-enum marker.
|
||||
TypedParamLast
|
||||
// TypeParamLast is just an end-of-enum marker.
|
||||
TypeParamLast
|
||||
)
|
||||
|
||||
// TypedParam represents libvirt's virTypedParameter, which is used to pass
|
||||
// typed parameters to libvirt functions. The `Value` field defined as a union
|
||||
// in libvirt, and given the current set of types inside it, is 8 bytes long.
|
||||
type TypedParam struct {
|
||||
Field string
|
||||
Type int
|
||||
Value [8]byte
|
||||
// typed parameters to libvirt functions. This is defined as an interface, and
|
||||
// implemented by TypedParam... concrete types.
|
||||
type TypedParam interface {
|
||||
Field() string
|
||||
Value() interface{}
|
||||
}
|
||||
|
||||
// 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.
|
||||
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.
|
||||
if len(name) > constants.TypedParamFieldLength {
|
||||
name = name[:constants.TypedParamFieldLength]
|
||||
}
|
||||
tp := TypedParam{
|
||||
Field: name,
|
||||
Type: TypedParamInt,
|
||||
tp := TypedParamInt{
|
||||
Fld: name,
|
||||
PType: TypeParamInt,
|
||||
Val: v,
|
||||
}
|
||||
binary.BigEndian.PutUint32(tp.Value[:], uint32(v))
|
||||
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.
|
||||
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.
|
||||
if len(name) > constants.TypedParamFieldLength {
|
||||
name = name[:constants.TypedParamFieldLength]
|
||||
}
|
||||
tp := TypedParam{
|
||||
Field: name,
|
||||
Type: TypedParamULLong,
|
||||
tp := TypedParamULongLong{
|
||||
Fld: name,
|
||||
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
|
||||
}
|
||||
|
||||
@ -1295,6 +1358,7 @@ type BlockLimit struct {
|
||||
// not necessarily the only possible values; different libvirt versions may add
|
||||
// or remove parameters from this list.
|
||||
const (
|
||||
QEMUBlockIOGroupName = "group_name"
|
||||
QEMUBlockIOTotalBytesSec = "total_bytes_sec"
|
||||
QEMUBlockIOReadBytesSec = "read_bytes_sec"
|
||||
QEMUBlockIOWriteBytesSec = "write_bytes_sec"
|
||||
@ -1347,14 +1411,13 @@ func (l *Libvirt) SetBlockIOTune(dom string, disk string, limits ...BlockLimit)
|
||||
|
||||
for _, limit := range limits {
|
||||
tp := NewTypedParamULongLong(limit.Name, limit.Value)
|
||||
payload.Params = append(payload.Params, *tp)
|
||||
payload.Params = append(payload.Params, tp)
|
||||
}
|
||||
|
||||
buf, err := encode(&payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := l.request(constants.ProcDomainSetBlockIOTune, constants.ProgramRemote, &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1403,20 +1466,49 @@ func (l *Libvirt) GetBlockIOTune(dom string, disk string) ([]BlockLimit, error)
|
||||
return nil, decodeError(r.Payload)
|
||||
}
|
||||
|
||||
dec := xdr.NewDecoder(bytes.NewReader(r.Payload))
|
||||
result := struct {
|
||||
Limits []TypedParam
|
||||
ParamCount uint32
|
||||
}{}
|
||||
_, err = dec.Decode(&result)
|
||||
var limits []BlockLimit
|
||||
rdr := bytes.NewReader(r.Payload)
|
||||
dec := xdr.NewDecoder(rdr)
|
||||
|
||||
// find out how many params were returned
|
||||
paramCount, _, err := dec.DecodeInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
limits := make([]BlockLimit, len(result.Limits))
|
||||
for ix := range result.Limits {
|
||||
limits[ix].Name = result.Limits[ix].Field
|
||||
limits[ix].Value = binary.BigEndian.Uint64(result.Limits[ix].Value[:])
|
||||
// now decode each of the returned TypedParams. To do this we read the field
|
||||
// name and type, then use the type information to decode the value.
|
||||
for param := int32(0); param < paramCount; param++ {
|
||||
// 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
|
||||
|
@ -414,7 +414,7 @@ var testSetBlockIoTuneReply = []byte{
|
||||
// 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!
|
||||
var testGetBlockIoTuneReply = []byte{
|
||||
0x00, 0x00, 0x02, 0xe0, // length
|
||||
0x00, 0x00, 0x03, 0x00, // length
|
||||
0x20, 0x00, 0x80, 0x86, // program
|
||||
0x00, 0x00, 0x00, 0x01, // version
|
||||
0x00, 0x00, 0x00, 0xfd, // procedure
|
||||
@ -422,7 +422,7 @@ var testGetBlockIoTuneReply = []byte{
|
||||
0x00, 0x00, 0x00, 0x00, // serial
|
||||
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
|
||||
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, 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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user