[libvirt] [PATCH v3 4/8] test: Implement snapshot create/delete/revert APIs

Michal Privoznik mprivozn at redhat.com
Thu Sep 26 14:44:48 UTC 2013


On 25.09.2013 21:15, Cole Robinson wrote:
> Again stolen from qemu_driver.c, but dropping all the unneeded bits.
> This aims to copy all the current qemu validation checks since that's
> the most commonly used real driver, but some of the checks are
> completely artificial in the test driver.
> 
> This only supports creation of internal snapshots for initial
> simplicity.
> ---
> 
> v3:
>     Use STRNEQ_NULLABLE for domain_conf.c change
> 
>  src/conf/domain_conf.c |   2 +-
>  src/test/test_driver.c | 504 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 504 insertions(+), 2 deletions(-)
> 
> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index 70fdafc..dbf239e 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -13515,7 +13515,7 @@ virDomainDefCheckABIStability(virDomainDefPtr src,
>                         virArchToString(src->os.arch));
>          return false;
>      }
> -    if (STRNEQ(src->os.machine, dst->os.machine)) {
> +    if (STRNEQ_NULLABLE(src->os.machine, dst->os.machine)) {
>          virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
>                         _("Target domain OS type %s does not match source %s"),
>                         dst->os.machine, src->os.machine);
> diff --git a/src/test/test_driver.c b/src/test/test_driver.c
> index 9a39087..1b79b70 100644
> --- a/src/test/test_driver.c
> +++ b/src/test/test_driver.c
> @@ -2860,9 +2860,11 @@ static int testDomainUndefineFlags(virDomainPtr domain,
>      testConnPtr privconn = domain->conn->privateData;
>      virDomainObjPtr privdom;
>      virDomainEventPtr event = NULL;
> +    int nsnapshots;
>      int ret = -1;
>  
> -    virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE, -1);
> +    virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE |
> +                  VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, -1);
>  
>      testDriverLock(privconn);
>      privdom = virDomainObjListFindByName(privconn->domains,
> @@ -2882,6 +2884,24 @@ static int testDomainUndefineFlags(virDomainPtr domain,
>      }
>      privdom->hasManagedSave = false;

This is exactly the problem I'm describing in 2/8. If something below
fails, we've undefined the managed save image.

>  
> +    /* Requiring an inactive VM is part of the documented API for
> +     * UNDEFINE_SNAPSHOTS_METADATA
> +     */
> +    if (!virDomainObjIsActive(privdom) &&
> +        (nsnapshots = virDomainSnapshotObjListNum(privdom->snapshots,
> +                                                  NULL, 0))) {
> +        if (!(flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA)) {
> +            virReportError(VIR_ERR_OPERATION_INVALID,
> +                           _("cannot delete inactive domain with %d "
> +                             "snapshots"),
> +                           nsnapshots);
> +            goto cleanup;
> +        }
> +
> +        /* There isn't actually anything to do, we are just emulating qemu
> +         * behavior here. */
> +    }
> +
>      event = virDomainEventNewFromObj(privdom,
>                                       VIR_DOMAIN_EVENT_UNDEFINED,
>                                       VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
> @@ -6475,6 +6495,485 @@ cleanup:
>      return ret;
>  }
>  
> +static int
> +testDomainSnapshotAlignDisks(virDomainObjPtr vm,
> +                             virDomainSnapshotDefPtr def,
> +                             unsigned int flags)
> +{
> +    int align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL;
> +    int align_match = true;
> +
> +    if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) {
> +        align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
> +        align_match = false;
> +        if (virDomainObjIsActive(vm))
> +            def->state = VIR_DOMAIN_DISK_SNAPSHOT;
> +        else
> +            def->state = VIR_DOMAIN_SHUTOFF;
> +        def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE;
> +    } else if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
> +        def->state = virDomainObjGetState(vm, NULL);
> +        align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
> +        align_match = false;
> +    } else {
> +        def->state = virDomainObjGetState(vm, NULL);
> +        def->memory = (def->state == VIR_DOMAIN_SHUTOFF ?
> +                       VIR_DOMAIN_SNAPSHOT_LOCATION_NONE :
> +                       VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL);

Needless parentheses.

> +    }
> +
> +    return virDomainSnapshotAlignDisks(def, align_location, align_match);
> +}
> +
> +static virDomainSnapshotPtr
> +testDomainSnapshotCreateXML(virDomainPtr domain,
> +                            const char *xmlDesc,
> +                            unsigned int flags)
> +{
> +    testConnPtr privconn = domain->conn->privateData;
> +    virDomainObjPtr vm = NULL;
> +    virDomainSnapshotDefPtr def = NULL;
> +    virDomainSnapshotObjPtr snap = NULL;
> +    virDomainSnapshotPtr snapshot = NULL;
> +    virDomainEventPtr event = NULL;
> +    char *xml = NULL;
> +    unsigned int parse_flags = VIR_DOMAIN_SNAPSHOT_PARSE_DISKS;
> +
> +    /*
> +     * REDEFINE + CURRENT: Not implemented yet
> +     * DISK_ONLY: Not implemented yet
> +     * REUSE_EXT: Not implemented yet
> +     *
> +     * NO_METADATA: Explicitly not implemented
> +     *
> +     * HALT: Implemented
> +     * QUIESCE: Nothing to do
> +     * ATOMIC: Nothing to do
> +     * LIVE: Nothing to do
> +     */
> +    virCheckFlags(
> +        VIR_DOMAIN_SNAPSHOT_CREATE_HALT |
> +        VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE |
> +        VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC |
> +        VIR_DOMAIN_SNAPSHOT_CREATE_LIVE, NULL);
> +
> +    if (!(vm = testDomObjFromDomain(domain)))
> +        goto cleanup;
> +
> +    testDriverLock(privconn);

No need to lock the driver this soon.

> +
> +    if (!vm->persistent && (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT)) {
> +        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
> +                       _("cannot halt after transient domain snapshot"));
> +        goto cleanup;
> +    }

