Re: [libvirt] [PATCHv2 02/15] blockjob: wire up qemu async virDomainBlockJobAbort

2012-04-06 Thread Adam Litke
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

2012-04-06 Thread Adam Litke
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

2014-01-24 Thread Adam Litke

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

2011-05-02 Thread Adam Litke
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

2011-05-09 Thread Adam Litke



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)

2011-05-18 Thread Adam Litke
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)

2011-05-20 Thread Adam Litke
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)

2011-05-20 Thread Adam Litke
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

2011-05-20 Thread Adam Litke


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

2011-05-20 Thread Adam Litke
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)

2011-05-23 Thread Adam Litke


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

2011-05-23 Thread Adam Litke
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

2011-05-24 Thread Adam Litke


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

2011-05-24 Thread Adam Litke


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

2011-05-24 Thread Adam Litke
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

2011-05-24 Thread Adam Litke
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

2011-06-01 Thread Adam Litke
*** 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

2011-06-01 Thread Adam Litke
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

2011-06-01 Thread Adam Litke
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.

2011-06-01 Thread Adam Litke
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

2011-06-01 Thread Adam Litke
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

2011-06-01 Thread Adam Litke
* 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

2011-06-01 Thread Adam Litke
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

2011-06-01 Thread Adam Litke
* 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

2011-06-01 Thread Adam Litke
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

2011-06-01 Thread Adam Litke
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

2011-06-01 Thread Adam Litke


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

2011-06-01 Thread Adam Litke
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

2011-06-06 Thread Adam Litke
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

2011-06-06 Thread Adam Litke
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

2011-06-06 Thread Adam Litke
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

2011-06-06 Thread Adam Litke
*** 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

2011-06-06 Thread Adam Litke
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

2011-06-06 Thread Adam Litke
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

2011-06-06 Thread Adam Litke
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.

2011-06-06 Thread Adam Litke
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

2011-06-06 Thread Adam Litke
* 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

2011-06-08 Thread Adam Litke
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

2011-06-09 Thread Adam Litke
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

2011-06-09 Thread Adam Litke
*** 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.

2011-06-09 Thread Adam Litke
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

2011-06-09 Thread Adam Litke
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

2011-06-09 Thread Adam Litke
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

2011-06-09 Thread Adam Litke
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

2011-06-09 Thread Adam Litke
* 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

2011-06-09 Thread Adam Litke
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

2011-06-09 Thread Adam Litke
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

2011-06-10 Thread Adam Litke


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

2011-06-10 Thread Adam Litke


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

2011-06-10 Thread Adam Litke
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

2011-06-14 Thread Adam Litke


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

2011-06-14 Thread Adam Litke


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

2011-06-14 Thread Adam Litke


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

2011-06-14 Thread Adam Litke


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

2011-06-14 Thread Adam Litke
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

2011-06-14 Thread Adam Litke
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

2011-06-14 Thread Adam Litke
*** 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

2011-06-14 Thread Adam Litke
* 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

2011-06-14 Thread Adam Litke
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

2011-06-14 Thread Adam Litke
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

2011-06-14 Thread Adam Litke
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

2011-06-15 Thread Adam Litke
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

2011-06-15 Thread Adam Litke


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

2011-06-16 Thread Adam Litke


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

2011-07-05 Thread Adam Litke


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

2011-07-08 Thread Adam Litke
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

2011-07-08 Thread Adam Litke


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

2011-07-13 Thread Adam Litke
: 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

2011-07-14 Thread Adam Litke


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

2011-07-14 Thread Adam Litke


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

2011-07-14 Thread Adam Litke


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

2011-07-15 Thread Adam Litke


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

2011-07-15 Thread Adam Litke
,
  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

2011-07-18 Thread Adam Litke


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

2011-07-18 Thread Adam Litke
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

2011-07-18 Thread Adam Litke


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

2011-07-18 Thread Adam Litke


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

2011-07-19 Thread Adam Litke


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

2011-07-19 Thread Adam Litke


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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke


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

2011-07-21 Thread Adam Litke


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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke


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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke
* 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.

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke
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

2011-07-21 Thread Adam Litke
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

2011-07-22 Thread Adam Litke


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

2011-07-22 Thread Adam Litke
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

2011-07-29 Thread Adam Litke
/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

2011-08-12 Thread Adam Litke
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

2011-08-15 Thread Adam Litke
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

2011-08-15 Thread Adam Litke
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

2011-08-15 Thread Adam Litke
 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


  1   2   3   >