Add Get/SetBlockIoTune to go-libvirt API. (#45)
* Add Get/SetBlockIoTune to go-libvirt API. This adds two libvirt entry points to the go-libvirt API: virDomainSetBlockIoTune and virDomainGetBlockIoTune. These can be used to control block device throttling for a VM.
This commit is contained in:
		| @@ -53,6 +53,8 @@ const ( | |||||||
| 	ProcDomainUndefineFlags        = 231 | 	ProcDomainUndefineFlags        = 231 | ||||||
| 	ProcDomainDestroyFlags         = 234 | 	ProcDomainDestroyFlags         = 234 | ||||||
| 	ProcDomainReset                = 245 | 	ProcDomainReset                = 245 | ||||||
|  | 	ProcDomainSetBlockIOTune       = 252 | ||||||
|  | 	ProcDomainGetBlockIOTune       = 253 | ||||||
| 	ProcDomainShutdownFlags        = 258 | 	ProcDomainShutdownFlags        = 258 | ||||||
| 	ProcConnectListAllDomains      = 273 | 	ProcConnectListAllDomains      = 273 | ||||||
| 	ProcConnectListAllStoragePools = 281 | 	ProcConnectListAllStoragePools = 281 | ||||||
| @@ -78,4 +80,8 @@ const ( | |||||||
|  |  | ||||||
| 	// UUIDSize is the length of a UUID, in bytes. | 	// UUIDSize is the length of a UUID, in bytes. | ||||||
| 	UUIDSize = 16 | 	UUIDSize = 16 | ||||||
|  |  | ||||||
|  | 	// TypedParamFieldLength is VIR_TYPED_PARAM_FIELD_LENGTH, and is the maximum | ||||||
|  | 	// length of the Field string in virTypedParameter structs. | ||||||
|  | 	TypedParamFieldLength = 80 | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										225
									
								
								libvirt.go
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								libvirt.go
									
									
									
									
									
								
							| @@ -19,6 +19,7 @@ package libvirt | |||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
| 	"bytes" | 	"bytes" | ||||||
|  | 	"encoding/binary" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -94,6 +95,86 @@ type qemuError struct { | |||||||
| // DomainXMLFlags specifies options for dumping a domain's XML. | // DomainXMLFlags specifies options for dumping a domain's XML. | ||||||
| type DomainXMLFlags uint32 | type DomainXMLFlags uint32 | ||||||
|  |  | ||||||
|  | // DomainAffectFlags specifies options for whether an operation affects the | ||||||
|  | // running VM, or the persistent VM configuration on disk. See FlagDomain... | ||||||
|  | // consts for values. | ||||||
|  | type DomainAffectFlags uint32 | ||||||
|  |  | ||||||
|  | // Consts used for flags | ||||||
|  |  | ||||||
|  | // virDomainModificationImpact and virTypedParameterFlags values. These are | ||||||
|  | // combined here because they are both used to set the same flags fields in the | ||||||
|  | // libvirt API. | ||||||
|  | const ( | ||||||
|  | 	// FlagDomainAffectCurrent means affect the current domain state | ||||||
|  | 	FlagDomainAffectCurrent DomainAffectFlags = 0 | ||||||
|  | 	// FlagDomainAffectLive means affect the running domain state | ||||||
|  | 	FlagDomainAffectLive = 1 << (iota - 1) | ||||||
|  | 	// FlagDomainAffectConfig means affect the persistent domain state. | ||||||
|  | 	FlagDomainAffectConfig | ||||||
|  | 	// FlagTypedParamStringOkay tells the server that this client understands | ||||||
|  | 	// TypedParameStrings. | ||||||
|  | 	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 | ||||||
|  |  | ||||||
|  | 	// TypedParamLast is just an end-of-enum marker. | ||||||
|  | 	TypedParamLast | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // 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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewTypedParamInt returns a TypedParam encoding for an int. | ||||||
|  | func NewTypedParamInt(name string, v int) *TypedParam { | ||||||
|  | 	// 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, | ||||||
|  | 	} | ||||||
|  | 	binary.BigEndian.PutUint32(tp.Value[:], uint32(v)) | ||||||
|  | 	return &tp | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewTypedParamULongLong returns a TypedParam encoding for an unsigned long long. | ||||||
|  | func NewTypedParamULongLong(name string, v uint64) *TypedParam { | ||||||
|  | 	// 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, | ||||||
|  | 	} | ||||||
|  | 	binary.BigEndian.PutUint64(tp.Value[:], v) | ||||||
|  | 	return &tp | ||||||
|  | } | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	// DomainXMLFlagSecure dumps XML with sensitive information included. | 	// DomainXMLFlagSecure dumps XML with sensitive information included. | ||||||
| 	DomainXMLFlagSecure DomainXMLFlags = 1 << iota | 	DomainXMLFlagSecure DomainXMLFlags = 1 << iota | ||||||
| @@ -601,7 +682,7 @@ func (l *Libvirt) Events(dom string) (<-chan DomainEvent, error) { | |||||||
|  |  | ||||||
| 	res := <-resp | 	res := <-resp | ||||||
| 	if res.Status != StatusOK { | 	if res.Status != StatusOK { | ||||||
| 		err := decodeError(res.Payload) | 		err = decodeError(res.Payload) | ||||||
| 		if err == ErrUnsupported { | 		if err == ErrUnsupported { | ||||||
| 			return nil, ErrEventsNotSupported | 			return nil, ErrEventsNotSupported | ||||||
| 		} | 		} | ||||||
| @@ -1199,6 +1280,148 @@ func (l *Libvirt) Reset(dom string) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // BlockLimit contains a name and value pair for a Get/SetBlockIOTune limit. The | ||||||
|  | // Name field is the name of the limit (to see a list of the limits that can be | ||||||
|  | // applied, execute the 'blkdeviotune' command on a VM in virsh). Callers can | ||||||
|  | // use the QEMUBlockIO... constants below for the Name value. The Value field is | ||||||
|  | // the limit to apply. | ||||||
|  | type BlockLimit struct { | ||||||
|  | 	Name  string | ||||||
|  | 	Value uint64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BlockIOTune-able values. These tunables are different for different | ||||||
|  | // hypervisors; currently only the tunables for QEMU are defined here. These are | ||||||
|  | // not necessarily the only possible values; different libvirt versions may add | ||||||
|  | // or remove parameters from this list. | ||||||
|  | const ( | ||||||
|  | 	QEMUBlockIOTotalBytesSec          = "total_bytes_sec" | ||||||
|  | 	QEMUBlockIOReadBytesSec           = "read_bytes_sec" | ||||||
|  | 	QEMUBlockIOWriteBytesSec          = "write_bytes_sec" | ||||||
|  | 	QEMUBlockIOTotalIOPSSec           = "total_iops_sec" | ||||||
|  | 	QEMUBlockIOReadIOPSSec            = "read_iops_sec" | ||||||
|  | 	QEMUBlockIOWriteIOPSSec           = "write_iops_sec" | ||||||
|  | 	QEMUBlockIOTotalBytesSecMax       = "total_bytes_sec_max" | ||||||
|  | 	QEMUBlockIOReadBytesSecMax        = "read_bytes_sec_max" | ||||||
|  | 	QEMUBlockIOWriteBytesSecMax       = "write_bytes_sec_max" | ||||||
|  | 	QEMUBlockIOTotalIOPSSecMax        = "total_iops_sec_max" | ||||||
|  | 	QEMUBlockIOReadIOPSSecMax         = "read_iops_sec_max" | ||||||
|  | 	QEMUBlockIOWriteIOPSSecMax        = "write_iops_sec_max" | ||||||
|  | 	QEMUBlockIOSizeIOPSSec            = "size_iops_sec" | ||||||
|  | 	QEMUBlockIOTotalBytesSecMaxLength = "total_bytes_sec_max_length" | ||||||
|  | 	QEMUBlockIOReadBytesSecMaxLength  = "read_bytes_sec_max_length" | ||||||
|  | 	QEMUBlockIOWriteBytesSecMaxLength = "write_bytes_sec_max_length" | ||||||
|  | 	QEMUBlockIOTotalIOPSSecMaxLength  = "total_iops_sec_max_length" | ||||||
|  | 	QEMUBlockIOReadIOPSSecMaxLength   = "read_iops_sec_max_length" | ||||||
|  | 	QEMUBlockIOWriteIOPSSecMaxLength  = "write_iops_sec_max_length" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // SetBlockIOTune changes the per-device block I/O tunables within a guest. | ||||||
|  | // Parameters are the name of the VM, the name of the disk device to which the | ||||||
|  | // limits should be applied, and 1 or more BlockLimit structs containing the | ||||||
|  | // actual limits. | ||||||
|  | // | ||||||
|  | // The limits which can be applied here are enumerated in the QEMUBlockIO... | ||||||
|  | // constants above, and you can also see the full list by executing the | ||||||
|  | // 'blkdeviotune' command on a VM in virsh. | ||||||
|  | // | ||||||
|  | // Example usage: | ||||||
|  | //  SetBlockIOTune("vm-name", "vda", BlockLimit{libvirt.QEMUBlockIOWriteBytesSec, 1000000}) | ||||||
|  | func (l *Libvirt) SetBlockIOTune(dom string, disk string, limits ...BlockLimit) error { | ||||||
|  | 	d, err := l.lookup(dom) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainSetBlockIoTune | ||||||
|  | 	payload := struct { | ||||||
|  | 		Domain Domain | ||||||
|  | 		Disk   string | ||||||
|  | 		Params []TypedParam | ||||||
|  | 		Flags  DomainAffectFlags | ||||||
|  | 	}{ | ||||||
|  | 		Domain: *d, | ||||||
|  | 		Disk:   disk, | ||||||
|  | 		Flags:  FlagDomainAffectLive, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, limit := range limits { | ||||||
|  | 		tp := NewTypedParamULongLong(limit.Name, limit.Value) | ||||||
|  | 		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 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r := <-resp | ||||||
|  | 	if r.Status != StatusOK { | ||||||
|  | 		return decodeError(r.Payload) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetBlockIOTune returns a slice containing the current block I/O tunables for | ||||||
|  | // a disk. | ||||||
|  | func (l *Libvirt) GetBlockIOTune(dom string, disk string) ([]BlockLimit, error) { | ||||||
|  | 	d, err := l.lookup(dom) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	payload := struct { | ||||||
|  | 		Domain     Domain | ||||||
|  | 		Disk       []string | ||||||
|  | 		ParamCount uint32 | ||||||
|  | 		Flags      DomainAffectFlags | ||||||
|  | 	}{ | ||||||
|  | 		Domain:     *d, | ||||||
|  | 		Disk:       []string{disk}, | ||||||
|  | 		ParamCount: 32, | ||||||
|  | 		Flags:      FlagTypedParamStringOkay, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf, err := encode(&payload) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp, err := l.request(constants.ProcDomainGetBlockIOTune, constants.ProgramRemote, &buf) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r := <-resp | ||||||
|  | 	if r.Status != StatusOK { | ||||||
|  | 		return nil, decodeError(r.Payload) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dec := xdr.NewDecoder(bytes.NewReader(r.Payload)) | ||||||
|  | 	result := struct { | ||||||
|  | 		Limits     []TypedParam | ||||||
|  | 		ParamCount uint32 | ||||||
|  | 	}{} | ||||||
|  | 	_, err = dec.Decode(&result) | ||||||
|  | 	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[:]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return limits, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // lookup returns a domain as seen by libvirt. | // lookup returns a domain as seen by libvirt. | ||||||
| func (l *Libvirt) lookup(name string) (*Domain, error) { | func (l *Libvirt) lookup(name string) (*Domain, error) { | ||||||
| 	payload := struct { | 	payload := struct { | ||||||
|   | |||||||
| @@ -437,3 +437,27 @@ func TestReset(t *testing.T) { | |||||||
| 		t.Fatalf("unexpected reset error: %v", err) | 		t.Fatalf("unexpected reset error: %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestSetBlockIOTune(t *testing.T) { | ||||||
|  | 	conn := libvirttest.New() | ||||||
|  | 	l := New(conn) | ||||||
|  |  | ||||||
|  | 	if err := l.SetBlockIOTune("test", "vda", BlockLimit{"write_bytes_sec", 5000000}); err != nil { | ||||||
|  | 		t.Fatalf("unexpected SetBlockIOTune error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetBlockIOTune(t *testing.T) { | ||||||
|  | 	conn := libvirttest.New() | ||||||
|  | 	l := New(conn) | ||||||
|  |  | ||||||
|  | 	limits, err := l.GetBlockIOTune("do-test", "vda") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("unexpected GetBlockIOTune error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lim := BlockLimit{"write_bytes_sec", 500000} | ||||||
|  | 	if limits[2] != lim { | ||||||
|  | 		t.Fatalf("unexpected result in limits list: %v", limits[2]) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -21,8 +21,9 @@ import ( | |||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
|  |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/digitalocean/go-libvirt/internal/constants" |  | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
|  | 	"github.com/digitalocean/go-libvirt/internal/constants" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var testDomainResponse = []byte{ | var testDomainResponse = []byte{ | ||||||
| @@ -399,6 +400,128 @@ var testRebootReply = []byte{ | |||||||
| 	0x00, 0x00, 0x00, 0x00, // status | 	0x00, 0x00, 0x00, 0x00, // status | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var testSetBlockIoTuneReply = []byte{ | ||||||
|  | 	0x00, 0x00, 0x00, 0x1c, // length | ||||||
|  | 	0x20, 0x00, 0x80, 0x86, // program | ||||||
|  | 	0x00, 0x00, 0x00, 0x01, // version | ||||||
|  | 	0x00, 0x00, 0x00, 0xfc, // procedure | ||||||
|  | 	0x00, 0x00, 0x00, 0x00, // type | ||||||
|  | 	0x00, 0x00, 0x00, 0x00, // serial | ||||||
|  | 	0x00, 0x00, 0x00, 0x00, // status | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // This result block was obtained by calling `fmt.Printf("%#v", r.Payload)` on | ||||||
|  | // 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 | ||||||
|  | 	0x20, 0x00, 0x80, 0x86, // program | ||||||
|  | 	0x00, 0x00, 0x00, 0x01, // version | ||||||
|  | 	0x00, 0x00, 0x00, 0xfd, // procedure | ||||||
|  | 	0x00, 0x00, 0x00, 0x00, // type | ||||||
|  | 	0x00, 0x00, 0x00, 0x00, // serial | ||||||
|  | 	0x00, 0x00, 0x00, 0x00, // status | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x13, // 13 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, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, // type | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // value | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0xe, | ||||||
|  | 	0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0xf, | ||||||
|  | 	0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xa1, 0x20, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0xe, | ||||||
|  | 	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0xd, | ||||||
|  | 	0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0xe, | ||||||
|  | 	0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x13, | ||||||
|  | 	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x12, | ||||||
|  | 	0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x13, | ||||||
|  | 	0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc3, 0x50, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x12, | ||||||
|  | 	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x11, | ||||||
|  | 	0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x0, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x12, | ||||||
|  | 	0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0xd, | ||||||
|  | 	0x73, 0x69, 0x7a, 0x65, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x0, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x1a, | ||||||
|  | 	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x19, | ||||||
|  | 	0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x0, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x1a, | ||||||
|  | 	0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x19, | ||||||
|  | 	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x0, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x18, | ||||||
|  | 	0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x19, | ||||||
|  | 	0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x69, 0x6f, 0x70, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x0, 0x0, 0x0, | ||||||
|  | 	0x0, 0x0, 0x0, 0x4, | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||||||
|  |  | ||||||
|  | 	0x0, 0x0, 0x0, 0x0, // End of TypedParams | ||||||
|  | } | ||||||
|  |  | ||||||
| // MockLibvirt provides a mock libvirt server for testing. | // MockLibvirt provides a mock libvirt server for testing. | ||||||
| type MockLibvirt struct { | type MockLibvirt struct { | ||||||
| 	net.Conn | 	net.Conn | ||||||
| @@ -486,6 +609,10 @@ func (m *MockLibvirt) handleRemote(procedure uint32, conn net.Conn) { | |||||||
| 		conn.Write(m.reply(testCreateWithFlags)) | 		conn.Write(m.reply(testCreateWithFlags)) | ||||||
| 	case constants.ProcDomainShutdownFlags: | 	case constants.ProcDomainShutdownFlags: | ||||||
| 		conn.Write(m.reply(testShutdownReply)) | 		conn.Write(m.reply(testShutdownReply)) | ||||||
|  | 	case constants.ProcDomainSetBlockIOTune: | ||||||
|  | 		conn.Write(m.reply(testSetBlockIoTuneReply)) | ||||||
|  | 	case constants.ProcDomainGetBlockIOTune: | ||||||
|  | 		conn.Write(m.reply(testGetBlockIoTuneReply)) | ||||||
| 	default: | 	default: | ||||||
| 		fmt.Fprintln(os.Stderr, "unknown procedure", procedure) | 		fmt.Fprintln(os.Stderr, "unknown procedure", procedure) | ||||||
| 	} | 	} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user