Re: [libvirt] [PATCHv2 02/15] blockjob: wire up qemu async virDomainBlockJobAbort
On Thu, Apr 05, 2012 at 10:36:48PM -0600, Eric Blake wrote: From: Adam Litke a...@us.ibm.com Without the VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC flag, libvirt will internally poll using qemu's query-block-jobs API and will not return until the operation has been completed. API users are advised that this operation is unbounded and further interaction with the domain during this period may block. Future patches may refactor things to allow other queries in parallel with this polling. Unfortunately, there's no good way to tell if qemu will emit the new event, so this implementation always polls to deal with older qemu. Signed-off-by: Adam Litke a...@us.ibm.com Cc: Stefan Hajnoczi stefa...@gmail.com Signed-off-by: Eric Blake ebl...@redhat.com Tested-by: Adam Litke a...@us.ibm.com --- src/qemu/qemu_driver.c | 55 +-- 1 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0456b34..455fa30 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11602,7 +11602,7 @@ cleanup: static int qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const char *base, unsigned long bandwidth, virDomainBlockJobInfoPtr info, - int mode) + int mode, unsigned int flags) { struct qemud_driver *driver = dom-conn-privateData; virDomainObjPtr vm = NULL; @@ -11644,6 +11644,45 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const char *base, ret = qemuMonitorBlockJob(priv-mon, device, base, bandwidth, info, mode); qemuDomainObjExitMonitorWithDriver(driver, vm); +/* Qemu provides asynchronous block job cancellation, but without + * the VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC flag libvirt guarantees a + * synchronous operation. Provide this behavior by waiting here, + * so we don't get confused by newly scheduled block jobs. + */ +if (ret == 0 mode == BLOCK_JOB_ABORT +!(flags VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)) { +ret = 1; +while (1) { +/* Poll every 50ms */ +struct timespec ts = { .tv_sec = 0, + .tv_nsec = 50 * 1000 * 1000ull }; +virDomainBlockJobInfo dummy; + +qemuDomainObjEnterMonitorWithDriver(driver, vm); +ret = qemuMonitorBlockJob(priv-mon, device, NULL, 0, dummy, + BLOCK_JOB_INFO); +qemuDomainObjExitMonitorWithDriver(driver, vm); + +if (ret = 0) +break; + +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); + +nanosleep(ts, NULL); + +qemuDriverLock(driver); +virDomainObjLock(vm); + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, %s, +_(domain is not running)); +ret = -1; +break; +} +} +} + endjob: if (qemuDomainObjEndJob(driver, vm) == 0) { vm = NULL; @@ -11661,8 +11700,9 @@ cleanup: static int qemuDomainBlockJobAbort(virDomainPtr dom, const char *path, unsigned int flags) { -virCheckFlags(0, -1); -return qemuDomainBlockJobImpl(dom, path, NULL, 0, NULL, BLOCK_JOB_ABORT); +virCheckFlags(VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC, -1); +return qemuDomainBlockJobImpl(dom, path, NULL, 0, NULL, BLOCK_JOB_ABORT, + flags); } static int @@ -11670,7 +11710,8 @@ qemuDomainGetBlockJobInfo(virDomainPtr dom, const char *path, virDomainBlockJobInfoPtr info, unsigned int flags) { virCheckFlags(0, -1); -return qemuDomainBlockJobImpl(dom, path, NULL, 0, info, BLOCK_JOB_INFO); +return qemuDomainBlockJobImpl(dom, path, NULL, 0, info, BLOCK_JOB_INFO, + flags); } static int @@ -11679,7 +11720,7 @@ qemuDomainBlockJobSetSpeed(virDomainPtr dom, const char *path, { virCheckFlags(0, -1); return qemuDomainBlockJobImpl(dom, path, NULL, bandwidth, NULL, - BLOCK_JOB_SPEED); + BLOCK_JOB_SPEED, flags); } static int @@ -11690,10 +11731,10 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base, virCheckFlags(0, -1); ret = qemuDomainBlockJobImpl(dom, path, base, bandwidth, NULL, - BLOCK_JOB_PULL); + BLOCK_JOB_PULL, flags); if (ret == 0 bandwidth != 0) ret = qemuDomainBlockJobImpl(dom, path, NULL, bandwidth, NULL, - BLOCK_JOB_SPEED); + BLOCK_JOB_SPEED, flags); return ret
Re: [libvirt] [PATCHv2 01/15] blockjob: add API for async virDomainBlockJobAbort
On Thu, Apr 05, 2012 at 10:36:47PM -0600, Eric Blake wrote: From: Adam Litke a...@us.ibm.com Qemu has changed the semantics of the block_job_cancel API. The original qed implementation (pretty much only backported to RHEL 6.2 qemu) was synchronous (ie. upon command completion, the operation was guaranteed to be completely stopped). With the new semantics going into qemu 1.1 for qcow2, a block_job_cancel merely requests that the operation be cancelled and an event is triggered once the cancellation request has been honored. To adopt the new semantics while preserving compatibility the following updates are made to the virDomainBlockJob API: A new block job event type VIR_DOMAIN_BLOCK_JOB_CANCELLED is recognized by libvirt. Regardless of the flags used with virDomainBlockJobAbort, this event will be raised whenever it is received from qemu. This event indicates that a block job has been successfully cancelled. For now, libvirt does not try to synthesize this event if using an older qemu that did not generate it. A new extension flag VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC is added to the virDomainBlockJobAbort API. When enabled, this function will operate asynchronously (ie, it can return before the job has actually been cancelled). When the API is used in this mode, it is the responsibility of the caller to wait for a VIR_DOMAIN_BLOCK_JOB_CANCELLED event or poll via the virDomainGetBlockJobInfo API to check the cancellation status; this flag is an error if it is not known if the hypervisor supports asynchronous cancel. This patch also exposes the new flag through virsh. Signed-off-by: Adam Litke a...@us.ibm.com Cc: Stefan Hajnoczi stefa...@gmail.com Signed-off-by: Eric Blake ebl...@redhat.com Tested-by: Adam Litke a...@us.ibm.com --- include/libvirt/libvirt.h.in | 10 + src/libvirt.c| 10 - src/qemu/qemu_monitor_json.c | 42 +++-- tools/virsh.c| 46 ++--- tools/virsh.pod |9 +-- 5 files changed, 89 insertions(+), 28 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 499dcd4..97ad99d 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1946,6 +1946,15 @@ typedef enum { #endif } virDomainBlockJobType; +/** + * virDomainBlockJobAbortFlags: + * + * VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC: Request only, do not wait for completion + */ +typedef enum { +VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC = 1 0, +} virDomainBlockJobAbortFlags; + /* An iterator for monitoring block job operations */ typedef unsigned long long virDomainBlockJobCursor; @@ -3617,6 +3626,7 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, typedef enum { VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0, VIR_DOMAIN_BLOCK_JOB_FAILED = 1, +VIR_DOMAIN_BLOCK_JOB_CANCELED = 2, #ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_BLOCK_JOB_LAST diff --git a/src/libvirt.c b/src/libvirt.c index 16d1fd5..af22232 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17902,7 +17902,7 @@ error: * virDomainBlockJobAbort: * @dom: pointer to domain object * @disk: path to the block device, or device shorthand - * @flags: extra flags; not used yet, so callers should always pass 0 + * @flags: bitwise-OR of virDomainBlockJobAbortFlags * * Cancel the active block job on the given disk. * @@ -17913,6 +17913,14 @@ error: * can be found by calling virDomainGetXMLDesc() and inspecting * elements within //domain/devices/disk. * + * By default, this function performs a synchronous operation and the caller + * may assume that the operation has completed when 0 is returned. However, + * BlockJob operations may take a long time to complete, and during this time + * further domain interactions may be unresponsive. To avoid this problem, + * pass VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC in the @flags argument to enable + * asynchronous behavior. Either way, when the job has been cancelled, a + * BlockJob event will be emitted, with status VIR_DOMAIN_BLOCK_JOB_CANCELLED. + * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockJobAbort(virDomainPtr dom, const char *disk, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 8eeb307..9a8da3a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -58,13 +58,14 @@ static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr dat static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data); -static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data
Re: [libvirt] [PATCH] network: disallow bandwidth/mac for bridged/macvtap networks
On 24/01/14 14:18 +0200, Laine Stump wrote: https://bugzilla.redhat.com/show_bug.cgi?id=1057321 pointed out that we weren't honoring the bandwidth element in libvirt networks using forward mode='bridge'/. In fact, these networks are just a method of giving a libvirt network name to an existing Linux host bridge on the system, and even if it were technically possible for us to set network-wide bandwidth limits for all the taps on a bridge, it's probably not a polite thing to do since libvirt is just using a bridge that was created by someone else for other purposes. So the proper thing is to just log an error when someone tries to put a bandwidth element in that type of network. While looking through the network XML documentation and comparing it to the networkValidate function, I noticed that we also ignore the presence of a mac address in the config, even though we do nothing with it in this case either. This patch updates networkValidate() (which is called any time a persistent network is defined, or a transient network created) to log an error and fail if it finds either a bandwidth or mac element and the network forward mode is anything except 'route'. 'nat', or nothing. (Yes, neither of those elements is acceptable for any macvtap mode, nor for a hostdev network). NB: This does *not* cause failure to start any existing network that contains one of those elements, so someone might have erroneously defined such a network in the past, and that network will continue to function unmodified. I considered it too disruptive to suddenly break working configs on the next reboot after a libvirt upgrade. Reviewed-by: Adam Litke ali...@redhat.com --- src/network/bridge_driver.c | 19 ++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 0b43a67..3b9b58d 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2407,8 +2407,17 @@ networkValidate(virNetworkDriverStatePtr driver, virNetworkSetBridgeMacAddr(def); } else { /* They are also the only types that currently support setting - * an IP address for the host-side device (bridge) + * a MAC or IP address for the host-side device (bridge), DNS + * configuration, or network-wide bandwidth limits. */ +if (def-mac_specified) { +virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _(Unsupported mac element in network %s + with forward mode='%s'), + def-name, + virNetworkForwardTypeToString(def-forward.type)); +return -1; +} if (virNetworkDefGetIpByIndex(def, AF_UNSPEC, 0)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _(Unsupported ip element in network %s @@ -2433,6 +2442,14 @@ networkValidate(virNetworkDriverStatePtr driver, virNetworkForwardTypeToString(def-forward.type)); return -1; } +if (def-bandwidth) { +virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _(Unsupported network-wide bandwidth element + in network %s with forward mode='%s'), + def-name, + virNetworkForwardTypeToString(def-forward.type)); +return -1; +} } /* We only support dhcp on one IPv4 address and -- 1.8.5.3 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 1/6] Add new API virDomainStreamDisk[Info] to header and drivers
After several long distractions, I am back to working on disk streaming. Before I hit the list with a new series of patches, I was hoping to reach some middle ground on the proposed streaming API. On Fri, 2011-04-08 at 14:31 +0100, Daniel P. Berrange wrote: On Thu, Apr 07, 2011 at 04:31:59PM -0500, Adam Litke wrote: /* + * Disk Streaming + */ +typedef enum { +VIR_STREAM_DISK_ONE = 1, /* Stream a single disk unit */ +VIR_STREAM_DISK_START = 2, /* Stream the entire disk */ +VIR_STREAM_DISK_STOP = 4, /* Stop streaming a disk */ +} virDomainStreamDiskFlags; Using flags to combine two separate tasks into one single API is rather unpleasant. As raised in the previous patch, the API should also be taking a offset+length in bytes, then there is no need for a special case transfer of an individual sector. This is a fair point. I will work with Stefan to support an offset/length qemu API. Since there doesn't seem to be a great way to query device sizes, I think we do need a convenient way to request that the entire disk be streamed. We could either do this with a flag or by overriding (offset==0 length==0) to mean stream the entire device. Do you have a preference? + +#define VIR_STREAM_PATH_BUFLEN 1024 +#define VIR_STREAM_DISK_MAX_STREAMS 10 + +typedef struct _virStreamDiskState virStreamDiskState; +struct _virStreamDiskState { +char path[VIR_STREAM_PATH_BUFLEN]; +/* + * The unit of measure for size and offset is unspecified. These fields + * are meant to indicate the progress of a continuous streaming operation. + */ +unsigned long long offset; /* Current offset of active streaming */ +unsigned long long size; /* Disk size */ +}; +typedef virStreamDiskState *virStreamDiskStatePtr; + +unsigned long long virDomainStreamDisk(virDomainPtr dom, + const char *path, + unsigned long long offset, + unsigned int flags); + +int virDomainStreamDiskInfo(virDomainPtr dom, + virStreamDiskStatePtr states, + unsigned int nr_states, + unsigned int flags); I would have liked it if we could use the existing JobInfo APIs for getting progress information, but if we need to allow concurrent usage for multiple disks per-VM, we can't. I think we should still use a similar style of API though. The goal is to eventually support concurrent streams. Therefore, we will probably need to roll our own Status and Abort APIs (see my proposed API below). There also doesn't appear to be a precise way to determine if the copying of an entire disk failed part way through, and if so, how much was actually copied. Taking all the previous points together, I think the API needs to look like this: typedef enum { /* If set, virDomainBlockAllocate() will return immediately * allowing polling for operation completion status */ VIR_DOMAIN_DISK_ALLOCATE_NONBLOCK, } virDomainBlockAllocateFlags; /* * @path: fully qualified filename of the virtual disk I probably misnamed it, but path is actually the device alias, not a path to an image file. * @offset: logical position in bytes, within the virtual disk * @length: amount of data to copy, in bytes starting from @offset * @flags: One of virDomainBlockAllocateFlags, or zero * * Ensure the virtual disk @path is fully allocated * between @offset and @offset+@length. If a backing * store is present, data will be filled from the * backing store, otherwise data will be fileld with * zeros * * If @flags contains VIR_DOMAIN_DISK_ALLOCATE_NONBLOCK, * this API will return immediately after initiating the * copy, otherwise it will block until copying is complete * */ int virDomainBlockAllocate(virDomainPtr dom, const char *path, unsigned long long offset, unsigned long long length, unsigned int flags); /* * @path: fully qualified filename of the virtual disk * @info: allocated struct to return progress info * * Query the progress of a disk allocation job. This * API must be used when virDomainBlockAllocate() was * invoked with the VIR_DOMAIN_DISK_ALLOCATE_NONBLOCK * flag set. * * The @info.type field will indicate whether the job * was completed successfully, or failed part way * through. * * The @info data progress fields will contain current * progress information. * * The hypervisor
Re: [libvirt] [PATCH 1/6] Add new API virDomainStreamDisk[Info] to header and drivers
On 05/09/2011 11:09 AM, Daniel P. Berrange wrote: On Mon, May 02, 2011 at 04:29:49PM -0500, Adam Litke wrote: After several long distractions, I am back to working on disk streaming. Before I hit the list with a new series of patches, I was hoping to reach some middle ground on the proposed streaming API. On Fri, 2011-04-08 at 14:31 +0100, Daniel P. Berrange wrote: On Thu, Apr 07, 2011 at 04:31:59PM -0500, Adam Litke wrote: /* + * Disk Streaming + */ +typedef enum { +VIR_STREAM_DISK_ONE = 1, /* Stream a single disk unit */ +VIR_STREAM_DISK_START = 2, /* Stream the entire disk */ +VIR_STREAM_DISK_STOP = 4, /* Stop streaming a disk */ +} virDomainStreamDiskFlags; Using flags to combine two separate tasks into one single API is rather unpleasant. As raised in the previous patch, the API should also be taking a offset+length in bytes, then there is no need for a special case transfer of an individual sector. This is a fair point. I will work with Stefan to support an offset/length qemu API. Since there doesn't seem to be a great way to query device sizes, I think we do need a convenient way to request that the entire disk be streamed. We could either do this with a flag or by overriding (offset==0 length==0) to mean stream the entire device. Do you have a preference? Since length==0 is otherwise meaningless, it is fine to use that to indicate until end of disk. This is consistent with what we do for virStorageVolUpload/Download which allow length=0 to indicate until end of disk. +#define VIR_STREAM_PATH_BUFLEN 1024 +#define VIR_STREAM_DISK_MAX_STREAMS 10 + +typedef struct _virStreamDiskState virStreamDiskState; +struct _virStreamDiskState { +char path[VIR_STREAM_PATH_BUFLEN]; +/* + * The unit of measure for size and offset is unspecified. These fields + * are meant to indicate the progress of a continuous streaming operation. + */ +unsigned long long offset; /* Current offset of active streaming */ +unsigned long long size; /* Disk size */ +}; +typedef virStreamDiskState *virStreamDiskStatePtr; + +unsigned long long virDomainStreamDisk(virDomainPtr dom, + const char *path, + unsigned long long offset, + unsigned int flags); + +int virDomainStreamDiskInfo(virDomainPtr dom, + virStreamDiskStatePtr states, + unsigned int nr_states, + unsigned int flags); I would have liked it if we could use the existing JobInfo APIs for getting progress information, but if we need to allow concurrent usage for multiple disks per-VM, we can't. I think we should still use a similar style of API though. The goal is to eventually support concurrent streams. Therefore, we will probably need to roll our own Status and Abort APIs (see my proposed API below). There also doesn't appear to be a precise way to determine if the copying of an entire disk failed part way through, and if so, how much was actually copied. Taking all the previous points together, I think the API needs to look like this: typedef enum { /* If set, virDomainBlockAllocate() will return immediately * allowing polling for operation completion status */ VIR_DOMAIN_DISK_ALLOCATE_NONBLOCK, } virDomainBlockAllocateFlags; /* * @path: fully qualified filename of the virtual disk I probably misnamed it, but path is actually the device alias, not a path to an image file. Hmm, I wonder whether that is a good choice or not. The other APIs all use the disk path. Perhaps we could use that as default and have a flag to indicate whether the path should be treated as a device alias instead. Thus getting both options. Does libvirt have an option to lookup a device alias by disk path? If so, then I am happy to use the file path and convert it to the form that qemu expects. * @offset: logical position in bytes, within the virtual disk * @length: amount of data to copy, in bytes starting from @offset * @flags: One of virDomainBlockAllocateFlags, or zero * * Ensure the virtual disk @path is fully allocated * between @offset and @offset+@length. If a backing * store is present, data will be filled from the * backing store, otherwise data will be fileld with * zeros * * If @flags contains VIR_DOMAIN_DISK_ALLOCATE_NONBLOCK, * this API will return immediately after initiating the * copy, otherwise it will block until copying is complete * */ int virDomainBlockAllocate(virDomainPtr dom, const char *path, unsigned long long offset, unsigned long long
[libvirt] API Proposal: Block device streaming (V3)
Version 2 of the block device streaming API diverged significantly from the original API provided by Qemu. In an effort to provide an API that is designed to work well for the specific task of streaming data to a local device from its backing store I am proposing this: version 3 of the block device streaming API. Some noteworthy changes from earlier versions: The offset and length parameters to virDomainStream have been replaced by an iterator type. Manual specification of ranges is outside of the scope of the API and does not add utility. Instead, an iterator is used to perform a series of individual operations in an efficient manner without exposing device format particulars. I have added a new call virDomainBlockStreamAbort() that can be used to cancel an active stream operation. This is an improvement over the use of flags to override the meaning of virDomainBlockStream(). Block devices are specified by the fully-qualified path to the source file. This information is easier for API users to provide than the block device alias. All API functions return an int. Rather than returning an unsigned long long, virDomainBlockStream() updates the the iterator (which is passed in by reference) in place. - API V3 - /* * Disk Streaming */ typedef enum { /* * If set, virDomainBlockStream() will begin streaming the entire device * continuously. The status can be checked and the operation aborted by using * the functions virDomainGetBlockStreamInfo() and virDomainBlockStreamAbort(). */ VIR_DOMAIN_BLOCK_STREAM_CONTINUOUS = 1, } virDomainStreamDiskFlags; #define VIR_DOMAIN_BLOCK_STREAM_PATH_MAX 1024 /* An iterator for initiating and monitoring block streaming operations */ typedef unsigned long long virDomainBlockStreamCursor; typedef struct _virDomainBlockStreamInfo virDomainBlockStreamInfo; struct _virDomainBlockStreamInfo { char path[VIR_DOMAIN_BLOCK_STREAM_PATH_MAX]; /* * The following fields provide an indication of streaming progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. To * approximate progress, divide @cur by @end. */ virDomainBlockStreamCursor cur; virDomainBlockStreamCursor end; }; typedef virDomainBlockStreamInfo *virDomainBlockStreamInfoPtr; /** * virDomainBlockStream: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @cursor: pointer to a virDomainBlockStreamCursor, or NULL * @flags: One of virDomainStreamDiskFlags, or zero * * Initate a streaming operation on the named block device associated with the * given domain. If VIR_DOMAIN_BLOCK_STREAM_CONTINUOUS is specified, the entire * device will be streamed in the background. Otherwise, the value stored in * cursor will be used to stream an increment. * * Returns -1 in case of failure, 0 when successful. On success and when @flags * does not contain VIR_DOMAIN_BLOCK_STREAM_CONTINUOUS, the iterator @cursor will * be updated to the proper value for use in a subsequent call. */ int virDomainBlockStream(virDomainPtr dom, const char *path, virDomainBlockStreamCursor *cursor, unsigned int flags); /** * virDomainBlockStreamAbort: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @flags: currently unused, for future extension * * Cancel a disk streaming operation that has been previously started by a call to * virDomainBlockStream() with the VIR_DOMAIN_BLOCK_STREAM_CONTINUOUS flag. * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockStreamAbort(virDomainPtr dom, const char *path, unsigned int flags); /** * virDomainGetBlockStreamInfo: * @dom: pointer to domain object * @info: pointer to an array of virDomainBlockStreamInfo objects * @nr_results: Number of results requested * @flags: currently unused, for future extension * * Request information on active block streaming operations for the given domain. * The caller must supply an allocated array of virDomainBlockStreamInfo objects * Information about up to @nr_results active streams will be stored in @info. * * Returns -1 in case of failure, or the number of results that were stored. */ int virDomainGetBlockStreamInfo(virDomainPtr dom, virDomainBlockStreamInfoPtr info, unsigned int nr_results, unsigned int flags); -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] API Proposal: Block device streaming (V3)
Hi Dan, thank you for another round of review. Stefan and Anthony, could you respond to Dan's suggestion of adding qemu events for stream completion and errors at the bottom of this message? On 05/20/2011 06:07 AM, Daniel P. Berrange wrote: On Wed, May 18, 2011 at 02:08:12PM -0500, Adam Litke wrote: Version 2 of the block device streaming API diverged significantly from the original API provided by Qemu. In an effort to provide an API that is designed to work well for the specific task of streaming data to a local device from its backing store I am proposing this: version 3 of the block device streaming API. Can you clarify what happens upon completion of streaming ? Is the backing store going to automagically go away, or will there need to be a separate API to explicitly remove the (now unncessary) backing store ? As soon as an image gains independence from its backing store, qemu removes the link to the backing file. I'm still not really a fan of the name 'Stream' in the APIs because it doesn't give any indication of what the effect of the API is. 'Streaming' refers to the manner in which the API operates, rather what its purpose/goal is. If the backing store is automagically removed, is this better described as a 'Rebase' command/operation ? qemu-img already has a 'rebase' command so I would hate to introduce confusion by overloading that term here. How about 'Populate'? Some noteworthy changes from earlier versions: The offset and length parameters to virDomainStream have been replaced by an iterator type. Manual specification of ranges is outside of the scope of the API and does not add utility. Instead, an iterator is used to perform a series of individual operations in an efficient manner without exposing device format particulars. I have added a new call virDomainBlockStreamAbort() that can be used to cancel an active stream operation. This is an improvement over the use of flags to override the meaning of virDomainBlockStream(). Block devices are specified by the fully-qualified path to the source file. This information is easier for API users to provide than the block device alias. All API functions return an int. Rather than returning an unsigned long long, virDomainBlockStream() updates the the iterator (which is passed in by reference) in place. - API V3 - /* * Disk Streaming */ typedef enum { /* * If set, virDomainBlockStream() will begin streaming the entire device * continuously. The status can be checked and the operation aborted by using * the functions virDomainGetBlockStreamInfo() and virDomainBlockStreamAbort(). */ VIR_DOMAIN_BLOCK_STREAM_CONTINUOUS = 1, } virDomainStreamDiskFlags; #define VIR_DOMAIN_BLOCK_STREAM_PATH_MAX 1024 /* An iterator for initiating and monitoring block streaming operations */ typedef unsigned long long virDomainBlockStreamCursor; typedef struct _virDomainBlockStreamInfo virDomainBlockStreamInfo; struct _virDomainBlockStreamInfo { char path[VIR_DOMAIN_BLOCK_STREAM_PATH_MAX]; We really shouldn't be having hardcoded path lengths in the public API. IMHO this field should be dropped and the StreamInfo API should have the path passed in I'm okay with changing this as you suggest, but the effect will be that we lose a way to query for active streams. I guess an API user could separately determine the paths of all connected disks and perform virDomainBlockStreamInfo() on each path. /* * The following fields provide an indication of streaming progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. To * approximate progress, divide @cur by @end. */ virDomainBlockStreamCursor cur; virDomainBlockStreamCursor end; }; typedef virDomainBlockStreamInfo *virDomainBlockStreamInfoPtr; /** * virDomainBlockStream: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @cursor: pointer to a virDomainBlockStreamCursor, or NULL * @flags: One of virDomainStreamDiskFlags, or zero * * Initate a streaming operation on the named block device associated with the * given domain. If VIR_DOMAIN_BLOCK_STREAM_CONTINUOUS is specified, the entire * device will be streamed in the background. Otherwise, the value stored in * cursor will be used to stream an increment. * * Returns -1 in case of failure, 0 when successful. On success and when @flags * does not contain VIR_DOMAIN_BLOCK_STREAM_CONTINUOUS, the iterator @cursor will * be updated to the proper value for use in a subsequent call. */ int virDomainBlockStream(virDomainPtr dom, const char *path, virDomainBlockStreamCursor *cursor, unsigned
Re: [libvirt] [PATCHv4 1/4] vcpupin: inroduce a new libvir API (virDomainPinVcpuFlags)
On 05/20/2011 04:08 AM, Taku Izumi wrote: /** + * virDomainPinVcpuFlags: + * @domain: pointer to domain object, or NULL for Domain0 + * @vcpu: virtual CPU number + * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN) + * Each bit set to 1 means that corresponding CPU is usable. + * Bytes are stored in little-endian order: CPU0-7, 8-15... + * In each byte, lowest CPU number is least significant bit. + * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in + * underlying virtualization system (Xen...). + * If maplen size, missing bytes are set to zero. + * If maplen size, failure code is returned. + * @flags: an OR'ed subset of virDomainVcpuFlags + * + * Dynamically change the real CPUs which can be allocated to a virtual CPU. + * This function requires privileged access to the hypervisor. + * + * @flags may include VIR_DOMAIN_VCPU_LIVE or VIR_DOMAIN_VCPU_CONFIG. + * Both flags may be set, but VIR_DOMAIN_VCPU_MAXIMUM cannot be set. + * If VIR_DOMAIN_VCPU_LIVE is set, the change affects a running domain + * and may fail if domain is not alive. + * If VIR_DOMAIN_VCPU_CONFIG is set, the change affects persistent state, + * and will fail for transient domains. + * If neither flag is specified (that is, @flags is VIR_DOMAIN_VCPU_CURRENT), + * then an inactive domain modifies persistent setup, while an active domain + * is hypervisor-dependent on whether just live or both live and persistent + * state is changed. + * Not all hypervisors can support all flag combinations. I am really confused about what the default flags (VIR_DOMAIN_VCPU_CURRENT) actually means. It doesn't seem like I can ever know what it means from an API perspective (since it's behavior is arbitrary depending on the hypervisor). Wouldn't it be better to require _either_ VIR_DOMAIN_VCPU_LIVE _or_ VIR_DOMAIN_VCPU_CONFIG to be set so that the intended behavior can be set with specificity? Also, shouldn't these flags be named VIR_DOMAIN_PIN_VCPU_FLAGS_{LIVE|CONFIG} since they are meant only for the virDomainPinVcpuFlags() API? -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCHv4 2/4] vcpupin: implement the code to address the new API in the qemu driver
On 05/20/2011 04:09 AM, Taku Izumi wrote: --- libvirt.orig/src/qemu/qemu_driver.c +++ libvirt/src/qemu/qemu_driver.c @@ -2859,17 +2859,24 @@ qemudDomainSetVcpus(virDomainPtr dom, un static int -qemudDomainPinVcpu(virDomainPtr dom, - unsigned int vcpu, - unsigned char *cpumap, - int maplen) { +qemudDomainPinVcpuFlags(virDomainPtr dom, +unsigned int vcpu, +unsigned char *cpumap, +int maplen, +unsigned int flags) { + struct qemud_driver *driver = dom-conn-privateData; virDomainObjPtr vm; +virDomainDefPtr persistentDef = NULL; int maxcpu, hostcpus; virNodeInfo nodeinfo; int ret = -1; +bool isActive; qemuDomainObjPrivatePtr priv; +virCheckFlags(VIR_DOMAIN_VCPU_LIVE | + VIR_DOMAIN_VCPU_CONFIG, -1); Is this even possible since you already checked it at the top level? -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCHv4 4/4] vcpupin: add the new options to virsh vcpupin command
On 05/20/2011 04:10 AM, Taku Izumi wrote: --- libvirt.orig/tools/virsh.c +++ libvirt/tools/virsh.c @@ -2757,6 +2757,9 @@ static const vshCmdOptDef opts_vcpupin[] {domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, {vcpu, VSH_OT_INT, VSH_OFLAG_REQ, N_(vcpu number)}, {cpulist, VSH_OT_DATA, VSH_OFLAG_REQ, N_(host cpu number(s) (comma separated))}, +{config, VSH_OT_BOOL, 0, N_(affect next boot)}, +{live, VSH_OT_BOOL, 0, N_(affect running domain)}, +{current, VSH_OT_BOOL, 0, N_(affect current domain)}, I am probably just unenlightened, but I still don't understand the difference between 'current domain' and 'running domain'. {NULL, 0, 0, NULL} }; snip --- libvirt.orig/tools/virsh.pod +++ libvirt/tools/virsh.pod @@ -767,10 +767,16 @@ values; these two flags cannot both be s Returns basic information about the domain virtual CPUs, like the number of vCPUs, the running time, the affinity to physical processors. -=item Bvcpupin Idomain-id Ivcpu Icpulist +=item Bvcpupin Idomain-id Ivcpu Icpulist optional I--live I--config +I--current Pin domain VCPUs to host physical CPUs. The Ivcpu number must be provided and Icpulist is a comma separated list of physical CPU numbers. +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--config flags may be given, but I--current is exclusive. +If no flag is specified, behavior is different depending on hypervisor. According to the API documentation, --current is the same as not specifying either flag. If that's correct, than both the default (no flags) and --current have the same behavior (which is hypervisor dependent). The --current switch doesn't even seem necessary. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCHv4 1/4] vcpupin: inroduce a new libvir API (virDomainPinVcpuFlags)
On 05/20/2011 05:17 PM, Eric Blake wrote: On 05/20/2011 02:54 PM, Adam Litke wrote: I am really confused about what the default flags (VIR_DOMAIN_VCPU_CURRENT) actually means. It doesn't seem like I can ever know what it means from an API perspective (since it's behavior is arbitrary depending on the hypervisor). Wouldn't it be better to require _either_ VIR_DOMAIN_VCPU_LIVE _or_ VIR_DOMAIN_VCPU_CONFIG to be set so that the intended behavior can be set with specificity? Anywhere we use _CURRENT, it is supposed to mean _LIVE (if the VM is running) or _CONFIG (if the VM is persistent but offline). Yes, explicitly specifying _LIVE, _CONFIG, or the combination of both, is probably better. And in the past, we haven't always used those conventions; that is, there are some APIs where _CURRENT is non-zero, and the default (0) was unclear in its meaning. But all new APIs should use _CURRENT as 0, so that the default has clear semantics. However, there are some hypervisors that cannot do just _LIVE on persistent domains - they can only do _LIVE|_CONFIG (for transient domains, though, it should be obvious that just _LIVE works, since _CONFIG cannot succeed on a transient domain). Thanks for your explanation, Eric. It's a lot clearer to me now and the API makes sense. Cleanups to the virsh documentation as you suggest will hopefully prevent future questions like mine. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] API Proposal: virDomainBlockPull() (Block device streaming) V4
Here is version 4 of the BlockStream API (now called BlockPull). Hopefully this version addresses all remaining concerns and I can begin to work on the code. Does everyone approve of the new name 'virDomainBlockPull'? Changes since V3: - Renamed the API to 'Block Pull' to emphasize the effect of the API over its method of operation. - Changed virDomainGetBlockPullInfo() to accept a path argument and to return information about that path alone. - Added a new virDomainEvent to report final status when using CONTINUOUS mode. /* * Block Pull API */ typedef enum { /* * If set, virDomainBlockPull() will operate on the entire device in the * background. The status can be checked and the operation aborted by * using the functions virDomainBlockPullInfo() and * virDomainBlockPullAbort(). */ VIR_DOMAIN_BLOCK_PULL_CONTINUOUS = 1, } virDomainBlockPullFlags; /* An iterator for initiating and monitoring block pull operations */ typedef unsigned long long virDomainBlockPullCursor; typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; struct _virDomainBlockPullInfo { /* * The following fields provide an indication of block pull progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. * To approximate progress, divide @cur by @end. */ virDomainBlockPullCursor cur; virDomainBlockPullCursor end; }; typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; /** * virDomainBlockPull: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @cursor: pointer to a virDomainBlockPullCursor, or NULL * @flags: One of virDomainBlockPullFlags, or zero * * Populate a disk image with data from its backing image. Once all data from * its backing image has been pulled, the disk no longer depends on a backing * image. * * If VIR_DOMAIN_BLOCK_PULL_CONTINUOUS is specified, the entire device will be * streamed in the background. Otherwise, the value stored in @cursor will be * used to stream an increment. * * Returns -1 in case of failure, 0 when successful. On success and when @flags * does not contain VIR_DOMAIN_BLOCK_PULL_CONTINUOUS, the iterator @cursor will * be updated to the proper value for use in a subsequent call. */ int virDomainBlockPull(virDomainPtr dom, const char *path, virDomainBlockPullCursor *cursor, unsigned int flags); /** * virDomainBlockPullAbort: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @flags: currently unused, for future extension * * Cancel a pull operation that has been previously started by a call to * virDomainBlockPull() with the VIR_DOMAIN_BLOCK_PULL_CONTINUOUS flag. * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags); /** * virDomainGetBlockPullInfo: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @info: pointer to a virDomainBlockPullInfo structure * @flags: currently unused, for future extension * * Request progress information on a block pull operation that has been started * with the VIR_DOMAIN_BLOCK_PULL_CONTINUOUS flag set. If an operation is * active for the given parameters, @info will be updated with the current * progress. * * Returns -1 in case of failure, 0 when successful. */ int virDomainGetBlockPullInfo(virDomainPtr dom, const char *path, virDomainBlockStreamInfoPtr info, unsigned int flags); The following new asynchronous event will be made available for subscription: VIR_DOMAIN_EVENT_ID_BLOCK_PULL = 7, typedef enum { VIR_DOMAIN_BLOCK_PULL_COMPLETED, VIR_DOMAIN_BLOCK_PULL_FAILED, } virConnectDomainEventBlockPullStatus; typedef void (*virConnectDomainEventBlockPullCallback(virConnectPtr conn, virDomainPtr dom, const char *path, int status); Upon receipt of this event and when the status field indicates success, libvirt will revoke access to the backing file which is no longer needed by the domain. NOTE: Qemu will emit an asynchronous event (VIR_DOMAIN_BLOCK_PULL_COMPLETED) after any operation that eliminates the dependency on the backing file. This could be a virDomainBlockPull() without VIR_DOMAIN_BLOCK_PULL_CONTINUOUS and will allow libvirt to manage backing file security regardless of the pull mode used. -- Adam
Re: [libvirt] API Proposal: virDomainBlockPull() (Block device streaming) V4
On 05/24/2011 07:19 AM, Daniel P. Berrange wrote: On Mon, May 23, 2011 at 11:56:03AM -0500, Adam Litke wrote: Here is version 4 of the BlockStream API (now called BlockPull). Hopefully this version addresses all remaining concerns and I can begin to work on the code. Does everyone approve of the new name 'virDomainBlockPull'? Changes since V3: - Renamed the API to 'Block Pull' to emphasize the effect of the API over its method of operation. - Changed virDomainGetBlockPullInfo() to accept a path argument and to return information about that path alone. - Added a new virDomainEvent to report final status when using CONTINUOUS mode. /* * Block Pull API */ typedef enum { /* * If set, virDomainBlockPull() will operate on the entire device in the * background. The status can be checked and the operation aborted by * using the functions virDomainBlockPullInfo() and * virDomainBlockPullAbort(). */ VIR_DOMAIN_BLOCK_PULL_CONTINUOUS = 1, } virDomainBlockPullFlags; /* An iterator for initiating and monitoring block pull operations */ typedef unsigned long long virDomainBlockPullCursor; typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; struct _virDomainBlockPullInfo { /* * The following fields provide an indication of block pull progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. * To approximate progress, divide @cur by @end. */ virDomainBlockPullCursor cur; virDomainBlockPullCursor end; }; typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; /** * virDomainBlockPull: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @cursor: pointer to a virDomainBlockPullCursor, or NULL * @flags: One of virDomainBlockPullFlags, or zero * * Populate a disk image with data from its backing image. Once all data from * its backing image has been pulled, the disk no longer depends on a backing * image. * * If VIR_DOMAIN_BLOCK_PULL_CONTINUOUS is specified, the entire device will be * streamed in the background. Otherwise, the value stored in @cursor will be * used to stream an increment. * * Returns -1 in case of failure, 0 when successful. On success and when @flags * does not contain VIR_DOMAIN_BLOCK_PULL_CONTINUOUS, the iterator @cursor will * be updated to the proper value for use in a subsequent call. */ int virDomainBlockPull(virDomainPtr dom, const char *path, virDomainBlockPullCursor *cursor, unsigned int flags); /** * virDomainBlockPullAbort: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @flags: currently unused, for future extension * * Cancel a pull operation that has been previously started by a call to * virDomainBlockPull() with the VIR_DOMAIN_BLOCK_PULL_CONTINUOUS flag. * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags); /** * virDomainGetBlockPullInfo: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @info: pointer to a virDomainBlockPullInfo structure * @flags: currently unused, for future extension * * Request progress information on a block pull operation that has been started * with the VIR_DOMAIN_BLOCK_PULL_CONTINUOUS flag set. If an operation is * active for the given parameters, @info will be updated with the current * progress. * * Returns -1 in case of failure, 0 when successful. */ int virDomainGetBlockPullInfo(virDomainPtr dom, const char *path, virDomainBlockStreamInfoPtr info, unsigned int flags); The following new asynchronous event will be made available for subscription: VIR_DOMAIN_EVENT_ID_BLOCK_PULL = 7, typedef enum { VIR_DOMAIN_BLOCK_PULL_COMPLETED, VIR_DOMAIN_BLOCK_PULL_FAILED, } virConnectDomainEventBlockPullStatus; typedef void (*virConnectDomainEventBlockPullCallback(virConnectPtr conn, virDomainPtr dom, const char *path, int status); Upon receipt of this event and when the status field indicates success, libvirt will revoke access to the backing file which is no longer needed by the domain. NOTE: Qemu will emit an asynchronous event (VIR_DOMAIN_BLOCK_PULL_COMPLETED
Re: [libvirt] API Proposal: virDomainBlockPull() (Block device streaming) V4
On 05/24/2011 07:16 AM, Daniel P. Berrange wrote: On Tue, May 24, 2011 at 01:00:04PM +0100, Stefan Hajnoczi wrote: On Mon, May 23, 2011 at 5:56 PM, Adam Litke a...@us.ibm.com wrote: /** * virDomainBlockPull: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @cursor: pointer to a virDomainBlockPullCursor, or NULL * @flags: One of virDomainBlockPullFlags, or zero * * Populate a disk image with data from its backing image. Once all data from * its backing image has been pulled, the disk no longer depends on a backing * image. * * If VIR_DOMAIN_BLOCK_PULL_CONTINUOUS is specified, the entire device will be * streamed in the background. Otherwise, the value stored in @cursor will be * used to stream an increment. * * Returns -1 in case of failure, 0 when successful. On success and when @flags * does not contain VIR_DOMAIN_BLOCK_PULL_CONTINUOUS, the iterator @cursor will * be updated to the proper value for use in a subsequent call. */ int virDomainBlockPull(virDomainPtr dom, const char *path, virDomainBlockPullCursor *cursor, unsigned int flags); If this function is used without VIR_DOMAIN_BLOCK_PULL_CONTINUOUS then the end value is unknown. Therefore it is not possible to calculate streaming progress. Perhaps instead of cursor we need a virDomainBlockStreamInfoPtr info argument? It almost feels like we should just not overload semantics using flags and have a separate APIs: Incremental, just iterate on: int virDomainBlockPull(virDomainPtr dom, const char *path, virDomainBlockPullInfoPtr *pos, We don't even need 'pos' anymore. See below. unsigned int flags); Continuous, invoke once: int virDomainBlockPullAll(virDomainPtr dom, const char *path, unsigned int flags); ...and wait for the async event notification for completion, or optionally poll on virDomainGetBlockPullInfo, or use BlockPullAbort() Yeah, despite adding four functions to the API this seems like a natural way to segment it out. NOTE: Qemu will emit an asynchronous event (VIR_DOMAIN_BLOCK_PULL_COMPLETED) after any operation that eliminates the dependency on the backing file. This could be a virDomainBlockPull() without VIR_DOMAIN_BLOCK_PULL_CONTINUOUS and will allow libvirt to manage backing file security regardless of the pull mode used. Currently QEMU does not change the backing file when incremental streaming is used (instead of continuous). This is because it is hard to know whether or not the entire file has finished streaming - it's an operation that requires traversing all block allocation metadata to check that the backing file is no longer necessary. Having different semantics for what happens at the end of streaming for incremental vs continuous is a really bad idea. I assume this limitation will be fixed in QEMU before streaming is finally merged ? After talking to Stefan I think we have resolved this issue. We have decided to drop the 'pos' argument to virDomainBlockPull() completely. If pos is a sequential, opaque cursor then there is only ever one valid value that can be passed to qemu at any given time. If qemu has to store the valid value to check against, then we might as well just store the position internally to qemu and save the API user the trouble of managing the iterator. An added benefit is that we now know it is safe to know that the entire disk has been pulled after the last cursor position has finished (regardless of which mode was used). I will follow-up with V5 of the API... -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] API Proposal: virDomainBlockPull() (Block device streaming) V4
I need to clarify this a bit... On 05/24/2011 08:06 AM, Adam Litke wrote: On 05/24/2011 07:16 AM, Daniel P. Berrange wrote: On Tue, May 24, 2011 at 01:00:04PM +0100, Stefan Hajnoczi wrote: On Mon, May 23, 2011 at 5:56 PM, Adam Litke a...@us.ibm.com wrote: /** * virDomainBlockPull: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @cursor: pointer to a virDomainBlockPullCursor, or NULL * @flags: One of virDomainBlockPullFlags, or zero * * Populate a disk image with data from its backing image. Once all data from * its backing image has been pulled, the disk no longer depends on a backing * image. * * If VIR_DOMAIN_BLOCK_PULL_CONTINUOUS is specified, the entire device will be * streamed in the background. Otherwise, the value stored in @cursor will be * used to stream an increment. * * Returns -1 in case of failure, 0 when successful. On success and when @flags * does not contain VIR_DOMAIN_BLOCK_PULL_CONTINUOUS, the iterator @cursor will * be updated to the proper value for use in a subsequent call. */ int virDomainBlockPull(virDomainPtr dom, const char *path, virDomainBlockPullCursor *cursor, unsigned int flags); If this function is used without VIR_DOMAIN_BLOCK_PULL_CONTINUOUS then the end value is unknown. Therefore it is not possible to calculate streaming progress. Perhaps instead of cursor we need a virDomainBlockStreamInfoPtr info argument? It almost feels like we should just not overload semantics using flags and have a separate APIs: Incremental, just iterate on: int virDomainBlockPull(virDomainPtr dom, const char *path, virDomainBlockPullInfoPtr *pos, We don't even need 'pos' anymore. See below. We still will want 'pos', but it changes from an in/out parameter to out-only. Qemu tracks the position internally so this is just for progress reporting. unsigned int flags); Continuous, invoke once: int virDomainBlockPullAll(virDomainPtr dom, const char *path, unsigned int flags); ...and wait for the async event notification for completion, or optionally poll on virDomainGetBlockPullInfo, or use BlockPullAbort() Yeah, despite adding four functions to the API this seems like a natural way to segment it out. NOTE: Qemu will emit an asynchronous event (VIR_DOMAIN_BLOCK_PULL_COMPLETED) after any operation that eliminates the dependency on the backing file. This could be a virDomainBlockPull() without VIR_DOMAIN_BLOCK_PULL_CONTINUOUS and will allow libvirt to manage backing file security regardless of the pull mode used. Currently QEMU does not change the backing file when incremental streaming is used (instead of continuous). This is because it is hard to know whether or not the entire file has finished streaming - it's an operation that requires traversing all block allocation metadata to check that the backing file is no longer necessary. Having different semantics for what happens at the end of streaming for incremental vs continuous is a really bad idea. I assume this limitation will be fixed in QEMU before streaming is finally merged ? After talking to Stefan I think we have resolved this issue. We have decided to drop the 'pos' argument to virDomainBlockPull() completely. If pos is a sequential, opaque cursor then there is only ever one valid value that can be passed to qemu at any given time. If qemu has to store the valid value to check against, then we might as well just store the position internally to qemu and save the API user the trouble of managing the iterator. An added benefit is that we now know it is safe to know that the entire disk has been pulled after the last cursor position has finished (regardless of which mode was used). As stated above, 'pos' remains in the API but becomes an out parameter. I will follow-up with V5 of the API... -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] API Proposal: virDomainBlockPull() V5
Changes since V4: - Split out BlockPull() and BlockPullAll(). - BlockPull() cursor changed to a virDomainBlockPullInfo for progress reporting only (from in/out to out-only parameter). /* An iterator for initiating and monitoring block pull operations */ typedef unsigned long long virDomainBlockPullCursor; typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; struct _virDomainBlockPullInfo { /* * The following fields provide an indication of block pull progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. * To approximate progress, divide @cur by @end. */ virDomainBlockPullCursor cur; virDomainBlockPullCursor end; }; typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; /** * virDomainBlockPull: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @pos: A pointer to a virDomainBlockPullInfo structure, or NULL * @flags: currently unused, for future extension * * Populate a disk image with data from its backing image. Once all data from * its backing image has been pulled, the disk no longer depends on a backing * image. This function works incrementally, performing a small amount of work * each time it is called. When successful, @pos is updated with the current * progress. * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockPull(virDomainPtr dom, const char *path, virDomainBlockPullInfoPtr *pos, unsigned int flags); /** * virDomainBlockPullAll: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @flags: currently unused, for future extension * * Populate a disk image with data from its backing image. Once all data from * its backing image has been pulled, the disk no longer depends on a backing * image. This function pulls data for the entire device in the background. * Progress of the operation can be checked with virDomainGetBlockPullInfo() and * the operation can be aborted with virDomainBlockPullAbort(). When finished, * an asynchronous event is raised to indicate the final status. * * Returns 0 if the operation has started, -1 on failure. */ int virDomainBlockPullAll(virDomainPtr dom, const char *path, unsigned int flags); /** * virDomainBlockPullAbort: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @flags: currently unused, for future extension * * Cancel a pull operation previously started by virDomainBlockPullAll(). * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags); /** * virDomainGetBlockPullInfo: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @info: pointer to a virDomainBlockPullInfo structure * @flags: currently unused, for future extension * * Request progress information on a block pull operation that has been started * with virDomainBlockPullAll(). If an operation is active for the given * parameters, @info will be updated with the current progress. * * Returns -1 in case of failure, 0 when successful. */ int virDomainGetBlockPullInfo(virDomainPtr dom, const char *path, virDomainBlockStreamInfoPtr info, unsigned int flags); The following new asynchronous event will be made available for subscription: VIR_DOMAIN_EVENT_ID_BLOCK_PULL = 7, typedef enum { VIR_DOMAIN_BLOCK_PULL_COMPLETED, VIR_DOMAIN_BLOCK_PULL_FAILED, } virConnectDomainEventBlockPullStatus; typedef void (*virConnectDomainEventBlockPullCallback(virConnectPtr conn, virDomainPtr dom, const char *path, int status); Upon receipt of this event and when the status field indicates success, libvirt will revoke access to the backing file which is no longer needed by the domain. NOTE: Qemu will emit an asynchronous event (VIR_DOMAIN_BLOCK_PULL_COMPLETED) after any operation that eliminates the dependency on the backing file. This will allow libvirt to manage backing file security. If the operation failed, an event (VIR_DOMAIN_BLOCK_PULL_FAILED) will indicate that the backing file has not been removed. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir
[libvirt] [PATCH 7/7] test: Python Unittests for DomainBlockPull API
*** Please do not consider for merging, example tests only *** Here are the testcases I am currently running to verify the correctness of this API. These are continuing to evolve. Signed-off-by: Adam Litke a...@us.ibm.com --- blockPull-test.py | 215 + 1 files changed, 215 insertions(+), 0 deletions(-) create mode 100755 blockPull-test.py diff --git a/blockPull-test.py b/blockPull-test.py new file mode 100755 index 000..8433b70 --- /dev/null +++ b/blockPull-test.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python + +import sys +import subprocess +import time +import unittest +import re +import threading +import libvirt + +qemu_img_bin = /home/aglitke/src/qemu/qemu-img + +dom_xml = +domain type='kvm' + nameblockPull-test/name + memory131072/memory + currentMemory131072/currentMemory + vcpu1/vcpu + os +type arch='x86_64' machine='pc-0.13'hvm/type +boot dev='hd'/ + /os + features +acpi/ +apic/ +pae/ + /features + clock offset='utc'/ + on_poweroffdestroy/on_poweroff + on_rebootrestart/on_reboot + on_crashrestart/on_crash + devices + emulator/home/aglitke/src/qemu/x86_64-softmmu/qemu-system-x86_64/emulator +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk1.qed' / + target dev='vda' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk2.qed' / + target dev='vdb' bus='virtio'/ +/disk +graphics type='vnc' port='-1' autoport='yes'/ + /devices +/domain + + +def qemu_img(*args): +global qemu_img_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([qemu_img_bin] + list(args), stdin=devnull, stdout=devnull) + +def make_baseimage(name, size_mb): +devnull = open('/dev/null', 'r+') +return subprocess.call(['dd', 'if=/dev/zero', of=%s % name, 'bs=1M', +'count=%i' % size_mb], stdin=devnull, stdout=devnull, stderr=devnull) + +def has_backing_file(path): +global qemu_img_bin +p1 = subprocess.Popen([qemu_img_bin, info, path], + stdout=subprocess.PIPE).communicate()[0] +matches = re.findall(^backing file:, p1, re.M) +if len(matches) 0: +return True +return False + +class BlockPullTestCase(unittest.TestCase): +def _error_handler(self, ctx, error, dummy=None): +pass + +def create_disks(self, sparse): +self.disks = [ '/tmp/disk1.qed', '/tmp/disk2.qed' ] +if sparse: +qemu_img('create', '-f', 'raw', '/tmp/backing1.img', '100M') +qemu_img('create', '-f', 'raw', '/tmp/backing2.img', '100M') +else: +make_baseimage('/tmp/backing1.img', 100) +make_baseimage('/tmp/backing2.img', 100) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing1.img,copy_on_read=on', self.disks[0]) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing2.img,copy_on_read=on', self.disks[1]) + +def begin(self, sparse=True): +global dom_xml + +libvirt.registerErrorHandler(self._error_handler, None) +self.create_disks(sparse) +self.conn = libvirt.open('qemu:///system') +self.dom = self.conn.createXML(dom_xml, 0) + +def end(self): +self.dom.destroy() +self.conn.close() + +class TestBasicErrors(BlockPullTestCase): +def setUp(self): +self.begin() + +def tearDown(self): +self.end() + +def test_bad_path(self): +try: +self.dom.blockPull('/dev/null', 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e[0]) + +def test_abort_no_stream(self): +try: +self.dom.blockPullAbort(self.disks[0], 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_INTERNAL_ERROR, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_INTERNAL_ERROR, e[0]) + +class TestBasicCommands(BlockPullTestCase): +def setUp(self): +pass + +def tearDown(self): +self.end() + +def test_start_stop(self): +self.begin(sparse=False) +self.dom.blockPullAll(self.disks[0], 0) +time.sleep(1) +self.assertIsNot(None, self.dom.blockPullInfo(self.disks[0], 0)) +self.dom.blockPullAbort(self.disks[0], 0) +time.sleep(1) +self.assertIs(None, self.dom.blockPullInfo(self.disks[0], 0)) + +self.dom.blockPull(self.disks[0], 0) +info = self.dom.blockPullInfo(self.disks[0], 0) +self.assertNotEqual(info['cur'], 0) + +def
[libvirt] [PATCH 3/7] Implement virDomainBlockPull for the qemu driver
The virDomainBlockPull* family of commands are enabled by the 'block_stream' and 'info block_stream' qemu monitor commands. * src/qemu/qemu_driver.c src/qemu/qemu_monitor_text.[ch]: implement disk streaming by using the stream and info stream text monitor commands * src/qemu/qemu_monitor_json.[ch]: implement commands using the qmp monitor Signed-off-by: Adam Litke a...@us.ibm.com --- src/qemu/qemu_driver.c | 112 +++- src/qemu/qemu_monitor.c | 16 + src/qemu/qemu_monitor.h | 13 src/qemu/qemu_monitor_json.c | 118 ++ src/qemu/qemu_monitor_json.h |5 ++ src/qemu/qemu_monitor_text.c | 145 ++ src/qemu/qemu_monitor_text.h |5 ++ 7 files changed, 410 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index cfbe199..481b168 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7080,6 +7080,110 @@ cleanup: return ret; } +static const char * +qemuDiskPathToAlias(virDomainObjPtr vm, const char *path) { +int i; +char *ret = NULL; + +for (i = 0 ; i vm-def-ndisks ; i++) { +virDomainDiskDefPtr disk = vm-def-disks[i]; + +if (disk-src != NULL STREQ(disk-src, path)) { +if (virAsprintf(ret, drive-%s, disk-info.alias) 0) { +virReportOOMError(); +return NULL; +} +break; +} +} + +if (!ret) { +qemuReportError(VIR_ERR_INVALID_ARG, +%s, _(No device found for specified path)); +} +return ret; +} + +static int +qemuDomainBlockPullImpl(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, +int mode) +{ +struct qemud_driver *driver = dom-conn-privateData; +virDomainObjPtr vm = NULL; +qemuDomainObjPrivatePtr priv; +char uuidstr[VIR_UUID_STRING_BUFLEN]; +const char *device = NULL; +int ret = -1; + +qemuDriverLock(driver); +virUUIDFormat(dom-uuid, uuidstr); +vm = virDomainFindByUUID(driver-domains, dom-uuid); +if (!vm) { +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +device = qemuDiskPathToAlias(vm, path); +if (!device) { +goto cleanup; +} + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +priv = vm-privateData; +ret = qemuMonitorBlockPull(priv-mon, device, info, mode); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +VIR_FREE(device); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + +static int +qemuDomainBlockPull(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_ONE); +} + +static int +qemuDomainBlockPullAll(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ALL); +} + +static int +qemuDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ABORT); +} + +static int +qemuDomainGetBlockPullInfo(virDomainPtr dom, const char *path, + virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_INFO); +} static virDriver qemuDriver = { VIR_DRV_QEMU, @@ -7192,10 +7296,10 @@ static virDriver qemuDriver = { qemuDomainSnapshotDelete, /* domainSnapshotDelete */ qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ qemuDomainOpenConsole, /* domainOpenConsole */ -NULL, /* domainBlockPull */ -NULL, /* domainBlockPullAll */ -NULL, /* domainBlockPullAbort */ -NULL, /* domainGetBlockPullInfo */ +qemuDomainBlockPull, /* domainBlockPull */ +qemuDomainBlockPullAll, /* domainBlockPullAll */ +qemuDomainBlockPullAbort, /* domainBlockPullAbort */ +qemuDomainGetBlockPullInfo, /* domainGetBlockPullInfo */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f89038e..60e4ee2 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2228,3 +2228,19 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply
[libvirt] [PATCH 6/7] Asynchronous event for BlockPull completion
When an operation started by virDomainBlockPullAll completes (either with success or with failure), raise an event to indicate the final status. This allows an API user to avoid polling on virDomainBlockPullInfo if they would prefer to use the event mechanism. * daemon/remote.c: Dispatch events to client * include/libvirt/libvirt.h.in: Define event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle the new event * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block_stream completion and emit a libvirt block pull event * src/remote/remote_driver.c: Receive and dispatch events to application * src/remote/remote_protocol.x: Wire protocol definition for the event * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_STREAM_COMPLETED event from QEMU monitor Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 32 include/libvirt/libvirt.h.in | 27 + python/libvirt-override-virConnect.py | 12 python/libvirt-override.c | 51 + src/conf/domain_event.c | 50 src/conf/domain_event.h |7 - src/libvirt_private.syms |2 + src/qemu/qemu_monitor.c | 12 src/qemu/qemu_monitor.h |8 + src/qemu/qemu_monitor_json.c | 30 +++ src/qemu/qemu_process.c | 28 ++ src/remote/remote_driver.c| 30 +++ src/remote/remote_protocol.x |9 +- 13 files changed, 296 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index f6aa78e..f45052c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -378,6 +378,37 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int remoteRelayDomainEventBlockPull(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *path, + int status, + void *opaque) +{ +struct qemud_client *client = opaque; +remote_domain_event_block_pull_msg data; + +if (!client) +return -1; + +VIR_DEBUG(Relaying domain block pull event %s %d %s %i, dom-name, dom-id, path, status); + +virMutexLock(client-lock); + +/* build return data */ +memset(data, 0, sizeof data); +make_nonnull_domain(data.dom, dom); +data.path = (char*)path; +data.status = status; + +remoteDispatchDomainEventSend(client, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_event_block_pull_msg, data); + +virMutexUnlock(client-lock); + +return 0; +} + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), @@ -387,6 +418,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), +VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockPull), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 9af1b76..c85e204 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2505,6 +2505,32 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventBlockPullStatus: + * + * The final status of a virDomainBlockPullAll() operation + */ +typedef enum { +VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0, +VIR_DOMAIN_BLOCK_PULL_FAILED = 1, +} virConnectDomainEventBlockPullStatus; + +/** + * virConnectDomainEventBlockPullCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @path: fully-qualified filename of the affected disk + * @status: final status of the operation (virConnectDomainEventBlockPullStatus) + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *path, + int status
[libvirt] [PATCH 5/7] Enable virDomainBlockPull in the python API.
virDomainBlockPullAll and virDomainBlockPullAbort are handled automatically. virDomainBlockPull and virDomainBlockPullInfo require manual overrides since they return a custom type. * python/generator.py: reenable bindings for this entry point * python/libvirt-override-api.xml python/libvirt-override.c: manual overrides Signed-off-by: Adam Litke a...@us.ibm.com --- python/generator.py |5 +-- python/libvirt-override-api.xml | 14 ++ python/libvirt-override.c | 53 +++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/python/generator.py b/python/generator.py index 52be85d..3684722 100755 --- a/python/generator.py +++ b/python/generator.py @@ -166,8 +166,6 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, -'virDomainBlockPull', -'virDomainGetBlockPullInfo', ] skipped_modules = { @@ -182,7 +180,6 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': No function types in python, 'virConnectDomainEventGraphicsCallback': No function types in python, 'virEventAddHandleFunc': No function types in python, - 'virDomainBlockPullInfoPtr': Not implemented yet, } ### @@ -344,6 +341,8 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', +'virDomainBlockPull', +'virDomainGetBlockPullInfo', ) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 54deeb5..af560e8 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -308,5 +308,19 @@ arg name='flags' type='unsigned int' info='flags, curently unused'/ return type='int' info=0 on success, -1 on error/ /function +function name='virDomainBlockPull' file='python' + infoInitiate an incremental BlockPull for the given disk/info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='path' type='const char *' info='Fully-qualified filename of disk'/ + arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/ + return type='virDomainBlockPullInfo' info='A dictionary containing progress information.' / +/function +function name='virDomainGetBlockPullInfo' file='python' + infoGet progress information for a background BlockPull operation/info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='path' type='const char *' info='Fully-qualified filename of disk'/ + arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/ + return type='virDomainBlockPullInfo' info='A dictionary containing progress information.' / +/function /symbols /api diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 4a9b432..ca90ef0 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -2350,6 +2350,57 @@ libvirt_virDomainGetJobInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { return(py_retval); } +static PyObject * +libvirt_virDomainBlockPullImpl(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args, int infoOnly) { +virDomainPtr domain; +PyObject *pyobj_domain; +const char *path; +unsigned int flags; +virDomainBlockPullInfo info; +int c_ret; +PyObject *ret; + +if (!PyArg_ParseTuple(args, (char *)Ozi:virDomainStreamDiskInfo, + pyobj_domain, path, flags)) +return(NULL); +domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + +LIBVIRT_BEGIN_ALLOW_THREADS; +if (infoOnly) +c_ret = virDomainGetBlockPullInfo(domain, path, info, flags); +else +c_ret = virDomainBlockPull(domain, path, info, flags); +LIBVIRT_END_ALLOW_THREADS; + +if (c_ret == -1) +return VIR_PY_NONE; + +if ((ret = PyDict_New()) == NULL) +return VIR_PY_NONE; + +PyDict_SetItem(ret, libvirt_constcharPtrWrap(cur), + libvirt_ulonglongWrap(info.cur)); +PyDict_SetItem(ret, libvirt_constcharPtrWrap(end), + libvirt_ulonglongWrap(info.end)); + +return ret; +} + +static PyObject * +libvirt_virDomainBlockPull(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +return libvirt_virDomainBlockPullImpl(self, args, 0); +} + +static PyObject * +libvirt_virDomainGetBlockPullInfo(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +return libvirt_virDomainBlockPullImpl(self, args, 1); +} + /*** * Helper functions to avoid importing modules @@ -3585,6 +3636,8 @@ static PyMethodDef libvirtMethods[] = { {(char *) virDomainGetJobInfo, libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) virDomainSnapshotListNames
[libvirt] [PATCH 4/7] Enable the virDomainBlockPull API in virsh
Define three new virsh commands: * blockpull: Perform incremental block pull * blockpull: Start/stop full device block pull * blockpullinfo: Retrieve progress info for full device block pull Share print_job_progress() with the migration code. * tools/virsh.c: implement the new commands Signed-off-by: Adam Litke a...@us.ibm.com --- tools/virsh.c | 134 +++-- 1 files changed, 130 insertions(+), 4 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 2b16714..e04de60 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3818,7 +3818,8 @@ out_sig: } static void -print_job_progress(unsigned long long remaining, unsigned long long total) +print_job_progress(const char *label, unsigned long long remaining, + unsigned long long total) { int progress; @@ -3838,7 +3839,7 @@ print_job_progress(unsigned long long remaining, unsigned long long total) } } -fprintf(stderr, \rMigration: [%3d %%], progress); +fprintf(stderr, \r%s: [%3d %%], label, progress); } static bool @@ -3925,7 +3926,7 @@ repoll: functionReturn = true; if (verbose) { /* print [100 %] */ -print_job_progress(0, 1); +print_job_progress(Migration, 0, 1); } } else functionReturn = false; @@ -3964,7 +3965,8 @@ repoll: pthread_sigmask(SIG_SETMASK, oldsigmask, NULL); #endif if (ret == 0) -print_job_progress(jobinfo.dataRemaining, jobinfo.dataTotal); +print_job_progress(Migration, jobinfo.dataRemaining, + jobinfo.dataTotal); } } @@ -4023,6 +4025,127 @@ done: return ret; } +typedef enum { +VSH_CMD_BLOCK_PULL_ONE = 0, +VSH_CMD_BLOCK_PULL_ALL = 1, +VSH_CMD_BLOCK_PULL_ABORT = 2, +VSH_CMD_BLOCK_PULL_INFO = 3 +} VSH_CMD_BLOCK_PULL_MODE; + +static int +blockPullImpl(vshControl *ctl, const vshCmd *cmd, + virDomainBlockPullInfoPtr info, int mode) +{ +virDomainPtr dom; +const char *name, *path; +int ret = -1; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, name))) +return false; + +if (vshCommandOptString(cmd, path, path) 0) +return false; + +if (mode == VSH_CMD_BLOCK_PULL_ONE) +ret = virDomainBlockPull(dom, path, info, 0); +else if (mode == VSH_CMD_BLOCK_PULL_ALL) +ret = virDomainBlockPullAll(dom, path, 0); +else if (mode == VSH_CMD_BLOCK_PULL_ABORT) +ret = virDomainBlockPullAbort(dom, path, 0); +else if (mode == VSH_CMD_BLOCK_PULL_INFO) +ret = virDomainGetBlockPullInfo(dom, path, info, 0); + +virDomainFree(dom); +return ret; +} + +/* + * blockpull command + */ +static const vshCmdInfo info_block_pull[] = { +{help, N_(Iteratively populate a disk from its backing image.)}, +{desc, N_(Iteratively populate a disk from its backing image.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockPull(vshControl *ctl, const vshCmd *cmd) +{ +virDomainBlockPullInfo info; + +if (blockPullImpl(ctl, cmd, info, VSH_CMD_BLOCK_PULL_ONE) != 0) +return false; +print_job_progress(Block pull, info.end - info.cur, info.end); +return true; +} + +/* + * blockpullall command + */ +static const vshCmdInfo info_block_pull_all[] = { +{help, N_(Start or stop populating a disk from its backing image.)}, +{desc, N_(Start or stop populating a disk from its backing image.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull_all[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{abort, VSH_OT_BOOL, VSH_OFLAG_NONE, N_(Abort the current operation)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockPullAll(vshControl *ctl, const vshCmd *cmd) +{ +int mode; + +if (vshCommandOptBool (cmd, abort)) +mode = VSH_CMD_BLOCK_PULL_ABORT; +else +mode = VSH_CMD_BLOCK_PULL_ALL; + +if (blockPullImpl(ctl, cmd, NULL, mode) != 0) +return false; +return true; +} + +/* + * blockpullinfo command + */ +static const vshCmdInfo info_block_pull_info[] = { +{help, N_(Check progress of an active block pull operation.)}, +{desc, N_(Check progress of an active block pull operation.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull_info[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk
[libvirt] [PATCH 2/7] Add virDomainBlockPull support to the remote driver. The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be written
* src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * daemon/remote_generator.pl: Specify the manually-written functions Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 71 + daemon/remote_generator.pl |8 +++- src/remote/remote_driver.c | 72 +++-- src/remote/remote_protocol.x | 44 +- 4 files changed, 188 insertions(+), 7 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 2220655..f6aa78e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1692,6 +1692,77 @@ no_memory: goto cleanup; } +static int +remoteDispatchDomainBlockPull(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_block_pull_args *args, + remote_domain_block_pull_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainBlockPull(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-info.cur = tmp.cur; +ret-info.end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + +static int +remoteDispatchDomainGetBlockPullInfo(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_get_block_pull_info_args *args, + remote_domain_get_block_pull_info_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainGetBlockPullInfo(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-info.cur = tmp.cur; +ret-info.end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + + /*-*/ static int diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index 062ccc1..d7e0383 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -278,7 +278,9 @@ elsif ($opt_b) { GetType, NodeDeviceGetParent, NodeGetSecurityModel, - SecretGetValue); + SecretGetValue, + DomainBlockPull, + DomainGetBlockPullInfo); } elsif ($structprefix eq qemu) { @ungeneratable = (MonitorCommand); } @@ -779,7 +781,9 @@ elsif ($opt_k) { GetType, NodeDeviceGetParent, NodeGetSecurityModel, - SecretGetValue); + SecretGetValue, + DomainBlockPull, + DomainGetBlockPullInfo); } elsif ($structprefix eq qemu) { @ungeneratable = (MonitorCommand); } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 07bc629..0a885a9 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2844,6 +2844,70 @@ done: return rv; } +static int remoteDomainBlockPull(virDomainPtr domain, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +int rv = -1; +remote_domain_block_pull_args args; +remote_domain_block_pull_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_block_pull_args, (char *)args, + (xdrproc_t
[libvirt] RFC: Add virDomainBlockPull API family to libvirt
To help speed the provisioning process for large domains, new QED disks are created with backing to a template image. These disks are configured with copy on read such that blocks that are read from the backing file are copied to the new disk. This reduces I/O over a potentially costly path to the backing image. In such a configuration, there is a desire to remove the dependency on the backing image as the domain runs. To accomplish this, qemu will provide an interface to perform sequential copy on read operations during normal VM operation. Once all data has been copied, the disk image's link to the backing file is removed. The virDomainBlockPull API family brings this functionality to libvirt. virDomainBlockPullAll() instructs the hypervisor to stream the entire device in the background. Progress of this operation can be checked with the function virDomainBlockPullInfo(). An ongoing stream can be cancelled with virDomainBlockPullAbort(). If a more controlled IO rate is desired, virDomainBlockPull() can be used to perform a single increment of IO. Subsequent calls to this function will automatically stream the appropriate next increment until the disk has been fully populated. An event (VIR_DOMAIN_EVENT_ID_BLOCK_PULL) will be emitted when a disk has been fully populated or if a BlockPullAll() operation was terminated due to an error. This event is useful to avoid polling on virDomainBlockPullInfo() for completion and could also be used by the security driver to revoke access to the backing file when it is no longer needed. Note: I am sending this series out now (even though image streaming is not quite committed to qemu upstream) because I want to start the review process. At this stage, I expect only minor changes to the qemu implementation. make check: PASS make syntax-check: PASS make -C tests valgrind: PASS I am testing this API with Python Unittest (see the last patch). [PATCH 0/7] virDomainBlockPull: Add public symbols to libvirt API [PATCH 2/7] Add virDomainBlockPull support to the remote driver [PATCH 3/7] Implement virDomainBlockPull for the qemu driver [PATCH 4/7] Enable the virDomainBlockPull API in virsh [PATCH 5/7] Enable virDomainBlockPull in the python API. [PATCH 6/7] Asynchronous event for BlockPull completion [PATCH 7/7] test: Python Unittests for DomainBlockPull API -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 1/7] virDomainBlockPull: Add public symbols to libvirt API
* src/libvirt.c: implement the main entry points * src/libvirt_public.syms: add them to the exported symbols Signed-off-by: Adam Litke a...@us.ibm.com --- src/libvirt.c | 252 +++ src/libvirt_public.syms |7 ++ 2 files changed, 259 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index abacf85..8f344de 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -13467,3 +13467,255 @@ error: virDispatchError(conn); return -1; } + +/** + * virDomainBlockPull: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @info: A pointer to a virDomainBlockPullInfo structure, or NULL + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function works incrementally, performing a small amount of work + * each time it is called. When successful, @info is updated with the current + * progress. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, info=%p, flags=%u, path, info, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (flags != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(flags must be zero)); +goto error; +} + +if (conn-driver-domainBlockPull) { +int ret; +ret = conn-driver-domainBlockPull(dom, path, info, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAll: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function pulls data for the entire device in the background. + * Progress of the operation can be checked with virDomainGetBlockPullInfo() and + * the operation can be aborted with virDomainBlockPullAbort(). When finished, + * an asynchronous event is raised to indicate the final status. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (flags != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(flags must be zero)); +goto error; +} + +if (conn-driver-domainBlockPullAll) { +int ret; +ret = conn-driver-domainBlockPullAll(dom, path, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAbort: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Cancel a pull operation previously started by virDomainBlockPullAll(). + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN
[libvirt] [PATCH] Add new API virDomainBlockPull* to header and drivers
Set up the types for the block pull functions and insert them into the virDriver structure definition. Because of static initializers, update every driver and set the new fields to NULL. * include/libvirt/libvirt.h.in: new API * src/driver.h src/*/*_driver.c src/vbox/vbox_tmpl.c: add the new entry to the driver structure * python/generator.py: fix compiler errors, the actual python bindings are implemented later Signed-off-by: Adam Litke a...@us.ibm.com --- include/libvirt/libvirt.h.in | 92 ++ python/generator.py |3 + src/driver.h | 21 ++ src/esx/esx_driver.c |4 ++ src/lxc/lxc_driver.c |4 ++ src/openvz/openvz_driver.c |4 ++ src/phyp/phyp_driver.c |4 ++ src/qemu/qemu_driver.c |4 ++ src/remote/remote_driver.c |4 ++ src/test/test_driver.c |4 ++ src/uml/uml_driver.c |4 ++ src/vbox/vbox_tmpl.c |4 ++ src/xen/xen_driver.c |4 ++ 13 files changed, 156 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5783303..9af1b76 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1145,6 +1145,98 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags); /* + * BlockPull API + */ + +/* An iterator for initiating and monitoring block pull operations */ +typedef unsigned long long virDomainBlockPullCursor; + +typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; +struct _virDomainBlockPullInfo { +/* + * The following fields provide an indication of block pull progress. @cur + * indicates the current position and will be between 0 and @end. @end is + * the final cursor position for this operation and represents completion. + * To approximate progress, divide @cur by @end. + */ +virDomainBlockPullCursor cur; +virDomainBlockPullCursor end; +}; +typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; + +/** + * virDomainBlockPull: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @info: A pointer to a virDomainBlockPullInfo structure, or NULL + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function works incrementally, performing a small amount of work + * each time it is called. When successful, @info is updated with the current + * progress. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + +/** + * virDomainBlockPullAll: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function pulls data for the entire device in the background. + * Progress of the operation can be checked with virDomainGetBlockPullInfo() and + * the operation can be aborted with virDomainBlockPullAbort(). When finished, + * an asynchronous event is raised to indicate the final status. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags); + +/** + * virDomainBlockPullAbort: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Cancel a pull operation previously started by virDomainBlockPullAll(). + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags); + +/** + * virDomainGetBlockPullInfo: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @info: pointer to a virDomainBlockPullInfo structure + * @flags: currently unused, for future extension + * + * Request progress information on a block pull operation that has been started + * with virDomainBlockPullAll(). If an operation is active for the given + * parameters, @info will be updated with the current progress. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainGetBlockPullInfo(virDomainPtr dom
Re: [libvirt] [PATCH 2/7] Add virDomainBlockPull support to the remote driver. The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be wri
On 06/01/2011 12:13 PM, Matthias Bolte wrote: 2011/6/1 Adam Litke a...@us.ibm.com: This commit has a pretty long summary line. Fixed. * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * daemon/remote_generator.pl: Specify the manually-written functions Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 71 + daemon/remote_generator.pl |8 +++- src/remote/remote_driver.c | 72 +++-- src/remote/remote_protocol.x | 44 +- 4 files changed, 188 insertions(+), 7 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 2220655..f6aa78e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1692,6 +1692,77 @@ no_memory: goto cleanup; } +static int +remoteDispatchDomainBlockPull(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_block_pull_args *args, + remote_domain_block_pull_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainBlockPull(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-info.cur = tmp.cur; +ret-info.end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + +static int +remoteDispatchDomainGetBlockPullInfo(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_get_block_pull_info_args *args, + remote_domain_get_block_pull_info_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainGetBlockPullInfo(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-info.cur = tmp.cur; +ret-info.end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} The generator should be able to deal with this. I might have to tweak it to handle multi-return-value procedures more general. That would be excellent. I am not doing anything particularly special. static int diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index 062ccc1..d7e0383 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -278,7 +278,9 @@ elsif ($opt_b) { GetType, NodeDeviceGetParent, NodeGetSecurityModel, - SecretGetValue); + SecretGetValue, + DomainBlockPull, + DomainGetBlockPullInfo); } elsif ($structprefix eq qemu) { @ungeneratable = (MonitorCommand); } @@ -779,7 +781,9 @@ elsif ($opt_k) { GetType, NodeDeviceGetParent, NodeGetSecurityModel, - SecretGetValue); + SecretGetValue, + DomainBlockPull, + DomainGetBlockPullInfo); } elsif ($structprefix eq qemu) { @ungeneratable = (MonitorCommand); } You need to rebase your series to git head as the generator has changed much recently. Ok. I will do that this afternoon. diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 07bc629..0a885a9 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2844,6 +2844,70 @@ done: return rv; } +static int remoteDomainBlockPull(virDomainPtr domain, + const char *path, + virDomainBlockPullInfoPtr info
Re: [libvirt] [PATCH 2/7] Add virDomainBlockPull support to the remote driver. The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be wri
On 06/01/2011 12:27 PM, Matthias Bolte wrote: 2011/6/1 Adam Litke a...@us.ibm.com: * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * daemon/remote_generator.pl: Specify the manually-written functions Signed-off-by: Adam Litke a...@us.ibm.com --- +static int remoteDomainBlockPull(virDomainPtr domain, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +int rv = -1; +remote_domain_block_pull_args args; +remote_domain_block_pull_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_block_pull_args, (char *)args, + (xdrproc_t)xdr_remote_domain_block_pull_ret, (char *)ret) == -1) +goto done; + +if (info) { +info-cur = ret.info.cur; +info-end = ret.info.end; +} +rv = 0; + +done: +remoteDriverUnlock(priv); +return rv; +} From the generator point-of-view I would want to avoid having the info parameter being NULLable, as this differs from the common pattern and results in special case code in the generator. From an API user's point of view, I think it's nice to allow NULL for the info struct. The user may not care about the current progress information. I could fix this at the global libvirt level by allocating a dummy struct to pass down to the driver implementations if the user passed NULL. Would that be acceptable? +struct remote_domain_block_pull_info { +unsigned hyper cur; +unsigned hyper end; +}; +struct remote_domain_block_pull_ret { +remote_domain_block_pull_info info; +}; +struct remote_domain_get_block_pull_info_ret { +remote_domain_block_pull_info info; +}; From the generator point-of-view I would avoid this approach of putting a struct in a struct because that differs from the common approach and results in special case code in the generator. It should look like this struct remote_domain_block_pull_ret { unsigned hyper cur; unsigned hyper end; }; struct remote_domain_get_block_pull_info_ret { unsigned hyper cur; unsigned hyper end; }; Ok, I will make this change. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] Add new API virDomainBlockPull* to header and drivers
Sorry, this one was left off from the initial post. It comes first in the series. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] RFC (v2): Add virDomainBlockPull API family to libvirt
Changes since V1: - Rebased to incorporate changes to generator and removal of static driver structure initializers - Other small fixups suggested by Matthias Bolte To help speed the provisioning process for large domains, new QED disks are created with backing to a template image. These disks are configured with copy on read such that blocks that are read from the backing file are copied to the new disk. This reduces I/O over a potentially costly path to the backing image. In such a configuration, there is a desire to remove the dependency on the backing image as the domain runs. To accomplish this, qemu will provide an interface to perform sequential copy on read operations during normal VM operation. Once all data has been copied, the disk image's link to the backing file is removed. The virDomainBlockPull API family brings this functionality to libvirt. virDomainBlockPullAll() instructs the hypervisor to stream the entire device in the background. Progress of this operation can be checked with the function virDomainBlockPullInfo(). An ongoing stream can be cancelled with virDomainBlockPullAbort(). If a more controlled IO rate is desired, virDomainBlockPull() can be used to perform a single increment of IO. Subsequent calls to this function will automatically stream the appropriate next increment until the disk has been fully populated. An event (VIR_DOMAIN_EVENT_ID_BLOCK_PULL) will be emitted when a disk has been fully populated or if a BlockPullAll() operation was terminated due to an error. This event is useful to avoid polling on virDomainBlockPullInfo() for completion and could also be used by the security driver to revoke access to the backing file when it is no longer needed. Note: I am sending this series out now (even though image streaming is not quite committed to qemu upstream) because I want to start the review process. At this stage, I expect only minor changes to the qemu implementation. make check: PASS make syntax-check: PASS make -C tests valgrind: PASS I am testing this API with Python Unittest (see the last patch). [PATCH 1/8] Add new API virDomainBlockPull* to headers [PATCH 2/8] virDomainBlockPull: Implement the main entry points [PATCH 3/8] Add virDomainBlockPull support to the remote driver [PATCH 4/8] Implement virDomainBlockPull for the qemu driver [PATCH 5/8] Enable the virDomainBlockPull API in virsh [PATCH 6/8] Enable virDomainBlockPull in the python API. [PATCH 7/8] Asynchronous event for BlockPull completion [PATCH 8/8] test: Python Unittests for DomainBlockPull API -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/8] Add virDomainBlockPull support to the remote driver
The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be written by hand. * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * src/remote_protocol-structs: structure definitions for protocol verification Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 71 ++ src/remote/remote_driver.c | 68 src/remote/remote_protocol.x | 40 +++- src/remote_protocol-structs | 28 4 files changed, 206 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 49058f2..e0b681c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1474,6 +1474,77 @@ cleanup: return rv; } +static int +remoteDispatchDomainBlockPull(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_block_pull_args *args, + remote_domain_block_pull_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainBlockPull(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-cur = tmp.cur; +ret-end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + +static int +remoteDispatchDomainGetBlockPullInfo(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_get_block_pull_info_args *args, + remote_domain_get_block_pull_info_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainGetBlockPullInfo(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-cur = tmp.cur; +ret-end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + + /*-*/ static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..de9359d 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2509,6 +2509,70 @@ done: return rv; } +static int remoteDomainBlockPull(virDomainPtr domain, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +int rv = -1; +remote_domain_block_pull_args args; +remote_domain_block_pull_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_block_pull_args, (char *)args, + (xdrproc_t)xdr_remote_domain_block_pull_ret, (char *)ret) == -1) +goto done; + +if (info) { +info-cur = ret.cur; +info-end = ret.end; +} +rv = 0; + +done: +remoteDriverUnlock(priv); +return rv; +} + +static int remoteDomainGetBlockPullInfo(virDomainPtr domain, +const char *path, +virDomainBlockPullInfoPtr info, +unsigned int flags) +{ +int rv = -1; +remote_domain_get_block_pull_info_args args; +remote_domain_get_block_pull_info_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO, + (xdrproc_t
[libvirt] [PATCH 1/8] Add new API virDomainBlockPull* to headers
Set up the types for the block pull functions and insert them into the virDriver structure definition. Symbols are exported in this patch to prevent documentation compile failures. * include/libvirt/libvirt.h.in: new API * src/driver.h: add the new entry to the driver structure * python/generator.py: fix compiler errors, the actual python bindings are implemented later * src/libvirt_public.syms: export symbols Signed-off-by: Adam Litke a...@us.ibm.com --- include/libvirt/libvirt.h.in | 39 +++ python/generator.py |3 +++ src/driver.h | 22 ++ src/libvirt_public.syms |4 4 files changed, 68 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index df213f1..ba547c1 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1156,6 +1156,45 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags); /* + * BlockPull API + */ + +/* An iterator for initiating and monitoring block pull operations */ +typedef unsigned long long virDomainBlockPullCursor; + +typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; +struct _virDomainBlockPullInfo { +/* + * The following fields provide an indication of block pull progress. @cur + * indicates the current position and will be between 0 and @end. @end is + * the final cursor position for this operation and represents completion. + * To approximate progress, divide @cur by @end. + */ +virDomainBlockPullCursor cur; +virDomainBlockPullCursor end; +}; +typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; + +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags); + +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags); + +int virDomainGetBlockPullInfo(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + + +/* * NUMA support */ diff --git a/python/generator.py b/python/generator.py index 7c38fdd..43e7414 100755 --- a/python/generator.py +++ b/python/generator.py @@ -178,6 +178,8 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, +'virDomainBlockPull', +'virDomainGetBlockPullInfo', ] skipped_modules = { @@ -192,6 +194,7 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': No function types in python, 'virConnectDomainEventGraphicsCallback': No function types in python, 'virEventAddHandleFunc': No function types in python, + 'virDomainBlockPullInfoPtr': Not implemented yet, } ### diff --git a/src/driver.h b/src/driver.h index 5df798a..4b30390 100644 --- a/src/driver.h +++ b/src/driver.h @@ -615,6 +615,24 @@ typedef int unsigned long flags, int cancelled); +typedef int +(*virDrvDomainBlockPull)(virDomainPtr dom, const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + +typedef int +(*virDrvDomainBlockPullAll)(virDomainPtr dom, const char *path, +unsigned int flags); + +typedef int +(*virDrvDomainBlockPullAbort)(virDomainPtr dom, const char *path, + unsigned int flags); + +typedef int +(*virDrvDomainGetBlockPullInfo)(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, +unsigned int flags); + /** * _virDriver: * @@ -749,6 +767,10 @@ struct _virDriver { virDrvDomainMigratePerform3domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3domainMigrateConfirm3; +virDrvDomainBlockPull domainBlockPull; +virDrvDomainBlockPullAll domainBlockPullAll; +virDrvDomainBlockPullAbort domainBlockPullAbort; +virDrvDomainGetBlockPullInfo domainGetBlockPullInfo; }; typedef int diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4d4299a..7d85d33 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -448,6 +448,10
[libvirt] [PATCH 8/8] test: Python Unittests for DomainBlockPull API
*** Please do not consider for merging, example tests only *** Here are the testcases I am currently running to verify the correctness of this API. These are continuing to evolve. Signed-off-by: Adam Litke a...@us.ibm.com --- blockPull-test.py | 245 + 1 files changed, 245 insertions(+), 0 deletions(-) create mode 100755 blockPull-test.py diff --git a/blockPull-test.py b/blockPull-test.py new file mode 100755 index 000..4a48d5a --- /dev/null +++ b/blockPull-test.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python + +import sys +import subprocess +import time +import unittest +import re +import threading +import libvirt + +qemu_img_bin = /home/aglitke/src/qemu/qemu-img +virsh_bin = /home/aglitke/src/libvirt/tools/virsh + +dom_xml = +domain type='kvm' + nameblockPull-test/name + memory131072/memory + currentMemory131072/currentMemory + vcpu1/vcpu + os +type arch='x86_64' machine='pc-0.13'hvm/type +boot dev='hd'/ + /os + features +acpi/ +apic/ +pae/ + /features + clock offset='utc'/ + on_poweroffdestroy/on_poweroff + on_rebootrestart/on_reboot + on_crashrestart/on_crash + devices + emulator/home/aglitke/src/qemu/x86_64-softmmu/qemu-system-x86_64/emulator +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk1.qed' / + target dev='vda' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk2.qed' / + target dev='vdb' bus='virtio'/ +/disk +graphics type='vnc' port='-1' autoport='yes'/ + /devices +/domain + + +def qemu_img(*args): +global qemu_img_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([qemu_img_bin] + list(args), stdin=devnull, stdout=devnull) + +def virsh(*args): +global virsh_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([virsh_bin] + list(args), + stdin=devnull, stdout=devnull, stderr=devnull) + +def make_baseimage(name, size_mb): +devnull = open('/dev/null', 'r+') +return subprocess.call(['dd', 'if=/dev/zero', of=%s % name, 'bs=1M', +'count=%i' % size_mb], stdin=devnull, stdout=devnull, stderr=devnull) + +def has_backing_file(path): +global qemu_img_bin +p1 = subprocess.Popen([qemu_img_bin, info, path], + stdout=subprocess.PIPE).communicate()[0] +matches = re.findall(^backing file:, p1, re.M) +if len(matches) 0: +return True +return False + +class BlockPullTestCase(unittest.TestCase): +def _error_handler(self, ctx, error, dummy=None): +pass + +def create_disks(self, sparse): +self.disks = [ '/tmp/disk1.qed', '/tmp/disk2.qed' ] +if sparse: +qemu_img('create', '-f', 'raw', '/tmp/backing1.img', '100M') +qemu_img('create', '-f', 'raw', '/tmp/backing2.img', '100M') +else: +make_baseimage('/tmp/backing1.img', 100) +make_baseimage('/tmp/backing2.img', 100) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing1.img,copy_on_read=on', self.disks[0]) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing2.img,copy_on_read=on', self.disks[1]) + +def begin(self, sparse=True): +global dom_xml + +libvirt.registerErrorHandler(self._error_handler, None) +self.create_disks(sparse) +self.conn = libvirt.open('qemu:///system') +self.dom = self.conn.createXML(dom_xml, 0) + +def end(self): +self.dom.destroy() +self.conn.close() + +class TestBasicErrors(BlockPullTestCase): +def setUp(self): +self.begin() + +def tearDown(self): +self.end() + +def test_bad_path(self): +try: +self.dom.blockPull('/dev/null', 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e[0]) + +def test_abort_no_stream(self): +try: +self.dom.blockPullAbort(self.disks[0], 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_INTERNAL_ERROR, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_INTERNAL_ERROR, e[0]) + +class TestBasicCommands(BlockPullTestCase): +def setUp(self): +pass + +def tearDown(self): +self.end() + +def test_start_stop(self): +self.begin(sparse=False) +self.dom.blockPullAll(self.disks[0], 0) +time.sleep(1) +self.assertIsNot(None, self.dom.blockPullInfo(self.disks[0], 0)) +self.dom.blockPullAbort(self.disks[0], 0) +time.sleep(1) +self.assertIs(None, self.dom.blockPullInfo(self.disks
[libvirt] [PATCH 4/8] Implement virDomainBlockPull for the qemu driver
The virDomainBlockPull* family of commands are enabled by the 'block_stream' and 'info block_stream' qemu monitor commands. * src/qemu/qemu_driver.c src/qemu/qemu_monitor_text.[ch]: implement disk streaming by using the stream and info stream text monitor commands * src/qemu/qemu_monitor_json.[ch]: implement commands using the qmp monitor Signed-off-by: Adam Litke a...@us.ibm.com --- src/qemu/qemu_driver.c | 108 +++ src/qemu/qemu_monitor.c | 16 + src/qemu/qemu_monitor.h | 13 src/qemu/qemu_monitor_json.c | 117 ++ src/qemu/qemu_monitor_json.h |4 + src/qemu/qemu_monitor_text.c | 145 ++ src/qemu/qemu_monitor_text.h |5 ++ 7 files changed, 408 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2957467..4b82398 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7973,6 +7973,110 @@ cleanup: return ret; } +static const char * +qemuDiskPathToAlias(virDomainObjPtr vm, const char *path) { +int i; +char *ret = NULL; + +for (i = 0 ; i vm-def-ndisks ; i++) { +virDomainDiskDefPtr disk = vm-def-disks[i]; + +if (disk-src != NULL STREQ(disk-src, path)) { +if (virAsprintf(ret, drive-%s, disk-info.alias) 0) { +virReportOOMError(); +return NULL; +} +break; +} +} + +if (!ret) { +qemuReportError(VIR_ERR_INVALID_ARG, +%s, _(No device found for specified path)); +} +return ret; +} + +static int +qemuDomainBlockPullImpl(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, +int mode) +{ +struct qemud_driver *driver = dom-conn-privateData; +virDomainObjPtr vm = NULL; +qemuDomainObjPrivatePtr priv; +char uuidstr[VIR_UUID_STRING_BUFLEN]; +const char *device = NULL; +int ret = -1; + +qemuDriverLock(driver); +virUUIDFormat(dom-uuid, uuidstr); +vm = virDomainFindByUUID(driver-domains, dom-uuid); +if (!vm) { +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +device = qemuDiskPathToAlias(vm, path); +if (!device) { +goto cleanup; +} + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +priv = vm-privateData; +ret = qemuMonitorBlockPull(priv-mon, device, info, mode); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +VIR_FREE(device); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + +static int +qemuDomainBlockPull(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_ONE); +} + +static int +qemuDomainBlockPullAll(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ALL); +} + +static int +qemuDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ABORT); +} + +static int +qemuDomainGetBlockPullInfo(virDomainPtr dom, const char *path, + virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_INFO); +} static virDriver qemuDriver = { .no = VIR_DRV_QEMU, @@ -8092,6 +8196,10 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainBlockPull = qemuDomainBlockPull, /* 0.9.2 */ +.domainBlockPullAll = qemuDomainBlockPullAll, /* 0.9.2 */ +.domainBlockPullAbort = qemuDomainBlockPullAbort, /* 0.9.2 */ +.domainGetBlockPullInfo = qemuDomainGetBlockPullInfo, /* 0.9.2 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 26bb814..dee354e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2376,3 +2376,19 @@ int qemuMonitorScreendump(qemuMonitorPtr mon, ret = qemuMonitorTextScreendump(mon, file); return ret; } + +int qemuMonitorBlockPull(qemuMonitorPtr mon
[libvirt] [PATCH 7/8] Asynchronous event for BlockPull completion
When an operation started by virDomainBlockPullAll completes (either with success or with failure), raise an event to indicate the final status. This allows an API user to avoid polling on virDomainBlockPullInfo if they would prefer to use the event mechanism. * daemon/remote.c: Dispatch events to client * include/libvirt/libvirt.h.in: Define event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle the new event * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block_stream completion and emit a libvirt block pull event * src/remote/remote_driver.c: Receive and dispatch events to application * src/remote/remote_protocol.x: Wire protocol definition for the event * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_STREAM_COMPLETED event from QEMU monitor Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 32 include/libvirt/libvirt.h.in | 27 + python/libvirt-override-virConnect.py | 12 python/libvirt-override.c | 51 + src/conf/domain_event.c | 50 src/conf/domain_event.h |7 - src/libvirt_private.syms |2 + src/qemu/qemu_monitor.c | 12 src/qemu/qemu_monitor.h |8 + src/qemu/qemu_monitor_json.c | 30 +++ src/qemu/qemu_process.c | 28 ++ src/remote/remote_driver.c| 30 +++ src/remote/remote_protocol.x |9 +- src/remote_protocol-structs |5 +++ 14 files changed, 301 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index e0b681c..a8ac276 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -396,6 +396,37 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int remoteRelayDomainEventBlockPull(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *path, + int status, + void *opaque) +{ +struct qemud_client *client = opaque; +remote_domain_event_block_pull_msg data; + +if (!client) +return -1; + +VIR_DEBUG(Relaying domain block pull event %s %d %s %i, dom-name, dom-id, path, status); + +virMutexLock(client-lock); + +/* build return data */ +memset(data, 0, sizeof data); +make_nonnull_domain(data.dom, dom); +data.path = (char*)path; +data.status = status; + +remoteDispatchDomainEventSend(client, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_event_block_pull_msg, data); + +virMutexUnlock(client-lock); + +return 0; +} + static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -434,6 +465,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), +VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockPull), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ba547c1..86b6d69 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2470,6 +2470,32 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventBlockPullStatus: + * + * The final status of a virDomainBlockPullAll() operation + */ +typedef enum { +VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0, +VIR_DOMAIN_BLOCK_PULL_FAILED = 1, +} virConnectDomainEventBlockPullStatus; + +/** + * virConnectDomainEventBlockPullCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @path: fully-qualified filename of the affected disk + * @status: final status of the operation (virConnectDomainEventBlockPullStatus) + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *path
[libvirt] [PATCH 5/8] Enable the virDomainBlockPull API in virsh
Define three new virsh commands: * blockpull: Perform incremental block pull * blockpull: Start/stop full device block pull * blockpullinfo: Retrieve progress info for full device block pull Share print_job_progress() with the migration code. * tools/virsh.c: implement the new commands Signed-off-by: Adam Litke a...@us.ibm.com --- tools/virsh.c | 134 +++-- 1 files changed, 130 insertions(+), 4 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index d98be1c..15c5b67 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -4095,7 +4095,8 @@ out_sig: } static void -print_job_progress(unsigned long long remaining, unsigned long long total) +print_job_progress(const char *label, unsigned long long remaining, + unsigned long long total) { int progress; @@ -4115,7 +4116,7 @@ print_job_progress(unsigned long long remaining, unsigned long long total) } } -fprintf(stderr, \rMigration: [%3d %%], progress); +fprintf(stderr, \r%s: [%3d %%], label, progress); } static bool @@ -4202,7 +4203,7 @@ repoll: functionReturn = true; if (verbose) { /* print [100 %] */ -print_job_progress(0, 1); +print_job_progress(Migration, 0, 1); } } else functionReturn = false; @@ -4241,7 +4242,8 @@ repoll: pthread_sigmask(SIG_SETMASK, oldsigmask, NULL); #endif if (ret == 0) -print_job_progress(jobinfo.dataRemaining, jobinfo.dataTotal); +print_job_progress(Migration, jobinfo.dataRemaining, + jobinfo.dataTotal); } } @@ -4300,6 +4302,127 @@ done: return ret; } +typedef enum { +VSH_CMD_BLOCK_PULL_ONE = 0, +VSH_CMD_BLOCK_PULL_ALL = 1, +VSH_CMD_BLOCK_PULL_ABORT = 2, +VSH_CMD_BLOCK_PULL_INFO = 3 +} VSH_CMD_BLOCK_PULL_MODE; + +static int +blockPullImpl(vshControl *ctl, const vshCmd *cmd, + virDomainBlockPullInfoPtr info, int mode) +{ +virDomainPtr dom; +const char *name, *path; +int ret = -1; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, name))) +return false; + +if (vshCommandOptString(cmd, path, path) 0) +return false; + +if (mode == VSH_CMD_BLOCK_PULL_ONE) +ret = virDomainBlockPull(dom, path, info, 0); +else if (mode == VSH_CMD_BLOCK_PULL_ALL) +ret = virDomainBlockPullAll(dom, path, 0); +else if (mode == VSH_CMD_BLOCK_PULL_ABORT) +ret = virDomainBlockPullAbort(dom, path, 0); +else if (mode == VSH_CMD_BLOCK_PULL_INFO) +ret = virDomainGetBlockPullInfo(dom, path, info, 0); + +virDomainFree(dom); +return ret; +} + +/* + * blockpull command + */ +static const vshCmdInfo info_block_pull[] = { +{help, N_(Iteratively populate a disk from its backing image.)}, +{desc, N_(Iteratively populate a disk from its backing image.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockPull(vshControl *ctl, const vshCmd *cmd) +{ +virDomainBlockPullInfo info; + +if (blockPullImpl(ctl, cmd, info, VSH_CMD_BLOCK_PULL_ONE) != 0) +return false; +print_job_progress(Block pull, info.end - info.cur, info.end); +return true; +} + +/* + * blockpullall command + */ +static const vshCmdInfo info_block_pull_all[] = { +{help, N_(Start or stop populating a disk from its backing image.)}, +{desc, N_(Start or stop populating a disk from its backing image.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull_all[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{abort, VSH_OT_BOOL, VSH_OFLAG_NONE, N_(Abort the current operation)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockPullAll(vshControl *ctl, const vshCmd *cmd) +{ +int mode; + +if (vshCommandOptBool (cmd, abort)) +mode = VSH_CMD_BLOCK_PULL_ABORT; +else +mode = VSH_CMD_BLOCK_PULL_ALL; + +if (blockPullImpl(ctl, cmd, NULL, mode) != 0) +return false; +return true; +} + +/* + * blockpullinfo command + */ +static const vshCmdInfo info_block_pull_info[] = { +{help, N_(Check progress of an active block pull operation.)}, +{desc, N_(Check progress of an active block pull operation.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull_info[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk
[libvirt] [PATCH 6/8] Enable virDomainBlockPull in the python API.
virDomainBlockPullAll and virDomainBlockPullAbort are handled automatically. virDomainBlockPull and virDomainBlockPullInfo require manual overrides since they return a custom type. * python/generator.py: reenable bindings for this entry point * python/libvirt-override-api.xml python/libvirt-override.c: manual overrides Signed-off-by: Adam Litke a...@us.ibm.com --- python/generator.py |5 +-- python/libvirt-override-api.xml | 14 ++ python/libvirt-override.c | 53 +++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/python/generator.py b/python/generator.py index 43e7414..be8419d 100755 --- a/python/generator.py +++ b/python/generator.py @@ -178,8 +178,6 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, -'virDomainBlockPull', -'virDomainGetBlockPullInfo', ] skipped_modules = { @@ -194,7 +192,6 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': No function types in python, 'virConnectDomainEventGraphicsCallback': No function types in python, 'virEventAddHandleFunc': No function types in python, - 'virDomainBlockPullInfoPtr': Not implemented yet, } ### @@ -359,6 +356,8 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', +'virDomainBlockPull', +'virDomainGetBlockPullInfo', ) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index ec08e69..4bdd5de 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -314,5 +314,19 @@ arg name='flags' type='unsigned int' info='flags, curently unused'/ return type='int' info=0 on success, -1 on error/ /function +function name='virDomainBlockPull' file='python' + infoInitiate an incremental BlockPull for the given disk/info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='path' type='const char *' info='Fully-qualified filename of disk'/ + arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/ + return type='virDomainBlockPullInfo' info='A dictionary containing progress information.' / +/function +function name='virDomainGetBlockPullInfo' file='python' + infoGet progress information for a background BlockPull operation/info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='path' type='const char *' info='Fully-qualified filename of disk'/ + arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/ + return type='virDomainBlockPullInfo' info='A dictionary containing progress information.' / +/function /symbols /api diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 763df00..07a55f6 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -2382,6 +2382,57 @@ libvirt_virDomainGetJobInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { return(py_retval); } +static PyObject * +libvirt_virDomainBlockPullImpl(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args, int infoOnly) { +virDomainPtr domain; +PyObject *pyobj_domain; +const char *path; +unsigned int flags; +virDomainBlockPullInfo info; +int c_ret; +PyObject *ret; + +if (!PyArg_ParseTuple(args, (char *)Ozi:virDomainStreamDiskInfo, + pyobj_domain, path, flags)) +return(NULL); +domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + +LIBVIRT_BEGIN_ALLOW_THREADS; +if (infoOnly) +c_ret = virDomainGetBlockPullInfo(domain, path, info, flags); +else +c_ret = virDomainBlockPull(domain, path, info, flags); +LIBVIRT_END_ALLOW_THREADS; + +if (c_ret == -1) +return VIR_PY_NONE; + +if ((ret = PyDict_New()) == NULL) +return VIR_PY_NONE; + +PyDict_SetItem(ret, libvirt_constcharPtrWrap(cur), + libvirt_ulonglongWrap(info.cur)); +PyDict_SetItem(ret, libvirt_constcharPtrWrap(end), + libvirt_ulonglongWrap(info.end)); + +return ret; +} + +static PyObject * +libvirt_virDomainBlockPull(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +return libvirt_virDomainBlockPullImpl(self, args, 0); +} + +static PyObject * +libvirt_virDomainGetBlockPullInfo(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +return libvirt_virDomainBlockPullImpl(self, args, 1); +} + /*** * Helper functions to avoid importing modules @@ -3613,6 +3664,8 @@ static PyMethodDef libvirtMethods[] = { {(char *) virDomainGetJobInfo, libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) virDomainSnapshotListNames
[libvirt] [PATCH 2/8] virDomainBlockPull: Implement the main entry points
* src/libvirt.c: implement the main entry points Signed-off-by: Adam Litke a...@us.ibm.com --- src/libvirt.c | 252 + 1 files changed, 252 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index cbe1926..46d32cf 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -15012,3 +15012,255 @@ error: virDispatchError(conn); return -1; } + +/** + * virDomainBlockPull: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @info: A pointer to a virDomainBlockPullInfo structure, or NULL + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function works incrementally, performing a small amount of work + * each time it is called. When successful, @info is updated with the current + * progress. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, info=%p, flags=%u, path, info, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (flags != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(flags must be zero)); +goto error; +} + +if (conn-driver-domainBlockPull) { +int ret; +ret = conn-driver-domainBlockPull(dom, path, info, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAll: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function pulls data for the entire device in the background. + * Progress of the operation can be checked with virDomainGetBlockPullInfo() and + * the operation can be aborted with virDomainBlockPullAbort(). When finished, + * an asynchronous event is raised to indicate the final status. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (flags != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(flags must be zero)); +goto error; +} + +if (conn-driver-domainBlockPullAll) { +int ret; +ret = conn-driver-domainBlockPullAll(dom, path, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAbort: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Cancel a pull operation previously started by virDomainBlockPullAll(). + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn
[libvirt] CFS Hardlimits and the libvirt cgroups implementation
Hi all. In this post I would like to bring up 3 issues which are tightly related: 1. unwanted behavior when using cfs hardlimits with libvirt, 2. Scaling cputune.share according to the number of vcpus, 3. API proposal for CFS hardlimits support. === 1 === Mark Peloquin (on cc:) has been looking at implementing CFS hard limit support on top of the existing libvirt cgroups implementation and he has run into some unwanted behavior when enabling quotas that seems to be affected by the cgroup hierarchy being used by libvirt. Here are Mark's words on the subject (posted by me while Mark joins this mailing list): -- I've conducted a number of measurements using CFS. The system config is a 2 socket Nehalem system with 64GB ram. Installed is RHEL6.1-snap4. The guest VMs being used have RHEL5.5 - 32bit. I've replaced the kernel with 2.6.39-rc6+ with patches from Paul-V6-upstream-breakout.tar.bz2 for CFS bandwidth. The test config uses 5 VMs of various vcpu and memory sizes. Being used are 2 VMs with 2 vcpus and 4GB of memory, 1 VM with 4vcpus/8GB, another VM with 8vcpus/16GB and finally a VM with 16vcpus/16GB. Thus far the tests have been limited to cpu intensive workloads. Each VM runs a single instance of the workload. The workload is configured to create one thread for each vcpu in the VM. The workload is then capable of completely saturation each vcpu in each VM. CFS was tested using two different topologies. First vcpu cgroups were created under each VM created by libvirt. The vcpu threads from the VM's cgroup/tasks were moved to the tasks list of each vcpu cgroup, one thread to each vcpu cgroup. This tree structure permits setting CFS quota and period per vcpu. Default values for cpu.shares (1024), quota (-1) and period (50us) was used in each VM cgroup and inherited by the vcpu croup. With these settings the workload generated system cpu utilization (measured in the host) of 99% guest, 0.1 idle, 0.14% user and 0.38 system. Second, using the same topology, the CFS quota in each vcpu's cgroup was set to 25us allowing each vcpu to consume 50% of a cpu. The cpu workloads was run again. This time the total system cpu utilization was measured at 75% guest, ~24% idle, 0.15% user and 0.40% system. The topology was changed such that a cgroup for each vcpu was created in /cgroup/cpu. The first test used the default/inherited shares and CFS quota and period. The measured system cpu utilization was 99% guest, ~0.5 idle, 0.13 user and 0.38 system, similar to the default settings using vcpu cgroups under libvirt. The next test, like before the topology change, set the vcpu quota values to 25us or 50% of a cpu. In this case the measured system cpu utilization was ~92% guest, ~7.5% idle, 0.15% user and 0.38% system. We can see that moving the vcpu cgroups from being under libvirt/qemu make a big difference in idle cpu time. Does this suggest a possible problems with libvirt? -- Has anyone else seen this type of behavior when using cgroups with CFS hardlimits? We are working with the kernel community to see if there might be a bug in cgroups itself. === 2 === Something else we are seeing is that libvirt's default setting for cputune.share is 1024 for any domain (regardless of how many vcpus are configured. This ends up hindering performance of really large VMs (with lots of vcpus) as compared to smaller ones since all domains are given equal share. Would folks consider changing the default for 'shares' to be a quantity scaled by the number of vcpus such that bigger domains get to use proportionally more host cpu resource? === 3 === Besides the above issues, I would like to open a discussion on what the libvirt API for enabling cpu hardlimits should look like. Here is what I was thinking: Two additional scheduler parameters (based on the names given in the cgroup fs) will be recognized for qemu domains: 'cfs_period' and 'cfs_quota'. These can use the existing virDomain[Get|Set]SchedulerParameters() API. The Domain XML schema would be updated to permit the following: --- snip --- cputune ... cfs_period100/cfs_period cfs_quota50/cfs_quota /cputune --- snip --- To actuate these configuration settings, we simply apply the values to the appropriate cgroup(s) for the domain. We would prefer that each vcpu be in its own cgroup to ensure equal and fair scheduling across all vcpus running on the system. (We will need to resolve the issues described by Mark in order to figure out where to hang these cgroups). Thanks for sticking with me through this long email. I greatly appreciate your thoughts and comments on these topics. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] RFC (v3): Add virDomainBlockPull API family to libvirt
I didn't receive too many comments on the last round. That probably means everybody has been busy with the 0.9.2 release. I think this is ready to be committed but we can wait until the qemu side has gone up (which should happen any day now). Changes since V2: - Rebased - Ensure error messages are consistent between JSON and Text interfaces - Added additional tests to the sample test bucket Changes since V1: - Rebased to incorporate changes to generator and removal of static driver structure initializers - Other small fixups suggested by Matthias Bolte To help speed the provisioning process for large domains, new QED disks are created with backing to a template image. These disks are configured with copy on read such that blocks that are read from the backing file are copied to the new disk. This reduces I/O over a potentially costly path to the backing image. In such a configuration, there is a desire to remove the dependency on the backing image as the domain runs. To accomplish this, qemu will provide an interface to perform sequential copy on read operations during normal VM operation. Once all data has been copied, the disk image's link to the backing file is removed. The virDomainBlockPull API family brings this functionality to libvirt. virDomainBlockPullAll() instructs the hypervisor to stream the entire device in the background. Progress of this operation can be checked with the function virDomainBlockPullInfo(). An ongoing stream can be cancelled with virDomainBlockPullAbort(). If a more controlled IO rate is desired, virDomainBlockPull() can be used to perform a single increment of IO. Subsequent calls to this function will automatically stream the appropriate next increment until the disk has been fully populated. An event (VIR_DOMAIN_EVENT_ID_BLOCK_PULL) will be emitted when a disk has been fully populated or if a BlockPullAll() operation was terminated due to an error. This event is useful to avoid polling on virDomainBlockPullInfo() for completion and could also be used by the security driver to revoke access to the backing file when it is no longer needed. make check: PASS make syntax-check: PASS make -C tests valgrind: PASS I am testing this API with Python Unittest (see the last patch). [PATCH 1/8] Add new API virDomainBlockPull* to headers [PATCH 2/8] virDomainBlockPull: Implement the main entry points [PATCH 3/8] Add virDomainBlockPull support to the remote driver [PATCH 4/8] Implement virDomainBlockPull for the qemu driver [PATCH 5/8] Enable the virDomainBlockPull API in virsh [PATCH 6/8] Enable virDomainBlockPull in the python API. [PATCH 7/8] Asynchronous event for BlockPull completion [PATCH 8/8] test: Python Unittests for DomainBlockPull API -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 8/8] test: Python Unittests for DomainBlockPull API
*** Please do not consider for merging, example tests only *** Here are the testcases I am currently running to verify the correctness of this API. These are continuing to evolve. Signed-off-by: Adam Litke a...@us.ibm.com --- blockPull-test.py | 301 + 1 files changed, 301 insertions(+), 0 deletions(-) create mode 100755 blockPull-test.py diff --git a/blockPull-test.py b/blockPull-test.py new file mode 100755 index 000..d9ccfb6 --- /dev/null +++ b/blockPull-test.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python + +import sys +import subprocess +import time +import unittest +import re +import threading +import libvirt + +qemu_img_bin = /home/aglitke/src/qemu/qemu-img +virsh_bin = /home/aglitke/src/libvirt/tools/virsh + +dom_xml = +domain type='kvm' + nameblockPull-test/name + memory131072/memory + currentMemory131072/currentMemory + vcpu1/vcpu + os +type arch='x86_64' machine='pc-0.13'hvm/type +boot dev='hd'/ + /os + features +acpi/ +apic/ +pae/ + /features + clock offset='utc'/ + on_poweroffdestroy/on_poweroff + on_rebootrestart/on_reboot + on_crashrestart/on_crash + devices + emulator/home/aglitke/src/qemu/x86_64-softmmu/qemu-system-x86_64/emulator +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk1.qed' / + target dev='vda' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk2.qed' / + target dev='vdb' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='raw'/ + source file='/tmp/disk3.raw' / + target dev='vdc' bus='virtio'/ +/disk +graphics type='vnc' port='-1' autoport='yes'/ + /devices +/domain + + +def qemu_img(*args): +global qemu_img_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([qemu_img_bin] + list(args), stdin=devnull, stdout=devnull) + +def virsh(*args): +global virsh_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([virsh_bin] + list(args), + stdin=devnull, stdout=devnull, stderr=devnull) + +def make_baseimage(name, size_mb): +devnull = open('/dev/null', 'r+') +return subprocess.call(['dd', 'if=/dev/zero', of=%s % name, 'bs=1M', +'count=%i' % size_mb], stdin=devnull, stdout=devnull, stderr=devnull) + +def has_backing_file(path): +global qemu_img_bin +p1 = subprocess.Popen([qemu_img_bin, info, path], + stdout=subprocess.PIPE).communicate()[0] +matches = re.findall(^backing file:, p1, re.M) +if len(matches) 0: +return True +return False + +class BlockPullTestCase(unittest.TestCase): +def _error_handler(self, ctx, error, dummy=None): +pass + +def create_disks(self, sparse): +self.disks = [ '/tmp/disk1.qed', '/tmp/disk2.qed', '/tmp/disk3.raw' ] +if sparse: +qemu_img('create', '-f', 'raw', '/tmp/backing1.img', '100M') +qemu_img('create', '-f', 'raw', '/tmp/backing2.img', '100M') +else: +make_baseimage('/tmp/backing1.img', 100) +make_baseimage('/tmp/backing2.img', 100) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing1.img,copy_on_read=on', self.disks[0]) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing2.img,copy_on_read=on', self.disks[1]) +qemu_img('create', '-f', 'raw', self.disks[2], '100M') + +def begin(self, sparse=True): +global dom_xml + +libvirt.registerErrorHandler(self._error_handler, None) +self.create_disks(sparse) +self.conn = libvirt.open('qemu:///system') +self.dom = self.conn.createXML(dom_xml, 0) + +def end(self): +self.dom.destroy() +self.conn.close() + +class TestBasicErrors(BlockPullTestCase): +def setUp(self): +self.begin() + +def tearDown(self): +self.end() + +def test_bad_path(self): +try: +self.dom.blockPull('/dev/null', 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e[0]) + +def test_abort_no_stream(self): +try: +self.dom.blockPullAbort(self.disks[0], 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_OPERATION_INVALID, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_OPERATION_INVALID, e[0]) + +def test_start_same_twice(self): +self.dom.blockPullAll(self.disks[0], 0) +try: +self.dom.blockPullAll(self.disks[0], 0) +except libvirt.libvirtError, e: +self.assertEqual
[libvirt] [PATCH 6/8] Enable virDomainBlockPull in the python API.
virDomainBlockPullAll and virDomainBlockPullAbort are handled automatically. virDomainBlockPull and virDomainBlockPullInfo require manual overrides since they return a custom type. * python/generator.py: reenable bindings for this entry point * python/libvirt-override-api.xml python/libvirt-override.c: manual overrides Signed-off-by: Adam Litke a...@us.ibm.com --- python/generator.py |5 +-- python/libvirt-override-api.xml | 14 ++ python/libvirt-override.c | 53 +++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/python/generator.py b/python/generator.py index 43e7414..be8419d 100755 --- a/python/generator.py +++ b/python/generator.py @@ -178,8 +178,6 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, -'virDomainBlockPull', -'virDomainGetBlockPullInfo', ] skipped_modules = { @@ -194,7 +192,6 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': No function types in python, 'virConnectDomainEventGraphicsCallback': No function types in python, 'virEventAddHandleFunc': No function types in python, - 'virDomainBlockPullInfoPtr': Not implemented yet, } ### @@ -359,6 +356,8 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', +'virDomainBlockPull', +'virDomainGetBlockPullInfo', ) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index ec08e69..4bdd5de 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -314,5 +314,19 @@ arg name='flags' type='unsigned int' info='flags, curently unused'/ return type='int' info=0 on success, -1 on error/ /function +function name='virDomainBlockPull' file='python' + infoInitiate an incremental BlockPull for the given disk/info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='path' type='const char *' info='Fully-qualified filename of disk'/ + arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/ + return type='virDomainBlockPullInfo' info='A dictionary containing progress information.' / +/function +function name='virDomainGetBlockPullInfo' file='python' + infoGet progress information for a background BlockPull operation/info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='path' type='const char *' info='Fully-qualified filename of disk'/ + arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/ + return type='virDomainBlockPullInfo' info='A dictionary containing progress information.' / +/function /symbols /api diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 974decb..cbdbc54 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -2382,6 +2382,57 @@ libvirt_virDomainGetJobInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { return(py_retval); } +static PyObject * +libvirt_virDomainBlockPullImpl(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args, int infoOnly) { +virDomainPtr domain; +PyObject *pyobj_domain; +const char *path; +unsigned int flags; +virDomainBlockPullInfo info; +int c_ret; +PyObject *ret; + +if (!PyArg_ParseTuple(args, (char *)Ozi:virDomainStreamDiskInfo, + pyobj_domain, path, flags)) +return(NULL); +domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + +LIBVIRT_BEGIN_ALLOW_THREADS; +if (infoOnly) +c_ret = virDomainGetBlockPullInfo(domain, path, info, flags); +else +c_ret = virDomainBlockPull(domain, path, info, flags); +LIBVIRT_END_ALLOW_THREADS; + +if (c_ret == -1) +return VIR_PY_NONE; + +if ((ret = PyDict_New()) == NULL) +return VIR_PY_NONE; + +PyDict_SetItem(ret, libvirt_constcharPtrWrap(cur), + libvirt_ulonglongWrap(info.cur)); +PyDict_SetItem(ret, libvirt_constcharPtrWrap(end), + libvirt_ulonglongWrap(info.end)); + +return ret; +} + +static PyObject * +libvirt_virDomainBlockPull(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +return libvirt_virDomainBlockPullImpl(self, args, 0); +} + +static PyObject * +libvirt_virDomainGetBlockPullInfo(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +return libvirt_virDomainBlockPullImpl(self, args, 1); +} + /*** * Helper functions to avoid importing modules @@ -3613,6 +3664,8 @@ static PyMethodDef libvirtMethods[] = { {(char *) virDomainGetJobInfo, libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) virDomainSnapshotListNames
[libvirt] [PATCH 4/8] Implement virDomainBlockPull for the qemu driver
The virDomainBlockPull* family of commands are enabled by the 'block_stream' and 'info block_stream' qemu monitor commands. * src/qemu/qemu_driver.c src/qemu/qemu_monitor_text.[ch]: implement disk streaming by using the stream and info stream text monitor commands * src/qemu/qemu_monitor_json.[ch]: implement commands using the qmp monitor Signed-off-by: Adam Litke a...@us.ibm.com --- src/qemu/qemu_driver.c | 108 + src/qemu/qemu_monitor.c | 16 src/qemu/qemu_monitor.h | 13 src/qemu/qemu_monitor_json.c | 131 +++ src/qemu/qemu_monitor_json.h |4 + src/qemu/qemu_monitor_text.c | 156 ++ src/qemu/qemu_monitor_text.h |5 ++ 7 files changed, 433 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 80de79a..1cbb151 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7971,6 +7971,110 @@ cleanup: return ret; } +static const char * +qemuDiskPathToAlias(virDomainObjPtr vm, const char *path) { +int i; +char *ret = NULL; + +for (i = 0 ; i vm-def-ndisks ; i++) { +virDomainDiskDefPtr disk = vm-def-disks[i]; + +if (disk-src != NULL STREQ(disk-src, path)) { +if (virAsprintf(ret, drive-%s, disk-info.alias) 0) { +virReportOOMError(); +return NULL; +} +break; +} +} + +if (!ret) { +qemuReportError(VIR_ERR_INVALID_ARG, +%s, _(No device found for specified path)); +} +return ret; +} + +static int +qemuDomainBlockPullImpl(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, +int mode) +{ +struct qemud_driver *driver = dom-conn-privateData; +virDomainObjPtr vm = NULL; +qemuDomainObjPrivatePtr priv; +char uuidstr[VIR_UUID_STRING_BUFLEN]; +const char *device = NULL; +int ret = -1; + +qemuDriverLock(driver); +virUUIDFormat(dom-uuid, uuidstr); +vm = virDomainFindByUUID(driver-domains, dom-uuid); +if (!vm) { +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +device = qemuDiskPathToAlias(vm, path); +if (!device) { +goto cleanup; +} + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +priv = vm-privateData; +ret = qemuMonitorBlockPull(priv-mon, device, info, mode); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +VIR_FREE(device); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + +static int +qemuDomainBlockPull(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_ONE); +} + +static int +qemuDomainBlockPullAll(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ALL); +} + +static int +qemuDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ABORT); +} + +static int +qemuDomainGetBlockPullInfo(virDomainPtr dom, const char *path, + virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_INFO); +} static virDriver qemuDriver = { .no = VIR_DRV_QEMU, @@ -8090,6 +8194,10 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainBlockPull = qemuDomainBlockPull, /* 0.9.2 */ +.domainBlockPullAll = qemuDomainBlockPullAll, /* 0.9.2 */ +.domainBlockPullAbort = qemuDomainBlockPullAbort, /* 0.9.2 */ +.domainGetBlockPullInfo = qemuDomainGetBlockPullInfo, /* 0.9.2 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 26bb814..dee354e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2376,3 +2376,19 @@ int qemuMonitorScreendump(qemuMonitorPtr mon, ret = qemuMonitorTextScreendump(mon, file); return ret; } + +int qemuMonitorBlockPull(qemuMonitorPtr mon, + const
[libvirt] [PATCH 5/8] Enable the virDomainBlockPull API in virsh
Define three new virsh commands: * blockpull: Perform incremental block pull * blockpull: Start/stop full device block pull * blockpullinfo: Retrieve progress info for full device block pull Share print_job_progress() with the migration code. * tools/virsh.c: implement the new commands Signed-off-by: Adam Litke a...@us.ibm.com --- tools/virsh.c | 134 +++-- 1 files changed, 130 insertions(+), 4 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 123781f..81c2e71 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -4095,7 +4095,8 @@ out_sig: } static void -print_job_progress(unsigned long long remaining, unsigned long long total) +print_job_progress(const char *label, unsigned long long remaining, + unsigned long long total) { int progress; @@ -4115,7 +4116,7 @@ print_job_progress(unsigned long long remaining, unsigned long long total) } } -fprintf(stderr, \rMigration: [%3d %%], progress); +fprintf(stderr, \r%s: [%3d %%], label, progress); } static bool @@ -4202,7 +4203,7 @@ repoll: functionReturn = true; if (verbose) { /* print [100 %] */ -print_job_progress(0, 1); +print_job_progress(Migration, 0, 1); } } else functionReturn = false; @@ -4241,7 +4242,8 @@ repoll: pthread_sigmask(SIG_SETMASK, oldsigmask, NULL); #endif if (ret == 0) -print_job_progress(jobinfo.dataRemaining, jobinfo.dataTotal); +print_job_progress(Migration, jobinfo.dataRemaining, + jobinfo.dataTotal); } } @@ -4344,6 +4346,127 @@ done: return ret; } +typedef enum { +VSH_CMD_BLOCK_PULL_ONE = 0, +VSH_CMD_BLOCK_PULL_ALL = 1, +VSH_CMD_BLOCK_PULL_ABORT = 2, +VSH_CMD_BLOCK_PULL_INFO = 3 +} VSH_CMD_BLOCK_PULL_MODE; + +static int +blockPullImpl(vshControl *ctl, const vshCmd *cmd, + virDomainBlockPullInfoPtr info, int mode) +{ +virDomainPtr dom; +const char *name, *path; +int ret = -1; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, name))) +return false; + +if (vshCommandOptString(cmd, path, path) 0) +return false; + +if (mode == VSH_CMD_BLOCK_PULL_ONE) +ret = virDomainBlockPull(dom, path, info, 0); +else if (mode == VSH_CMD_BLOCK_PULL_ALL) +ret = virDomainBlockPullAll(dom, path, 0); +else if (mode == VSH_CMD_BLOCK_PULL_ABORT) +ret = virDomainBlockPullAbort(dom, path, 0); +else if (mode == VSH_CMD_BLOCK_PULL_INFO) +ret = virDomainGetBlockPullInfo(dom, path, info, 0); + +virDomainFree(dom); +return ret; +} + +/* + * blockpull command + */ +static const vshCmdInfo info_block_pull[] = { +{help, N_(Iteratively populate a disk from its backing image.)}, +{desc, N_(Iteratively populate a disk from its backing image.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockPull(vshControl *ctl, const vshCmd *cmd) +{ +virDomainBlockPullInfo info; + +if (blockPullImpl(ctl, cmd, info, VSH_CMD_BLOCK_PULL_ONE) != 0) +return false; +print_job_progress(Block pull, info.end - info.cur, info.end); +return true; +} + +/* + * blockpullall command + */ +static const vshCmdInfo info_block_pull_all[] = { +{help, N_(Start or stop populating a disk from its backing image.)}, +{desc, N_(Start or stop populating a disk from its backing image.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull_all[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{abort, VSH_OT_BOOL, VSH_OFLAG_NONE, N_(Abort the current operation)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockPullAll(vshControl *ctl, const vshCmd *cmd) +{ +int mode; + +if (vshCommandOptBool (cmd, abort)) +mode = VSH_CMD_BLOCK_PULL_ABORT; +else +mode = VSH_CMD_BLOCK_PULL_ALL; + +if (blockPullImpl(ctl, cmd, NULL, mode) != 0) +return false; +return true; +} + +/* + * blockpullinfo command + */ +static const vshCmdInfo info_block_pull_info[] = { +{help, N_(Check progress of an active block pull operation.)}, +{desc, N_(Check progress of an active block pull operation.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull_info[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk
[libvirt] [PATCH 7/8] Asynchronous event for BlockPull completion
When an operation started by virDomainBlockPullAll completes (either with success or with failure), raise an event to indicate the final status. This allows an API user to avoid polling on virDomainBlockPullInfo if they would prefer to use the event mechanism. * daemon/remote.c: Dispatch events to client * include/libvirt/libvirt.h.in: Define event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle the new event * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block_stream completion and emit a libvirt block pull event * src/remote/remote_driver.c: Receive and dispatch events to application * src/remote/remote_protocol.x: Wire protocol definition for the event * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_STREAM_COMPLETED event from QEMU monitor Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 32 include/libvirt/libvirt.h.in | 27 + python/libvirt-override-virConnect.py | 12 python/libvirt-override.c | 51 + src/conf/domain_event.c | 50 src/conf/domain_event.h |7 - src/libvirt_private.syms |2 + src/qemu/qemu_monitor.c | 12 src/qemu/qemu_monitor.h |8 + src/qemu/qemu_monitor_json.c | 30 +++ src/qemu/qemu_process.c | 28 ++ src/remote/remote_driver.c| 30 +++ src/remote/remote_protocol.x |9 +- src/remote_protocol-structs |5 +++ 14 files changed, 301 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index e0b681c..a8ac276 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -396,6 +396,37 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int remoteRelayDomainEventBlockPull(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *path, + int status, + void *opaque) +{ +struct qemud_client *client = opaque; +remote_domain_event_block_pull_msg data; + +if (!client) +return -1; + +VIR_DEBUG(Relaying domain block pull event %s %d %s %i, dom-name, dom-id, path, status); + +virMutexLock(client-lock); + +/* build return data */ +memset(data, 0, sizeof data); +make_nonnull_domain(data.dom, dom); +data.path = (char*)path; +data.status = status; + +remoteDispatchDomainEventSend(client, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_event_block_pull_msg, data); + +virMutexUnlock(client-lock); + +return 0; +} + static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -434,6 +465,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), +VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockPull), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ba547c1..86b6d69 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2470,6 +2470,32 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventBlockPullStatus: + * + * The final status of a virDomainBlockPullAll() operation + */ +typedef enum { +VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0, +VIR_DOMAIN_BLOCK_PULL_FAILED = 1, +} virConnectDomainEventBlockPullStatus; + +/** + * virConnectDomainEventBlockPullCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @path: fully-qualified filename of the affected disk + * @status: final status of the operation (virConnectDomainEventBlockPullStatus) + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *path
[libvirt] [PATCH 2/8] virDomainBlockPull: Implement the main entry points
* src/libvirt.c: implement the main entry points Signed-off-by: Adam Litke a...@us.ibm.com --- src/libvirt.c | 252 + 1 files changed, 252 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 997d4a2..71afea9 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -14957,3 +14957,255 @@ error: virDispatchError(conn); return -1; } + +/** + * virDomainBlockPull: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @info: A pointer to a virDomainBlockPullInfo structure, or NULL + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function works incrementally, performing a small amount of work + * each time it is called. When successful, @info is updated with the current + * progress. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, info=%p, flags=%u, path, info, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (flags != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(flags must be zero)); +goto error; +} + +if (conn-driver-domainBlockPull) { +int ret; +ret = conn-driver-domainBlockPull(dom, path, info, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAll: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function pulls data for the entire device in the background. + * Progress of the operation can be checked with virDomainGetBlockPullInfo() and + * the operation can be aborted with virDomainBlockPullAbort(). When finished, + * an asynchronous event is raised to indicate the final status. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (flags != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(flags must be zero)); +goto error; +} + +if (conn-driver-domainBlockPullAll) { +int ret; +ret = conn-driver-domainBlockPullAll(dom, path, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAbort: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Cancel a pull operation previously started by virDomainBlockPullAll(). + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn
[libvirt] [PATCH 3/8] Add virDomainBlockPull support to the remote driver
The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be written by hand. * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * src/remote_protocol-structs: structure definitions for protocol verification Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 71 ++ src/remote/remote_driver.c | 68 src/remote/remote_protocol.x | 40 +++- src/remote_protocol-structs | 28 4 files changed, 206 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 49058f2..e0b681c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1474,6 +1474,77 @@ cleanup: return rv; } +static int +remoteDispatchDomainBlockPull(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_block_pull_args *args, + remote_domain_block_pull_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainBlockPull(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-cur = tmp.cur; +ret-end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + +static int +remoteDispatchDomainGetBlockPullInfo(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_get_block_pull_info_args *args, + remote_domain_get_block_pull_info_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainGetBlockPullInfo(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-cur = tmp.cur; +ret-end = tmp.end; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + + /*-*/ static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..de9359d 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2509,6 +2509,70 @@ done: return rv; } +static int remoteDomainBlockPull(virDomainPtr domain, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +int rv = -1; +remote_domain_block_pull_args args; +remote_domain_block_pull_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_block_pull_args, (char *)args, + (xdrproc_t)xdr_remote_domain_block_pull_ret, (char *)ret) == -1) +goto done; + +if (info) { +info-cur = ret.cur; +info-end = ret.end; +} +rv = 0; + +done: +remoteDriverUnlock(priv); +return rv; +} + +static int remoteDomainGetBlockPullInfo(virDomainPtr domain, +const char *path, +virDomainBlockPullInfoPtr info, +unsigned int flags) +{ +int rv = -1; +remote_domain_get_block_pull_info_args args; +remote_domain_get_block_pull_info_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO, + (xdrproc_t
[libvirt] [PATCH 1/8] Add new API virDomainBlockPull* to headers
Set up the types for the block pull functions and insert them into the virDriver structure definition. Symbols are exported in this patch to prevent documentation compile failures. * include/libvirt/libvirt.h.in: new API * src/driver.h: add the new entry to the driver structure * python/generator.py: fix compiler errors, the actual python bindings are implemented later * src/libvirt_public.syms: export symbols Signed-off-by: Adam Litke a...@us.ibm.com --- include/libvirt/libvirt.h.in | 39 +++ python/generator.py |3 +++ src/driver.h | 22 ++ src/libvirt_public.syms |7 +++ 4 files changed, 71 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index df213f1..ba547c1 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1156,6 +1156,45 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags); /* + * BlockPull API + */ + +/* An iterator for initiating and monitoring block pull operations */ +typedef unsigned long long virDomainBlockPullCursor; + +typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; +struct _virDomainBlockPullInfo { +/* + * The following fields provide an indication of block pull progress. @cur + * indicates the current position and will be between 0 and @end. @end is + * the final cursor position for this operation and represents completion. + * To approximate progress, divide @cur by @end. + */ +virDomainBlockPullCursor cur; +virDomainBlockPullCursor end; +}; +typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; + +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags); + +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags); + +int virDomainGetBlockPullInfo(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + + +/* * NUMA support */ diff --git a/python/generator.py b/python/generator.py index 7c38fdd..43e7414 100755 --- a/python/generator.py +++ b/python/generator.py @@ -178,6 +178,8 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, +'virDomainBlockPull', +'virDomainGetBlockPullInfo', ] skipped_modules = { @@ -192,6 +194,7 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': No function types in python, 'virConnectDomainEventGraphicsCallback': No function types in python, 'virEventAddHandleFunc': No function types in python, + 'virDomainBlockPullInfoPtr': Not implemented yet, } ### diff --git a/src/driver.h b/src/driver.h index 5df798a..4b30390 100644 --- a/src/driver.h +++ b/src/driver.h @@ -615,6 +615,24 @@ typedef int unsigned long flags, int cancelled); +typedef int +(*virDrvDomainBlockPull)(virDomainPtr dom, const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + +typedef int +(*virDrvDomainBlockPullAll)(virDomainPtr dom, const char *path, +unsigned int flags); + +typedef int +(*virDrvDomainBlockPullAbort)(virDomainPtr dom, const char *path, + unsigned int flags); + +typedef int +(*virDrvDomainGetBlockPullInfo)(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, +unsigned int flags); + /** * _virDriver: * @@ -749,6 +767,10 @@ struct _virDriver { virDrvDomainMigratePerform3domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3domainMigrateConfirm3; +virDrvDomainBlockPull domainBlockPull; +virDrvDomainBlockPullAll domainBlockPullAll; +virDrvDomainBlockPullAbort domainBlockPullAbort; +virDrvDomainGetBlockPullInfo domainGetBlockPullInfo; }; typedef int diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4d4299a..f2637ae 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -451,3 +451,10
Re: [libvirt] CFS Hardlimits and the libvirt cgroups implementation
On 06/10/2011 05:25 AM, Taku Izumi wrote: --- snip --- cputune ... cfs_period100/cfs_period cfs_quota50/cfs_quota /cputune --- snip --- I think the element name should be generic and the percentage (0..100 or 0..vcpu*100) is better as the element value. That's intuitive to me. How about the follwing? cputune capping50/capping cputune If it is not possible to control enough only one capping parameter, how about passing the other parameter as qemu driver's parameter via /etc/libvirt/qemu.conf ? I think it is important to be able to set the period. The reason that cpu.cfs_period_us is exposed at all by the cgroup controller is because the setting might need to be changed. What about making period optional by defining the units for period and quota as follows: period: An optional attribute specified in microseconds. quota: A percentage of the period that the cpus may run. This way 'period' can be omitted or changed independently from quota. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] CFS Hardlimits and the libvirt cgroups implementation
On 06/10/2011 04:20 AM, Daniel P. Berrange wrote: On Wed, Jun 08, 2011 at 02:20:23PM -0500, Adam Litke wrote: Hi all. In this post I would like to bring up 3 issues which are tightly related: 1. unwanted behavior when using cfs hardlimits with libvirt, 2. Scaling cputune.share according to the number of vcpus, 3. API proposal for CFS hardlimits support. === 1 === Mark Peloquin (on cc:) has been looking at implementing CFS hard limit support on top of the existing libvirt cgroups implementation and he has run into some unwanted behavior when enabling quotas that seems to be affected by the cgroup hierarchy being used by libvirt. Here are Mark's words on the subject (posted by me while Mark joins this mailing list): -- I've conducted a number of measurements using CFS. The system config is a 2 socket Nehalem system with 64GB ram. Installed is RHEL6.1-snap4. The guest VMs being used have RHEL5.5 - 32bit. I've replaced the kernel with 2.6.39-rc6+ with patches from Paul-V6-upstream-breakout.tar.bz2 for CFS bandwidth. The test config uses 5 VMs of various vcpu and memory sizes. Being used are 2 VMs with 2 vcpus and 4GB of memory, 1 VM with 4vcpus/8GB, another VM with 8vcpus/16GB and finally a VM with 16vcpus/16GB. Thus far the tests have been limited to cpu intensive workloads. Each VM runs a single instance of the workload. The workload is configured to create one thread for each vcpu in the VM. The workload is then capable of completely saturation each vcpu in each VM. CFS was tested using two different topologies. First vcpu cgroups were created under each VM created by libvirt. The vcpu threads from the VM's cgroup/tasks were moved to the tasks list of each vcpu cgroup, one thread to each vcpu cgroup. This tree structure permits setting CFS quota and period per vcpu. Default values for cpu.shares (1024), quota (-1) and period (50us) was used in each VM cgroup and inherited by the vcpu croup. With these settings the workload generated system cpu utilization (measured in the host) of 99% guest, 0.1 idle, 0.14% user and 0.38 system. Second, using the same topology, the CFS quota in each vcpu's cgroup was set to 25us allowing each vcpu to consume 50% of a cpu. The cpu workloads was run again. This time the total system cpu utilization was measured at 75% guest, ~24% idle, 0.15% user and 0.40% system. The topology was changed such that a cgroup for each vcpu was created in /cgroup/cpu. The first test used the default/inherited shares and CFS quota and period. The measured system cpu utilization was 99% guest, ~0.5 idle, 0.13 user and 0.38 system, similar to the default settings using vcpu cgroups under libvirt. The next test, like before the topology change, set the vcpu quota values to 25us or 50% of a cpu. In this case the measured system cpu utilization was ~92% guest, ~7.5% idle, 0.15% user and 0.38% system. We can see that moving the vcpu cgroups from being under libvirt/qemu make a big difference in idle cpu time. Does this suggest a possible problems with libvirt? -- I can't really understand from your description what the different setups are. You're talking about libvirt vcpu cgroups, but nothing in libvirt does vcpu based cgroups, our cgroup granularity is always per-VM. I should have been more clear. In Mark's testing, he found unacceptable levels of idle time when mixing domain level cgroups with cfs. The idle time was reduced to an acceptable level when each vcpu thread was confined in its own cgroup. To achieve the per-vcpu scenarios, he reassigned the cgroups to vcpus after the domain was started to override the default libvirt configuration. === 2 === Something else we are seeing is that libvirt's default setting for cputune.share is 1024 for any domain (regardless of how many vcpus are configured. This ends up hindering performance of really large VMs (with lots of vcpus) as compared to smaller ones since all domains are given equal share. Would folks consider changing the default for 'shares' to be a quantity scaled by the number of vcpus such that bigger domains get to use proportionally more host cpu resource? Well that's just the kernel default setting actually. The intent of the default cgroups configuration for a VM, is that it should be identical to the configuration if the VM was *not* in any cgroups. So I think that gives some justification for setting the cpu shares relative to the # of vCPUs by default, otherwise we have a regression vs not using cgroups. Yes, I agree. === 3 === Besides the above issues, I would like to open a discussion on what the libvirt API for enabling cpu hardlimits should look like. Here is what I was thinking: Two additional scheduler parameters (based on the names given in the cgroup fs) will be recognized for qemu domains: 'cfs_period' and 'cfs_quota'. These can use the existing virDomain[Get|Set
Re: [libvirt] CFS Hardlimits and the libvirt cgroups implementation
On 06/10/2011 04:45 AM, Wen Congyang wrote: === 3 === Besides the above issues, I would like to open a discussion on what the libvirt API for enabling cpu hardlimits should look like. Here is what I was thinking: Two additional scheduler parameters (based on the names given in the cgroup fs) will be recognized for qemu domains: 'cfs_period' and 'cfs_quota'. These can use the existing virDomain[Get|Set]SchedulerParameters() API. The Domain XML schema would be updated to permit the following: --- snip --- cputune ... cfs_period100/cfs_period cfs_quota50/cfs_quota /cputune --- snip --- I don't think 'cfs_' should be in the names here. These absolute limits on CPU time could easily be applicable to non-CFS schedulars or non-Linux hypervisors. Do you mean the element's name should be period and quota? The name of the file provided by cfs bandwidth is:cpu.cfs_period_us and cpu.cfs_quota_us. I think he uses 'cfs_' because it's similar as the filename. But I do not mind the element's name. I am making the patch, so I want to know which element's name should be used. I think it's a good idea to use 'period' and 'quota'. In my other email I suggest that the unit for period should be microseconds and quota should be a percentage of the period. That way quota can be used without needing to know or change the default period. Please Cc: me on your patch. We are very interested in this feature and would like to review/test it. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 2/8] virDomainBlockPull: Implement the main entry points
On 06/14/2011 04:49 AM, Daniel P. Berrange wrote: On Thu, Jun 09, 2011 at 12:10:08PM -0500, Adam Litke wrote: +if (flags != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(flags must be zero)); +goto error; +} We don't want to check 'flags' in this place, because you could be a client app talking to a libvirt daemon which *does* support some flag values. Instead just have a 'virCheckFlags(0, -1)' in the QEMU driver impls. Same comment applies to all the other APIs in this patch Yep, makes sense and changed in the next version. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 3/8] Add virDomainBlockPull support to the remote driver
On 06/14/2011 04:49 AM, Daniel P. Berrange wrote: On Thu, Jun 09, 2011 at 12:10:09PM -0500, Adam Litke wrote: The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be written by hand. * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * src/remote_protocol-structs: structure definitions for protocol verification Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 71 ++ src/remote/remote_driver.c | 68 src/remote/remote_protocol.x | 40 +++- src/remote_protocol-structs | 28 4 files changed, 206 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 49058f2..e0b681c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1474,6 +1474,77 @@ cleanup: return rv; } +static int +remoteDispatchDomainBlockPull(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_block_pull_args *args, + remote_domain_block_pull_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainBlockPull(dom, args-path, tmp, args-flags) 0) +goto cleanup; +rv = 0; +ret-cur = tmp.cur; +ret-end = tmp.end; 'rv = 0' should really be the last statement before the cleanup label. Likewise for next method. Ok. static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -6337,6 +6401,10 @@ static virDriver remote_driver = { .domainMigratePerform3 = remoteDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ +.domainBlockPull = remoteDomainBlockPull, /* 0.9.2 */ +.domainBlockPullAll = remoteDomainBlockPullAll, /* 0.9.2 */ +.domainBlockPullAbort = remoteDomainBlockPullAbort, /* 0.9.2 */ +.domainGetBlockPullInfo = remoteDomainGetBlockPullInfo, /* 0.9.2 */ }; These need updating to 0.9.3 Ok, Got these and the qemu ones. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 5/8] Enable the virDomainBlockPull API in virsh
On 06/14/2011 04:59 AM, Daniel P. Berrange wrote: +static bool +cmdBlockPullAll(vshControl *ctl, const vshCmd *cmd) +{ +int mode; + +if (vshCommandOptBool (cmd, abort)) +mode = VSH_CMD_BLOCK_PULL_ABORT; +else +mode = VSH_CMD_BLOCK_PULL_ALL; + +if (blockPullImpl(ctl, cmd, NULL, mode) != 0) +return false; +return true; +} In constrast to the API, I think it might be worth merging this command with blockpull, just having a '--all' flag for it. This was an easy change to make. You'll see it in the next posting. I've added the flags '--all' and '--abort' to the blockpull command. Now we have just two virsh commands: blockpull and blockpullinfo. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 7/8] Asynchronous event for BlockPull completion
On 06/14/2011 05:03 AM, Daniel P. Berrange wrote: +static virDomainEventPtr +virDomainEventBlockPullNew(int id, const char *name, unsigned char *uuid, + const char *path, int status) +{ +virDomainEventPtr ev = +virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_BLOCK_PULL, + id, name, uuid); + +if (ev) { +if (!(ev-data.blockPull.path = strdup(path))) { +virDomainEventFree(ev); +return NULL; You want a virReportOOMError() call there. Yes, of course. +static int +qemuProcessHandleBlockPull(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *diskAlias, + int status) +{ +struct qemud_driver *driver = qemu_driver; +virDomainEventPtr blockPullEvent = NULL; +const char *path; +virDomainDiskDefPtr disk; + +virDomainObjLock(vm); +disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias); + +if (disk) +path = disk-src; +else +path = ; If we can't find the disk associated with the alias, then I think we should just discard the event rather than emitting one with a zero-length path. Yes, this makes sense. Dome for the next series. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 1/8] Add new API virDomainBlockPull* to headers
Set up the types for the block pull functions and insert them into the virDriver structure definition. Symbols are exported in this patch to prevent documentation compile failures. * include/libvirt/libvirt.h.in: new API * src/driver.h: add the new entry to the driver structure * python/generator.py: fix compiler errors, the actual python bindings are implemented later * src/libvirt_public.syms: export symbols Signed-off-by: Adam Litke a...@us.ibm.com Acked-by: Daniel P. Berrange berra...@redhat.com --- include/libvirt/libvirt.h.in | 39 +++ python/generator.py |3 +++ src/driver.h | 22 ++ src/libvirt_public.syms |7 +++ 4 files changed, 71 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index df213f1..ba547c1 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1156,6 +1156,45 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags); /* + * BlockPull API + */ + +/* An iterator for initiating and monitoring block pull operations */ +typedef unsigned long long virDomainBlockPullCursor; + +typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; +struct _virDomainBlockPullInfo { +/* + * The following fields provide an indication of block pull progress. @cur + * indicates the current position and will be between 0 and @end. @end is + * the final cursor position for this operation and represents completion. + * To approximate progress, divide @cur by @end. + */ +virDomainBlockPullCursor cur; +virDomainBlockPullCursor end; +}; +typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; + +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags); + +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags); + +int virDomainGetBlockPullInfo(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + + +/* * NUMA support */ diff --git a/python/generator.py b/python/generator.py index 7c38fdd..43e7414 100755 --- a/python/generator.py +++ b/python/generator.py @@ -178,6 +178,8 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, +'virDomainBlockPull', +'virDomainGetBlockPullInfo', ] skipped_modules = { @@ -192,6 +194,7 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': No function types in python, 'virConnectDomainEventGraphicsCallback': No function types in python, 'virEventAddHandleFunc': No function types in python, + 'virDomainBlockPullInfoPtr': Not implemented yet, } ### diff --git a/src/driver.h b/src/driver.h index 5df798a..4b30390 100644 --- a/src/driver.h +++ b/src/driver.h @@ -615,6 +615,24 @@ typedef int unsigned long flags, int cancelled); +typedef int +(*virDrvDomainBlockPull)(virDomainPtr dom, const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags); + +typedef int +(*virDrvDomainBlockPullAll)(virDomainPtr dom, const char *path, +unsigned int flags); + +typedef int +(*virDrvDomainBlockPullAbort)(virDomainPtr dom, const char *path, + unsigned int flags); + +typedef int +(*virDrvDomainGetBlockPullInfo)(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, +unsigned int flags); + /** * _virDriver: * @@ -749,6 +767,10 @@ struct _virDriver { virDrvDomainMigratePerform3domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3domainMigrateConfirm3; +virDrvDomainBlockPull domainBlockPull; +virDrvDomainBlockPullAll domainBlockPullAll; +virDrvDomainBlockPullAbort domainBlockPullAbort; +virDrvDomainGetBlockPullInfo domainGetBlockPullInfo; }; typedef int diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4d4299a..f2637ae 100644 --- a/src/libvirt_public.syms +++ b
[libvirt] [PATCH 3/8] Add virDomainBlockPull support to the remote driver
The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be written by hand. * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * src/remote_protocol-structs: structure definitions for protocol verification Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 71 ++ src/remote/remote_driver.c | 68 src/remote/remote_protocol.x | 40 +++- src/remote_protocol-structs | 28 4 files changed, 206 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 49058f2..1fb3b02 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1474,6 +1474,77 @@ cleanup: return rv; } +static int +remoteDispatchDomainBlockPull(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_block_pull_args *args, + remote_domain_block_pull_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainBlockPull(dom, args-path, tmp, args-flags) 0) +goto cleanup; +ret-cur = tmp.cur; +ret-end = tmp.end; +rv = 0; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + +static int +remoteDispatchDomainGetBlockPullInfo(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error * rerr, + remote_domain_get_block_pull_info_args *args, + remote_domain_get_block_pull_info_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockPullInfo tmp; +int rv = -1; + +if (!conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainGetBlockPullInfo(dom, args-path, tmp, args-flags) 0) +goto cleanup; +ret-cur = tmp.cur; +ret-end = tmp.end; +rv = 0; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + + /*-*/ static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..65b9c0f 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2509,6 +2509,70 @@ done: return rv; } +static int remoteDomainBlockPull(virDomainPtr domain, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +int rv = -1; +remote_domain_block_pull_args args; +remote_domain_block_pull_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_block_pull_args, (char *)args, + (xdrproc_t)xdr_remote_domain_block_pull_ret, (char *)ret) == -1) +goto done; + +if (info) { +info-cur = ret.cur; +info-end = ret.end; +} +rv = 0; + +done: +remoteDriverUnlock(priv); +return rv; +} + +static int remoteDomainGetBlockPullInfo(virDomainPtr domain, +const char *path, +virDomainBlockPullInfoPtr info, +unsigned int flags) +{ +int rv = -1; +remote_domain_get_block_pull_info_args args; +remote_domain_get_block_pull_info_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO, + (xdrproc_t
[libvirt] [PATCH 8/8] test: Python Unittests for DomainBlockPull API
*** Please do not consider for merging, example tests only *** Here are the testcases I am currently running to verify the correctness of this API. These are continuing to evolve. Signed-off-by: Adam Litke a...@us.ibm.com --- blockPull-test.py | 301 + 1 files changed, 301 insertions(+), 0 deletions(-) create mode 100755 blockPull-test.py diff --git a/blockPull-test.py b/blockPull-test.py new file mode 100755 index 000..4001477 --- /dev/null +++ b/blockPull-test.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python + +import sys +import subprocess +import time +import unittest +import re +import threading +import libvirt + +qemu_img_bin = /home/aglitke/src/qemu/qemu-img +virsh_bin = /home/aglitke/src/libvirt/tools/virsh + +dom_xml = +domain type='kvm' + nameblockPull-test/name + memory131072/memory + currentMemory131072/currentMemory + vcpu1/vcpu + os +type arch='x86_64' machine='pc-0.13'hvm/type +boot dev='hd'/ + /os + features +acpi/ +apic/ +pae/ + /features + clock offset='utc'/ + on_poweroffdestroy/on_poweroff + on_rebootrestart/on_reboot + on_crashrestart/on_crash + devices + emulator/home/aglitke/src/qemu/x86_64-softmmu/qemu-system-x86_64/emulator +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk1.qed' / + target dev='vda' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk2.qed' / + target dev='vdb' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='raw'/ + source file='/tmp/disk3.raw' / + target dev='vdc' bus='virtio'/ +/disk +graphics type='vnc' port='-1' autoport='yes'/ + /devices +/domain + + +def qemu_img(*args): +global qemu_img_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([qemu_img_bin] + list(args), stdin=devnull, stdout=devnull) + +def virsh(*args): +global virsh_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([virsh_bin] + list(args), + stdin=devnull, stdout=devnull, stderr=devnull) + +def make_baseimage(name, size_mb): +devnull = open('/dev/null', 'r+') +return subprocess.call(['dd', 'if=/dev/zero', of=%s % name, 'bs=1M', +'count=%i' % size_mb], stdin=devnull, stdout=devnull, stderr=devnull) + +def has_backing_file(path): +global qemu_img_bin +p1 = subprocess.Popen([qemu_img_bin, info, path], + stdout=subprocess.PIPE).communicate()[0] +matches = re.findall(^backing file:, p1, re.M) +if len(matches) 0: +return True +return False + +class BlockPullTestCase(unittest.TestCase): +def _error_handler(self, ctx, error, dummy=None): +pass + +def create_disks(self, sparse): +self.disks = [ '/tmp/disk1.qed', '/tmp/disk2.qed', '/tmp/disk3.raw' ] +if sparse: +qemu_img('create', '-f', 'raw', '/tmp/backing1.img', '100M') +qemu_img('create', '-f', 'raw', '/tmp/backing2.img', '100M') +else: +make_baseimage('/tmp/backing1.img', 100) +make_baseimage('/tmp/backing2.img', 100) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing1.img,copy_on_read=on', self.disks[0]) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing2.img,copy_on_read=on', self.disks[1]) +qemu_img('create', '-f', 'raw', self.disks[2], '100M') + +def begin(self, sparse=True): +global dom_xml + +libvirt.registerErrorHandler(self._error_handler, None) +self.create_disks(sparse) +self.conn = libvirt.open('qemu:///system') +self.dom = self.conn.createXML(dom_xml, 0) + +def end(self): +self.dom.destroy() +self.conn.close() + +class TestBasicErrors(BlockPullTestCase): +def setUp(self): +self.begin() + +def tearDown(self): +self.end() + +def test_bad_path(self): +try: +self.dom.blockPull('/dev/null', 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e[0]) + +def test_abort_no_stream(self): +try: +self.dom.blockPullAbort(self.disks[0], 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_OPERATION_INVALID, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_OPERATION_INVALID, e[0]) + +def test_start_same_twice(self): +self.dom.blockPullAll(self.disks[0], 0) +try: +self.dom.blockPullAll(self.disks[0], 0) +except libvirt.libvirtError, e: +self.assertEqual
[libvirt] [PATCH 2/8] virDomainBlockPull: Implement the main entry points
* src/libvirt.c: implement the main entry points Signed-off-by: Adam Litke a...@us.ibm.com Acked-by: Daniel P. Berrange berra...@redhat.com --- src/libvirt.c | 228 + 1 files changed, 228 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 997d4a2..bd9da7b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -14957,3 +14957,231 @@ error: virDispatchError(conn); return -1; } + +/** + * virDomainBlockPull: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @info: A pointer to a virDomainBlockPullInfo structure, or NULL + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function works incrementally, performing a small amount of work + * each time it is called. When successful, @info is updated with the current + * progress. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPull(virDomainPtr dom, + const char *path, + virDomainBlockPullInfoPtr info, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, info=%p, flags=%u, path, info, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (conn-driver-domainBlockPull) { +int ret; +ret = conn-driver-domainBlockPull(dom, path, info, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAll: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Populate a disk image with data from its backing image. Once all data from + * its backing image has been pulled, the disk no longer depends on a backing + * image. This function pulls data for the entire device in the background. + * Progress of the operation can be checked with virDomainGetBlockPullInfo() and + * the operation can be aborted with virDomainBlockPullAbort(). When finished, + * an asynchronous event is raised to indicate the final status. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int virDomainBlockPullAll(virDomainPtr dom, + const char *path, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (conn-driver-domainBlockPullAll) { +int ret; +ret = conn-driver-domainBlockPullAll(dom, path, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPullAbort: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Cancel a pull operation previously started by virDomainBlockPullAll(). + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockPullAbort(virDomainPtr dom, +const char *path, +unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%u, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL
[libvirt] RFC (v4): Add virDomainBlockPull API family to libvirt
This round addresses all review comments from the last posting and should be ready for inclusion. Thanks for the review. Changes since V3: - Don't check flags at the libvirt API level - qemu: Check disk-type before looking up alias - virsh: Merge blockpullall into blockpull - event: Drop events for unknown disk paths - Misc style fixes and comment updates Changes since V2: - Rebased - Ensure error messages are consistent between JSON and Text interfaces - Added additional tests to the sample test bucket Changes since V1: - Rebased to incorporate changes to generator and removal of static driver structure initializers - Other small fixups suggested by Matthias Bolte To help speed the provisioning process for large domains, new QED disks are created with backing to a template image. These disks are configured with copy on read such that blocks that are read from the backing file are copied to the new disk. This reduces I/O over a potentially costly path to the backing image. In such a configuration, there is a desire to remove the dependency on the backing image as the domain runs. To accomplish this, qemu will provide an interface to perform sequential copy on read operations during normal VM operation. Once all data has been copied, the disk image's link to the backing file is removed. The virDomainBlockPull API family brings this functionality to libvirt. virDomainBlockPullAll() instructs the hypervisor to stream the entire device in the background. Progress of this operation can be checked with the function virDomainBlockPullInfo(). An ongoing stream can be cancelled with virDomainBlockPullAbort(). If a more controlled IO rate is desired, virDomainBlockPull() can be used to perform a single increment of IO. Subsequent calls to this function will automatically stream the appropriate next increment until the disk has been fully populated. An event (VIR_DOMAIN_EVENT_ID_BLOCK_PULL) will be emitted when a disk has been fully populated or if a BlockPullAll() operation was terminated due to an error. This event is useful to avoid polling on virDomainBlockPullInfo() for completion and could also be used by the security driver to revoke access to the backing file when it is no longer needed. make check: PASS make syntax-check: PASS make -C tests valgrind: PASS I am testing this API with Python Unittest (see the last patch). [PATCH 1/8] Add new API virDomainBlockPull* to headers [PATCH 2/8] virDomainBlockPull: Implement the main entry points [PATCH 3/8] Add virDomainBlockPull support to the remote driver [PATCH 4/8] Implement virDomainBlockPull for the qemu driver [PATCH 5/8] Enable the virDomainBlockPull API in virsh [PATCH 6/8] Enable virDomainBlockPull in the python API. [PATCH 7/8] Asynchronous event for BlockPull completion [PATCH 8/8] test: Python Unittests for DomainBlockPull API -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 7/8] Asynchronous event for BlockPull completion
When an operation started by virDomainBlockPullAll completes (either with success or with failure), raise an event to indicate the final status. This allows an API user to avoid polling on virDomainBlockPullInfo if they would prefer to use the event mechanism. * daemon/remote.c: Dispatch events to client * include/libvirt/libvirt.h.in: Define event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle the new event * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block_stream completion and emit a libvirt block pull event * src/remote/remote_driver.c: Receive and dispatch events to application * src/remote/remote_protocol.x: Wire protocol definition for the event * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_STREAM_COMPLETED event from QEMU monitor Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 32 include/libvirt/libvirt.h.in | 27 + python/libvirt-override-virConnect.py | 12 python/libvirt-override.c | 51 + src/conf/domain_event.c | 51 + src/conf/domain_event.h |7 - src/libvirt_private.syms |2 + src/qemu/qemu_monitor.c | 12 src/qemu/qemu_monitor.h |8 + src/qemu/qemu_monitor_json.c | 30 +++ src/qemu/qemu_process.c | 30 +++ src/remote/remote_driver.c| 30 +++ src/remote/remote_protocol.x |9 +- src/remote_protocol-structs |5 +++ 14 files changed, 304 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 1fb3b02..4b46db0 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -396,6 +396,37 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int remoteRelayDomainEventBlockPull(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *path, + int status, + void *opaque) +{ +struct qemud_client *client = opaque; +remote_domain_event_block_pull_msg data; + +if (!client) +return -1; + +VIR_DEBUG(Relaying domain block pull event %s %d %s %i, dom-name, dom-id, path, status); + +virMutexLock(client-lock); + +/* build return data */ +memset(data, 0, sizeof data); +make_nonnull_domain(data.dom, dom); +data.path = (char*)path; +data.status = status; + +remoteDispatchDomainEventSend(client, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL, + (xdrproc_t)xdr_remote_domain_event_block_pull_msg, data); + +virMutexUnlock(client-lock); + +return 0; +} + static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -434,6 +465,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), +VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockPull), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ba547c1..86b6d69 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2470,6 +2470,32 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventBlockPullStatus: + * + * The final status of a virDomainBlockPullAll() operation + */ +typedef enum { +VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0, +VIR_DOMAIN_BLOCK_PULL_FAILED = 1, +} virConnectDomainEventBlockPullStatus; + +/** + * virConnectDomainEventBlockPullCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @path: fully-qualified filename of the affected disk + * @status: final status of the operation (virConnectDomainEventBlockPullStatus) + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *path
[libvirt] [PATCH 4/8] Implement virDomainBlockPull for the qemu driver
The virDomainBlockPull* family of commands are enabled by the 'block_stream' and 'info block_stream' qemu monitor commands. * src/qemu/qemu_driver.c src/qemu/qemu_monitor_text.[ch]: implement disk streaming by using the stream and info stream text monitor commands * src/qemu/qemu_monitor_json.[ch]: implement commands using the qmp monitor Signed-off-by: Adam Litke a...@us.ibm.com Acked-by: Daniel P. Berrange berra...@redhat.com --- src/qemu/qemu_driver.c | 112 ++ src/qemu/qemu_monitor.c | 16 src/qemu/qemu_monitor.h | 13 src/qemu/qemu_monitor_json.c | 131 +++ src/qemu/qemu_monitor_json.h |4 + src/qemu/qemu_monitor_text.c | 156 ++ src/qemu/qemu_monitor_text.h |5 ++ 7 files changed, 437 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 80de79a..3be9dcc 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7971,6 +7971,114 @@ cleanup: return ret; } +static const char * +qemuDiskPathToAlias(virDomainObjPtr vm, const char *path) { +int i; +char *ret = NULL; + +for (i = 0 ; i vm-def-ndisks ; i++) { +virDomainDiskDefPtr disk = vm-def-disks[i]; + +if (disk-type != VIR_DOMAIN_DISK_TYPE_BLOCK +disk-type != VIR_DOMAIN_DISK_TYPE_FILE) +continue; + +if (disk-src != NULL STREQ(disk-src, path)) { +if (virAsprintf(ret, drive-%s, disk-info.alias) 0) { +virReportOOMError(); +return NULL; +} +break; +} +} + +if (!ret) { +qemuReportError(VIR_ERR_INVALID_ARG, +%s, _(No device found for specified path)); +} +return ret; +} + +static int +qemuDomainBlockPullImpl(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, +int mode) +{ +struct qemud_driver *driver = dom-conn-privateData; +virDomainObjPtr vm = NULL; +qemuDomainObjPrivatePtr priv; +char uuidstr[VIR_UUID_STRING_BUFLEN]; +const char *device = NULL; +int ret = -1; + +qemuDriverLock(driver); +virUUIDFormat(dom-uuid, uuidstr); +vm = virDomainFindByUUID(driver-domains, dom-uuid); +if (!vm) { +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +device = qemuDiskPathToAlias(vm, path); +if (!device) { +goto cleanup; +} + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +priv = vm-privateData; +ret = qemuMonitorBlockPull(priv-mon, device, info, mode); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +VIR_FREE(device); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + +static int +qemuDomainBlockPull(virDomainPtr dom, const char *path, +virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_ONE); +} + +static int +qemuDomainBlockPullAll(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ALL); +} + +static int +qemuDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, NULL, BLOCK_PULL_MODE_ABORT); +} + +static int +qemuDomainGetBlockPullInfo(virDomainPtr dom, const char *path, + virDomainBlockPullInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_INFO); +} static virDriver qemuDriver = { .no = VIR_DRV_QEMU, @@ -8090,6 +8198,10 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainBlockPull = qemuDomainBlockPull, /* 0.9.3 */ +.domainBlockPullAll = qemuDomainBlockPullAll, /* 0.9.3 */ +.domainBlockPullAbort = qemuDomainBlockPullAbort, /* 0.9.3 */ +.domainGetBlockPullInfo = qemuDomainGetBlockPullInfo, /* 0.9.3 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 26bb814..dee354e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2376,3 +2376,19 @@ int
Re: [libvirt] [PATCH 3/8] Add virDomainBlockPull support to the remote driver
Thanks Eric. The push looks good. There is a small (known) issue with the text monitor interface and a one-liner will fix that. I can send that out once I have verified against upstream qemu. On 06/14/2011 10:56 PM, Eric Blake wrote: On 06/14/2011 08:36 AM, Adam Litke wrote: The generator can handle DomainBlockPullAll and DomainBlockPullAbort. DomainBlockPull and DomainBlockPullInfo must be written by hand. * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * src/remote_protocol-structs: structure definitions for protocol verification Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 71 ++ src/remote/remote_driver.c | 68 src/remote/remote_protocol.x | 40 +++- src/remote_protocol-structs | 28 4 files changed, 206 insertions(+), 1 deletions(-) ACK for addressing the v3 comments, but there are merge conflicts due to all of today's patches. Double-check my work, but I think I got it all right. Pushed. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RFC 0/5] support cpu bandwidth in libvirt
On 06/15/2011 01:24 AM, Wen Congyang wrote: I need this feature immediately after CFS bandwidth patchset is merged into upstream kernel. So I prepare this patchset, and post it here for reviewing before CFS bandwidth is merged into upstream kernel. quota is an optional attribute specified in microseconds not a percentage of period. TODO: 1. quota should be in the range [1000, 18446744073709551(2^64/1000)] or less than 0. But I only limit it less or equal than 2^64/1000. Does anyone have a better way to limit quota? What are the semantics of quota 0, of quota == 0? How are you storing such a large signed integer in the kernel? I will guess that any value less than 0 means: no limit, and 0 means: no quota (ie. the cgroup cannot run on the cpu). I am not sure what the libvirt convention for dealing with a situation like this is. I think you have two options: 1) override an invalid value (ie. 1) to represent the case (n 0) or, 2) use a struct to represent the quota: struct _virCfsQuota { unsigned long long val; int unlimited; }; When the quota is unlimited, .unlimited == 1 and val is undefined. Otherwise, the quota is in val. Wen Congyang (5): cgroup: Implement cpu.cfs_period_us and cpu.cfs_quota_us tuning API Update XML Schema for new entries qemu: Implement period and quota tunable XML configuration and parsing. qemu: Implement cfs_period and cfs_quota's modification doc: Add documentation for new cputune elements period and quota docs/formatdomain.html.in | 19 +++ docs/schemas/domain.rng | 25 - src/conf/domain_conf.c | 20 +++- src/conf/domain_conf.h |2 + src/libvirt_private.syms|4 + src/qemu/qemu_cgroup.c | 43 +- src/qemu/qemu_driver.c | 162 +++ src/util/cgroup.c | 81 +++- src/util/cgroup.h |6 + tests/qemuxml2argvdata/qemuxml2argv-cputune.xml |2 + 10 files changed, 328 insertions(+), 36 deletions(-) -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RFC 0/5] support cpu bandwidth in libvirt
On 06/16/2011 12:44 AM, Wen Congyang wrote: In the kernel, the value of quota is stored in uint64_t: === struct cfs_bandwidth { #ifdef CONFIG_CFS_BANDWIDTH raw_spinlock_t lock; ktime_t period; u64 quota; == here is u64, not s64 u64 runtime; u64 runtime_expires; s64 hierarchal_quota; int idle; struct hrtimer period_timer, slack_timer; struct list_head throttled_cfs_rq; /* statistics */ int nr_periods, nr_throttled; u64 throttled_time; #endif }; === The unit of quota in kernel is ns, but the value we write to cpu.cfs_quota_us is us. So the max value we can write is 2^64/1000. In the kernel, if quota is ~0ULL, it means unlimited. Ok, I would think you can use the same semantics in libvirt then. No need for a structure. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 06/10] vcpubandwidth: introduce two new libvirt APIs
On 07/05/2011 02:06 AM, Wen Congyang wrote: At 07/04/2011 07:19 PM, Nikunj A. Dadhania Write: On Thu, 30 Jun 2011 11:13:18 +0800, Wen Congyang we...@cn.fujitsu.com wrote: We want to control bandwidth for each vcpu, so we can not use the API virDomainSetSchedulerParameters(). Introduce two new APIs to change and query bandwidth for each vcpu. Will we have different cpu bandwidth for different vcpus? Something like this: vcpu1: 100/25 vcpu2: 100/50 vcpu3: 100/30 vcpu4: 100/40 IMO, that is not required, we can have a top level bandwitdh for the VM and then redistribute it among vcpus equally, without user knowing about it. Something like this: VM1(4vcpu) has to be throttled at 1CPU bandwidth using SetSchedParamters. Internally libvirt splits it equally: vcpu1: 100/25 vcpu2: 100/25 vcpu3: 100/25 vcpu4: 100/25 So why introduce VCPU level apis? Adam Litke said IBM's performance team nead to control cpu bandwidth for each vcpu. Nikunj is correct here. We only need to ensure that the bandwidth is distributed equally between all of the cpus. This can be accomplished internally by creating a cgroup for each vcpu and ensuring that 'cpu.share' is the same for each vcpu cgroup. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] Exposing qemu support for SDL via capabilities
Hi all, In order to nicely support domains that use qemu's SDL support, libvirt-cim is looking for a way to confirm if the underlying qemu emulator can support SDL. Libvirt already knows this information internally. It seems to me that the best way to provide this information is by reporting it as a guest feature via the capabilities API call. I was thinking the node '/capabilities/guest/features/sdl' could be added when qemu supports SDL. Is this a good idea? -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] Exposing qemu support for SDL via capabilities
On 07/08/2011 10:14 AM, Richard W.M. Jones wrote: On Fri, Jul 08, 2011 at 09:19:46AM -0500, Adam Litke wrote: Hi all, In order to nicely support domains that use qemu's SDL support, libvirt-cim is looking for a way to confirm if the underlying qemu emulator can support SDL. Libvirt already knows this information internally. It seems to me that the best way to provide this information is by reporting it as a guest feature via the capabilities API call. I was thinking the node '/capabilities/guest/features/sdl' could be added when qemu supports SDL. Is this a good idea? Seems like clearly a good idea to me. (Although I don't have to code it :-) Don't worry :) I think we have a motivated party. Would it be worth having a separate graphics element underneath features, so the path would be /capabilities/guest/features/graphics/sdl ? I only think this would be needed if we are going to add other graphics-related features. Can you think of other features that would fit? I think libvirt always assumes some form of vnc support so there may not be a need to enumerate graphics types. We might want to advertise spice support, but that wouldn't fit strictly under graphics because spice affects much more than the graphics device. Is there a desire to eventually add support for enumerating the different models of devices that qemu can emulate? For example, [ne2k_pci, i82551, i82557b, i82559er, rtl8139, e1000, pcnet, virtio] for network models? If so, we may want to place this information in a more structured hierarchy /capabilities/guest/devices/net/models/. Either way, SDL support isn't part of the device model so it would probably make sense to place it directly under 'features' IMO. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] RFC New virDomainBlockPull API family to libvirt
: pointer to domain object * @path: fully-qualified filename of disk * @info: pointer to a virDomainBlockPullInfo structure * @flags: currently unused, for future extension * * Request progress information on a block pull operation that has been started * with virDomainBlockPull(). If an operation is active for the given * parameters, @info will be updated with the current progress. * * Returns -1 in case of failure, 0 when successful. */ int virDomainGetBlockPullInfo(virDomainPtr dom, const char *path, virDomainBlockPullInfoPtr info, unsigned int flags); /** * virConnectDomainEventBlockPullStatus: * * The final status of a virDomainBlockPull() operation */ typedef enum { VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0, VIR_DOMAIN_BLOCK_PULL_FAILED = 1, } virConnectDomainEventBlockPullStatus; /** * virConnectDomainEventBlockPullCallback: * @conn: connection object * @dom: domain on which the event occurred * @path: fully-qualified filename of the affected disk * @status: final status of the operation (virConnectDomainEventBlockPullStatus * * The callback signature to use when registering for an event of type * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() */ typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, virDomainPtr dom, const char *path, int status, void *opaque); -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] RFC New virDomainBlockPull API family to libvirt
On 07/13/2011 08:04 PM, Daniel Veillard wrote: On Wed, Jul 13, 2011 at 03:46:30PM -0500, Adam Litke wrote: Unfortunately, after committing the blockPull API to libvirt, the qemu community decided to change the API somewhat and we needed to revert the libvirt implementation. Now that the qemu API is settling out again, I would like to propose an updated libvirt public API which has required only a minor set of changes to sync back up to the qemu API. Summary of changes: - Qemu dropped incremental streaming so remove libvirt incremental BlockPull() API - Rename virDomainBlockPullAll() to virDomainBlockPull() - Changes required to qemu monitor handlers for changed command names Okay. Currently, qemu block device streaming completely flattens a disk image's backing file chain. Consider the following chain: A-B-C where C is a leaf image that is backed by B and B is backed by A. The current disk streaming command will produce an independent image C with no backing file. Future versions of qemu block streaming may support an option to specify a new base image from the current chain. For example: stream --backing_file B C would pull all blocks that are only in A to produce the chain: B-C (thus eliminating a dependency on A but maintaining B as a backing image. Do we want to create room in the BlockPull API to support this advanced usage in the future? If so, then a new parameter must be added to BlockPull: const char *backing_path. Even if we extend the API in this manner, the initial implementation will not support it because qemu will not support it immediately, and libvirt is missing core functionality to support it (no public enumeration of the disk backing file chain). Thoughts? My own preference would rather be to defer this, and provide a separate API when QEmu implementation comes. The problem is that the single backing store file might not be sufficient to fully support what may come if QEmu adds this and as you pointed out we aren't really ready for this on libvirt side either. Yes, I agree with you here. On the other hand I suspect that we are missing the mechanism to control the rate of the transfer in the new API, which is something which could be implemented in the old incremental mechanism, but not anymore. So I wonder if the virDomainBlockPull() shouldn't get an unsigned long bandwidth (to stay coherent with migration APIs) before the flags. I don't know if the support is ready in QEmu but I suspect that will be an important point, so if not present will be added :-) Hopefully Stefan can comment on any throttling mechanisms that might be added to the qemu implementation. It would be nice to have this feature built into the API to match the migration APIs, but if that is not feasible then we could always add it later. -- To help speed the provisioning process for large domains, new QED disks are created with backing to a template image. These disks are configured with copy on read such that blocks that are read from the backing file are copied to the new disk. This reduces I/O over a potentially costly path to the backing image. In such a configuration, there is a desire to remove the dependency on the backing image as the domain runs. To accomplish this, qemu will provide an interface to perform sequential copy on read operations during normal VM operation. Once all data has been copied, the disk image's link to the backing file is removed. The virDomainBlockPull API family brings this functionality to libvirt. virDomainBlockPull() instructs the hypervisor to stream the entire device in the background. Progress of this operation can be checked with the function virDomainBlockPullInfo(). An ongoing stream can be cancelled with virDomainBlockPullAbort(). An event (VIR_DOMAIN_EVENT_ID_BLOCK_PULL) will be emitted when a disk has been fully populated or if a BlockPull() operation was terminated due to an error. This event is useful to avoid polling on virDomainBlockPullInfo() for completion and could also be used by the security driver to revoke access to the backing file when it is no longer needed. /* * BlockPull API */ /* An iterator for initiating and monitoring block pull operations */ typedef unsigned long long virDomainBlockPullCursor; typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; struct _virDomainBlockPullInfo { /* * The following fields provide an indication of block pull progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. * To approximate progress, divide @cur by @end. */ virDomainBlockPullCursor cur; virDomainBlockPullCursor end; }; typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; /** * virDomainBlockPull: * @dom: pointer to domain object * @path: Fully-qualified
Re: [libvirt] RFC New virDomainBlockPull API family to libvirt
On 07/14/2011 04:21 AM, Jiri Denemark wrote: On Wed, Jul 13, 2011 at 15:46:30 -0500, Adam Litke wrote: ... /* * BlockPull API */ /* An iterator for initiating and monitoring block pull operations */ typedef unsigned long long virDomainBlockPullCursor; typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; struct _virDomainBlockPullInfo { /* * The following fields provide an indication of block pull progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. * To approximate progress, divide @cur by @end. */ virDomainBlockPullCursor cur; virDomainBlockPullCursor end; }; typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; ... /** * virDomainBlockPullAbort: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @flags: currently unused, for future extension * * Cancel a pull operation previously started by virDomainBlockPullAll(). * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags); /** * virDomainGetBlockPullInfo: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @info: pointer to a virDomainBlockPullInfo structure * @flags: currently unused, for future extension * * Request progress information on a block pull operation that has been started * with virDomainBlockPull(). If an operation is active for the given * parameters, @info will be updated with the current progress. * * Returns -1 in case of failure, 0 when successful. */ int virDomainGetBlockPullInfo(virDomainPtr dom, const char *path, virDomainBlockPullInfoPtr info, unsigned int flags); /** * virConnectDomainEventBlockPullStatus: * * The final status of a virDomainBlockPull() operation */ typedef enum { VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0, VIR_DOMAIN_BLOCK_PULL_FAILED = 1, } virConnectDomainEventBlockPullStatus; /** * virConnectDomainEventBlockPullCallback: * @conn: connection object * @dom: domain on which the event occurred * @path: fully-qualified filename of the affected disk * @status: final status of the operation (virConnectDomainEventBlockPullStatus * * The callback signature to use when registering for an event of type * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() */ typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, virDomainPtr dom, const char *path, int status, void *opaque); Could these managing functions and event callback become general and usable by other block operations such as block copy? Also I think it would be nice to make the virDomainBlockPullInfo more similar to virDomainJobInfo. I was thinking about something along the following lines: We've already discussed using a JobInfo-like structure in the past but the fields are not really appropriate for this operation. The number of blocks remaining is not available via the qemu API and it is a non-deterministic value that depends on other guest IO that is ongoing. typedef struct _virDomainBlockJobInfo virDomainBlockJobInfo; struct _virDomainBlockJobInfo { int job; /* BLOCK_PULL, BLOCK_COPY, ... */ int status; /* COMPLETED, FAILED, CANCELLED */ unsigned long long timeElapsed; unsigned long long total; unsigned long long processed; unsigned long long remaining; } int virDomainBlockJobAbort(virDomainPtr dom, const char *path, unsigned int flags); int virDomainGetBlockJobInfo(virDomainPtr dom, const char *path, virDomainBlockJobInfoPtr info, unsigned int flags); typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, virDomainPtr dom, const char *path, int job, int status); I doubt it's practical to support more than one block operation on a single disk at a time, since that sounds to me like asking for troubles and data corruption. But if someone thinks we should keep the door open for this, we can add 'job
Re: [libvirt] RFC New virDomainBlockPull API family to libvirt
On 07/14/2011 05:33 AM, Stefan Hajnoczi wrote: On Thu, Jul 14, 2011 at 11:19 AM, Jiri Denemark jdene...@redhat.com wrote: On Thu, Jul 14, 2011 at 10:58:31 +0100, Stefan Hajnoczi wrote: On Thu, Jul 14, 2011 at 10:21 AM, Jiri Denemark jdene...@redhat.com wrote: On Wed, Jul 13, 2011 at 15:46:30 -0500, Adam Litke wrote: ... /* * BlockPull API */ /* An iterator for initiating and monitoring block pull operations */ typedef unsigned long long virDomainBlockPullCursor; typedef struct _virDomainBlockPullInfo virDomainBlockPullInfo; struct _virDomainBlockPullInfo { /* * The following fields provide an indication of block pull progress. @cur * indicates the current position and will be between 0 and @end. @end is * the final cursor position for this operation and represents completion. * To approximate progress, divide @cur by @end. */ virDomainBlockPullCursor cur; virDomainBlockPullCursor end; }; typedef virDomainBlockPullInfo *virDomainBlockPullInfoPtr; ... /** * virDomainBlockPullAbort: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @flags: currently unused, for future extension * * Cancel a pull operation previously started by virDomainBlockPullAll(). * * Returns -1 in case of failure, 0 when successful. */ int virDomainBlockPullAbort(virDomainPtr dom, const char *path, unsigned int flags); /** * virDomainGetBlockPullInfo: * @dom: pointer to domain object * @path: fully-qualified filename of disk * @info: pointer to a virDomainBlockPullInfo structure * @flags: currently unused, for future extension * * Request progress information on a block pull operation that has been started * with virDomainBlockPull(). If an operation is active for the given * parameters, @info will be updated with the current progress. * * Returns -1 in case of failure, 0 when successful. */ int virDomainGetBlockPullInfo(virDomainPtr dom, const char *path, virDomainBlockPullInfoPtr info, unsigned int flags); /** * virConnectDomainEventBlockPullStatus: * * The final status of a virDomainBlockPull() operation */ typedef enum { VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0, VIR_DOMAIN_BLOCK_PULL_FAILED = 1, } virConnectDomainEventBlockPullStatus; /** * virConnectDomainEventBlockPullCallback: * @conn: connection object * @dom: domain on which the event occurred * @path: fully-qualified filename of the affected disk * @status: final status of the operation (virConnectDomainEventBlockPullStatus * * The callback signature to use when registering for an event of type * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() */ typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, virDomainPtr dom, const char *path, int status, void *opaque); Could these managing functions and event callback become general and usable by other block operations such as block copy? Hi Jiri, Live block copy will certainly be possible using the streaming API. Before you start streaming you need to use the snapshot_blkdev command to create the destination file with the source file as its backing image. You can then use the streaming API to copy data from the source file into the destination file. On completion the source file is no longer needed. Well, I'm not talking about using the same API for block copy or implementing block copy internally as block streaming. I'm talking about making GetInfo, Abort and event callback general to be usable not only for block streaming or block copy but also for other possible block operations in the future. The reason is that starting a block operation (streaming, copy, whatever) may need different parameters so they should be different APIs. But once the operation is started, we just need to know how far it got and we need the ability to abort the job. So this can share the same APIs for all operations. It doesn't make sense to require any block operation to provide their own set of managing APIs. It's analogous to virDomainSave, virDomainMigrate, virDomainCoreDump. They are all different jobs but all of them can be monitored and aborted using virDomainGetJobInfo and virDomainAbortJob. I understand. I can't comment on libvirt API specifics but yes, there's a similarity here and a chance we'll have other operations in the future too. I'm thinking things like compacting images, compressing
Re: [libvirt] RFC New virDomainBlockPull API family to libvirt
On 07/15/2011 05:39 AM, Stefan Hajnoczi wrote: On Thu, Jul 14, 2011 at 7:47 PM, Adam Litke a...@us.ibm.com wrote: On 07/13/2011 08:04 PM, Daniel Veillard wrote: On Wed, Jul 13, 2011 at 03:46:30PM -0500, Adam Litke wrote: Unfortunately, after committing the blockPull API to libvirt, the qemu community decided to change the API somewhat and we needed to revert the libvirt implementation. Now that the qemu API is settling out again, I would like to propose an updated libvirt public API which has required only a minor set of changes to sync back up to the qemu API. Summary of changes: - Qemu dropped incremental streaming so remove libvirt incremental BlockPull() API - Rename virDomainBlockPullAll() to virDomainBlockPull() - Changes required to qemu monitor handlers for changed command names Okay. snip On the other hand I suspect that we are missing the mechanism to control the rate of the transfer in the new API, which is something which could be implemented in the old incremental mechanism, but not anymore. So I wonder if the virDomainBlockPull() shouldn't get an unsigned long bandwidth (to stay coherent with migration APIs) before the flags. I don't know if the support is ready in QEmu but I suspect that will be an important point, so if not present will be added :-) Hopefully Stefan can comment on any throttling mechanisms that might be added to the qemu implementation. It would be nice to have this feature built into the API to match the migration APIs, but if that is not feasible then we could always add it later. If we follow the live migration API then a block_stream_set_speed command would be used. libvirt would issue it as a separate command immediately after starting the streaming operation. There is the window of time after streaming has started but before block_stream_set_speed has been called where no throttling takes place, but I don't think this is a practical problem. It also opens the possibility of allowing the user to change the rate limit value while the block streaming operation is active. In light of our decision to provide a generic BlockJobAbort/BlockJobInfo interface, should SetSpeed be generic too? virDomainBlockJobSetSpeed() or virDomainBlockPullSetSpeed() ? -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] RFC (V2) New virDomainBlockPull API family to libvirt
, unsigned int flags); /** * virConnectDomainEventBlockJobStatus: * * The final status of a block job */ typedef enum { VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0, VIR_DOMAIN_BLOCK_JOB_FAILED = 1, } virConnectDomainEventBlockJobStatus; /** * virConnectDomainEventBlockPullCallback: * @conn: connection object * @dom: domain on which the event occurred * @path: fully-qualified filename of the affected disk * @type: type of block job (virDomainBlockJobType) * @status: final status of the operation (virConnectDomainEventBlockPullStatus) * @opaque: callback context * * The callback signature to use when registering for an event of type * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny() */ typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn, virDomainPtr dom, const char *path, int type, int status, void *opaque); /** * virDomainBlockPull: * @dom: pointer to domain object * @path: Fully-qualified filename of disk * @bandwidth: (optional) specify copy bandwidth limit in Mbps * @flags: currently unused, for future extension * * Populate a disk image with data from its backing image. Once all data from * its backing image has been pulled, the disk no longer depends on a backing * image. This function pulls data for the entire device in the background. * Progress of the operation can be checked with virDomainGetBlockJobInfo() and * the operation can be aborted with virDomainBlockJobAbort(). When finished, * an asynchronous event is raised to indicate the final status. * * The maximum bandwidth (in Mbps) that will be used to do the copy can be * specified with the bandwidth parameter. If set to 0, libvirt will choose a * suitable default. Some hypervisors do not support this feature and will * return an error if bandwidth is not 0. * * Returns 0 if the operation has started, -1 on failure. */ int virDomainBlockPull(virDomainPtr dom, const char *path, unsigned long bandwidth, unsigned int flags); -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] RFC New virDomainBlockPull API family to libvirt
On 07/18/2011 09:35 AM, Stefan Hajnoczi wrote: On the other hand I suspect that we are missing the mechanism to control the rate of the transfer in the new API, which is something which could be implemented in the old incremental mechanism, but not anymore. So I wonder if the virDomainBlockPull() shouldn't get an unsigned long bandwidth (to stay coherent with migration APIs) before the flags. I don't know if the support is ready in QEmu but I suspect that will be an important point, so if not present will be added :-) Hopefully Stefan can comment on any throttling mechanisms that might be added to the qemu implementation. It would be nice to have this feature built into the API to match the migration APIs, but if that is not feasible then we could always add it later. If we follow the live migration API then a block_stream_set_speed command would be used. libvirt would issue it as a separate command immediately after starting the streaming operation. There is the window of time after streaming has started but before block_stream_set_speed has been called where no throttling takes place, but I don't think this is a practical problem. It also opens the possibility of allowing the user to change the rate limit value while the block streaming operation is active. In light of our decision to provide a generic BlockJobAbort/BlockJobInfo interface, should SetSpeed be generic too? virDomainBlockJobSetSpeed() or virDomainBlockPullSetSpeed() ? The block_stream_set_speed semantics allow the command to be used when no streaming operation is active. This can be used to set the speed without a window of time after creation and before set_speed is called. If we switch to block_job_set_speed then it is cleaner to restrict the command to work on active jobs only. This is because there is only one speed variable kept but there might be multiple job types (streaming, compaction, etc) which would need different settings. What do you think about this? I think the block_job_set_speed semantics seem reasonable to me. What is the method for querying the current speed setting? I would suggest extending the query-block-job command to include this information. In this case, I would extend virDomainBlockJobInfo to contain: /* The maximum allowable bandwidth for this job (MB/s) */ unsigned long bandwidth; block_job_set_speed --- Set maximum speed for a background block operation. This is a per-block device command that can only be issued when there is an active block job. Throttling can be disabled by setting the speed to 0. Arguments: - device: device name (json-string) - value: maximum speed, in bytes per second (json-int) Errors: NotSupported: job type does not support speed setting Example: - { execute: block_job_set_speed, arguments: { device: virtio0, value: 1024 } } Stefan -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RFC v3 3/6] Update XML Schema for new entries
On 07/18/2011 04:41 AM, Wen Congyang wrote: --- docs/schemas/domain.rng | 26 +- 1 files changed, 25 insertions(+), 1 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 8a4e3fe..5f8151d 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -388,6 +388,16 @@ ref name=cpushares/ /element /optional + optional +element name=period + ref name=cpuperiod/ +/element + /optional + optional +element name=quota + ref name=cpuquota/ +/element + /optional zeroOrMore element name=vcpupin attribute name=vcpu Yes, this is exactly the interface we are looking for. @@ -2401,7 +2411,21 @@ data type=unsignedInt param name=pattern[0-9]+/param /data - /define + /define + define name=cpuperiod +data type=unsignedLong + param name=pattern[0-9]+/param + param name=minInclusive1000/param + param name=maxInclusive100/param +/data + /define + define name=cpuquota +data type=long + param name=pattern-?[0-9]+/param + param name=maxInclusive18446744073709551/param + param name='minInclusive'-1/param +/data + /define define name=PortNumber data type=short param name=minInclusive-1/param -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RFC v3 5/6] qemu: Implement cfs_period and cfs_quota's modification
On 07/18/2011 04:42 AM, Wen Congyang wrote: @@ -5983,7 +6169,30 @@ out: goto cleanup; } -*nparams = 1; +if (*nparams 1) { +params[1].value.ul = period; +params[1].type = VIR_TYPED_PARAM_ULLONG; +if (virStrcpyStatic(params[1].field, cfs_period) == NULL) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +%s, +_(Field cfs_period too long for destination)); +goto cleanup; +} + +params[2].value.ul = quota; Possible buffer overflow if *nparams == 2 ... +params[2].type = VIR_TYPED_PARAM_LLONG; +if (virStrcpyStatic(params[2].field, cfs_quota) == NULL) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +%s, +_(Field cfs_quota too long for destination)); +goto cleanup; +} + +*nparams = 3; +} else { +*nparams = 1; +} + ret = 0; cleanup: -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v3 4/6] qemu: Implement period and quota tunable XML configuration and parsing
On 07/18/2011 04:42 AM, Wen Congyang wrote: +int qemuSetupCgroupForVcpu(struct qemud_driver *driver, virDomainObjPtr vm) +{ +virCgroupPtr cgroup = NULL; +virCgroupPtr cgroup_vcpu = NULL; +qemuDomainObjPrivatePtr priv = vm-privateData; +int rc; +unsigned int i; +unsigned long long period = vm-def-cputune.period; +long long quota = vm-def-cputune.quota; + +if (driver-cgroup == NULL) +return 0; /* Not supported, so claim success */ + +rc = virCgroupForDomain(driver-cgroup, vm-def-name, cgroup, 0); +if (rc != 0) { +virReportSystemError(-rc, + _(Unable to find cgroup for %s), + vm-def-name); +goto cleanup; +} + +if (priv-nvcpupids == 0 || priv-vcpupids[0] == vm-pid) { +/* If we does not know VCPU-PID mapping or all vcpu runs in the same + * thread, we can not control each vcpu. + */ +if (period || quota) { +if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { +if (qemuSetupCgroupVcpuBW(cgroup, period, quota) 0) +goto cleanup; +} +} +return 0; +} I found a problem above. In the case where we are controlling quota at the domain level cgroup we must multiply the user-specified quota by the number of vcpus in the domain in order to get the same performance as we would with per-vcpu cgroups. As written, the vm will be essentially capped at 1 vcpu worth of quota regardless of the number of vcpus. You will also have to apply this logic in reverse when reporting the scheduler statistics so that the quota number is a per-vcpu quantity. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v3 4/6] qemu: Implement period and quota tunable XML configuration and parsing
On 07/18/2011 07:46 PM, Wen Congyang wrote: At 07/19/2011 04:35 AM, Adam Litke Write: This is looking good to me. I am pleased to see that the global case is handled as expected when per-vcpu threads are not active. On 07/18/2011 04:42 AM, Wen Congyang wrote: snip +int qemuSetupCgroupForVcpu(struct qemud_driver *driver, virDomainObjPtr vm) +{ +virCgroupPtr cgroup = NULL; +virCgroupPtr cgroup_vcpu = NULL; +qemuDomainObjPrivatePtr priv = vm-privateData; +int rc; +unsigned int i; +unsigned long long period = vm-def-cputune.period; +long long quota = vm-def-cputune.quota; + +if (driver-cgroup == NULL) +return 0; /* Not supported, so claim success */ I just want to check: Is the above logic still valid? Failure to apply This logic is the same as the logic in the similar function qemuSetupCgroup(). Yes, I am aware. However, the introduction of cpu quotas is a major feature that defines the amount of computing capacity a domain has available. In my opinion, if a domain XML file specifies a quota then 'virsh create' should fail of that quota cannot be set up for any reason. a quota setting (0) could have fairly substantial implications for system performance. I am not convinced either way but I do think this point merits some discussion. + +rc = virCgroupForDomain(driver-cgroup, vm-def-name, cgroup, 0); +if (rc != 0) { +virReportSystemError(-rc, + _(Unable to find cgroup for %s), + vm-def-name); +goto cleanup; +} + +if (priv-nvcpupids == 0 || priv-vcpupids[0] == vm-pid) { +/* If we does not know VCPU-PID mapping or all vcpu runs in the same + * thread, we can not control each vcpu. + */ +if (period || quota) { +if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { +if (qemuSetupCgroupVcpuBW(cgroup, period, quota) 0) +goto cleanup; +} +} +return 0; +} + +for (i = 0; i priv-nvcpupids; i++) { +rc = virCgroupForVcpu(cgroup, i, cgroup_vcpu, 1); +if (rc 0) { +virReportSystemError(-rc, + _(Unable to create vcpu cgroup for %s(vcpu: +%d)), + vm-def-name, i); +goto cleanup; +} + +/* move the thread for vcpu to sub dir */ +rc = virCgroupAddTask(cgroup_vcpu, priv-vcpupids[i]); +if (rc 0) { +virReportSystemError(-rc, + _(unable to add vcpu %d task %d to cgroup), + i, priv-vcpupids[i]); +goto cleanup; +} + +if (period || quota) { +if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { +if (qemuSetupCgroupVcpuBW(cgroup_vcpu, period, quota) 0) +goto cleanup; +} +} + +virCgroupFree(cgroup_vcpu); +} + +virCgroupFree(cgroup_vcpu); +virCgroupFree(cgroup); +return 0; + +cleanup: +virCgroupFree(cgroup_vcpu); +if (cgroup) { +virCgroupRemove(cgroup); +virCgroupFree(cgroup); +} + +return -1; +} + int qemuRemoveCgroup(struct qemud_driver *driver, virDomainObjPtr vm, -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v3 4/6] qemu: Implement period and quota tunable XML configuration and parsing
On 07/18/2011 07:51 PM, Wen Congyang wrote: At 07/19/2011 04:59 AM, Adam Litke Write: On 07/18/2011 04:42 AM, Wen Congyang wrote: +int qemuSetupCgroupForVcpu(struct qemud_driver *driver, virDomainObjPtr vm) +{ +virCgroupPtr cgroup = NULL; +virCgroupPtr cgroup_vcpu = NULL; +qemuDomainObjPrivatePtr priv = vm-privateData; +int rc; +unsigned int i; +unsigned long long period = vm-def-cputune.period; +long long quota = vm-def-cputune.quota; + +if (driver-cgroup == NULL) +return 0; /* Not supported, so claim success */ + +rc = virCgroupForDomain(driver-cgroup, vm-def-name, cgroup, 0); +if (rc != 0) { +virReportSystemError(-rc, + _(Unable to find cgroup for %s), + vm-def-name); +goto cleanup; +} + +if (priv-nvcpupids == 0 || priv-vcpupids[0] == vm-pid) { +/* If we does not know VCPU-PID mapping or all vcpu runs in the same + * thread, we can not control each vcpu. + */ +if (period || quota) { +if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { +if (qemuSetupCgroupVcpuBW(cgroup, period, quota) 0) +goto cleanup; +} +} +return 0; +} I found a problem above. In the case where we are controlling quota at the domain level cgroup we must multiply the user-specified quota by the number of vcpus in the domain in order to get the same performance as we would with per-vcpu cgroups. As written, the vm will be essentially capped at 1 vcpu worth of quota regardless of the number of vcpus. You will also have to apply this logic in reverse when reporting the scheduler statistics so that the quota number is a per-vcpu quantity. When quota is 1000, and per-vcpu thread is not active, we can start vm successfully. When the per-vcpu thread is active, and the num of vcpu is more than 1, we can not start vm if we multiply the user-specified quota. It will confuse the user: sometimes the vm can be started, but sometimes the vm can not be started with the same configuration. I am not sure I understand what you mean. When vcpu threads are active, the patches work correctly. It is only when you disable vcpu threads that you need to multiply the quota by the number of vcpus (since you are now applying it globally). A 4 vcpu guest that is started using an emulator with vcpu threads active will get 4 times the cpu bandwidth as compared to starting the identical configuration using an emulator without vcpu threads. This is because you currently apply the same quota setting to the full process as you were applying to a single vcpu. I know that what I am asking for is confusing at first. The quota value in a domain XML may not match up with the value actually written to the cgroup filesystem. The same applies for the schedinfo API vs. cgroupfs. However, my suggestion will result in quotas that match user expectation. For a 4 vcpu guest with 50% cpu quota, it is more logical to set period=50,quota=25 without having to know if my qemu supports vcpu threads. For example, to limit a guests to 50% CPU we would have these settings (when period == 50): 1 VCPU2 VCPU4 VCPU8 VCPU VCPU-threads ON 25252525 VCPU-threads OFF 2550 100 200 With VCPU threads on, the value is applied to each VCPU whereas with VCPU threads off it is applied globally. This will yield roughly equivalent performance regardless of whether the underlying qemu process enables vcpu threads. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RESEND RFC v4 1/6] Introduce the function virCgroupForVcpu
Added Anthony to give him the opportunity to address the finer points of this one especially with respect to the qemu IO thread(s). This feature is really about capping the compute performance of a VM such that we get consistent top end performance. Yes, qemu has non-VCPU threads that this patch set doesn't govern, but that's the point. We are not attempting to throttle IO or device emulation with this feature. It's true that an IO-intensive guest may consume more host resources than a compute intensive guest, but they should still have equal top-end CPU performance when viewed from the guest's perspective. On 07/21/2011 05:09 AM, Daniel P. Berrange wrote: On Thu, Jul 21, 2011 at 10:08:03AM +0800, Wen Congyang wrote: Introduce the function virCgroupForVcpu() to create sub directory for each vcpu. I'm far from convinced that this is a good idea. Setting policies on individual VCPUs is giving the host admin a misleading illusion of control over individual guest VCPUs. The problem is that QEMU has many non-VCPU threads which consume non-trivial CPU time. CPU time generated by a CPU in the guest, does not neccessarily map to a VCPU thread in the host, but could instead go to a I/O thread or a display thread, etc. IMHO we should only be doing controls at the whole VM level here. Daniel -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RESEND RFC v4 5/6] qemu: Implement cfs_period and cfs_quota's modification
On 07/20/2011 09:11 PM, Wen Congyang wrote: +static int +qemuGetVcpusBWLive(virDomainObjPtr vm, virCgroupPtr cgroup, + unsigned long long *period, long long *quota) +{ +virCgroupPtr cgroup_vcpu = NULL; +qemuDomainObjPrivatePtr priv = NULL; +int rc; +int ret = -1; + +priv = vm-privateData; +if (priv-nvcpupids == 0 || priv-vcpupids[0] == vm-pid) { +/* We do not create sub dir for each vcpu */ +rc = qemuGetVcpuBWLive(cgroup, period, quota); +if (rc 0) +goto cleanup; + +if (*quota 0) +*quota /= vm-def-vcpus; +goto out; +} Are you sure the above is correct? Based on my earlier suggestion, quota is always specified as the amount of runtime afforded to a single vcpu. Hence, if you are changing quota to cover for all of a vm's vcpus, wouldn't you want to: *quota *= vm-def-vcpus; ? -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RESEND RFC v4 5/6] qemu: Implement cfs_period and cfs_quota's modification
On 07/21/2011 06:01 AM, Daniel P. Berrange wrote: On Thu, Jul 21, 2011 at 10:11:32AM +0800, Wen Congyang wrote: @@ -5851,7 +5953,6 @@ static int qemuSetSchedulerParametersFlags(virDomainPtr dom, virTypedParameterPtr param = params[i]; if (STREQ(param-field, cpu_shares)) { -int rc; if (param-type != VIR_TYPED_PARAM_ULLONG) { qemuReportError(VIR_ERR_INVALID_ARG, %s, _(invalid type for cpu_shares tunable, expected a 'ullong')); @@ -5870,19 +5971,47 @@ static int qemuSetSchedulerParametersFlags(virDomainPtr dom, } if (flags VIR_DOMAIN_AFFECT_CONFIG) { -persistentDef = virDomainObjGetPersistentDef(driver-caps, vm); -if (!persistentDef) { -qemuReportError(VIR_ERR_INTERNAL_ERROR, %s, -_(can't get persistentDef)); +vmdef-cputune.shares = params[i].value.ul; +} +} else if (STREQ(param-field, cfs_period)) { [snip] +} else if (STREQ(param-field, cfs_quota)) { In the XML file we now have cputune shares1024/shares period9/period quota0/quota /cputune But the schedinfo parameter are being named cpu_shares: 1024 cfs_period: 9 cfs_quota: 0 These new tunables should be named 'cpu_period' and 'cpu_quota' too. You mean 'cfs_period' and 'cfs_quota', right? The only problem I see with that naming (specifically for the quota) is that it implies a direct relationship to the tunable in cgroupfs. While true for 'cpu_shares', it is not true for quota since the proper setting to apply depends on the threading behavior of qemu. +static int +qemuGetVcpusBWLive(virDomainObjPtr vm, virCgroupPtr cgroup, + unsigned long long *period, long long *quota) +{ +virCgroupPtr cgroup_vcpu = NULL; +qemuDomainObjPrivatePtr priv = NULL; +int rc; +int ret = -1; + +priv = vm-privateData; +if (priv-nvcpupids == 0 || priv-vcpupids[0] == vm-pid) { +/* We do not create sub dir for each vcpu */ +rc = qemuGetVcpuBWLive(cgroup, period, quota); +if (rc 0) +goto cleanup; + +if (*quota 0) +*quota /= vm-def-vcpus; +goto out; +} + +/* get period and quota for vcpu0 */ +rc = virCgroupForVcpu(cgroup, 0, cgroup_vcpu, 0); +if (!cgroup_vcpu) { +virReportSystemError(-rc, + _(Unable to find vcpu cgroup for %s(vcpu: 0)), + vm-def-name); +goto cleanup; +} + +rc = qemuGetVcpuBWLive(cgroup_vcpu, period, quota); +if (rc 0) +goto cleanup; This is also bad IMHO, giving different semantics for the operation of the API, depending on whether the QEMU impl has VCPUs threads or not. Again this just says to me that we need this to apply at the VM level not the VCPU level. IMO, this firms up the semantics of the libvirt API. As these patches are written, the libvirt semantics are: Apply cpu capping to this domain. Period is the measurement interval. Quota is the amount of time each vcpu may run within the period. We are able to insulate the users from details of the underlying qemu -- whether it supports threads or not. Another reason a per-vcpu quota setting is more desirable than a global setting is seen when changing the number of vcpus assigned to a domain. Each time you change the vcpu count, you would have to change the quota number too. Imagine all of the complaints from users when they increase the vcpu count and find their domain actually performs worse. If we really do need finer per-VCPU controls, in addition to a per-VM control, I think that should likely be done as a separate tunable, or separate CPU eg, we could have 2 sets of tunables, one that applies to the VM and the second set that adds further controls to the VCPUs. How would this behave when qemu doesn't support vcpu threads? We'd either have to throw an error, or just ignore the vcpu_ settings. either way, you're exposing users to qemu internals. They would need to change their domain.xml files when moving from a multi-threaded qemu to a single-threaded qemu. cputune shares1024/shares period9/period quota0/quota vcpu_shares1024/vcpu_shares vcpu_period9/vcpu_period vcpu_quota0/vcpu_quota /cputune I actually do like the vcpu_* naming because it makes two things more clear: Units apply at the vcpu level, and these stats don't necessarily map directly to cgroupfs. However, I would still maintain that the current logic should be used for applying the settings (per-vcpu if supported, multiply by nvcpus and apply globally if vcpu threads are not supported). -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https
Re: [libvirt] [PATCH RESEND RFC v4 1/6] Introduce the function virCgroupForVcpu
On 07/21/2011 08:34 AM, Daniel P. Berrange wrote: On Thu, Jul 21, 2011 at 07:54:05AM -0500, Adam Litke wrote: Added Anthony to give him the opportunity to address the finer points of this one especially with respect to the qemu IO thread(s). This feature is really about capping the compute performance of a VM such that we get consistent top end performance. Yes, qemu has non-VCPU threads that this patch set doesn't govern, but that's the point. We are not attempting to throttle IO or device emulation with this feature. It's true that an IO-intensive guest may consume more host resources than a compute intensive guest, but they should still have equal top-end CPU performance when viewed from the guest's perspective. I could be mis-understanding, what you're trying to achieve, here, so perhaps we should consider an example. From your example, it's clear to me that you understand the use case well. - A machine has 4 physical CPUs - There are 4 guests on the machine - Each guest has 2 virtual CPUs So we've overcommit the host CPU resources x2 here. Lets say that we want to use this feature to ensure consistent top end performance of every guest, splitting the host pCPUs resources evenly across all guests, so each guest is ensured 1 pCPU worth of CPU time overall. This patch lets you do this by assigning caps per VCPU. So in this example, each VCPU cgroup would have to be configured to cap the VCPUs at 50% of a single pCPU. This leaves the other QEMU threads uncapped / unaccounted for. If any one guest causes non-trivial compute load in a non-VCPU thread, this can/will impact the top-end compute performance of all the other guests on the machine. If we did caps per VM, then you could set the VM cgroup such that the VM as a whole had 100% of a single pCPU. If a guest is 100% compute bound, it can use its full 100% of a pCPU allocation in vCPU threads. If any other guest is causing CPU time in a non-VCPU thread, it cannot impact the top end compute performance of VCPU threads in the other guests. A per-VM cap would, however, mean a guest with 2 vCPUs could have unequal scheduling, where one vCPU claimed 75% of the pCPU and the othe vCPU got left with only 25%. So AFAICT, per-VM cgroups is better for ensuring top end compute performance of a guest as a whole, but per-VCPU cgroups can ensure consistent top end performance across vCPUs within a guest. IMHO, per-VM cgroups is the more useful because it is the only way to stop guests impacting each other, but there could be additional benefits of *also* have per-VCPU cgroups if you want to ensure fairness of top-end performance across vCPUs inside a single VM. What this says to me is that per-VM cgroups _in_addition_to_ per-vcpu cgroups is the _most_ useful situation. Since I can't think of any cases where someone would want per-vm and not per-vcpu, how about we always do both when supported. We can still use one pair of tunables (period and quota) and try to do the right thing. For example: vcpus2/vcpus cputune period50/period quota25/quota /cputune Would have the following behavior for qemu-kvm (vcpu threads) Global VM cgroup: cfs_period:50 cfs_quota:50 Each vcpu cgroup: cfs_period:50 cfs_quota:25 and this behavior for qemu with no vcpu threads Global VM cgroup: cfs_period:50 cfs_quota:50 It's true that IO could still throw off the scheduling balance somewhat among vcpus _within_ a VM, but this effect would be confined within the vm itself. Best of both worlds? -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH RESEND RFC v4 1/6] Introduce the function virCgroupForVcpu
On 07/21/2011 09:29 AM, Daniel P. Berrange wrote: On Thu, Jul 21, 2011 at 08:49:28AM -0500, Adam Litke wrote: On 07/21/2011 08:34 AM, Daniel P. Berrange wrote: On Thu, Jul 21, 2011 at 07:54:05AM -0500, Adam Litke wrote: Added Anthony to give him the opportunity to address the finer points of this one especially with respect to the qemu IO thread(s). This feature is really about capping the compute performance of a VM such that we get consistent top end performance. Yes, qemu has non-VCPU threads that this patch set doesn't govern, but that's the point. We are not attempting to throttle IO or device emulation with this feature. It's true that an IO-intensive guest may consume more host resources than a compute intensive guest, but they should still have equal top-end CPU performance when viewed from the guest's perspective. I could be mis-understanding, what you're trying to achieve, here, so perhaps we should consider an example. From your example, it's clear to me that you understand the use case well. - A machine has 4 physical CPUs - There are 4 guests on the machine - Each guest has 2 virtual CPUs So we've overcommit the host CPU resources x2 here. Lets say that we want to use this feature to ensure consistent top end performance of every guest, splitting the host pCPUs resources evenly across all guests, so each guest is ensured 1 pCPU worth of CPU time overall. This patch lets you do this by assigning caps per VCPU. So in this example, each VCPU cgroup would have to be configured to cap the VCPUs at 50% of a single pCPU. This leaves the other QEMU threads uncapped / unaccounted for. If any one guest causes non-trivial compute load in a non-VCPU thread, this can/will impact the top-end compute performance of all the other guests on the machine. If we did caps per VM, then you could set the VM cgroup such that the VM as a whole had 100% of a single pCPU. If a guest is 100% compute bound, it can use its full 100% of a pCPU allocation in vCPU threads. If any other guest is causing CPU time in a non-VCPU thread, it cannot impact the top end compute performance of VCPU threads in the other guests. A per-VM cap would, however, mean a guest with 2 vCPUs could have unequal scheduling, where one vCPU claimed 75% of the pCPU and the othe vCPU got left with only 25%. So AFAICT, per-VM cgroups is better for ensuring top end compute performance of a guest as a whole, but per-VCPU cgroups can ensure consistent top end performance across vCPUs within a guest. IMHO, per-VM cgroups is the more useful because it is the only way to stop guests impacting each other, but there could be additional benefits of *also* have per-VCPU cgroups if you want to ensure fairness of top-end performance across vCPUs inside a single VM. What this says to me is that per-VM cgroups _in_addition_to_ per-vcpu cgroups is the _most_ useful situation. Since I can't think of any cases where someone would want per-vm and not per-vcpu, how about we always do both when supported. We can still use one pair of tunables (period and quota) and try to do the right thing. For example: vcpus2/vcpus cputune period50/period quota25/quota /cputune Would have the following behavior for qemu-kvm (vcpu threads) Global VM cgroup: cfs_period:50 cfs_quota:50 Each vcpu cgroup: cfs_period:50 cfs_quota:25 and this behavior for qemu with no vcpu threads So, whatever quota value is in the XML, you would multiply that by the number of vCPUS and use it to set the VM quota value ? Yep. I'm trying to think if there is ever a case where you don't want the VM to be a plain multiple of the VCPU value, but I can't think of one. So only the real discussion point here, is whether the quota value in the XML, is treated as a per-VM value, or a per-VCPU value. I think it has to be per-VCPU. Otherwise the user will have to remember to do the multiplication themselves. If they forget to do this they will get a nasty performance surprise. cpu_shares is treated as a per-VM value, period doesn't matter but cpu_quota would be a per-VCPU value, multiplied to get a per-VM value when needed. I still find this mis-match rather wierd to be fair. Yes, this is unfortunate. But cpu_shares is a comparative value whereas quota is quantitative. In the future we could apply 'shares' at the vcpu level too. In that case we'd just pick some arbitrary number and apply it to each vcpu cgroup. So current behaviour if vCPU threads set quota in vCPU group else set nVCPUs * quota in VM group Would change to set nVCPUs * quota in VM group if vCPU threads set quota in vCPU group ? Yes. We need to remember to update the VM cgroup if we change the number of vCPUs on a running guest of course When can this happen? Does libvirt support cpu hotplug? -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir
[libvirt] RFC (V2) New virDomainBlockPull API family to libvirt
Here are the patches to implement the BlockPull/BlockJob API as discussed and agreed to. I am testing with a python script (included for completeness as the final patch). The qemu monitor interface is not expected to change in the future. Stefan is planning to submit placeholder commands for upstream qemu until the generic streaming support is implemented. Changes since V1: - Make virDomainBlockPullAbort() and virDomainGetBlockPullInfo() into a generic BlockJob interface. - Added virDomainBlockJobSetSpeed() - Rename VIR_DOMAIN_EVENT_ID_BLOCK_PULL event to fit into block job API - Add bandwidth argument to virDomainBlockPull() Summary of changes since first generation patch series: - Qemu dropped incremental streaming so remove libvirt incremental BlockPull() API - Rename virDomainBlockPullAll() to virDomainBlockPull() - Changes required to qemu monitor handlers for changed command names -- To help speed the provisioning process for large domains, new QED disks are created with backing to a template image. These disks are configured with copy on read such that blocks that are read from the backing file are copied to the new disk. This reduces I/O over a potentially costly path to the backing image. In such a configuration, there is a desire to remove the dependency on the backing image as the domain runs. To accomplish this, qemu will provide an interface to perform sequential copy on read operations during normal VM operation. Once all data has been copied, the disk image's link to the backing file is removed. The virDomainBlockPull API family brings this functionality to libvirt. virDomainBlockPull() instructs the hypervisor to stream the entire device in the background. Progress of this operation can be checked with the function virDomainBlockJobInfo(). An ongoing stream can be cancelled with virDomainBlockJobAbort(). virDomainBlockJobSetSpeed() allows you to limit the bandwidth that the operation may consume. An event (VIR_DOMAIN_EVENT_ID_BLOCK_JOB) will be emitted when a disk has been fully populated or if a BlockPull() operation was terminated due to an error. This event is useful to avoid polling on virDomainBlockJobInfo() for completion and could also be used by the security driver to revoke access to the backing file when it is no longer needed. make check: PASS make syntax-check: PASS make -C tests valgrind: PASS [PATCH 1/8] Add new API virDomainBlockPull* to headers [PATCH 2/8] virDomainBlockPull: Implement the main entry points [PATCH 3/8] Add virDomainBlockPull support to the remote driver [PATCH 4/8] Implement virDomainBlockPull for the qemu driver [PATCH 5/8] Enable the virDomainBlockPull API in virsh [PATCH 6/8] Enable virDomainBlockPull in the python API. [PATCH 7/8] Asynchronous event for BlockJob completion [PATCH 8/8] Test the blockJob/BlockPull API -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 5/8] Enable the virDomainBlockPull API in virsh
Define two new virsh commands: * blockpull: Initiate a blockPull for the given disk * blockjob: Retrieve progress info, modify speed, and cancel active block jobs Share print_job_progress() with the migration code. * tools/virsh.c: implement the new commands Signed-off-by: Adam Litke a...@us.ibm.com --- tools/virsh.c | 135 +++-- 1 files changed, 131 insertions(+), 4 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index a6803d8..2b590d3 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -4527,7 +4527,8 @@ out_sig: } static void -print_job_progress(unsigned long long remaining, unsigned long long total) +print_job_progress(const char *label, unsigned long long remaining, + unsigned long long total) { int progress; @@ -4547,7 +4548,7 @@ print_job_progress(unsigned long long remaining, unsigned long long total) } } -fprintf(stderr, \rMigration: [%3d %%], progress); +fprintf(stderr, \r%s: [%3d %%], label, progress); } static bool @@ -4632,7 +4633,7 @@ repoll: functionReturn = true; if (verbose) { /* print [100 %] */ -print_job_progress(0, 1); +print_job_progress(Migration, 0, 1); } } else functionReturn = false; @@ -4668,7 +4669,8 @@ repoll: ret = virDomainGetJobInfo(dom, jobinfo); pthread_sigmask(SIG_SETMASK, oldsigmask, NULL); if (ret == 0) -print_job_progress(jobinfo.dataRemaining, jobinfo.dataTotal); +print_job_progress(Migration, jobinfo.dataRemaining, + jobinfo.dataTotal); } } @@ -4771,6 +4773,129 @@ done: return ret; } +typedef enum { +VSH_CMD_BLOCK_JOB_ABORT = 0, +VSH_CMD_BLOCK_JOB_INFO = 1, +VSH_CMD_BLOCK_JOB_SPEED = 2, +VSH_CMD_BLOCK_JOB_PULL = 3, +} VSH_CMD_BLOCK_JOB_MODE; + +static int +blockJobImpl(vshControl *ctl, const vshCmd *cmd, + virDomainBlockJobInfoPtr info, int mode) +{ +virDomainPtr dom = NULL; +const char *name, *path; +unsigned long bandwidth = 0; +int ret = -1; + +if (!vshConnectionUsability(ctl, ctl-conn)) +goto out; + +if (!(dom = vshCommandOptDomain(ctl, cmd, name))) +goto out; + +if (vshCommandOptString(cmd, path, path) 0) +goto out; + +if (vshCommandOptUL(cmd, bandwidth, bandwidth) 0) +goto out; + +if (mode == VSH_CMD_BLOCK_JOB_ABORT) +ret = virDomainBlockJobAbort(dom, path, 0); +else if (mode == VSH_CMD_BLOCK_JOB_INFO) +ret = virDomainGetBlockJobInfo(dom, path, info, 0); +else if (mode == VSH_CMD_BLOCK_JOB_SPEED) +ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0); +else if (mode == VSH_CMD_BLOCK_JOB_PULL) +ret = virDomainBlockPull(dom, path, bandwidth, 0); + +out: +virDomainFree(dom); +return ret; +} + +/* + * blockpull command + */ +static const vshCmdInfo info_block_pull[] = { +{help, N_(Populate a disk from its backing image.)}, +{desc, N_(Populate a disk from its backing image.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_pull[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{bandwidth, VSH_OT_DATA, VSH_OFLAG_NONE, N_(Bandwidth limit in MB/s)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockPull(vshControl *ctl, const vshCmd *cmd) +{ +if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL) != 0) +return false; +return true; +} + +/* + * blockjobinfo command + */ +static const vshCmdInfo info_block_job[] = { +{help, N_(Manage active block operations.)}, +{desc, N_(Manage active block operations.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_block_job[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{path, VSH_OT_DATA, VSH_OFLAG_REQ, N_(Fully-qualified path of disk)}, +{abort, VSH_OT_BOOL, VSH_OFLAG_NONE, N_(Abort the active job on the speficied disk)}, +{info, VSH_OT_BOOL, VSH_OFLAG_NONE, N_(Get active job information for the specified disk)}, +{bandwidth, VSH_OT_DATA, VSH_OFLAG_NONE, N_(Set the Bandwidth limit in MB/s)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdBlockJob(vshControl *ctl, const vshCmd *cmd) +{ +int mode; +virDomainBlockJobInfo info; +const char *type; +int ret; + +if (vshCommandOptBool (cmd, abort)) { +mode = VSH_CMD_BLOCK_JOB_ABORT; +} else if (vshCommandOptBool (cmd, info)) { +mode = VSH_CMD_BLOCK_JOB_INFO; +} else if (vshCommandOptBool (cmd, bandwidth)) { +mode = VSH_CMD_BLOCK_JOB_SPEED; +} else { +vshError(ctl, %s, + _(One of --abort, --info, or --bandwidth is required
[libvirt] [PATCH 1/8] Add new API virDomainBlockPull* to headers
Set up the types for the block pull functions and insert them into the virDriver structure definition. Symbols are exported in this patch to prevent documentation compile failures. * include/libvirt/libvirt.h.in: new API * src/driver.h: add the new entry to the driver structure * python/generator.py: fix compiler errors, the actual python bindings are implemented later * src/libvirt_public.syms: export symbols * docs/apibuild.py: Extend 'unsigned long' parameter exception to this API Signed-off-by: Adam Litke a...@us.ibm.com --- docs/apibuild.py |7 - include/libvirt/libvirt.h.in | 44 ++ python/generator.py |2 + src/driver.h | 24 ++ src/libvirt_public.syms |7 ++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/docs/apibuild.py b/docs/apibuild.py index 6e35cfb..53b3421 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -1641,7 +1641,9 @@ class CParser: virDomainMigrateSetMaxSpeed: (False, (bandwidth)), virDomainSetMaxMemory : (False, (memory)), virDomainSetMemory : (False, (memory)), -virDomainSetMemoryFlags: (False, (memory)) } +virDomainSetMemoryFlags: (False, (memory)), +virDomainBlockJobSetSpeed : (False, (bandwidth)), +virDomainBlockPull : (False, (bandwidth)) } def checkLongLegacyFunction(self, name, return_type, signature): if long in return_type and long long not in return_type: @@ -1667,7 +1669,8 @@ class CParser: # [unsigned] long long long_legacy_struct_fields = \ { _virDomainInfo : (maxMem, memory), -_virNodeInfo : (memory) } +_virNodeInfo : (memory), +_virDomainBlockJobInfo : (bandwidth) } def checkLongLegacyStruct(self, name, fields): for field in fields: diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 607b5bc..23947c7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1375,6 +1375,50 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags); /* + * BlockJob API + */ + +/** + * virDomainBlockJobType: + * + * VIR_DOMAIN_BLOCK_JOB_TYPE_PULL: Block Pull (virDomainBlockPull) + */ +typedef enum { +VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN = 0, +VIR_DOMAIN_BLOCK_JOB_TYPE_PULL = 1, +} virDomainBlockJobType; + +/* An iterator for monitoring block job operations */ +typedef unsigned long long virDomainBlockJobCursor; + +typedef struct _virDomainBlockJobInfo virDomainBlockJobInfo; +struct _virDomainBlockJobInfo { +virDomainBlockJobType type; +unsigned long bandwidth; +/* + * The following fields provide an indication of block job progress. @cur + * indicates the current position and will be between 0 and @end. @end is + * the final cursor position for this operation and represents completion. + * To approximate progress, divide @cur by @end. + */ +virDomainBlockJobCursor cur; +virDomainBlockJobCursor end; +}; +typedef virDomainBlockJobInfo *virDomainBlockJobInfoPtr; + +int virDomainBlockJobAbort(virDomainPtr dom, const char *path, + unsigned int flags); +int virDomainGetBlockJobInfo(virDomainPtr dom, const char *path, + virDomainBlockJobInfoPtr info, + unsigned int flags); +intvirDomainBlockJobSetSpeed(virDomainPtr dom, const char *path, + unsigned long bandwidth, unsigned int flags); + +int virDomainBlockPull(virDomainPtr dom, const char *path, + unsigned long bandwidth, unsigned int flags); + + +/* * NUMA support */ diff --git a/python/generator.py b/python/generator.py index 1cb82f5..b25c74e 100755 --- a/python/generator.py +++ b/python/generator.py @@ -186,6 +186,7 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, +'virDomainGetBlockJobInfo', ] skipped_modules = { @@ -202,6 +203,7 @@ skipped_types = { 'virStreamEventCallback': No function types in python, 'virEventHandleCallback': No function types in python, 'virEventTimeoutCallback': No function types in python, + 'virDomainBlockJobInfoPtr': Not implemented yet, } ### diff --git a/src/driver.h b/src/driver.h index 9d0d3de..776bb7f 100644 --- a/src/driver.h +++ b/src/driver.h @@ -661,6 +661,26 @@ typedef int unsigned long flags, int cancelled); + +typedef int +(*virDrvDomainBlockJobAbort)(virDomainPtr dom, const char *path, + unsigned int flags); + +typedef
[libvirt] [PATCH 4/8] Implement virDomainBlockPull for the qemu driver
The virDomainBlockPull* family of commands are enabled by the following HMP/QMP commands: 'block_stream', 'block_job_cancel', 'info block-jobs' / 'query-block-jobs', and 'block_job_set_speed'. * src/qemu/qemu_driver.c src/qemu/qemu_monitor_text.[ch]: implement disk streaming by using the proper qemu monitor commands. * src/qemu/qemu_monitor_json.[ch]: implement commands using the qmp monitor Signed-off-by: Adam Litke a...@us.ibm.com --- src/qemu/qemu_driver.c | 113 + src/qemu/qemu_monitor.c | 18 + src/qemu/qemu_monitor.h | 13 src/qemu/qemu_monitor_json.c | 147 ++ src/qemu/qemu_monitor_json.h |5 ++ src/qemu/qemu_monitor_text.c | 162 ++ src/qemu/qemu_monitor_text.h |6 ++ 7 files changed, 464 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8870e33..0f556a9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8493,6 +8493,115 @@ cleanup: return ret; } +static const char * +qemuDiskPathToAlias(virDomainObjPtr vm, const char *path) { +int i; +char *ret = NULL; + +for (i = 0 ; i vm-def-ndisks ; i++) { +virDomainDiskDefPtr disk = vm-def-disks[i]; + +if (disk-type != VIR_DOMAIN_DISK_TYPE_BLOCK +disk-type != VIR_DOMAIN_DISK_TYPE_FILE) +continue; + +if (disk-src != NULL STREQ(disk-src, path)) { +if (virAsprintf(ret, drive-%s, disk-info.alias) 0) { +virReportOOMError(); +return NULL; +} +break; +} +} + +if (!ret) { +qemuReportError(VIR_ERR_INVALID_ARG, +%s, _(No device found for specified path)); +} +return ret; +} + +static int +qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, + unsigned long bandwidth, virDomainBlockJobInfoPtr info, + int mode) +{ +struct qemud_driver *driver = dom-conn-privateData; +virDomainObjPtr vm = NULL; +qemuDomainObjPrivatePtr priv; +char uuidstr[VIR_UUID_STRING_BUFLEN]; +const char *device = NULL; +int ret = -1; + +qemuDriverLock(driver); +virUUIDFormat(dom-uuid, uuidstr); +vm = virDomainFindByUUID(driver-domains, dom-uuid); +if (!vm) { +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +device = qemuDiskPathToAlias(vm, path); +if (!device) { +goto cleanup; +} + +if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) 0) +goto cleanup; +ignore_value(qemuDomainObjEnterMonitorWithDriver(driver, vm)); +priv = vm-privateData; +ret = qemuMonitorBlockJob(priv-mon, device, bandwidth, info, mode); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(driver, vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +VIR_FREE(device); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + +static int +qemuDomainBlockJobAbort(virDomainPtr dom, const char *path, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockJobImpl(dom, path, 0, NULL, BLOCK_JOB_ABORT); +} + +static int +qemuDomainGetBlockJobInfo(virDomainPtr dom, const char *path, + virDomainBlockJobInfoPtr info, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockJobImpl(dom, path, 0, info, BLOCK_JOB_INFO); +} + +static int +qemuDomainBlockJobSetSpeed(virDomainPtr dom, const char *path, + unsigned long bandwidth, unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockJobImpl(dom, path, bandwidth, NULL, BLOCK_JOB_SPEED); +} + +static int +qemuDomainBlockPull(virDomainPtr dom, const char *path, unsigned long bandwidth, +unsigned int flags) +{ +virCheckFlags(0, -1); +return qemuDomainBlockJobImpl(dom, path, bandwidth, NULL, BLOCK_JOB_PULL); +} static virDriver qemuDriver = { .no = VIR_DRV_QEMU, @@ -8619,6 +8728,10 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainBlockJobAbort = qemuDomainBlockJobAbort, /* 0.9.4 */ +.domainGetBlockJobInfo = qemuDomainGetBlockJobInfo, /* 0.9.4 */ +.domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ +.domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu
[libvirt] [PATCH 7/8] Asynchronous event for BlockJob completion
When an operation started by virDomainBlockPull completes (either with success or with failure), raise an event to indicate the final status. This allows an API user to avoid polling on virDomainGetBlockJobInfo if they would prefer to use the event mechanism. * daemon/remote.c: Dispatch events to client * include/libvirt/libvirt.h.in: Define event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle the new event * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block_stream completion and emit a libvirt block pull event * src/remote/remote_driver.c: Receive and dispatch events to application * src/remote/remote_protocol.x: Wire protocol definition for the event * src/remote_protocol-structs: structure definitions for protocol verification * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_STREAM_COMPLETED event from QEMU monitor Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 31 ++ include/libvirt/libvirt.h.in | 29 + python/libvirt-override-virConnect.py | 12 +++ python/libvirt-override.c | 52 ++ src/conf/domain_event.c | 56 + src/conf/domain_event.h |9 +- src/libvirt_private.syms |2 + src/qemu/qemu_monitor.c | 13 +++ src/qemu/qemu_monitor.h | 10 ++ src/qemu/qemu_monitor_json.c | 40 +++ src/qemu/qemu_process.c | 31 ++ src/remote/remote_driver.c| 31 ++ src/remote/remote_protocol.x | 10 +- src/remote_protocol-structs |8 - 14 files changed, 331 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index b471abc..939044c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -339,6 +339,36 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int remoteRelayDomainEventBlockJob(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *path, + int type, + int status, + void *opaque) +{ +virNetServerClientPtr client = opaque; +remote_domain_event_block_job_msg data; + +if (!client) +return -1; + +VIR_DEBUG(Relaying domain block job event %s %d %s %i, %i, + dom-name, dom-id, path, type, status); + +/* build return data */ +memset(data, 0, sizeof data); +make_nonnull_domain(data.dom, dom); +data.path = (char*)path; +data.type = type; +data.status = status; + +remoteDispatchDomainEventSend(client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB, + (xdrproc_t)xdr_remote_domain_event_block_job_msg, data); + +return 0; +} + static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -373,6 +403,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), +VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 23947c7..d215655 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2735,6 +2735,34 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventBlockJobStatus: + * + * The final status of a virDomainBlockPullAll() operation + */ +typedef enum { +VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0, +VIR_DOMAIN_BLOCK_JOB_FAILED = 1, +} virConnectDomainEventBlockJobStatus; + +/** + * virConnectDomainEventBlockJobCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @path: fully-qualified filename of the affected disk + * @type: type of block job (virDomainBlockJobType) + * @status: final status of the operation (virConnectDomainEventBlockJobStatus) + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BLOCK_JOB with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn
[libvirt] [PATCH 2/8] virDomainBlockPull: Implement the main entry points
* src/libvirt.c: implement the main entry points Signed-off-by: Adam Litke a...@us.ibm.com --- src/libvirt.c | 226 + 1 files changed, 226 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 39e2041..5ca8c03 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -15417,3 +15417,229 @@ error: virDispatchError(conn); return -1; } + +/** + * virDomainBlockJobAbort: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @flags: currently unused, for future extension + * + * Cancel the active block job on the given disk. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockJobAbort(virDomainPtr dom, const char *path, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, flags=%x, path, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (conn-driver-domainBlockJobAbort) { +int ret; +ret = conn-driver-domainBlockJobAbort(dom, path, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainGetBlockJobInfo: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @info: pointer to a virDomainBlockJobInfo structure + * @flags: currently unused, for future extension + * + * Request block job information for the given disk. If an operation is active + * @info will be updated with the current progress. + * + * Returns -1 in case of failure, 0 when nothing found, 1 when info was found. + */ +int virDomainGetBlockJobInfo(virDomainPtr dom, const char *path, + virDomainBlockJobInfoPtr info, unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, info=%p, flags=%x, path, info, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (!info) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(info is NULL)); +goto error; +} + +if (conn-driver-domainGetBlockJobInfo) { +int ret; +ret = conn-driver-domainGetBlockJobInfo(dom, path, info, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockJobSetSpeed: + * @dom: pointer to domain object + * @path: fully-qualified filename of disk + * @bandwidth: specify bandwidth limit in Mbps + * @flags: currently unused, for future extension + * + * Set the maximimum allowable bandwidth that a block job may consume. If + * bandwidth is 0, the limit will revert to the hypervisor default. + * + * Returns -1 in case of failure, 0 when successful. + */ +int virDomainBlockJobSetSpeed(virDomainPtr dom, const char *path, + unsigned long bandwidth, unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, path=%p, bandwidth=%lu, flags=%x, + path, bandwidth, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +conn = dom-conn; + +if (dom-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +if (!path) { +virLibDomainError(VIR_ERR_INVALID_ARG, + _(path is NULL)); +goto error; +} + +if (conn-driver-domainBlockJobSetSpeed) { +int ret; +ret = conn-driver-domainBlockJobSetSpeed(dom, path, bandwidth, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} + +/** + * virDomainBlockPull: + * @dom: pointer to domain object + * @path: Fully-qualified filename of disk + * @bandwidth: (optional) specify copy bandwidth
[libvirt] [PATCH 6/8] Enable virDomainBlockPull in the python API.
virDomainGetBlockJobInfo requires manual override since it returns a custom type. * python/generator.py: reenable bindings for this entry point * python/libvirt-override-api.xml python/libvirt-override.c: manual overrides Signed-off-by: Adam Litke a...@us.ibm.com --- python/generator.py |2 +- python/libvirt-override-api.xml |7 +++ python/libvirt-override.c | 39 +++ 3 files changed, 47 insertions(+), 1 deletions(-) diff --git a/python/generator.py b/python/generator.py index b25c74e..d0d3ae6 100755 --- a/python/generator.py +++ b/python/generator.py @@ -186,7 +186,6 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ virConnectListDomains, -'virDomainGetBlockJobInfo', ] skipped_modules = { @@ -370,6 +369,7 @@ skip_impl = ( 'virDomainSendKey', 'virNodeGetCPUStats', 'virNodeGetMemoryStats', +'virDomainGetBlockJobInfo', ) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 01207d6..268f897 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -320,5 +320,12 @@ arg name='flags' type='unsigned int' info='flags, curently unused'/ return type='int' info=0 on success, -1 on error/ /function +function name='virDomainGetBlockJobInfo' file='python' + infoGet progress information for a block job/info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='path' type='const char *' info='Fully-qualified filename of disk'/ + arg name='flags' type='unsigned int' info='fine-tuning flags, currently unused, pass 0.'/ + return type='virDomainBlockJobInfo' info='A dictionary containing job information.' / +/function /symbols /api diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b713b6a..e89bc97 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -2413,6 +2413,44 @@ libvirt_virDomainGetJobInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { return(py_retval); } +static PyObject * +libvirt_virDomainGetBlockJobInfo(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +virDomainPtr domain; +PyObject *pyobj_domain; +const char *path; +unsigned int flags; +virDomainBlockJobInfo info; +int c_ret; +PyObject *ret; + +if (!PyArg_ParseTuple(args, (char *)Ozi:virDomainGetBlockJobInfo, + pyobj_domain, path, flags)) +return(NULL); +domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + +LIBVIRT_BEGIN_ALLOW_THREADS; +c_ret = virDomainGetBlockJobInfo(domain, path, info, flags); +LIBVIRT_END_ALLOW_THREADS; + +if (c_ret != 1) +return VIR_PY_NONE; + +if ((ret = PyDict_New()) == NULL) +return VIR_PY_NONE; + +PyDict_SetItem(ret, libvirt_constcharPtrWrap(type), + libvirt_intWrap(info.type)); +PyDict_SetItem(ret, libvirt_constcharPtrWrap(bandwidth), + libvirt_ulongWrap(info.bandwidth)); +PyDict_SetItem(ret, libvirt_constcharPtrWrap(cur), + libvirt_ulonglongWrap(info.cur)); +PyDict_SetItem(ret, libvirt_constcharPtrWrap(end), + libvirt_ulonglongWrap(info.end)); + +return ret; +} /*** * Helper functions to avoid importing modules @@ -3872,6 +3910,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) virDomainGetJobInfo, libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) virDomainSnapshotListNames, libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) virDomainRevertToSnapshot, libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, +{(char *) virDomainGetBlockJobInfo, libvirt_virDomainGetBlockJobInfo, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- 1.7.3 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/8] Add virDomainBlockPull support to the remote driver
The generator can handle everything except virDomainGetBlockJobInfo(). * src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side for virDomainGetBlockJobInfo. * src/remote_protocol-structs: structure definitions for protocol verification * src/rpc/gendispatch.pl: Permit some unsigned long parameters Signed-off-by: Adam Litke a...@us.ibm.com --- daemon/remote.c | 42 ++ src/remote/remote_driver.c | 42 ++ src/remote/remote_protocol.x | 41 - src/remote_protocol-structs | 34 ++ src/rpc/gendispatch.pl |2 ++ 5 files changed, 160 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index daad39d..b471abc 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1587,6 +1587,48 @@ no_memory: goto cleanup; } +static int +remoteDispatchDomainGetBlockJobInfo(virNetServerPtr server ATTRIBUTE_UNUSED, +virNetServerClientPtr client ATTRIBUTE_UNUSED, +virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, +virNetMessageErrorPtr rerr, +remote_domain_get_block_job_info_args *args, +remote_domain_get_block_job_info_ret *ret) +{ +virDomainPtr dom = NULL; +virDomainBlockJobInfo tmp; +int rv = -1; +struct daemonClientPrivate *priv = +virNetServerClientGetPrivateData(client); + +if (!priv-conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(priv-conn, args-dom))) +goto cleanup; + +rv = virDomainGetBlockJobInfo(dom, args-path, tmp, args-flags); +if (rv = 0) +goto cleanup; + +ret-type = tmp.type; +ret-bandwidth = tmp.bandwidth; +ret-cur = tmp.cur; +ret-end = tmp.end; +ret-found = 1; +rv = 0; + +cleanup: +if (rv 0) +virNetMessageSaveError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + + /*-*/ static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c2f8bbd..a70b455 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1995,6 +1995,44 @@ done: return rv; } +static int remoteDomainGetBlockJobInfo(virDomainPtr domain, + const char *path, + virDomainBlockJobInfoPtr info, + unsigned int flags) +{ +int rv = -1; +remote_domain_get_block_job_info_args args; +remote_domain_get_block_job_info_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.path = (char *)path; +args.flags = flags; + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_GET_BLOCK_JOB_INFO, + (xdrproc_t)xdr_remote_domain_get_block_job_info_args, + (char *)args, + (xdrproc_t)xdr_remote_domain_get_block_job_info_ret, + (char *)ret) == -1) +goto done; + +if (ret.found) { +info-type = ret.type; +info-bandwidth = ret.bandwidth; +info-cur = ret.cur; +info-end = ret.end; +rv = 1; +} else { +rv = 0; +} + +done: +remoteDriverUnlock(priv); +return rv; +} + /*--*/ static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -4254,6 +4292,10 @@ static virDriver remote_driver = { .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ .domainSendKey = remoteDomainSendKey, /* 0.9.3 */ +.domainBlockJobAbort = remoteDomainBlockJobAbort, /* 0.9.4 */ +.domainGetBlockJobInfo = remoteDomainGetBlockJobInfo, /* 0.9.4 */ +.domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ +.domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index d72a60d..96113d8 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -980,6 +980,40 @@ struct remote_domain_set_autostart_args { int autostart; }; +struct remote_domain_block_job_abort_args { +remote_nonnull_domain dom; +remote_nonnull_string path; +unsigned int flags; +}; + +struct remote_domain_get_block_job_info_args { +remote_nonnull_domain dom; +remote_nonnull_string path; +unsigned int flags; +}; + +struct
[libvirt] [PATCH 8/8] Test the blockJob/BlockPull API
This patch is for information only and should not be comitted. Signed-off-by: Adam Litke a...@us.ibm.com --- blockPull-test.py | 281 + 1 files changed, 281 insertions(+), 0 deletions(-) create mode 100644 blockPull-test.py diff --git a/blockPull-test.py b/blockPull-test.py new file mode 100644 index 000..a75e0d9 --- /dev/null +++ b/blockPull-test.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python + +import sys +import subprocess +import time +import unittest +import re +import threading +import libvirt + +qemu_img_bin = /home/aglitke/src/qemu/qemu-img +virsh_bin = /home/aglitke/src/libvirt/tools/virsh + +dom_xml = +domain type='kvm' + nameblockPull-test/name + memory131072/memory + currentMemory131072/currentMemory + vcpu1/vcpu + os +type arch='x86_64' machine='pc-0.13'hvm/type +boot dev='hd'/ + /os + features +acpi/ +apic/ +pae/ + /features + clock offset='utc'/ + on_poweroffdestroy/on_poweroff + on_rebootrestart/on_reboot + on_crashrestart/on_crash + devices + emulator/home/aglitke/src/qemu/x86_64-softmmu/qemu-system-x86_64/emulator +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk1.qed' / + target dev='vda' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='qed'/ + source file='/tmp/disk2.qed' / + target dev='vdb' bus='virtio'/ +/disk +disk type='file' device='disk' + driver name='qemu' type='raw'/ + source file='/tmp/disk3.raw' / + target dev='vdc' bus='virtio'/ +/disk +graphics type='vnc' port='-1' autoport='yes'/ + /devices +/domain + + +def qemu_img(*args): +global qemu_img_bin + +devnull = open('/dev/null', 'r+') +return subprocess.call([qemu_img_bin] + list(args), stdin=devnull, stdout=devnull) + +def virsh(*args): +global virsh_bin + +devnull = open('/dev/null', 'r+') +return subprocess.Popen([virsh_bin] + list(args), +stdout=subprocess.PIPE).communicate()[0] +#return subprocess.call([virsh_bin] + list(args), +# stdin=devnull, stdout=devnull, stderr=devnull) + +def make_baseimage(name, size_mb): +devnull = open('/dev/null', 'r+') +return subprocess.call(['dd', 'if=/dev/zero', of=%s % name, 'bs=1M', +'count=%i' % size_mb], stdin=devnull, stdout=devnull, stderr=devnull) + +def has_backing_file(path): +global qemu_img_bin +p1 = subprocess.Popen([qemu_img_bin, info, path], + stdout=subprocess.PIPE).communicate()[0] +matches = re.findall(^backing file:, p1, re.M) +if len(matches) 0: +return True +return False + +class BlockPullTestCase(unittest.TestCase): +def _error_handler(self, ctx, error, dummy=None): +pass + +def create_disks(self, sparse): +self.disks = [ '/tmp/disk1.qed', '/tmp/disk2.qed', '/tmp/disk3.raw' ] +if sparse: +qemu_img('create', '-f', 'raw', '/tmp/backing1.img', '100M') +qemu_img('create', '-f', 'raw', '/tmp/backing2.img', '100M') +else: +make_baseimage('/tmp/backing1.img', 100) +make_baseimage('/tmp/backing2.img', 100) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing1.img', self.disks[0]) +qemu_img('create', '-f', 'qed', '-o', 'backing_file=/tmp/backing2.img', self.disks[1]) +qemu_img('create', '-f', 'raw', self.disks[2], '100M') + +def begin(self, sparse=True): +global dom_xml + +libvirt.registerErrorHandler(self._error_handler, None) +self.create_disks(sparse) +self.conn = libvirt.open('qemu:///system') +self.dom = self.conn.createXML(dom_xml, 0) + +def end(self): +self.dom.destroy() +self.conn.close() + +class TestBasicErrors(BlockPullTestCase): +def setUp(self): +self.begin() + +def tearDown(self): +self.end() + +def test_bad_path(self): +try: +self.dom.blockPull('/dev/null', 0, 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_INVALID_ARG, e[0]) + +def test_abort_no_stream(self): +try: +self.dom.blockJobAbort(self.disks[0], 0) +except libvirt.libvirtError, e: +self.assertEqual(libvirt.VIR_ERR_OPERATION_INVALID, e.get_error_code()) +else: +e = self.conn.virConnGetLastError() +self.assertEqual(libvirt.VIR_ERR_OPERATION_INVALID, e[0]) + +def test_start_same_twice(self): +self.dom.blockPull(self.disks[0], 0, 0) +try: +self.dom.blockPull(self.disks[0], 0, 0) +except libvirt.libvirtError, e: +self.assertEqual
Re: [libvirt] [PATCH RESEND RFC v4 1/6] Introduce the function virCgroupForVcpu
On 07/22/2011 01:56 AM, Wen Congyang wrote: So current behaviour if vCPU threads set quota in vCPU group else set nVCPUs * quota in VM group Would change to set nVCPUs * quota in VM group if vCPU threads set quota in vCPU group ? Yes. I treat this answer as you agree with Daniel P. Berrange's idea. If so, I will implement it. Yes, I am in agreement. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] RFC (V2) New virDomainBlockPull API family to libvirt
Thanks Daniel. The upstream code is looking good. I will work on adding some documentation to the development guide. On 07/22/2011 01:07 AM, Daniel Veillard wrote: On Thu, Jul 21, 2011 at 01:55:04PM -0500, Adam Litke wrote: Here are the patches to implement the BlockPull/BlockJob API as discussed and agreed to. I am testing with a python script (included for completeness as the final patch). The qemu monitor interface is not expected to change in the future. Stefan is planning to submit placeholder commands for upstream qemu until the generic streaming support is implemented. Changes since V1: - Make virDomainBlockPullAbort() and virDomainGetBlockPullInfo() into a generic BlockJob interface. - Added virDomainBlockJobSetSpeed() - Rename VIR_DOMAIN_EVENT_ID_BLOCK_PULL event to fit into block job API - Add bandwidth argument to virDomainBlockPull() Summary of changes since first generation patch series: - Qemu dropped incremental streaming so remove libvirt incremental BlockPull() API - Rename virDomainBlockPullAll() to virDomainBlockPull() - Changes required to qemu monitor handlers for changed command names -- To help speed the provisioning process for large domains, new QED disks are created with backing to a template image. These disks are configured with copy on read such that blocks that are read from the backing file are copied to the new disk. This reduces I/O over a potentially costly path to the backing image. In such a configuration, there is a desire to remove the dependency on the backing image as the domain runs. To accomplish this, qemu will provide an interface to perform sequential copy on read operations during normal VM operation. Once all data has been copied, the disk image's link to the backing file is removed. The virDomainBlockPull API family brings this functionality to libvirt. virDomainBlockPull() instructs the hypervisor to stream the entire device in the background. Progress of this operation can be checked with the function virDomainBlockJobInfo(). An ongoing stream can be cancelled with virDomainBlockJobAbort(). virDomainBlockJobSetSpeed() allows you to limit the bandwidth that the operation may consume. An event (VIR_DOMAIN_EVENT_ID_BLOCK_JOB) will be emitted when a disk has been fully populated or if a BlockPull() operation was terminated due to an error. This event is useful to avoid polling on virDomainBlockJobInfo() for completion and could also be used by the security driver to revoke access to the backing file when it is no longer needed. Thanks Adam for that revised patch set. ACK It all looked good to me, based on previous review and a last look. I just had to fix a few merge conflicts due to new entry points being added in the meantime and one commit message, but basically it was clean :-) So I pushed the set except 8 of course. I'm not sure if we should try to store it in the example, or on the wiki. The Wiki might be a bit more logical because I'm not sure we can run the test as is now in all setups. I think the remaining item would be to add documentation about how to use this, the paragraphs above should probably land somewhere on the web site, ideally on the development guide http://libvirt.org/devguide.html but I'm open to suggestions :-) Daniel -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] Fix bug #611823 prohibit pools with duplicate storage
/storage_driver.c index 9c353e3..8ee63f6 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -536,6 +536,9 @@ storagePoolCreate(virConnectPtr conn, if (virStoragePoolObjIsDuplicate(driver-pools, def, 1) 0) goto cleanup; +if (virStoragePoolTargetDuplicate(driver-pools, def) 0) +goto cleanup; + if ((backend = virStorageBackendForType(def-type)) == NULL) goto cleanup; More whitespace problems here. @@ -589,6 +592,9 @@ storagePoolDefine(virConnectPtr conn, if (virStoragePoolObjIsDuplicate(driver-pools, def, 0) 0) goto cleanup; +if (virStoragePoolTargetDuplicate(driver-pools, def) 0) +goto cleanup; + if (virStorageBackendForType(def-type) == NULL) goto cleanup; ... and here. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH] doc: Document the Block Job API
Hi DV, This patch adds some basic documentation to the development guide for the Block Job APIs as you requested. Also included is a sample program that shows end-to-end use of the BlockPull operation. I hope this is close to what you had in mind. Signed-off-by: Adam Litke a...@us.ibm.com --- en-US/Guest_Domains.xml | 154 +++ 1 files changed, 154 insertions(+), 0 deletions(-) diff --git a/en-US/Guest_Domains.xml b/en-US/Guest_Domains.xml index 0324e78..202881d 100644 --- a/en-US/Guest_Domains.xml +++ b/en-US/Guest_Domains.xml @@ -1219,6 +1219,160 @@ fprintf(stderr, Device is suitable for passthrough to a guest\n); /section +section id=Application_Development_Guide-Live_Config-Block_Jobs + titleBlock Device Jobs/title + + para +Libvirt provides a generic Block Job API that can be used to initiate +and manage operations on disks that belong to a domain. Jobs are +started by calling the function associated with the desired operation +(eg. literalvirDomainBlockPull/literal). Once started, all block +jobs are managed in the same manner. They can be aborted, throttled, +and queried. Upon completion, an asynchronous event is issued to +indicate the final status. + /para + + para +The following block jobs can be started: + /para + orderedlist +listitem + para + literalvirDomainBlockPull()/literal starts a block pull +operation for the specified disk. This operation is valid only for +specially configured disks. BlockPull will populate a disk image +with data from its backing image. Once all data from its backing +image has been pulled, the disk no longer depends on a backing +image. + /para +/listitem + /orderedlist + + para +A disk can be queried for active block jobs by using +literalvirDomainGetBlockJobInfo()/literal. If found, job +information is reported in a structure that contains: the job type, +bandwidth throttling setting, and progress information. + /para + + para +literalvirDomainBlockJobAbort()/literal can be used to cancel the +active block job on the specified disk. + /para + + para +Use literalvirDomainBlockJobSetSpeed()/literal to limit the amount +of bandwidth that a block job may consume. Bandwidth is specified in +units of MB/sec. + /para + + para +When a block job operation completes, the final status is reported using +an asynchronous event. To receive this event, register a +literalvirConnectDomainEventBlockJobCallback/literal function which +will receive the disk, event type, and status as parameters. + /para + + programlisting +![CDATA[/* example blockpull-example.c */ +/* compile with: gcc -g -Wall blockpull-example.c -o blockpull-example -lvirt */ +#include stdio.h +#include stdlib.h +#include unistd.h +#include libvirt/libvirt.h + +int do_cmd(const char *cmdline) +{ +int status = system(cmdline); +if (status 0) +return -1; +else +return WEXITSTATUS(status); +} + +virDomainPtr make_domain(virConnectPtr conn) +{ +virDomainPtr dom; +char domxml[] = \ + domain type='kvm' \ + nameexample/name \ + memory131072/memory \ + vcpu1/vcpu \ + os \ +type arch='x86_64' machine='pc-0.13'hvm/type \ + /os \ + devices \ +disk type='file' device='disk' \ + driver name='qemu' type='qed'/ \ + source file='/var/lib/libvirt/images/example.qed' / \ + target dev='vda' bus='virtio'/ \ +/disk \ + /devices \ +/domain; + +do_cmd(qemu-img create -f raw /var/lib/libvirt/images/backing.qed 100M); +do_cmd(qemu-img create -f qed -b /var/lib/libvirt/images/backing.qed \ +/var/lib/libvirt/images/example.qed); + +dom = virDomainCreateXML(conn, domxml, 0); +return dom; +} + +int main(int argc, char *argv[]) +{ +virConnectPtr conn; +virDomainPtr dom = NULL; +char disk[] = /var/lib/libvirt/images/example.qed; + +conn = virConnectOpen(qemu:///system); +if (conn == NULL) { +fprintf(stderr, Failed to open connection to qemu:///system\n); +goto error; +} + +dom = make_domain(conn); +if (dom == NULL) { +fprintf(stderr, Failed to create domain\n); +goto error; +} + +if ((virDomainBlockPull(dom, disk, 0, 0)) 0) { +fprintf(stderr, Failed to start block pull); +goto error; +} + +while (1) { +virDomainBlockJobInfo info; +int ret = virDomainGetBlockJobInfo(dom, disk, info, 0); + +if (ret == 1) { +printf(BlockPull progress: %0.0f %%\n, +(float)(100
Re: [libvirt] RFC (V2) New virDomainBlockPull API family to libvirt
On 08/14/2011 11:40 PM, Zhi Yong Wu wrote: HI, Deniel and Adam. Have the patchset been merged into libvirt upstream? Yes they have. However, the functionality is still missing from qemu. The two communities have agreed upon the interface and semantics, but work continues on the qemu implementation. Let me know if you would like a link to some qemu patches that support this functionality for qed images. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] qemu: Get memory balloon info correctly for text monitor
On 08/15/2011 08:23 AM, Osier Yang wrote: 于 2011年08月15日 21:58, Osier Yang 写道: * src/qemu/qemu_monitor_text.c: BALLOON_PREFIX was defined as balloon: actual=, which cause actual= is stripped early before the real parsing. This patch changes BALLOON_PREFIX into balloon: , and modifies related functions, also renames qemuMonitorParseExtraBalloonInfo to qemuMonitorParseBalloonInfo, as after the changing, it parses all the info returned by info balloon. Forgot to mention the problem, e.g. virsh dommemstat $domain returns empty result. That is because qemu has disabled stats reporting and so the extra fields are not present in the info balloon response. -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] qemu: Get memory balloon info correctly for text monitor
support ballooning Why not just use qemuMonitorParseBalloonInfo() for the logic above? You only need to request one stat since actual must be first. @@ -660,9 +673,7 @@ int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) { offset += strlen(BALLOON_PREFIX); -if ((offset = strchr(offset, ',')) != NULL) { -ret = qemuMonitorParseExtraBalloonInfo(offset, stats, nr_stats); -} +ret = qemuMonitorParseBalloonInfo(offset, stats, nr_stats); } VIR_FREE(reply); -- Adam Litke IBM Linux Technology Center -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list