1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2024-12-23 21:34:54 +03:00

blockcommit: document semantics of committing active layer

Now that qemu 2.0 allows commit of the active layer, people are
attempting to use virsh blockcommit and getting into a stuck
state, because libvirt is unprepared to handle the two-phase
commit required by qemu.

Stepping back a bit, there are two valid semantics for a
commit operation:

1. Maintain a 'golden' base, and a transient overlay. Make
changes in the overlay, and if everything appears to work,
commit those changes into the base, but still keep the overlay
for the next round of changes; repeat the cycle as desired.

2. Create an external snapshot, then back up the stable state
in the backing file. Once the backup is complete, commit the
overlay back into the base, and delete the temporary snapshot.

Since qemu doesn't know up front which of the two styles is
preferred, a block commit of the active layer merely gets
the job into a synchronized state, and sends an event; then
the user must either cancel (case 1) or complete (case 2),
where qemu then sends a second event that actually ends the
job.  However, until commit e6bcbcd, libvirt was blindly
assuming the semantics that apply to a commit of an
intermediate image, where there is only one sane conclusion
(the job automatically ends with fewer elements in the chain);
and getting stuck because it wasn't prepared for qemu to enter
a second phase of the job.

This patch adds a flag to the libvirt API that a user MUST
supply in order to acknowledge that they will be using two-phase
semantics.  It might be possible to have a mode where if the
flag is omitted, we automatically do the case 2 semantics on
the user's behalf; but before that happens, I must do additional
patches to track the fact that we are doing an active commit
in the domain XML.  Later patches will add support of the flag,
and once 2-phase semantics are working, we can then decide
whether to relax things to allow an omitted flag to cause an
automatic pivot.

* include/libvirt/libvirt.h.in (VIR_DOMAIN_BLOCK_COMMIT_ACTIVE)
(VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT): New enums.
* src/libvirt.c (virDomainBlockCommit): Document two-phase job
when committing active layer, through new flag.
(virDomainBlockJobAbort): Document that pivot also occurs after
active commit.
* tools/virsh-domain.c (vshDomainBlockJob): Cover new job.
* src/qemu/qemu_driver.c (qemuDomainBlockCommit): Explicitly
reject active copy; later patches will add it in.

Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Eric Blake 2014-05-16 13:48:43 -06:00
parent 350f816a00
commit b298025063
4 changed files with 69 additions and 25 deletions

View File