IE vm is locked here and this has no impact on the driver.

> +
> +    if (!(def = virDomainSnapshotDefParseString(xmlDesc,
> +                                                privconn->caps,
> +                                                privconn->xmlopt,
> +                                                1 << VIR_DOMAIN_VIRT_TEST,
> +                                                parse_flags)))
> +        goto cleanup;

Neither has this ...

> +
> +    if (!(xml = virDomainDefFormat(vm->def, VIR_DOMAIN_XML_INACTIVE)) ||
> +        !(def->dom = virDomainDefParseString(xml,
> +                                             privconn->caps,
> +                                             privconn->xmlopt,
> +                                             1 << VIR_DOMAIN_VIRT_TEST,
> +                                             VIR_DOMAIN_XML_INACTIVE)))
> +        goto cleanup;

.. or this ..
BTW we have virDomainDefCopy().


> +
> +    if (testDomainSnapshotAlignDisks(vm, def, flags) < 0)
> +        goto cleanup;
> +

If you intended the driver lock as mutex for vm->snapshots I don't think
it's necessary since vm is locked throughout the whole API. So there is
no chance for other API to hop in and change the vm->snapshots.

> +    if (!(snap = virDomainSnapshotAssignDef(vm->snapshots, def)))
> +        goto cleanup;
> +    def = NULL;

I don't think this is gonna work. If flags has _REDEFINE flag set, the
virDomainSnapshotAssignDef() will fail and the code below won't get any
chance to reverse the upcoming error.

> +
> +    if (vm->current_snapshot) {
> +        if (!(flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) &&
> +            VIR_STRDUP(snap->def->parent, vm->current_snapshot->def->name) < 0)
> +            goto cleanup;
> +    }
> +
> +    if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT) &&
> +        virDomainObjIsActive(vm)) {
> +        testDomainShutdownState(domain, vm, VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT);
> +        event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
> +                                        VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT);
> +    }
> +
> +
> +    snapshot = virGetDomainSnapshot(domain, snap->def->name);
> +cleanup:
> +    VIR_FREE(xml);
> +    if (vm) {
> +        if (snapshot) {
> +            virDomainSnapshotObjPtr other;
> +            vm->current_snapshot = snap;
> +            other = virDomainSnapshotFindByName(vm->snapshots,
> +                                                snap->def->parent);
> +            snap->parent = other;
> +            other->nchildren++;
> +            snap->sibling = other->first_child;
> +            other->first_child = snap;
> +        }
> +        virObjectUnlock(vm);
> +    }
> +    if (event) {

In fact this is the only place that require test driver to be locked.

> +        testDomainEventQueue(privconn, event);
> +    }
> +    testDriverUnlock(privconn);
> +    virDomainSnapshotDefFree(def);
> +    return snapshot;
> +}
> +
> +
> +typedef struct _testSnapRemoveData testSnapRemoveData;
> +typedef testSnapRemoveData *testSnapRemoveDataPtr;
> +struct _testSnapRemoveData {
> +    virDomainObjPtr vm;
> +    bool current;
> +};
> +
> +static void
> +testDomainSnapshotDiscard(void *payload,
> +                          const void *name ATTRIBUTE_UNUSED,
> +                          void *data)

