[libvirt] [PATCHv4 0/6] Add new command domiftune

This series adds a new command domiftune to get/set parameters of domain's network interfaces. Supported parameters are bandwidth settings. Currently the network interface bandwidth can only be set: - in domain's xml before the domain is up - when attaching an interface by attach-interface With this series, users can change network interface bandwidth settings using virsh, whether the domain is running or not. changes: - fix a bug that previous bandwidth parameters are lost when setting only inbound or outbound lively - some minor improvements - rename virDomainFindNetDef to virDomainNetFind Hu Tao (6): Add API virDomain{S,G}etInterfaceParameters virDomain{S,G}etInterfaceParameters: the main entry points Add virDomain{S,G}etInterfaceparameters support to the remote driver Add a function virDomainNetFind Add virDomain{S,G}etInterfaceParameters support to qemu driver Enable the virDomain{S,G}etInterfaceParameters in virsh daemon/remote.c | 64 ++++++++ include/libvirt/libvirt.h.in | 50 ++++++ python/generator.py | 2 + src/conf/domain_conf.c | 39 +++++ src/conf/domain_conf.h | 3 + src/driver.h | 12 ++ src/libvirt.c | 130 ++++++++++++++++ src/libvirt_private.syms | 1 + src/libvirt_public.syms | 2 + src/qemu/qemu_driver.c | 353 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 52 ++++++ src/remote/remote_protocol.x | 28 ++++- src/remote_protocol-structs | 24 +++ tools/virsh.c | 198 +++++++++++++++++++++++ tools/virsh.pod | 21 +++ 15 files changed, 978 insertions(+), 1 deletions(-) -- 1.7.4.4

The APIs are used to set/get domain's network interface's parameters. Currently supported parameters are bandwidth settings. * include/libvirt/libvirt.h.in: new API and parameters definition * python/generator.py: skip the Python API generation * src/driver.h: add new entry to the driver structure * src/libvirt_public.syms: export symbols --- include/libvirt/libvirt.h.in | 50 ++++++++++++++++++++++++++++++++++++++++++ python/generator.py | 2 + src/driver.h | 12 ++++++++++ src/libvirt_public.syms | 2 + 4 files changed, 66 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7f26521..f59c3b1 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -645,6 +645,48 @@ typedef virTypedParameter *virTypedParameterPtr; */ #define VIR_DOMAIN_SCHEDULER_SHARES "shares" +/** + * VIR_DOMAIN_BANDWIDTH_IN_AVERAGE: + * + * Macro represents the inbound average of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_AVERAGE "inbound.average" + +/** + * VIR_DOMAIN_BANDWIDTH_IN_PEAK: + * + * Macro represents the inbound peak of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_PEAK "inbound.peak" + +/** + * VIR_DOMAIN_BANDWIDTH_IN_BURST: + * + * Macro represents the inbound burst of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_BURST "inbound.burst" + +/** + * VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE: + * + * Macro represents the outbound average of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE "outbound.average" + +/** + * VIR_DOMAIN_BANDWIDTH_OUT_PEAK: + * + * Macro represents the outbound peak of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_OUT_PEAK "outbound.peak" + +/** + * VIR_DOMAIN_BANDWIDTH_OUT_BURST: + * + * Macro represents the outbound burst of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_OUT_BURST "outbound.burst" + /* * Fetch scheduler parameters, caller allocates 'params' field of size 'nparams' */ @@ -1446,6 +1488,14 @@ int virDomainInterfaceStats (virDomainPtr dom, const char *path, virDomainInterfaceStatsPtr stats, size_t size); +int virDomainSetInterfaceParameters (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int nparams, unsigned int flags); +int virDomainGetInterfaceParameters (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int *nparams, unsigned int flags); int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, diff --git a/python/generator.py b/python/generator.py index 181f70e..6fee3a4 100755 --- a/python/generator.py +++ b/python/generator.py @@ -419,6 +419,8 @@ skip_impl = ( 'virDomainBlockStatsFlags', 'virDomainSetBlockIoTune', 'virDomainGetBlockIoTune', + 'virDomainSetInterfaceParameters', + 'virDomainGetInterfaceParameters', ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index bbd6417..ec4abf3 100644 --- a/src/driver.h +++ b/src/driver.h @@ -375,6 +375,16 @@ typedef int (virDomainPtr domain, const char *path, struct _virDomainInterfaceStats *stats); +typedef int + (*virDrvDomainSetInterfaceParameters) (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int nparams, unsigned int flags); +typedef int + (*virDrvDomainGetInterfaceParameters) (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int *nparams, unsigned int flags); typedef int (*virDrvDomainMemoryStats) @@ -883,6 +893,8 @@ struct _virDriver { virDrvDomainBlockStats domainBlockStats; virDrvDomainBlockStatsFlags domainBlockStatsFlags; virDrvDomainInterfaceStats domainInterfaceStats; + virDrvDomainSetInterfaceParameters domainSetInterfaceParameters; + virDrvDomainGetInterfaceParameters domainGetInterfaceParameters; virDrvDomainMemoryStats domainMemoryStats; virDrvDomainBlockPeek domainBlockPeek; virDrvDomainMemoryPeek domainMemoryPeek; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 3709f08..ea6d562 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -512,6 +512,8 @@ LIBVIRT_0.9.9 { global: virDomainGetNumaParameters; virDomainSetNumaParameters; + virDomainGetInterfaceParameters; + virDomainSetInterfaceParameters; } LIBVIRT_0.9.8; # .... define new API here using predicted next version number .... -- 1.7.4.4