@ -2513,6 +2513,7 @@ typedef enum {
VIR_DOMAIN_BLOCK_JOB_TYPE_PULL = 1,
VIR_DOMAIN_BLOCK_JOB_TYPE_COPY = 2,
VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT = 3,
VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT = 4,
#ifdef VIR_ENUM_SENTINELS
VIR_DOMAIN_BLOCK_JOB_TYPE_LAST
@ -2523,7 +2524,8 @@ typedef enum {
* virDomainBlockJobAbortFlags:
*
* VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC: Request only, do not wait for completion
* VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT: Pivot to mirror when ending a copy job
* VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT: Pivot to new file when ending a copy or
* active commit job
*/
typedef enum {
VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC = 1 << 0,
@ -2588,6 +2590,8 @@ typedef enum {
VIR_DOMAIN_BLOCK_COMMIT_DELETE = 1 << 1, /* Delete any files that are now
invalid after their contents
have been committed */
VIR_DOMAIN_BLOCK_COMMIT_ACTIVE = 1 << 2, /* Allow a two-phase commit when
top is the active layer */
} virDomainBlockCommitFlags;
int virDomainBlockCommit(virDomainPtr dom, const char *disk, const char *base,
@ -4823,8 +4827,8 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn,
/**
* virConnectDomainEventBlockJobStatus:
*
* The final status of a virDomainBlockPull() or virDomainBlockRebase()
* operation
* Tracks status of a virDomainBlockPull(), virDomainBlockRebase(),
* or virDomainBlockCommit() operation
*/
typedef enum {
VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0,
@ -4843,7 +4847,7 @@ typedef enum {
* @dom: domain on which the event occurred
* @disk: fully-qualified filename of the affected disk
* @type: type of block job (virDomainBlockJobType)
* @status: final status of the operation (virConnectDomainEventBlockJobStatus)
* @status: status of the operation (virConnectDomainEventBlockJobStatus)
* @opaque: application specified data
*
* The callback signature to use when registering for an event of type

View File

@ -19473,13 +19473,16 @@ virDomainOpenChannel(virDomainPtr dom,
*
* If the current block job for @disk is VIR_DOMAIN_BLOCK_JOB_TYPE_COPY, then
* the default is to abort the mirroring and revert to the source disk;
* adding @flags of VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT causes this call to
* fail with VIR_ERR_BLOCK_COPY_ACTIVE if the copy is not fully populated,
* otherwise it will swap the disk over to the copy to end the mirroring. An
* event will be issued when the job is ended, and it is possible to use
* VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC to control whether this command waits
* for the completion of the job. Restarting this job requires starting
* over from the beginning of the first phase.
* likewise, if the current job is VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT,
* the default is to abort without changing the active layer of @disk.
* Adding @flags of VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT causes this call to
* fail with VIR_ERR_BLOCK_COPY_ACTIVE if the copy or commit is not yet
* ready; otherwise it will swap the disk over to the new active image
* to end the mirroring or active commit. An event will be issued when the
* job is ended, and it is possible to use VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC
* to control whether this command waits for the completion of the job.
* Restarting a copy or active commit job requires starting over from the
* beginning of the first phase.
*
* Returns -1 in case of failure, 0 when successful.
*/
@ -19849,17 +19852,32 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk,
* the job is aborted, it is up to the hypervisor whether starting a new
* job will resume from the same point, or start over.
*
* As a special case, if @top is the active image (or NULL), and @flags
* includes VIR_DOMAIN_BLOCK_COMMIT_ACTIVE, the block job will have a type
* of VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT, and operates in two phases.
* In the first phase, the contents are being committed into @base, and the
* job can only be canceled. The job transitions to the second phase when
* the job info states cur == end, and remains alive to keep all further
* changes to @top synchronized into @base; an event with status
* VIR_DOMAIN_BLOCK_JOB_READY is also issued to mark the job transition.
* Once in the second phase, the user must choose whether to cancel the job
* (keeping @top as the active image, but now containing only the changes
* since the time the job ended) or to pivot the job (adjusting to @base as
* the active image, and invalidating @top).
*
* Be aware that this command may invalidate files even if it is aborted;
* the user is cautioned against relying on the contents of invalidated
* intermediate files such as @top without manually rebasing those files
* to use a backing file of a read-only copy of @base prior to the point
* where the commit operation was started (although such a rebase cannot
* be safely done until the commit has successfully completed). However,
* the domain itself will not have any issues; the active layer remains
* valid throughout the entire commit operation. As a convenience,
* if @flags contains VIR_DOMAIN_BLOCK_COMMIT_DELETE, this command will
* unlink all files that were invalidated, after the commit successfully
* completes.
* intermediate files such as @top (when @top is not the active image)
* without manually rebasing those files to use a backing file of a
* read-only copy of @base prior to the point where the commit operation
* was started (and such a rebase cannot be safely done until the commit
* has successfully completed). However, the domain itself will not have
* any issues; the active layer remains valid throughout the entire commit
* operation.
*
* Some hypervisors may support a shortcut where if @flags contains
* VIR_DOMAIN_BLOCK_COMMIT_DELETE, then this command will unlink all files
* that were invalidated, after the commit successfully completes.
*
* By default, if @base is NULL, the commit target will be the bottom of
* the backing chain; if @flags contains VIR_DOMAIN_BLOCK_COMMIT_SHALLOW,
@ -19867,8 +19885,9 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk,
* is NULL, the active image at the top of the chain will be used. Some
* hypervisors place restrictions on how much can be committed, and might
* fail if @base is not the immediate backing file of @top, or if @top is
* the active layer in use by a running domain, or if @top is not the
* top-most file; restrictions may differ for online vs. offline domains.
* the active layer in use by a running domain but @flags did not include
* VIR_DOMAIN_BLOCK_COMMIT_ACTIVE, or if @top is not the top-most file;
* restrictions may differ for online vs. offline domains.
*
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as

View File

@ -15128,6 +15128,8 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
* to prevent newly scheduled block jobs from confusing us. */
if (mode == BLOCK_JOB_ABORT) {
if (!async) {
/* Older qemu that lacked async reporting also lacked
* active commit, so we can hardcode the event to pull */
int type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL;
int status = VIR_DOMAIN_BLOCK_JOB_CANCELED;
event = virDomainEventBlockJobNewFromObj(vm, disk->src->path, type,
@ -15482,6 +15484,7 @@ qemuDomainBlockCommit(virDomainPtr dom,
const char *top_parent = NULL;
bool clean_access = false;
/* XXX Add support for COMMIT_ACTIVE, COMMIT_DELETE */
virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW, -1);
if (!(vm = qemuDomObjFromDomain(dom)))
@ -15532,8 +15535,25 @@ qemuDomainBlockCommit(virDomainPtr dom,
* process; qemu 2.1 is further improving active commit. We need
* to start supporting it in libvirt. */
if (topSource == disk->src) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("committing the active layer not supported yet"));
/* We assume that no one will backport qemu 2.0 active commit
* to an earlier qemu without also backporting the block job
* ready event; but this makes sure of that fact */
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKJOB_ASYNC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("active commit not supported with this QEMU binary"));
goto endjob;
}
/* XXX Should we auto-pivot when COMMIT_ACTIVE is not specified? */
if (!(flags & VIR_DOMAIN_BLOCK_COMMIT_ACTIVE)) {
virReportError(VIR_ERR_INVALID_ARG,
_("commit of '%s' active layer requires active flag"),
disk->dst);
goto endjob;
}
} else if (flags & VIR_DOMAIN_BLOCK_COMMIT_ACTIVE) {
virReportError(VIR_ERR_INVALID_ARG,
_("active commit requested but '%s' is not active"),
topSource->path);
goto endjob;
}

View File

@ -1968,7 +1968,8 @@ VIR_ENUM_IMPL(vshDomainBlockJob,
N_("Unknown job"),
N_("Block Pull"),
N_("Block Copy"),
N_("Block Commit"))
N_("Block Commit"),
N_("Active Block Commit"))
static const char *
vshDomainBlockJobToString(int type)