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:
		
							
								
								
									
										184
									
								
								libvirt.go
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								libvirt.go
									
									
									
									
									
								
							@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user