Hi HuTao, I haven't seen any related APIs implementation and registration in python/libvirt-override.c, or plan to add the libvirt_virDomain{S, G}etInterfaceParameters APIs into next patches? Regards & Thanks, Alex ----- Original Message ----- From: "Hu Tao" <hutao@cn.fujitsu.com> To: libvir-list@redhat.com Sent: Thursday, December 29, 2011 3:33:16 PM Subject: [libvirt] [PATCHv4 1/6] Add API virDomain{S, G}etInterfaceParameters The APIs are used to set/get domain's network interface's parameters. Currently supported parameters are bandwidth settings. * include/libvirt/libvirt.h.in: new API and parameters definition * python/generator.py: skip the Python API generation * src/driver.h: add new entry to the driver structure * src/libvirt_public.syms: export symbols --- include/libvirt/libvirt.h.in | 50 ++++++++++++++++++++++++++++++++++++++++++ python/generator.py | 2 + src/driver.h | 12 ++++++++++ src/libvirt_public.syms | 2 + 4 files changed, 66 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7f26521..f59c3b1 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -645,6 +645,48 @@ typedef virTypedParameter *virTypedParameterPtr; */ #define VIR_DOMAIN_SCHEDULER_SHARES "shares" +/** + * VIR_DOMAIN_BANDWIDTH_IN_AVERAGE: + * + * Macro represents the inbound average of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_AVERAGE "inbound.average" + +/** + * VIR_DOMAIN_BANDWIDTH_IN_PEAK: + * + * Macro represents the inbound peak of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_PEAK "inbound.peak" + +/** + * VIR_DOMAIN_BANDWIDTH_IN_BURST: + * + * Macro represents the inbound burst of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_BURST "inbound.burst" + +/** + * VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE: + * + * Macro represents the outbound average of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE "outbound.average" + +/** + * VIR_DOMAIN_BANDWIDTH_OUT_PEAK: + * + * Macro represents the outbound peak of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_OUT_PEAK "outbound.peak" + +/** + * VIR_DOMAIN_BANDWIDTH_OUT_BURST: + * + * Macro represents the outbound burst of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_OUT_BURST "outbound.burst" + /* * Fetch scheduler parameters, caller allocates 'params' field of size 'nparams' */ @@ -1446,6 +1488,14 @@ int virDomainInterfaceStats (virDomainPtr dom, const char *path, virDomainInterfaceStatsPtr stats, size_t size); +int virDomainSetInterfaceParameters (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int nparams, unsigned int flags); +int virDomainGetInterfaceParameters (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int *nparams, unsigned int flags); int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, diff --git a/python/generator.py b/python/generator.py index 181f70e..6fee3a4 100755 --- a/python/generator.py +++ b/python/generator.py @@ -419,6 +419,8 @@ skip_impl = ( 'virDomainBlockStatsFlags', 'virDomainSetBlockIoTune', 'virDomainGetBlockIoTune', + 'virDomainSetInterfaceParameters', + 'virDomainGetInterfaceParameters', ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index bbd6417..ec4abf3 100644 --- a/src/driver.h +++ b/src/driver.h @@ -375,6 +375,16 @@ typedef int (virDomainPtr domain, const char *path, struct _virDomainInterfaceStats *stats); +typedef int + (*virDrvDomainSetInterfaceParameters) (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int nparams, unsigned int flags); +typedef int + (*virDrvDomainGetInterfaceParameters) (virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int *nparams, unsigned int flags); typedef int (*virDrvDomainMemoryStats) @@ -883,6 +893,8 @@ struct _virDriver { virDrvDomainBlockStats domainBlockStats; virDrvDomainBlockStatsFlags domainBlockStatsFlags; virDrvDomainInterfaceStats domainInterfaceStats; + virDrvDomainSetInterfaceParameters domainSetInterfaceParameters; + virDrvDomainGetInterfaceParameters domainGetInterfaceParameters; virDrvDomainMemoryStats domainMemoryStats; virDrvDomainBlockPeek domainBlockPeek; virDrvDomainMemoryPeek domainMemoryPeek; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 3709f08..ea6d562 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -512,6 +512,8 @@ LIBVIRT_0.9.9 { global: virDomainGetNumaParameters; virDomainSetNumaParameters; + virDomainGetInterfaceParameters; + virDomainSetInterfaceParameters; } LIBVIRT_0.9.8; # .... define new API here using predicted next version number .... -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 12/29/2011 12:33 AM, Hu Tao wrote:
The APIs are used to set/get domain's network interface's parameters. Currently supported parameters are bandwidth settings.
* include/libvirt/libvirt.h.in: new API and parameters definition * python/generator.py: skip the Python API generation * src/driver.h: add new entry to the driver structure * src/libvirt_public.syms: export symbols --- include/libvirt/libvirt.h.in | 50 ++++++++++++++++++++++++++++++++++++++++++ python/generator.py | 2 + src/driver.h | 12 ++++++++++ src/libvirt_public.syms | 2 + 4 files changed, 66 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7f26521..f59c3b1 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -645,6 +645,48 @@ typedef virTypedParameter *virTypedParameterPtr; */ #define VIR_DOMAIN_SCHEDULER_SHARES "shares"
+/** + * VIR_DOMAIN_BANDWIDTH_IN_AVERAGE: + * + * Macro represents the inbound average of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_AVERAGE "inbound.average"
We should modify these comments to state what the expected associated type will be when using this named parameter, as well as which scheduler it will be used with (see VIR_DOMAIN_SCHEDULER_CPU_SHARES for an example). -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 12/31/2011 12:01 PM, Eric Blake wrote:
On 12/29/2011 12:33 AM, Hu Tao wrote:
The APIs are used to set/get domain's network interface's parameters. Currently supported parameters are bandwidth settings.
+++ b/include/libvirt/libvirt.h.in @@ -645,6 +645,48 @@ typedef virTypedParameter *virTypedParameterPtr; */ #define VIR_DOMAIN_SCHEDULER_SHARES "shares"
+/** + * VIR_DOMAIN_BANDWIDTH_IN_AVERAGE: + * + * Macro represents the inbound average of NIC bandwidth. + */ +#define VIR_DOMAIN_BANDWIDTH_IN_AVERAGE "inbound.average"
We should modify these comments to state what the expected associated type will be when using this named parameter, as well as which scheduler it will be used with (see VIR_DOMAIN_SCHEDULER_CPU_SHARES for an example).
Actually, I think these macro definitions should be moved down closer to the declaration of virDomainGetInterfaceParameters, as they have nothing to do with virDomainGetSchedulerParameters. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