Please keep the 'All' suffix just like the qemu counterpart has. It made
me wondering why is the function header different to
qemuDomainSnapshotDiscard, while I needed to look at
qemuDomainSnaphostDiscardAll with the very same header.

> +{
> +    virDomainSnapshotObjPtr snap = payload;
> +    testSnapRemoveDataPtr curr = data;
> +
> +    if (snap->def->current)
> +        curr->current = true;
> +    virDomainSnapshotObjListRemove(curr->vm->snapshots, snap);
> +}
> +
> +typedef struct _testSnapReparentData testSnapReparentData;
> +typedef testSnapReparentData *testSnapReparentDataPtr;
> +struct _testSnapReparentData {
> +    virDomainSnapshotObjPtr parent;
> +    virDomainObjPtr vm;
> +    int err;
> +    virDomainSnapshotObjPtr last;
> +};
> +
> +static void
> +testDomainSnapshotReparentChildren(void *payload,
> +                                   const void *name ATTRIBUTE_UNUSED,
> +                                   void *data)
> +{
> +    virDomainSnapshotObjPtr snap = payload;
> +    testSnapReparentDataPtr rep = data;
> +
> +    if (rep->err < 0) {
> +        return;
> +    }
> +
> +    VIR_FREE(snap->def->parent);
> +    snap->parent = rep->parent;
> +
> +    if (rep->parent->def &&
> +        VIR_STRDUP(snap->def->parent, rep->parent->def->name) < 0) {
> +        rep->err = -1;
> +        return;
> +    }
> +
> +    if (!snap->sibling)
> +        rep->last = snap;
> +}
> +
> +static int
> +testDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
> +                         unsigned int flags)
> +{
> +    virDomainObjPtr vm = NULL;
> +    virDomainSnapshotObjPtr snap = NULL;
> +    virDomainSnapshotObjPtr parentsnap = NULL;
> +    int ret = -1;
> +
> +    virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
> +                  VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY, -1);
> +
> +    if (!(vm = testDomObjFromSnapshot(snapshot)))
> +        return -1;
> +
> +    if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
> +        goto cleanup;
> +
> +    if (flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
> +                 VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY)) {
> +        testSnapRemoveData rem;
> +        rem.vm = vm;
> +        rem.current = false;
> +        virDomainSnapshotForEachDescendant(snap,
> +                                           testDomainSnapshotDiscard,

s/Discard/DiscardAll/

> +                                           &rem);
> +        if (rem.current) {
> +            if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) {
> +                snap->def->current = true;
> +            }
> +            vm->current_snapshot = snap;
> +        }
> +    } else if (snap->nchildren) {
> +        testSnapReparentData rep;
> +        rep.parent = snap->parent;
> +        rep.vm = vm;
> +        rep.err = 0;
> +        rep.last = NULL;
> +        virDomainSnapshotForEachChild(snap,
> +                                      testDomainSnapshotReparentChildren,
> +                                      &rep);
> +        if (rep.err < 0)
> +            goto cleanup;
> +
> +        /* Can't modify siblings during ForEachChild, so do it now.  */
> +        snap->parent->nchildren += snap->nchildren;
> +        rep.last->sibling = snap->parent->first_child;
> +        snap->parent->first_child = snap->first_child;
> +    }
> +
> +    if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) {
> +        snap->nchildren = 0;
> +        snap->first_child = NULL;
> +    } else {
> +        virDomainSnapshotDropParent(snap);
> +        if (snap == vm->current_snapshot) {
> +            if (snap->def->parent) {
> +                parentsnap = virDomainSnapshotFindByName(vm->snapshots,
> +                                                         snap->def->parent);
> +                if (!parentsnap) {
> +                    VIR_WARN("missing parent snapshot matching name '%s'",
> +                             snap->def->parent);
> +                } else {
> +                    parentsnap->def->current = true;
> +                }
> +            }
> +            vm->current_snapshot = parentsnap;
> +        }
> +        virDomainSnapshotObjListRemove(vm->snapshots, snap);
> +    }
> +
> +    ret = 0;
> +cleanup:
> +    if (vm)
> +        virObjectUnlock(vm);
> +    return ret;
> +}
> +
> +static int
> +testDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> +                           unsigned int flags)
> +{
> +    testConnPtr privconn = snapshot->domain->conn->privateData;
> +    virDomainObjPtr vm = NULL;
> +    virDomainSnapshotObjPtr snap = NULL;
> +    virDomainEventPtr event = NULL;
> +    virDomainEventPtr event2 = NULL;
> +    virDomainDefPtr config = NULL;
> +    int ret = -1;
> +
> +    virCheckFlags(VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING |
> +                  VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED |
> +                  VIR_DOMAIN_SNAPSHOT_REVERT_FORCE, -1);
> +
> +    /* We have the following transitions, which create the following events:
> +     * 1. inactive -> inactive: none
> +     * 2. inactive -> running:  EVENT_STARTED
> +     * 3. inactive -> paused:   EVENT_STARTED, EVENT_PAUSED
> +     * 4. running  -> inactive: EVENT_STOPPED
> +     * 5. running  -> running:  none
> +     * 6. running  -> paused:   EVENT_PAUSED
> +     * 7. paused   -> inactive: EVENT_STOPPED
> +     * 8. paused   -> running:  EVENT_RESUMED
> +     * 9. paused   -> paused:   none
> +     * Also, several transitions occur even if we fail partway through,
> +     * and use of FORCE can cause multiple transitions.
> +     */
> +
> +    if (!(vm = testDomObjFromSnapshot(snapshot)))
> +        return -1;
> +
> +    if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
> +        goto cleanup;
> +
> +    testDriverLock(privconn);

This looks suspicious again.

> +
> +    if (!vm->persistent &&
> +        snap->def->state != VIR_DOMAIN_RUNNING &&
> +        snap->def->state != VIR_DOMAIN_PAUSED &&
> +        (flags & (VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING |
> +                  VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED)) == 0) {
> +        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
> +                       _("transient domain needs to request run or pause "
> +                         "to revert to inactive snapshot"));
> +        goto cleanup;
> +    }
> +
> +    if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_FORCE)) {
> +        if (!snap->def->dom) {
> +            virReportError(VIR_ERR_SNAPSHOT_REVERT_RISKY,
> +                           _("snapshot '%s' lacks domain '%s' rollback info"),
> +                           snap->def->name, vm->def->name);
> +            goto cleanup;
> +        }
> +        if (virDomainObjIsActive(vm) &&
> +            !(snap->def->state == VIR_DOMAIN_RUNNING
> +              || snap->def->state == VIR_DOMAIN_PAUSED) &&
> +            (flags & (VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING |
> +                      VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED))) {
> +            virReportError(VIR_ERR_SNAPSHOT_REVERT_RISKY, "%s",
> +                           _("must respawn guest to start inactive snapshot"));
> +            goto cleanup;
> +        }
> +    }
> +
> +
> +    if (vm->current_snapshot) {
> +        vm->current_snapshot->def->current = false;
> +        vm->current_snapshot = NULL;
> +    }
> +
> +    snap->def->current = true;
> +    config = virDomainDefCopy(snap->def->dom,
> +                              privconn->caps, privconn->xmlopt, true);
> +    if (!config)
> +        goto cleanup;
> +
> +    if (snap->def->state == VIR_DOMAIN_RUNNING ||
> +        snap->def->state == VIR_DOMAIN_PAUSED) {
> +        /* Transitions 2, 3, 5, 6, 8, 9 */
> +        bool was_running = false;
> +        bool was_stopped = false;
> +
> +        if (virDomainObjIsActive(vm)) {
> +            /* Transitions 5, 6, 8, 9 */
> +            /* Check for ABI compatibility.  */
> +            if (!virDomainDefCheckABIStability(vm->def, config)) {
> +                virErrorPtr err = virGetLastError();
> +
> +                if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_FORCE)) {
> +                    /* Re-spawn error using correct category. */
> +                    if (err->code == VIR_ERR_CONFIG_UNSUPPORTED)
> +                        virReportError(VIR_ERR_SNAPSHOT_REVERT_RISKY, "%s",
> +                                       err->str2);
> +                    goto cleanup;
> +                }
> +
> +                virResetError(err);
> +                testDomainShutdownState(snapshot->domain, vm,
> +                                        VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT);
> +                event = virDomainEventNewFromObj(vm,
> +                            VIR_DOMAIN_EVENT_STOPPED,
> +                            VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT);
> +                if (event)
> +                    testDomainEventQueue(privconn, event);
> +                goto load;
> +            }
> +
> +            if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
> +                /* Transitions 5, 6 */
> +                was_running = true;
> +                virDomainObjSetState(vm, VIR_DOMAIN_PAUSED,
> +                                     VIR_DOMAIN_PAUSED_FROM_SNAPSHOT);