* src/libvirt.c: implement the main entry points --- src/libvirt.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 130 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 574be16..9507902 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -7075,6 +7075,136 @@ error: return -1; } + /** + * virDomainSetInterfaceParameters: + * @domain: pointer to domain object + * @device: the interface name or mac address + * @params: pointer to interface parameter objects + * @nparams: number of interface parameter (this value can be the same or + * less than the number of parameters supported) + * @flags: bitwise-OR of virDomainModificationImpact + * + * Currently this function sets bandwidth parameters of interface. + * This function may require privileged access to the hypervisor. + * + * Returns -1 in case of error, 0 in case of success. + */ +int +virDomainSetInterfaceParameters(virDomainPtr domain, + const char *device, + virTypedParameterPtr params, + int nparams, unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "device=%s, params=%p, nparams=%d, flags=%x", + device, params, nparams, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + if ((nparams <= 0) || (params == NULL)) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if (virTypedParameterValidateSet(domain, params, nparams) < 0) + return -1; + + conn = domain->conn; + + if (conn->driver->domainSetInterfaceParameters) { + int ret; + ret = conn->driver->domainSetInterfaceParameters(domain, device, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + + /** + * virDomainGetInterfaceParameters: + * @domain: pointer to domain object + * @device: the interface name or mac address + * @params: pointer to interface parameter objects + * (return value, allocated by the caller) + * @nparams: pointer to number of interface parameter + * @flags: one of virDomainModificationImpact + * + * Get all interface parameters. On input, @nparams gives the size of + * the @params array; on output, @nparams gives how many slots were + * filled with parameter information, which might be less but will not + * exceed the input value. + * + * As a special case, calling with @params as NULL and @nparams as 0 on + * input will cause @nparams on output to contain the number of parameters + * supported by the hypervisor. The caller should then allocate @params + * array, i.e. (sizeof(@virTypedParameter) * @nparams) bytes and call the + * API again. See virDomainGetMemoryParameters() for an equivalent usage + * example. + * + * This function may require privileged access to the hypervisor. This function + * expects the caller to allocate the @params. + * + * Returns -1 in case of error, 0 in case of success. + */ + +int +virDomainGetInterfaceParameters(virDomainPtr domain, + const char *device, + virTypedParameterPtr params, + int *nparams, unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "device=%s, params=%p, nparams=%d, flags=%x", + device, params, (nparams) ? *nparams : -1, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if ((nparams == NULL) || (*nparams < 0) || + (params == NULL && *nparams != 0)) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_TYPED_PARAM_STRING)) + flags |= VIR_TYPED_PARAM_STRING_OKAY; + + conn = domain->conn; + + if (conn->driver->domainGetInterfaceParameters) { + int ret; + ret = conn->driver->domainGetInterfaceParameters (domain, device, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + /** * virDomainMemoryStats: * @dom: pointer to the domain object -- 1.7.4.4

* daemon/remote.c: implement the server side support * src/remote/remote_driver.c: implement the client side support * src/remote/remote_protocol.x: definitions for the new entry points * src/remote_protocol-structs: structure definitions --- daemon/remote.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 52 ++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 28 +++++++++++++++++- src/remote_protocol-structs | 24 +++++++++++++++ 4 files changed, 167 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 316d530..b7ba321 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3436,6 +3436,70 @@ cleanup: return rv; } +static int +remoteDispatchDomainGetInterfaceParameters(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_interface_parameters_args *args, + remote_domain_get_interface_parameters_ret *ret) +{ + virDomainPtr dom = NULL; + virTypedParameterPtr params = NULL; + const char *device = args->device; + int nparams = args->nparams; + unsigned int flags; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + flags = args->flags; + + if (nparams > REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large")); + goto cleanup; + } + if (VIR_ALLOC_N(params, nparams) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) < 0) + goto cleanup; + + /* In this case, we need to send back the number of parameters + * supported + */ + if (args->nparams == 0) { + ret->nparams = nparams; + goto success; + } + + if (remoteSerializeTypedParameters(params, nparams, + &ret->params.params_val, + &ret->params.params_len, + flags) < 0) + goto cleanup; + +success: + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + VIR_FREE(params); + return rv; +} /*----- Helpers. -----*/ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index cd3da53..7580477 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4396,6 +4396,56 @@ call (virConnectPtr conn, } +static int +remoteDomainGetInterfaceParameters (virDomainPtr domain, + const char *device, + virTypedParameterPtr params, int *nparams, + unsigned int flags) +{ + int rv = -1; + remote_domain_get_interface_parameters_args args; + remote_domain_get_interface_parameters_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + args.device = (char *)device; + args.nparams = *nparams; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS, + (xdrproc_t) xdr_remote_domain_get_interface_parameters_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_get_interface_parameters_ret, (char *) &ret) == -1) + goto done; + + /* Handle the case when the caller does not know the number of parameters + * and is asking for the number of parameters supported + */ + if (*nparams == 0) { + *nparams = ret.nparams; + rv = 0; + goto cleanup; + } + + if (remoteDeserializeTypedParameters(ret.params.params_val, + ret.params.params_len, + REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX, + params, + nparams) < 0) + goto cleanup; + + rv = 0; + +cleanup: + xdr_free ((xdrproc_t) xdr_remote_domain_get_interface_parameters_ret, + (char *) &ret); +done: + remoteDriverUnlock(priv); + return rv; +} + static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) { @@ -4619,6 +4669,8 @@ static virDriver remote_driver = { .domainBlockStats = remoteDomainBlockStats, /* 0.3.2 */ .domainBlockStatsFlags = remoteDomainBlockStatsFlags, /* 0.9.5 */ .domainInterfaceStats = remoteDomainInterfaceStats, /* 0.3.2 */ + .domainSetInterfaceParameters = remoteDomainSetInterfaceParameters, /* 0.9.9 */ + .domainGetInterfaceParameters = remoteDomainGetInterfaceParameters, /* 0.9.9 */ .domainMemoryStats = remoteDomainMemoryStats, /* 0.7.5 */ .domainBlockPeek = remoteDomainBlockPeek, /* 0.4.2 */ .domainMemoryPeek = remoteDomainMemoryPeek, /* 0.4.2 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 18dba8c..ca739ff 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -202,6 +202,11 @@ const REMOTE_CPU_BASELINE_MAX = 256; */ const REMOTE_DOMAIN_SEND_KEY_MAX = 16; +/* + * Upper limit on list of interface parameters + */ +const REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX = 16; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -608,6 +613,25 @@ struct remote_domain_interface_stats_ret { /* insert@2 */ hyper tx_drop; }; +struct remote_domain_set_interface_parameters_args { + remote_nonnull_domain dom; + remote_nonnull_string device; + remote_typed_param params<REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX>; + unsigned int flags; +}; + +struct remote_domain_get_interface_parameters_args { + remote_nonnull_domain dom; + remote_nonnull_string device; + int nparams; + unsigned int flags; +}; + +struct remote_domain_get_interface_parameters_ret { + remote_typed_param params<REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX>; + int nparams; +}; + struct remote_domain_memory_stats_args { remote_nonnull_domain dom; unsigned int maxStats; @@ -2627,7 +2651,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_BLOCK_IO_TUNE = 252, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_BLOCK_IO_TUNE = 253, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_SET_NUMA_PARAMETERS = 254, /* autogen autogen */ - REMOTE_PROC_DOMAIN_GET_NUMA_PARAMETERS = 255 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_GET_NUMA_PARAMETERS = 255, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_INTERFACE_PARAMETERS = 256, /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index f6d8ff5..2758315 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -328,6 +328,28 @@ struct remote_domain_interface_stats_ret { int64_t tx_errs; int64_t tx_drop; }; +struct remote_domain_set_interface_parameters_args { + remote_nonnull_domain dom; + remote_nonnull_string device; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + u_int flags; +}; +struct remote_domain_get_interface_parameters_args { + remote_nonnull_domain dom; + remote_nonnull_string device; + int nparams; + u_int flags; +}; +struct remote_domain_get_interface_parameters_ret { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + int nparams; +}; struct remote_domain_memory_stats_args { remote_nonnull_domain dom; u_int maxStats; @@ -2066,4 +2088,6 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_BLOCK_IO_TUNE = 253, REMOTE_PROC_DOMAIN_SET_NUMA_PARAMETERS = 254, REMOTE_PROC_DOMAIN_GET_NUMA_PARAMETERS = 255, + REMOTE_PROC_DOMAIN_SET_INTERFACE_PARAMETERS = 256, + REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257, }; -- 1.7.4.4

On 12/29/2011 12:33 AM, Hu Tao wrote:
* daemon/remote.c: implement the server side support * src/remote/remote_driver.c: implement the client side support * src/remote/remote_protocol.x: definitions for the new entry points * src/remote_protocol-structs: structure definitions --- daemon/remote.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 52 ++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 28 +++++++++++++++++- src/remote_protocol-structs | 24 +++++++++++++++ 4 files changed, 167 insertions(+), 1 deletions(-)
+static int +remoteDispatchDomainGetInterfaceParameters(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_interface_parameters_args *args, + remote_domain_get_interface_parameters_ret *ret) +{ + virDomainPtr dom = NULL; + virTypedParameterPtr params = NULL;
+ if (VIR_ALLOC_N(params, nparams) < 0) { + virReportOOMError(); + goto cleanup; + } +
+ +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + VIR_FREE(params);
Memory leak if the hypervisor returned any string types. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add a function virDomainNetFind to find a domain's net def. --- src/conf/domain_conf.c | 39 +++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 1 + 3 files changed, 43 insertions(+), 0 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2897b4a..7fa4adb 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -13726,3 +13726,42 @@ virDomainGraphicsListenSetNetwork(virDomainGraphicsDefPtr def, return 0; } + +/** + * virDomainNetFind: + * @def: domain's def + * @device: could be the interface name or MAC address + * + * Finds a domain's net def, given the interface name or MAC address + * + * Returns a pointer to the net def or NULL if not found. + */ +virDomainNetDefPtr +virDomainNetFind(virDomainDefPtr def, const char *device) +{ + bool isMac = false; + virDomainNetDefPtr net = NULL; + unsigned char mac[VIR_MAC_BUFLEN]; + int i; + + if (virParseMacAddr(device, mac) == 0) + isMac = true; + + if (isMac) { + for (i = 0; i < def->nnets; i++) { + if (memcmp(mac, def->nets[i]->mac, VIR_MAC_BUFLEN) == 0) { + net = def->nets[i]; + break; + } + } + } else { /* ifname */ + for (i = 0; i < def->nnets; i++) { + if (STREQ(device, def->nets[i]->ifname)) { + net = def->nets[i]; + break; + } + } + } + + return net; +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 1f6e442..9bcdb3a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2033,4 +2033,7 @@ VIR_ENUM_DECL(virDomainTimerTickpolicy) VIR_ENUM_DECL(virDomainTimerMode) VIR_ENUM_DECL(virDomainStartupPolicy) + +virDomainNetDefPtr virDomainNetFind(virDomainDefPtr def, + const char *device); #endif /* __DOMAIN_CONF_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6fa9093..216a5a5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -308,6 +308,7 @@ virDomainDiskSnapshotTypeFromString; virDomainDiskSnapshotTypeToString; virDomainDiskTypeFromString; virDomainDiskTypeToString; +virDomainNetFind; virDomainFSDefFree; virDomainFSTypeFromString; virDomainFSTypeToString; -- 1.7.4.4

On 2011年12月29日 15:33, Hu Tao wrote:
Add a function virDomainNetFind to find a domain's net def. --- src/conf/domain_conf.c | 39 +++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 1 + 3 files changed, 43 insertions(+), 0 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2897b4a..7fa4adb 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -13726,3 +13726,42 @@ virDomainGraphicsListenSetNetwork(virDomainGraphicsDefPtr def,
return 0; } + +/** + * virDomainNetFind: + * @def: domain's def + * @device: could be the interface name or MAC address + * + * Finds a domain's net def, given the interface name or MAC address + * + * Returns a pointer to the net def or NULL if not found. + */ +virDomainNetDefPtr +virDomainNetFind(virDomainDefPtr def, const char *device) +{ + bool isMac = false; + virDomainNetDefPtr net = NULL; + unsigned char mac[VIR_MAC_BUFLEN]; + int i; + + if (virParseMacAddr(device, mac) == 0) + isMac = true; + + if (isMac) { + for (i = 0; i< def->nnets; i++) { + if (memcmp(mac, def->nets[i]->mac, VIR_MAC_BUFLEN) == 0) { + net = def->nets[i]; + break; + } + } + } else { /* ifname */ + for (i = 0; i< def->nnets; i++) { + if (STREQ(device, def->nets[i]->ifname)) {
Oh, I missed this, this can cause libvirtd to crash if def->nets[i]->ifname is NULL. s/STREQ/STREQ_NULLABLE/ No need to post v5, I will squash the change in when pushing. Regards, Osier

On 12/29/2011 12:33 AM, Hu Tao wrote:
Add a function virDomainNetFind to find a domain's net def. --- src/conf/domain_conf.c | 39 +++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 1 + 3 files changed, 43 insertions(+), 0 deletions(-)
+++ b/src/libvirt_private.syms @@ -308,6 +308,7 @@ virDomainDiskSnapshotTypeFromString; virDomainDiskSnapshotTypeToString; virDomainDiskTypeFromString; virDomainDiskTypeToString; +virDomainNetFind; virDomainFSDefFree;
Unsorted. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

* src/qemu/qemu_driver.c: implement the qemu driver support --- src/qemu/qemu_driver.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 353 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a840c1f..c04a5de 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -119,6 +119,8 @@ #define QEMU_NB_BLKIO_PARAM 2 +#define QEMU_NB_BANDWIDTH_PARAM 6 + static void processWatchdogEvent(void *data, void *opaque); static int qemudShutdown(void); @@ -7852,6 +7854,355 @@ qemudDomainInterfaceStats (virDomainPtr dom, #endif static int +qemuDomainSetInterfaceParameters(virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + int i; + virCgroupPtr group = NULL; + virDomainObjPtr vm = NULL; + virDomainDefPtr persistentDef = NULL; + int ret = -1; + virDomainNetDefPtr net = NULL, persistentNet = NULL; + virNetDevBandwidthPtr bandwidth; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + qemuDriverLock(driver); + + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (vm == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("No such domain %s"), dom->uuid); + goto cleanup; + } + + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, + &persistentDef) < 0) + goto cleanup; + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + net = virDomainNetFind(vm->def, device); + if (!net) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Can't find device %s"), device); + goto cleanup; + } + } + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + persistentNet = virDomainNetFind(persistentDef, device); + if (!persistentNet) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Can't find device %s"), device); + ret = -1; + goto cleanup; + } + } + + if (VIR_ALLOC(bandwidth) < 0) { + virReportOOMError(); + goto cleanup; + } + if (VIR_ALLOC(bandwidth->in) < 0) { + virReportOOMError(); + goto cleanup; + } + memset(bandwidth->in, 0, sizeof(*bandwidth->in)); + if (VIR_ALLOC(bandwidth->out) < 0) { + virReportOOMError(); + goto cleanup; + } + memset(bandwidth->out, 0, sizeof(*bandwidth->out)); + + for (i = 0; i < nparams; i++) { + virTypedParameterPtr param = ¶ms[i]; + + if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth average tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->in->average = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_PEAK)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth peak tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->in->peak = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_BURST)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth burst tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->in->burst = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth average tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->out->average = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth peak tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->out->peak = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_BURST)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth burst tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->out->burst = params[i].value.ui; + } else { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Parameter `%s' not supported"), + param->field); + goto cleanup; + } + } + + /* average is mandatory, peak and burst is optional. So if no + * average is given, we free inbound/outbound here which causes + * inbound/outbound won't be set. */ + if (!bandwidth->in->average) + VIR_FREE(bandwidth->in); + if (!bandwidth->out->average) + VIR_FREE(bandwidth->out); + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + virNetDevBandwidthPtr newBandwidth; + + if (VIR_ALLOC(newBandwidth) < 0) { + virReportOOMError(); + goto cleanup; + } + + memset(newBandwidth, 0, sizeof(newBandwidth)); + + /* virNetDevBandwidthSet() will clear any previous value of + * bandwidth parameters, so merge with old bandwidth parameters + * here to prevent them from losing. */ + if (bandwidth->in || net->bandwidth->in) { + if (VIR_ALLOC(newBandwidth->in) < 0) { + virReportOOMError(); + goto cleanup; + } + if (bandwidth->in) + memcpy(newBandwidth->in, bandwidth->in, sizeof(*newBandwidth->in)); + else if (net->bandwidth->in) + memcpy(newBandwidth->in, net->bandwidth->in, sizeof(*newBandwidth->in)); + } + if (bandwidth->out || net->bandwidth->out) { + if (VIR_ALLOC(newBandwidth->out) < 0) { + virReportOOMError(); + goto cleanup; + } + if (bandwidth->out) + memcpy(newBandwidth->out, bandwidth->out, sizeof(*newBandwidth->out)); + else if (net->bandwidth->out) + memcpy(newBandwidth->out, net->bandwidth->out, sizeof(*newBandwidth->out)); + } + + if (virNetDevBandwidthSet(net->ifname, newBandwidth) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot set bandwidth limits on %s"), + device); + ret = -1; + goto cleanup; + } + + virNetDevBandwidthFree(net->bandwidth); + net->bandwidth = newBandwidth; + } + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + if (!persistentNet->bandwidth) { + persistentNet->bandwidth = bandwidth; + bandwidth = NULL; + } else { + if (bandwidth->in) { + VIR_FREE(persistentNet->bandwidth->in); + persistentNet->bandwidth->in = bandwidth->in; + bandwidth->in = NULL; + } + if (bandwidth->out) { + VIR_FREE(persistentNet->bandwidth->out); + persistentNet->bandwidth->out = bandwidth->out; + bandwidth->out = NULL; + } + } + + if (virDomainSaveConfig(driver->configDir, persistentDef) < 0) + goto cleanup; + } + + ret = 0; +cleanup: + if (bandwidth) { + VIR_FREE(bandwidth->in); + VIR_FREE(bandwidth->out); + VIR_FREE(bandwidth); + } + virCgroupFree(&group); + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + +static int +qemuDomainGetInterfaceParameters(virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int *nparams, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + int i; + virCgroupPtr group = NULL; + virDomainObjPtr vm = NULL; + virDomainDefPtr def = NULL; + virDomainDefPtr persistentDef = NULL; + virDomainNetDefPtr net = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG | + VIR_TYPED_PARAM_STRING_OKAY, -1); + + qemuDriverLock(driver); + + flags &= ~VIR_TYPED_PARAM_STRING_OKAY; + + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (vm == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("No such domain %s"), dom->uuid); + goto cleanup; + } + + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, + &persistentDef) < 0) + goto cleanup; + + if ((*nparams) == 0) { + *nparams = QEMU_NB_BANDWIDTH_PARAM; + ret = 0; + goto cleanup; + } + + def = persistentDef; + if (!def) + def = vm->def; + + net = virDomainNetFind(def, device); + if (!net) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Can't find device %s"), device); + goto cleanup; + } + + for (i = 0; i < *nparams && i < QEMU_NB_BANDWIDTH_PARAM; i++) { + params[i].value.ui = 0; + params[i].type = VIR_TYPED_PARAM_UINT; + + switch(i) { + case 0: /* inbound.average */ + if (virStrcpyStatic(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BANDWIDTH_IN_AVERAGE); + goto cleanup; + } + if (net->bandwidth && net->bandwidth->in) + params[i].value.ui = net->bandwidth->in->average; + break; + case 1: /* inbound.peak */ + if (virStrcpyStatic(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_PEAK) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BANDWIDTH_IN_PEAK); + goto cleanup; + } + if (net->bandwidth && net->bandwidth->in) + params[i].value.ui = net->bandwidth->in->peak; + break; + case 2: /* inbound.burst */ + if (virStrcpyStatic(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_BURST) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BANDWIDTH_IN_BURST); + goto cleanup; + } + if (net->bandwidth && net->bandwidth->in) + params[i].value.ui = net->bandwidth->in->burst; + break; + case 3: /* outbound.average */ + if (virStrcpyStatic(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE); + goto cleanup; + } + if (net->bandwidth && net->bandwidth->out) + params[i].value.ui = net->bandwidth->out->average; + break; + case 4: /* outbound.peak */ + if (virStrcpyStatic(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BANDWIDTH_OUT_PEAK); + goto cleanup; + } + if (net->bandwidth && net->bandwidth->out) + params[i].value.ui = net->bandwidth->out->peak; + break; + case 5: /* outbound.burst */ + if (virStrcpyStatic(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_BURST) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BANDWIDTH_OUT_BURST); + goto cleanup; + } + if (net->bandwidth && net->bandwidth->out) + params[i].value.ui = net->bandwidth->out->burst; + break; + default: + break; + /* should not hit here */ + } + } + + if (*nparams > QEMU_NB_BANDWIDTH_PARAM) + *nparams = QEMU_NB_BANDWIDTH_PARAM; + ret = 0; + +cleanup: + if (group) + virCgroupFree(&group); + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + +static int qemudDomainMemoryStats (virDomainPtr dom, struct _virDomainMemoryStat *stats, unsigned int nr_stats, @@ -11642,6 +11993,8 @@ static virDriver qemuDriver = { .domainGetBlockIoTune = qemuDomainGetBlockIoTune, /* 0.9.8 */ .domainSetNumaParameters = qemuDomainSetNumaParameters, /* 0.9.9 */ .domainGetNumaParameters = qemuDomainGetNumaParameters, /* 0.9.9 */ + .domainGetInterfaceParameters = qemuDomainGetInterfaceParameters, /* 0.9.9 */ + .domainSetInterfaceParameters = qemuDomainSetInterfaceParameters, /* 0.9.9 */ }; -- 1.7.4.4

On 2011年12月29日 15:33, Hu Tao wrote:
* src/qemu/qemu_driver.c: implement the qemu driver support --- src/qemu/qemu_driver.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 353 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a840c1f..c04a5de 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -119,6 +119,8 @@
#define QEMU_NB_BLKIO_PARAM 2
+#define QEMU_NB_BANDWIDTH_PARAM 6 + static void processWatchdogEvent(void *data, void *opaque);
static int qemudShutdown(void); @@ -7852,6 +7854,355 @@ qemudDomainInterfaceStats (virDomainPtr dom, #endif
static int +qemuDomainSetInterfaceParameters(virDomainPtr dom, + const char *device, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + int i; + virCgroupPtr group = NULL; + virDomainObjPtr vm = NULL; + virDomainDefPtr persistentDef = NULL; + int ret = -1; + virDomainNetDefPtr net = NULL, persistentNet = NULL; + virNetDevBandwidthPtr bandwidth; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + qemuDriverLock(driver); + + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (vm == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("No such domain %s"), dom->uuid); + goto cleanup; + } + + if (virDomainLiveConfigHelperMethod(driver->caps, vm,&flags, +&persistentDef)< 0) + goto cleanup; + + if (flags& VIR_DOMAIN_AFFECT_LIVE) { + net = virDomainNetFind(vm->def, device); + if (!net) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Can't find device %s"), device); + goto cleanup;
This can cause libvirtd to crash, as "bandwidth" is not initialized yet.
+ } + } + if (flags& VIR_DOMAIN_AFFECT_CONFIG) { + persistentNet = virDomainNetFind(persistentDef, device); + if (!persistentNet) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Can't find device %s"), device); + ret = -1; + goto cleanup;
Likewise
+ } + } + + if (VIR_ALLOC(bandwidth)< 0) { + virReportOOMError(); + goto cleanup; + } + if (VIR_ALLOC(bandwidth->in)< 0) { + virReportOOMError(); + goto cleanup; + } + memset(bandwidth->in, 0, sizeof(*bandwidth->in)); + if (VIR_ALLOC(bandwidth->out)< 0) { + virReportOOMError(); + goto cleanup; + } + memset(bandwidth->out, 0, sizeof(*bandwidth->out)); + + for (i = 0; i< nparams; i++) { + virTypedParameterPtr param =¶ms[i]; + + if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth average tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->in->average = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_PEAK)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth peak tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->in->peak = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_BURST)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth burst tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->in->burst = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth average tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->out->average = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth peak tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->out->peak = params[i].value.ui; + } else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_BURST)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth burst tunable, expected a 'unsigned int'")); + goto cleanup; + } + + bandwidth->out->burst = params[i].value.ui; + } else { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Parameter `%s' not supported"), + param->field); + goto cleanup; + } + } + + /* average is mandatory, peak and burst is optional. So if no + * average is given, we free inbound/outbound here which causes + * inbound/outbound won't be set. */ + if (!bandwidth->in->average) + VIR_FREE(bandwidth->in); + if (!bandwidth->out->average) + VIR_FREE(bandwidth->out); + + if (flags& VIR_DOMAIN_AFFECT_LIVE) { + virNetDevBandwidthPtr newBandwidth; + + if (VIR_ALLOC(newBandwidth)< 0) { + virReportOOMError(); + goto cleanup; + } + + memset(newBandwidth, 0, sizeof(newBandwidth)); + + /* virNetDevBandwidthSet() will clear any previous value of + * bandwidth parameters, so merge with old bandwidth parameters + * here to prevent them from losing. */ + if (bandwidth->in || net->bandwidth->in) {
Similiar, as bandwidth->in can be free()ed if no "average" is given.
+ if (VIR_ALLOC(newBandwidth->in)< 0) { + virReportOOMError(); + goto cleanup; + } + if (bandwidth->in) + memcpy(newBandwidth->in, bandwidth->in, sizeof(*newBandwidth->in)); + else if (net->bandwidth->in) + memcpy(newBandwidth->in, net->bandwidth->in, sizeof(*newBandwidth->in)); + } + if (bandwidth->out || net->bandwidth->out) {
Just like bandwidth->in.
+ if (VIR_ALLOC(newBandwidth->out)< 0) { + virReportOOMError(); + goto cleanup; + } + if (bandwidth->out) + memcpy(newBandwidth->out, bandwidth->out, sizeof(*newBandwidth->out)); + else if (net->bandwidth->out) + memcpy(newBandwidth->out, net->bandwidth->out, sizeof(*newBandwidth->out)); + } + + if (virNetDevBandwidthSet(net->ifname, newBandwidth)< 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot set bandwidth limits on %s"), + device); + ret = -1; + goto cleanup; + } + + virNetDevBandwidthFree(net->bandwidth); + net->bandwidth = newBandwidth; + } + if (flags& VIR_DOMAIN_AFFECT_CONFIG) { + if (!persistentNet->bandwidth) { + persistentNet->bandwidth = bandwidth; + bandwidth = NULL; + } else { + if (bandwidth->in) { + VIR_FREE(persistentNet->bandwidth->in); + persistentNet->bandwidth->in = bandwidth->in; + bandwidth->in = NULL; + } + if (bandwidth->out) { + VIR_FREE(persistentNet->bandwidth->out); + persistentNet->bandwidth->out = bandwidth->out; + bandwidth->out = NULL; + } + } + + if (virDomainSaveConfig(driver->configDir, persistentDef)< 0) + goto cleanup; + } + + ret = 0; +cleanup: + if (bandwidth) { + VIR_FREE(bandwidth->in); + VIR_FREE(bandwidth->out); + VIR_FREE(bandwidth); + }
This can be simplified using virNetDevBandwidthFree. Will push with attached patch squashed in.

On 12/29/2011 12:33 AM, Hu Tao wrote:
* src/qemu/qemu_driver.c: implement the qemu driver support --- src/qemu/qemu_driver.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 353 insertions(+), 0 deletions(-)
+ if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth average tunable, expected a 'unsigned int'"));
s/a 'unsigned int'/an 'unsigned int'/ Also, these are long lines.
+ /* average is mandatory, peak and burst is optional. So if no
s/peak and burst is/peak and burst are/
+ * average is given, we free inbound/outbound here which causes + * inbound/outbound won't be set. */
s/won't be/to not be/
+ /* virNetDevBandwidthSet() will clear any previous value of + * bandwidth parameters, so merge with old bandwidth parameters + * here to prevent them from losing. */
s/from losing/from being lost/ -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 12/31/2011 02:12 PM, Eric Blake wrote:
On 12/29/2011 12:33 AM, Hu Tao wrote:
* src/qemu/qemu_driver.c: implement the qemu driver support --- src/qemu/qemu_driver.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 353 insertions(+), 0 deletions(-)
+ if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE)) { + if (param->type != VIR_TYPED_PARAM_UINT) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid type for bandwidth average tunable, expected a 'unsigned int'"));
s/a 'unsigned int'/an 'unsigned int'/ Also, these are long lines.
I think I'll refactor this into a set of helper methods; patch coming up soon.
+ /* average is mandatory, peak and burst is optional. So if no
s/peak and burst is/peak and burst are/
Meanwhile, I've posted a patch to address all my comments: https://www.redhat.com/archives/libvir-list/2012-January/msg00018.html -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add a new command domiftune to get/set interface parameters. * tools/virsh.c: implement the new command * tools/virsh.pod: documentation of the new command --- tools/virsh.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 21 ++++++ 2 files changed, 219 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index b2d3968..e089f7b 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -427,6 +427,8 @@ static void *_vshCalloc(vshControl *ctl, size_t nmemb, size_t sz, const char *fi static char *_vshStrdup(vshControl *ctl, const char *s, const char *filename, int line); #define vshStrdup(_ctl, _s) _vshStrdup(_ctl, _s, __FILE__, __LINE__) +static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate); + static void * _vshMalloc(vshControl *ctl, size_t size, const char *filename, int line) { @@ -1601,6 +1603,201 @@ cleanup: return ret; } +/* "domiftune" command + */ +static const vshCmdInfo info_domiftune[] = { + {"help", N_("get/set parameters of a virtual interface")}, + {"desc", N_("Get/set parameters of a domain's virtual interface.")}, + {NULL,NULL} +}; + +static const vshCmdOptDef opts_domiftune[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device")}, + {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")}, + {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")}, + {"config", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect next boot")}, + {"live", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect running domain")}, + {"current", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect current domain")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdDomIftune(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + const char *name = NULL, *device = NULL, + *inboundStr = NULL, *outboundStr = NULL; + unsigned int flags = 0; + int nparams = 0; + virTypedParameterPtr params = NULL; + bool ret = false; + int current = vshCommandOptBool(cmd, "current"); + int config = vshCommandOptBool(cmd, "config"); + int live = vshCommandOptBool(cmd, "live"); + virNetDevBandwidthRate inbound, outbound; + int i; + + if (current) { + if (live || config) { + vshError(ctl, "%s", _("--current must be specified exclusively")); + return false; + } + flags = VIR_DOMAIN_AFFECT_CURRENT; + } else { + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + } + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, &name))) + return false; + + if (vshCommandOptString(cmd, "interface", &device) <= 0) { + virDomainFree(dom); + return false; + } + + if (vshCommandOptString(cmd, "inbound", &inboundStr) < 0 || + vshCommandOptString(cmd, "outbound", &outboundStr) < 0) { + vshError(ctl, "missing argument"); + goto cleanup; + } + + memset(&inbound, 0, sizeof(inbound)); + memset(&outbound, 0, sizeof(outbound)); + + if (inboundStr) { + if (parseRateStr(inboundStr, &inbound) < 0) { + vshError(ctl, _("inbound format is incorrect")); + goto cleanup; + } + if (inbound.average == 0) { + vshError(ctl, _("inbound average is mandatory")); + goto cleanup; + } + nparams++; /* average */ + if (inbound.peak) nparams++; + if (inbound.burst) nparams++; + } + if (outboundStr) { + if (parseRateStr(outboundStr, &outbound) < 0) { + vshError(ctl, _("outbound format is incorrect")); + goto cleanup; + } + if (outbound.average == 0) { + vshError(ctl, _("outbound average is mandatory")); + goto cleanup; + } + nparams++; /* average */ + if (outbound.peak) nparams++; + if (outbound.burst) nparams++; + } + + if (nparams == 0) { + /* get the number of interface parameters */ + if (virDomainGetInterfaceParameters(dom, device, NULL, &nparams, flags) != 0) { + vshError(ctl, "%s", + _("Unable to get number of interface parameters")); + goto cleanup; + } + + if (nparams == 0) { + /* nothing to output */ + ret = true; + goto cleanup; + } + + /* get all interface parameters */ + params = vshCalloc(ctl, nparams, sizeof(*params)); + if (!params) { + virReportOOMError(); + goto cleanup; + } + if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) != 0) { + vshError(ctl, "%s", _("Unable to get interface parameters")); + goto cleanup; + } + + for (i = 0; i < nparams; i++) { + char *str = vshGetTypedParamValue(ctl, ¶ms[i]); + vshPrint(ctl, "%-15s: %s\n", params[i].field, str); + VIR_FREE(str); + } + } else { + /* set the interface parameters */ + params = vshCalloc(ctl, nparams, sizeof(*params)); + if (!params) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < nparams; i++) + params[i].type = VIR_TYPED_PARAM_UINT; + + i = 0; + if (inbound.average && i < nparams) { + if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE, + sizeof(params[i].field))) + goto cleanup; + params[i].value.ui = inbound.average; + i++; + } + if (inbound.peak && i < nparams) { + if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_PEAK, + sizeof(params[i].field))) + goto cleanup; + params[i].value.ui = inbound.peak; + i++; + } + if (inbound.burst && i < nparams) { + if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_BURST, + sizeof(params[i].field))) + goto cleanup; + params[i].value.ui = inbound.burst; + i++; + } + if (outbound.average && i < nparams) { + if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE, + sizeof(params[i].field))) + goto cleanup; + params[i].value.ui = outbound.average; + i++; + } + if (outbound.peak && i < nparams) { + if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK, + sizeof(params[i].field))) + goto cleanup; + params[i].value.ui = outbound.peak; + i++; + } + if (outbound.burst && i < nparams) { + if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_BURST, + sizeof(params[i].field))) + goto cleanup; + params[i].value.ui = outbound.burst; + i++; + } + + if (virDomainSetInterfaceParameters(dom, device, params, nparams, flags) != 0) { + vshError(ctl, "%s", _("Unable to set interface parameters")); + goto cleanup; + } + } + + ret = true; + +cleanup: + virTypedParameterArrayClear(params, nparams); + VIR_FREE(params); + virDomainFree(dom); + return ret; +} + /* * "dommemstats" command */ @@ -15589,6 +15786,7 @@ static const vshCmdDef domManagementCmds[] = { info_detach_interface, 0}, {"domid", cmdDomid, opts_domid, info_domid, 0}, {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink, 0}, + {"domiftune", cmdDomIftune, opts_domiftune, info_domiftune, 0}, {"domjobabort", cmdDomjobabort, opts_domjobabort, info_domjobabort, 0}, {"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo, 0}, {"domname", cmdDomname, opts_domname, info_domname, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 4e29421..138f886 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -480,6 +480,27 @@ configuration of the domain is modified. Query link state of the domain's virtual interface. If --persistent is specified, query the persistent configuration. +=item B<domiftune> I<domain> I<interface-device> +[[I<--config>] [I<--live>] | [I<--current>]] +[I<--inbound average,peak,burst>] +[I<--outbound average,peak,burst>] + +Set or query the domain's network interface's bandwidth parameters. +I<interface-device> can be the interface's target name (<target dev='name'/>), +or the MAC address. + +If no I<--inbound> or I<--outbound> is specified, this command will +query and show the bandwidth settings. Otherwise, it will set the +inbound or outbound bandwidth. I<average,peak,burst> is the same as +in command I<attach-interface>. + +If I<--live> is specified, affect a running guest. +If I<--config> is specified, affect the next boot of a persistent guest. +If I<--current> is specified, affect the current guest state. +Both I<--live> and I<--current> flags may be given, but I<--current> is +exclusive. If no flag is specified, behavior is different depending +on hypervisor. + =item B<dommemstat> I<domain> Get memory stats for a running domain. -- 1.7.4.4

On 2011年12月29日 15:33, Hu Tao wrote:
This series adds a new command domiftune to get/set parameters of domain's network interfaces. Supported parameters are bandwidth settings.
Currently the network interface bandwidth can only be set:
- in domain's xml before the domain is up - when attaching an interface by attach-interface
With this series, users can change network interface bandwidth settings using virsh, whether the domain is running or not.
changes:
- fix a bug that previous bandwidth parameters are lost when setting only inbound or outbound lively - some minor improvements - rename virDomainFindNetDef to virDomainNetFind
Hu Tao (6): Add API virDomain{S,G}etInterfaceParameters virDomain{S,G}etInterfaceParameters: the main entry points Add virDomain{S,G}etInterfaceparameters support to the remote driver Add a function virDomainNetFind Add virDomain{S,G}etInterfaceParameters support to qemu driver Enable the virDomain{S,G}etInterfaceParameters in virsh
daemon/remote.c | 64 ++++++++ include/libvirt/libvirt.h.in | 50 ++++++ python/generator.py | 2 + src/conf/domain_conf.c | 39 +++++ src/conf/domain_conf.h | 3 + src/driver.h | 12 ++ src/libvirt.c | 130 ++++++++++++++++ src/libvirt_private.syms | 1 + src/libvirt_public.syms | 2 + src/qemu/qemu_driver.c | 353 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 52 ++++++ src/remote/remote_protocol.x | 28 ++++- src/remote_protocol-structs | 24 +++ tools/virsh.c | 198 +++++++++++++++++++++++ tools/virsh.pod | 21 +++ 15 files changed, 978 insertions(+), 1 deletions(-)
Pushed with the addon patches on patches 4/6 and 5/6. Thanks! Regards, Osier
participants (4)
-
Alex Jia
-
Eric Blake
-
Hu Tao
-
Osier Yang