Shouldn't this be testDomainShutdownState() instead of
virDomainObjSetState()?

> +                /* Create an event now in case the restore fails, so
> +                 * that user will be alerted that they are now paused.
> +                 * If restore later succeeds, we might replace this. */
> +                event = virDomainEventNewFromObj(vm,
> +                                VIR_DOMAIN_EVENT_SUSPENDED,
> +                                VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT);
> +            }
> +            virDomainObjAssignDef(vm, config, false, NULL);
> +
> +        } else {
> +            /* Transitions 2, 3 */
> +        load:
> +            was_stopped = true;
> +            virDomainObjAssignDef(vm, config, false, NULL);
> +            if (testDomainStartState(privconn, vm,
> +                                VIR_DOMAIN_RUNNING_FROM_SNAPSHOT) < 0)
> +                goto cleanup;
> +            event = virDomainEventNewFromObj(vm,
> +                                VIR_DOMAIN_EVENT_STARTED,
> +                                VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT);
> +        }
> +
> +        /* Touch up domain state.  */
> +        if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING) &&
> +            (snap->def->state == VIR_DOMAIN_PAUSED ||
> +             (flags & VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED))) {
> +            /* Transitions 3, 6, 9 */
> +            virDomainObjSetState(vm, VIR_DOMAIN_PAUSED,
> +                                 VIR_DOMAIN_PAUSED_FROM_SNAPSHOT);
> +            if (was_stopped) {
> +                /* Transition 3, use event as-is and add event2 */
> +                event2 = virDomainEventNewFromObj(vm,
> +                                VIR_DOMAIN_EVENT_SUSPENDED,
> +                                VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT);
> +            } /* else transition 6 and 9 use event as-is */
> +        } else {
> +            /* Transitions 2, 5, 8 */
> +            virDomainEventFree(event);
> +            event = NULL;
> +
> +            if (was_stopped) {
> +                /* Transition 2 */
> +                event = virDomainEventNewFromObj(vm,
> +                                VIR_DOMAIN_EVENT_STARTED,
> +                                VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT);
> +            } else if (was_running) {
> +                /* Transition 8 */
> +                event = virDomainEventNewFromObj(vm,
> +                                VIR_DOMAIN_EVENT_RESUMED,
> +                                VIR_DOMAIN_EVENT_RESUMED);
> +            }
> +        }
> +    } else {
> +        /* Transitions 1, 4, 7 */
> +        virDomainObjAssignDef(vm, config, false, NULL);
> +
> +        if (virDomainObjIsActive(vm)) {
> +            /* Transitions 4, 7 */
> +            testDomainShutdownState(snapshot->domain, vm,
> +                                    VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT);
> +            event = virDomainEventNewFromObj(vm,
> +                                    VIR_DOMAIN_EVENT_STOPPED,
> +                                    VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT);
> +        }
> +
> +        if (flags & (VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING |
> +                     VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED)) {
> +            /* Flush first event, now do transition 2 or 3 */
> +            bool paused = (flags & VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED) != 0;
> +
> +            if (event)
> +                testDomainEventQueue(privconn, event);
> +            event = virDomainEventNewFromObj(vm,
> +                            VIR_DOMAIN_EVENT_STARTED,
> +                            VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT);
> +            if (paused) {
> +                event2 = virDomainEventNewFromObj(vm,
> +                                VIR_DOMAIN_EVENT_SUSPENDED,
> +                                VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT);
> +            }
> +        }
> +    }
> +
> +    vm->current_snapshot = snap;
> +    ret = 0;
> +cleanup:
> +    if (event) {
> +        testDomainEventQueue(privconn, event);
> +        if (event2)
> +            testDomainEventQueue(privconn, event2);
> +    }
> +    virObjectUnlock(vm);
> +    testDriverUnlock(privconn);
> +
> +    return ret;
> +}
> +
> +
>  
>  static virDriver testDriver = {
>      .no = VIR_DRV_TEST,
> @@ -6566,6 +7065,9 @@ static virDriver testDriver = {
>      .domainSnapshotCurrent = testDomainSnapshotCurrent, /* 1.1.3 */
>      .domainSnapshotIsCurrent = testDomainSnapshotIsCurrent, /* 1.1.3 */
>      .domainSnapshotHasMetadata = testDomainSnapshotHasMetadata, /* 1.1.3 */
> +    .domainSnapshotCreateXML = testDomainSnapshotCreateXML, /* 1.1.3 */
> +    .domainRevertToSnapshot = testDomainRevertToSnapshot, /* 1.1.3 */
> +    .domainSnapshotDelete = testDomainSnapshotDelete, /* 1.1.3 */
>  };
>  
>  static virNetworkDriver testNetworkDriver = {
> 

ACK if you address the nits I've pointed out.

Michal




More information about the libvir-list mailing list