Message ID | 20210204124834.774401-13-berrange@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | migration: bring improved savevm/loadvm/delvm to QMP | expand |
This is (intermittently?) failing for me because of ordering issues: --- /home/dgilbert/git/migpull/tests/qemu-iotests/tests/internal-snapshots-qapi.out +++ internal-snapshots-qapi.out.bad @@ -344,8 +344,8 @@ "vmstate": "diskfmt0", "devices": ["diskfmt0"]}} {"return": {}} +qemu-system-x86_64: Unknown savevm section or instance '0000:00:02.0/virtio-rng' 0. Make sure that your current VM setup matches your saved VM setup, including any hotplugged devices {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-err-stderr"}} -qemu-system-x86_64: Unknown savevm section or instance '0000:00:02.0/virtio-rng' 0. Make sure that your current VM setup matches your saved VM setup, including any hotplugged devices {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-err-stderr"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-err-stderr"}} Not run: 259 Failures: internal-snapshots-qapi Failed 1 of 124 iotests I'll disable the test for now. Dave * Daniel P. Berrangé (berrange@redhat.com) wrote: > savevm, loadvm and delvm are some of the few HMP commands that have never > been converted to use QMP. The reasons for the lack of conversion are > that they blocked execution of the event thread, and the semantics > around choice of disks were ill-defined. > > Despite this downside, however, libvirt and applications using libvirt > have used these commands for as long as QMP has existed, via the > "human-monitor-command" passthrough command. IOW, while it is clearly > desirable to be able to fix the problems, they are not a blocker to > all real world usage. > > Meanwhile there is a need for other features which involve adding new > parameters to the commands. This is possible with HMP passthrough, but > it provides no reliable way for apps to introspect features, so using > QAPI modelling is highly desirable. > > This patch thus introduces new snapshot-{load,save,delete} commands to > QMP that are intended to replace the old HMP counterparts. The new > commands are given different names, because they will be using the new > QEMU job framework and thus will have diverging behaviour from the HMP > originals. It would thus be misleading to keep the same name. > > While this design uses the generic job framework, the current impl is > still blocking. The intention that the blocking problem is fixed later. > None the less applications using these new commands should assume that > they are asynchronous and thus wait for the job status change event to > indicate completion. > > In addition to using the job framework, the new commands require the > caller to be explicit about all the block device nodes used in the > snapshot operations, with no built-in default heuristics in use. > > Note that the existing "query-named-block-nodes" can be used to query > what snapshots currently exist for block nodes. > > Acked-by: Markus Armbruster <armbru@redhat.com> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> > --- > migration/savevm.c | 184 +++++++ > qapi/job.json | 9 +- > qapi/migration.json | 173 ++++++ > .../tests/internal-snapshots-qapi | 386 +++++++++++++ > .../tests/internal-snapshots-qapi.out | 520 ++++++++++++++++++ > 5 files changed, 1271 insertions(+), 1 deletion(-) > create mode 100755 tests/qemu-iotests/tests/internal-snapshots-qapi > create mode 100644 tests/qemu-iotests/tests/internal-snapshots-qapi.out > > diff --git a/migration/savevm.c b/migration/savevm.c > index 48186918a3..6b320423c7 100644 > --- a/migration/savevm.c > +++ b/migration/savevm.c > @@ -3077,3 +3077,187 @@ bool vmstate_check_only_migratable(const VMStateDescription *vmsd) > > return !(vmsd && vmsd->unmigratable); > } > + > +typedef struct SnapshotJob { > + Job common; > + char *tag; > + char *vmstate; > + strList *devices; > + Coroutine *co; > + Error **errp; > + bool ret; > +} SnapshotJob; > + > +static void qmp_snapshot_job_free(SnapshotJob *s) > +{ > + g_free(s->tag); > + g_free(s->vmstate); > + qapi_free_strList(s->devices); > +} > + > + > +static void snapshot_load_job_bh(void *opaque) > +{ > + Job *job = opaque; > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + int orig_vm_running; > + > + job_progress_set_remaining(&s->common, 1); > + > + orig_vm_running = runstate_is_running(); > + vm_stop(RUN_STATE_RESTORE_VM); > + > + s->ret = load_snapshot(s->tag, s->vmstate, true, s->devices, s->errp); > + if (s->ret && orig_vm_running) { > + vm_start(); > + } > + > + job_progress_update(&s->common, 1); > + > + qmp_snapshot_job_free(s); > + aio_co_wake(s->co); > +} > + > +static void snapshot_save_job_bh(void *opaque) > +{ > + Job *job = opaque; > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + > + job_progress_set_remaining(&s->common, 1); > + s->ret = save_snapshot(s->tag, false, s->vmstate, > + true, s->devices, s->errp); > + job_progress_update(&s->common, 1); > + > + qmp_snapshot_job_free(s); > + aio_co_wake(s->co); > +} > + > +static void snapshot_delete_job_bh(void *opaque) > +{ > + Job *job = opaque; > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + > + job_progress_set_remaining(&s->common, 1); > + s->ret = delete_snapshot(s->tag, true, s->devices, s->errp); > + job_progress_update(&s->common, 1); > + > + qmp_snapshot_job_free(s); > + aio_co_wake(s->co); > +} > + > +static int coroutine_fn snapshot_save_job_run(Job *job, Error **errp) > +{ > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + s->errp = errp; > + s->co = qemu_coroutine_self(); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + snapshot_save_job_bh, job); > + qemu_coroutine_yield(); > + return s->ret ? 0 : -1; > +} > + > +static int coroutine_fn snapshot_load_job_run(Job *job, Error **errp) > +{ > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + s->errp = errp; > + s->co = qemu_coroutine_self(); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + snapshot_load_job_bh, job); > + qemu_coroutine_yield(); > + return s->ret ? 0 : -1; > +} > + > +static int coroutine_fn snapshot_delete_job_run(Job *job, Error **errp) > +{ > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + s->errp = errp; > + s->co = qemu_coroutine_self(); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + snapshot_delete_job_bh, job); > + qemu_coroutine_yield(); > + return s->ret ? 0 : -1; > +} > + > + > +static const JobDriver snapshot_load_job_driver = { > + .instance_size = sizeof(SnapshotJob), > + .job_type = JOB_TYPE_SNAPSHOT_LOAD, > + .run = snapshot_load_job_run, > +}; > + > +static const JobDriver snapshot_save_job_driver = { > + .instance_size = sizeof(SnapshotJob), > + .job_type = JOB_TYPE_SNAPSHOT_SAVE, > + .run = snapshot_save_job_run, > +}; > + > +static const JobDriver snapshot_delete_job_driver = { > + .instance_size = sizeof(SnapshotJob), > + .job_type = JOB_TYPE_SNAPSHOT_DELETE, > + .run = snapshot_delete_job_run, > +}; > + > + > +void qmp_snapshot_save(const char *job_id, > + const char *tag, > + const char *vmstate, > + strList *devices, > + Error **errp) > +{ > + SnapshotJob *s; > + > + s = job_create(job_id, &snapshot_save_job_driver, NULL, > + qemu_get_aio_context(), JOB_MANUAL_DISMISS, > + NULL, NULL, errp); > + if (!s) { > + return; > + } > + > + s->tag = g_strdup(tag); > + s->vmstate = g_strdup(vmstate); > + s->devices = QAPI_CLONE(strList, devices); > + > + job_start(&s->common); > +} > + > +void qmp_snapshot_load(const char *job_id, > + const char *tag, > + const char *vmstate, > + strList *devices, > + Error **errp) > +{ > + SnapshotJob *s; > + > + s = job_create(job_id, &snapshot_load_job_driver, NULL, > + qemu_get_aio_context(), JOB_MANUAL_DISMISS, > + NULL, NULL, errp); > + if (!s) { > + return; > + } > + > + s->tag = g_strdup(tag); > + s->vmstate = g_strdup(vmstate); > + s->devices = QAPI_CLONE(strList, devices); > + > + job_start(&s->common); > +} > + > +void qmp_snapshot_delete(const char *job_id, > + const char *tag, > + strList *devices, > + Error **errp) > +{ > + SnapshotJob *s; > + > + s = job_create(job_id, &snapshot_delete_job_driver, NULL, > + qemu_get_aio_context(), JOB_MANUAL_DISMISS, > + NULL, NULL, errp); > + if (!s) { > + return; > + } > + > + s->tag = g_strdup(tag); > + s->devices = QAPI_CLONE(strList, devices); > + > + job_start(&s->common); > +} > diff --git a/qapi/job.json b/qapi/job.json > index 280c2f76f1..1a6ef03451 100644 > --- a/qapi/job.json > +++ b/qapi/job.json > @@ -22,10 +22,17 @@ > # > # @amend: image options amend job type, see "x-blockdev-amend" (since 5.1) > # > +# @snapshot-load: snapshot load job type, see "snapshot-load" (since 6.0) > +# > +# @snapshot-save: snapshot save job type, see "snapshot-save" (since 6.0) > +# > +# @snapshot-delete: snapshot delete job type, see "snapshot-delete" (since 6.0) > +# > # Since: 1.7 > ## > { 'enum': 'JobType', > - 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend'] } > + 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend', > + 'snapshot-load', 'snapshot-save', 'snapshot-delete'] } > > ## > # @JobStatus: > diff --git a/qapi/migration.json b/qapi/migration.json > index d1d9632c2a..5ca0ff9bed 100644 > --- a/qapi/migration.json > +++ b/qapi/migration.json > @@ -1843,3 +1843,176 @@ > # Since: 5.2 > ## > { 'command': 'query-dirty-rate', 'returns': 'DirtyRateInfo' } > + > +## > +# @snapshot-save: > +# > +# Save a VM snapshot > +# > +# @job-id: identifier for the newly created job > +# @tag: name of the snapshot to create > +# @vmstate: block device node name to save vmstate to > +# @devices: list of block device node names to save a snapshot to > +# > +# Applications should not assume that the snapshot save is complete > +# when this command returns. The job commands / events must be used > +# to determine completion and to fetch details of any errors that arise. > +# > +# Note that execution of the guest CPUs may be stopped during the > +# time it takes to save the snapshot. A future version of QEMU > +# may ensure CPUs are executing continuously. > +# > +# It is strongly recommended that @devices contain all writable > +# block device nodes if a consistent snapshot is required. > +# > +# If @tag already exists, an error will be reported > +# > +# Returns: nothing > +# > +# Example: > +# > +# -> { "execute": "snapshot-save", > +# "data": { > +# "job-id": "snapsave0", > +# "tag": "my-snap", > +# "vmstate": "disk0", > +# "devices": ["disk0", "disk1"] > +# } > +# } > +# <- { "return": { } } > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "created", "id": "snapsave0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "running", "id": "snapsave0"}} > +# <- {"event": "STOP"} > +# <- {"event": "RESUME"} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "waiting", "id": "snapsave0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "pending", "id": "snapsave0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "concluded", "id": "snapsave0"}} > +# -> {"execute": "query-jobs"} > +# <- {"return": [{"current-progress": 1, > +# "status": "concluded", > +# "total-progress": 1, > +# "type": "snapshot-save", > +# "id": "snapsave0"}]} > +# > +# Since: 6.0 > +## > +{ 'command': 'snapshot-save', > + 'data': { 'job-id': 'str', > + 'tag': 'str', > + 'vmstate': 'str', > + 'devices': ['str'] } } > + > +## > +# @snapshot-load: > +# > +# Load a VM snapshot > +# > +# @job-id: identifier for the newly created job > +# @tag: name of the snapshot to load. > +# @vmstate: block device node name to load vmstate from > +# @devices: list of block device node names to load a snapshot from > +# > +# Applications should not assume that the snapshot load is complete > +# when this command returns. The job commands / events must be used > +# to determine completion and to fetch details of any errors that arise. > +# > +# Note that execution of the guest CPUs will be stopped during the > +# time it takes to load the snapshot. > +# > +# It is strongly recommended that @devices contain all writable > +# block device nodes that can have changed since the original > +# @snapshot-save command execution. > +# > +# Returns: nothing > +# > +# Example: > +# > +# -> { "execute": "snapshot-load", > +# "data": { > +# "job-id": "snapload0", > +# "tag": "my-snap", > +# "vmstate": "disk0", > +# "devices": ["disk0", "disk1"] > +# } > +# } > +# <- { "return": { } } > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "created", "id": "snapload0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "running", "id": "snapload0"}} > +# <- {"event": "STOP"} > +# <- {"event": "RESUME"} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "waiting", "id": "snapload0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "pending", "id": "snapload0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "concluded", "id": "snapload0"}} > +# -> {"execute": "query-jobs"} > +# <- {"return": [{"current-progress": 1, > +# "status": "concluded", > +# "total-progress": 1, > +# "type": "snapshot-load", > +# "id": "snapload0"}]} > +# > +# Since: 6.0 > +## > +{ 'command': 'snapshot-load', > + 'data': { 'job-id': 'str', > + 'tag': 'str', > + 'vmstate': 'str', > + 'devices': ['str'] } } > + > +## > +# @snapshot-delete: > +# > +# Delete a VM snapshot > +# > +# @job-id: identifier for the newly created job > +# @tag: name of the snapshot to delete. > +# @devices: list of block device node names to delete a snapshot from > +# > +# Applications should not assume that the snapshot delete is complete > +# when this command returns. The job commands / events must be used > +# to determine completion and to fetch details of any errors that arise. > +# > +# Returns: nothing > +# > +# Example: > +# > +# -> { "execute": "snapshot-delete", > +# "data": { > +# "job-id": "snapdelete0", > +# "tag": "my-snap", > +# "devices": ["disk0", "disk1"] > +# } > +# } > +# <- { "return": { } } > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "created", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "running", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "waiting", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "pending", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "concluded", "id": "snapdelete0"}} > +# -> {"execute": "query-jobs"} > +# <- {"return": [{"current-progress": 1, > +# "status": "concluded", > +# "total-progress": 1, > +# "type": "snapshot-delete", > +# "id": "snapdelete0"}]} > +# > +# Since: 6.0 > +## > +{ 'command': 'snapshot-delete', > + 'data': { 'job-id': 'str', > + 'tag': 'str', > + 'devices': ['str'] } } > diff --git a/tests/qemu-iotests/tests/internal-snapshots-qapi b/tests/qemu-iotests/tests/internal-snapshots-qapi > new file mode 100755 > index 0000000000..6467eaaac0 > --- /dev/null > +++ b/tests/qemu-iotests/tests/internal-snapshots-qapi > @@ -0,0 +1,386 @@ > +#!/usr/bin/env bash > +# group: rw auto quick snapshot > +# > +# Test which nodes are involved in internal snapshots > +# > +# Copyright (C) 2020-2021 Red Hat, Inc. > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 2 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see <http://www.gnu.org/licenses/>. > +# > + > +# creator > +owner=berrange@redhat.com > + > +seq=`basename $0` > +echo "QA output created by $seq" > + > +status=1 # failure is the default! > + > +_cleanup() > +{ > + _cleanup_qemu > + _cleanup_test_img > + TEST_IMG="$TEST_IMG.alt1" _cleanup_test_img > + TEST_IMG="$TEST_IMG.alt2" _cleanup_test_img > + rm -f "$SOCK_DIR/nbd" > +} > +trap "_cleanup; exit \$status" 0 1 2 3 15 > + > +# get standard environment, filters and checks > +. ../common.rc > +. ../common.filter > +. ../common.qemu > + > +_supported_fmt qcow2 > +_supported_proto file > +_supported_os Linux > +_require_drivers copy-on-read > + > +# Internal snapshots are (currently) impossible with refcount_bits=1, > +# and generally impossible with external data files > +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file > + > +_require_devices virtio-blk > + > + > +size=128M > + > +if [ -n "$BACKING_FILE" ]; then > + _make_test_img -b "$BACKING_FILE" -F $IMGFMT $size > +else > + _make_test_img $size > +fi > +TEST_IMG="$TEST_IMG.alt1" _make_test_img $size > +IMGOPTS= IMGFMT=raw TEST_IMG="$TEST_IMG.alt2" _make_test_img $size > + > +export capture_events="JOB_STATUS_CHANGE STOP RESUME" > + > +wait_job() > +{ > + local job=$1 > + shift > + > + # All jobs start with two events... > + # > + # created > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > + # running > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > + > + # Next events vary depending on job type and > + # whether it succeeds or not. > + for evname in $@ > + do > + _wait_event $QEMU_HANDLE $evname > + done > + > + # All jobs finish off with two more events... > + # concluded > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"query-jobs\"}" "return" > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"job-dismiss\", \"arguments\": {\"id\": \"$job\"}}" "return" > + # null > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > +} > + > +run_save() > +{ > + local job=$1 > + local vmstate=$2 > + local devices=$3 > + local fail=$4 > + > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-save\", > + \"arguments\": { > + \"job-id\": \"$job\", > + \"tag\": \"snap0\", > + \"vmstate\": \"$vmstate\", > + \"devices\": $devices}}" "return" > + > + if [ $fail = 0 ]; then > + # job status: waiting, pending > + wait_job $job "STOP" "RESUME" "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" > + else > + # job status: aborting > + wait_job $job "JOB_STATUS_CHANGE" > + fi > +} > + > +run_load() > +{ > + local job=$1 > + local vmstate=$2 > + local devices=$3 > + local fail=$4 > + > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-load\", > + \"arguments\": { > + \"job-id\": \"$job\", > + \"tag\": \"snap0\", > + \"vmstate\": \"$vmstate\", > + \"devices\": $devices}}" "return" > + if [ $fail = 0 ]; then > + # job status: waiting, pending > + wait_job $job "STOP" "RESUME" "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" > + else > + # job status: aborting > + wait_job $job "STOP" "JOB_STATUS_CHANGE" > + fi > +} > + > +run_delete() > +{ > + local job=$1 > + local devices=$2 > + local fail=$3 > + > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-delete\", > + \"arguments\": { > + \"job-id\": \"$job\", > + \"tag\": \"snap0\", > + \"devices\": $devices}}" "return" > + if [ $fail = 0 ]; then > + # job status: waiting, pending > + wait_job $job "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" > + else > + # job status: aborting > + wait_job $job "JOB_STATUS_CHANGE" > + fi > +} > + > +start_qemu() > +{ > + keep_stderr=y > + _launch_qemu -nodefaults -nographic "$@" > + > + _send_qemu_cmd $QEMU_HANDLE '{"execute": "qmp_capabilities"}' 'return' > +} > + > +stop_qemu() > +{ > + _send_qemu_cmd $QEMU_HANDLE '{"execute": "quit"}' 'return' > + > + wait=1 _cleanup_qemu > +} > + > + > +echo > +echo "===== Snapshot single qcow2 image =====" > +echo > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-simple" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_load "load-simple" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_delete "delete-simple" "[\"diskfmt0\"]" 0 > +stop_qemu > + > + > +echo > +echo "===== Snapshot no image =====" > +echo > + > +# When snapshotting we need to pass at least one writable disk > +# otherwise there's no work to do > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-no-image" "diskfmt0" "[]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot missing image =====" > +echo > + > +# The block node names we pass need to actually exist > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-missing-image" "diskfmt1729" "[\"diskfmt1729\"]" 1 > +stop_qemu > + > +echo > +echo "===== Snapshot vmstate not in devices list =====" > +echo > + > +# The node name referred to for vmstate must be one of the nodes > +# being included in the snapshot, otherwise the vmstate that is > +# captured is liable to be overwritten making subsequent load > +# impossible > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" > +run_save "save-excluded-vmstate" "diskfmt0" "[\"diskfmt1\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot protocol instead of format =====" > +echo > + > +# The snapshot has to be done against the qcow2 format layer > +# not the underlying file protocol layer > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-proto-not-fmt" "disk0" "[\"disk0\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot dual qcow2 image =====" > +echo > + > +# We can snapshot multiple qcow2 disks at the same time > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" > +run_save "save-dual-image" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_load "load-dual-image" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_delete "delete-dual-image" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +stop_qemu > + > + > +echo > +echo "===== Snapshot error with raw image =====" > +echo > + > +# If we're snapshotting multiple disks, all must be capable > +# of supporting snapshots. A raw disk in the list must cause > +# an error. > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt2','node-name':'disk2'}" \ > + -blockdev "{'driver':'raw','file':'disk2','node-name':'diskfmt2'}" > +run_save "save-raw-fmt" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\", \"diskfmt2\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot with raw image excluded =====" > +echo > + > +# If we're snapshotting multiple disks, all must be capable > +# of supporting snapshots. A writable raw disk can be excluded > +# from the snapshot, though it means its data won't be restored > +# by later snapshot load operation. > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt2','node-name':'disk2'}" \ > + -blockdev "{'driver':'raw','file':'disk2','node-name':'diskfmt2'}" > +run_save "save-skip-raw" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_load "load-skip-raw" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_delete "delete-skip-raw" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +stop_qemu > + > +echo > +echo "===== Snapshot bad error reporting to stderr =====" > +echo > + > +# This demonstrates that we're not capturing vmstate loading failures > +# into QMP errors, they're ending up in stderr instead. vmstate needs > +# to report errors via Error object but that is a major piece of work > +# for the future. This test case's expected output log will need > +# adjusting when that is done. > + > +start_qemu \ > + -device virtio-rng \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > + > +run_save "save-err-stderr" "diskfmt0" "[\"diskfmt0\"]" 0 > +stop_qemu > + > +# leave off virtio-rng to provoke vmstate failure > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > + > +run_load "load-err-stderr" "diskfmt0" "[\"diskfmt0\"]" 1 > +run_delete "delete-err-stderr" "[\"diskfmt0\"]" 0 > + > +stop_qemu > + > + > +echo > +echo "===== Snapshot reuse same tag =====" > +echo > + > +# Validates that we get an error when reusing a snapshot tag that > +# already exists > + > +start_qemu \ > + -device virtio-rng \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > + > +run_save "save-err-stderr-initial" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_save "save-err-stderr-repeat1" "diskfmt0" "[\"diskfmt0\"]" 1 > +run_delete "delete-err-stderr" "[\"diskfmt0\"]" 0 > +run_save "save-err-stderr-repeat2" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_delete "delete-err-stderr-repeat2" "[\"diskfmt0\"]" 0 > + > +stop_qemu > + > +echo > +echo "===== Snapshot load does not exist =====" > +echo > + > +# Validates that we get an error when loading a snapshot that does > +# not exist > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_load "load-missing-snapshot" "diskfmt0" "[\"diskfmt0\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot delete does not exist =====" > +echo > + > +# Validates that we don't get an error when deleting a snapshot that > +# does not exist > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_delete "delete-missing-snapshot" "[\"diskfmt0\"]" 0 > +stop_qemu > + > + > +# success, all done > +echo "*** done" > +rm -f $seq.full > +status=0 > diff --git a/tests/qemu-iotests/tests/internal-snapshots-qapi.out b/tests/qemu-iotests/tests/internal-snapshots-qapi.out > new file mode 100644 > index 0000000000..26ff4a838c > --- /dev/null > +++ b/tests/qemu-iotests/tests/internal-snapshots-qapi.out > @@ -0,0 +1,520 @@ > +QA output created by internal-snapshots-qapi > +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 > +Formatting 'TEST_DIR/t.IMGFMT.alt1', fmt=IMGFMT size=134217728 > +Formatting 'TEST_DIR/t.qcow2.alt2', fmt=IMGFMT size=134217728 > + > +===== Snapshot single qcow2 image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-simple", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-simple"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-simple"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-simple"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-simple"}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-simple", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-simple"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-simple"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-simple"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-simple"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-simple", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-simple"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-simple"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-simple"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-simple"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot no image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-no-image", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": []}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-no-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-no-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-no-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-no-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-no-image", "error": "At least one device is required for snapshot"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-no-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-no-image"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot missing image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-missing-image", > + "tag": "snap0", > + "vmstate": "diskfmt1729", > + "devices": ["diskfmt1729"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-missing-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-missing-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-missing-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-missing-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-missing-image", "error": "No block device node 'diskfmt1729'"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-missing-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-missing-image"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot vmstate not in devices list ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-excluded-vmstate", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-excluded-vmstate"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-excluded-vmstate"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-excluded-vmstate"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-excluded-vmstate"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-excluded-vmstate", "error": "vmstate block device 'diskfmt0' does not exist"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-excluded-vmstate"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-excluded-vmstate"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot protocol instead of format ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-proto-not-fmt", > + "tag": "snap0", > + "vmstate": "disk0", > + "devices": ["disk0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-proto-not-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-proto-not-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-proto-not-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-proto-not-fmt"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-proto-not-fmt", "error": "Device 'disk0' is writable but does not support snapshots"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-proto-not-fmt"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-proto-not-fmt"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot dual qcow2 image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-dual-image", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-dual-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-dual-image"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-dual-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-dual-image"}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-dual-image", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-dual-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-dual-image"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-dual-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-dual-image"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-dual-image", > + "tag": "snap0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-dual-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-dual-image"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-dual-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-dual-image"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot error with raw image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-raw-fmt", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1", "diskfmt2"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-raw-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-raw-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-raw-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-raw-fmt"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-raw-fmt", "error": "Device 'diskfmt2' is writable but does not support snapshots"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-raw-fmt"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-raw-fmt"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot with raw image excluded ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-skip-raw", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-skip-raw"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-skip-raw"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-skip-raw"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-skip-raw"}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-skip-raw", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-skip-raw"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-skip-raw"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-skip-raw"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-skip-raw"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-skip-raw", > + "tag": "snap0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-skip-raw"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-skip-raw"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-skip-raw"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-skip-raw"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot bad error reporting to stderr ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-err-stderr", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-err-stderr"}} > +qemu-system-x86_64: Unknown savevm section or instance '0000:00:02.0/virtio-rng' 0. Make sure that your current VM setup matches your saved VM setup, including any hotplugged devices > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-err-stderr", "error": "Error -22 while loading VM state"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-err-stderr"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-err-stderr", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot reuse same tag ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr-initial", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-initial"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-initial"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-initial"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-initial"}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr-repeat1", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-repeat1"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-repeat1"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-err-stderr-repeat1"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-repeat1"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-repeat1", "error": "Snapshot 'snap0' already exists in one or more devices"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-repeat1"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-repeat1"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-err-stderr", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr"}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr-repeat2", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-repeat2"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-repeat2"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-repeat2"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-repeat2"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-err-stderr-repeat2", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr-repeat2"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr-repeat2"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr-repeat2"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr-repeat2"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot load does not exist ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-missing-snapshot", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-missing-snapshot"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-missing-snapshot", "error": "Snapshot 'snap0' does not exist in one or more devices"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-missing-snapshot"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-missing-snapshot"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot delete does not exist ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-missing-snapshot", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-missing-snapshot"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-missing-snapshot"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-missing-snapshot"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-missing-snapshot"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > +*** done > -- > 2.29.2 > >
On Thu, Feb 04, 2021 at 03:34:33PM +0000, Dr. David Alan Gilbert wrote: > This is (intermittently?) failing for me because of ordering issues: > > --- /home/dgilbert/git/migpull/tests/qemu-iotests/tests/internal-snapshots-qapi.out > +++ internal-snapshots-qapi.out.bad > @@ -344,8 +344,8 @@ > "vmstate": "diskfmt0", > "devices": ["diskfmt0"]}} > {"return": {}} > +qemu-system-x86_64: Unknown savevm section or instance '0000:00:02.0/virtio-rng' 0. Make sure that your current VM setup matches your saved VM setup, including any hotplugged devices > {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-err-stderr"}} > -qemu-system-x86_64: Unknown savevm section or instance '0000:00:02.0/virtio-rng' 0. Make sure that your current VM setup matches your saved VM setup, including any hotplugged devices > {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-err-stderr"}} > {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-err-stderr"}} > Not run: 259 > Failures: internal-snapshots-qapi > Failed 1 of 124 iotests > > I'll disable the test for now. Ok. I'm working on a patch series to make migration code use "Error **errp" that ought to fix this properly. Regards, Daniel
On 2/4/21 6:48 AM, Daniel P. Berrangé wrote: > savevm, loadvm and delvm are some of the few HMP commands that have never > been converted to use QMP. The reasons for the lack of conversion are > that they blocked execution of the event thread, and the semantics > around choice of disks were ill-defined. > > > Note that the existing "query-named-block-nodes" can be used to query > what snapshots currently exist for block nodes. > > Acked-by: Markus Armbruster <armbru@redhat.com> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> > --- > migration/savevm.c | 184 +++++++ > qapi/job.json | 9 +- > qapi/migration.json | 173 ++++++ > .../tests/internal-snapshots-qapi | 386 +++++++++++++ > .../tests/internal-snapshots-qapi.out | 520 ++++++++++++++++++ > 5 files changed, 1271 insertions(+), 1 deletion(-) > create mode 100755 tests/qemu-iotests/tests/internal-snapshots-qapi > create mode 100644 tests/qemu-iotests/tests/internal-snapshots-qapi.out I compared v10 and v11, and see that you addressed my concerns. Reviewed-by: Eric Blake <eblake@redhat.com>
On 2/4/21 7:48 AM, Daniel P. Berrangé wrote: > savevm, loadvm and delvm are some of the few HMP commands that have never > been converted to use QMP. The reasons for the lack of conversion are > that they blocked execution of the event thread, and the semantics > around choice of disks were ill-defined. > > Despite this downside, however, libvirt and applications using libvirt > have used these commands for as long as QMP has existed, via the > "human-monitor-command" passthrough command. IOW, while it is clearly > desirable to be able to fix the problems, they are not a blocker to > all real world usage. > > Meanwhile there is a need for other features which involve adding new > parameters to the commands. This is possible with HMP passthrough, but > it provides no reliable way for apps to introspect features, so using > QAPI modelling is highly desirable. > > This patch thus introduces new snapshot-{load,save,delete} commands to > QMP that are intended to replace the old HMP counterparts. The new > commands are given different names, because they will be using the new > QEMU job framework and thus will have diverging behaviour from the HMP > originals. It would thus be misleading to keep the same name. > > While this design uses the generic job framework, the current impl is > still blocking. The intention that the blocking problem is fixed later. > None the less applications using these new commands should assume that > they are asynchronous and thus wait for the job status change event to > indicate completion. > > In addition to using the job framework, the new commands require the > caller to be explicit about all the block device nodes used in the > snapshot operations, with no built-in default heuristics in use. > > Note that the existing "query-named-block-nodes" can be used to query > what snapshots currently exist for block nodes. > I wasn't sure how you were actually tackling this, but the approach laid out in the commit message here looks like a very good idea that doesn't require the full resolution of the savevm problem. Acked-by: John Snow <jsnow@redhat.com> > Acked-by: Markus Armbruster <armbru@redhat.com> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> > --- > migration/savevm.c | 184 +++++++ > qapi/job.json | 9 +- > qapi/migration.json | 173 ++++++ > .../tests/internal-snapshots-qapi | 386 +++++++++++++ > .../tests/internal-snapshots-qapi.out | 520 ++++++++++++++++++ > 5 files changed, 1271 insertions(+), 1 deletion(-) > create mode 100755 tests/qemu-iotests/tests/internal-snapshots-qapi > create mode 100644 tests/qemu-iotests/tests/internal-snapshots-qapi.out > > diff --git a/migration/savevm.c b/migration/savevm.c > index 48186918a3..6b320423c7 100644 > --- a/migration/savevm.c > +++ b/migration/savevm.c > @@ -3077,3 +3077,187 @@ bool vmstate_check_only_migratable(const VMStateDescription *vmsd) > > return !(vmsd && vmsd->unmigratable); > } > + > +typedef struct SnapshotJob { > + Job common; > + char *tag; > + char *vmstate; > + strList *devices; > + Coroutine *co; > + Error **errp; > + bool ret; > +} SnapshotJob; > + > +static void qmp_snapshot_job_free(SnapshotJob *s) > +{ > + g_free(s->tag); > + g_free(s->vmstate); > + qapi_free_strList(s->devices); > +} > + > + > +static void snapshot_load_job_bh(void *opaque) > +{ > + Job *job = opaque; > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + int orig_vm_running; > + > + job_progress_set_remaining(&s->common, 1); > + > + orig_vm_running = runstate_is_running(); > + vm_stop(RUN_STATE_RESTORE_VM); > + > + s->ret = load_snapshot(s->tag, s->vmstate, true, s->devices, s->errp); > + if (s->ret && orig_vm_running) { > + vm_start(); > + } > + > + job_progress_update(&s->common, 1); > + > + qmp_snapshot_job_free(s); > + aio_co_wake(s->co); > +} > + > +static void snapshot_save_job_bh(void *opaque) > +{ > + Job *job = opaque; > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + > + job_progress_set_remaining(&s->common, 1); > + s->ret = save_snapshot(s->tag, false, s->vmstate, > + true, s->devices, s->errp); > + job_progress_update(&s->common, 1); > + > + qmp_snapshot_job_free(s); > + aio_co_wake(s->co); > +} > + > +static void snapshot_delete_job_bh(void *opaque) > +{ > + Job *job = opaque; > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + > + job_progress_set_remaining(&s->common, 1); > + s->ret = delete_snapshot(s->tag, true, s->devices, s->errp); > + job_progress_update(&s->common, 1); > + > + qmp_snapshot_job_free(s); > + aio_co_wake(s->co); > +} > + > +static int coroutine_fn snapshot_save_job_run(Job *job, Error **errp) > +{ > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + s->errp = errp; > + s->co = qemu_coroutine_self(); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + snapshot_save_job_bh, job); > + qemu_coroutine_yield(); > + return s->ret ? 0 : -1; > +} > + > +static int coroutine_fn snapshot_load_job_run(Job *job, Error **errp) > +{ > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + s->errp = errp; > + s->co = qemu_coroutine_self(); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + snapshot_load_job_bh, job); > + qemu_coroutine_yield(); > + return s->ret ? 0 : -1; > +} > + > +static int coroutine_fn snapshot_delete_job_run(Job *job, Error **errp) > +{ > + SnapshotJob *s = container_of(job, SnapshotJob, common); > + s->errp = errp; > + s->co = qemu_coroutine_self(); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + snapshot_delete_job_bh, job); > + qemu_coroutine_yield(); > + return s->ret ? 0 : -1; > +} > + > + > +static const JobDriver snapshot_load_job_driver = { > + .instance_size = sizeof(SnapshotJob), > + .job_type = JOB_TYPE_SNAPSHOT_LOAD, > + .run = snapshot_load_job_run, > +}; > + > +static const JobDriver snapshot_save_job_driver = { > + .instance_size = sizeof(SnapshotJob), > + .job_type = JOB_TYPE_SNAPSHOT_SAVE, > + .run = snapshot_save_job_run, > +}; > + > +static const JobDriver snapshot_delete_job_driver = { > + .instance_size = sizeof(SnapshotJob), > + .job_type = JOB_TYPE_SNAPSHOT_DELETE, > + .run = snapshot_delete_job_run, > +}; > + > + > +void qmp_snapshot_save(const char *job_id, > + const char *tag, > + const char *vmstate, > + strList *devices, > + Error **errp) > +{ > + SnapshotJob *s; > + > + s = job_create(job_id, &snapshot_save_job_driver, NULL, > + qemu_get_aio_context(), JOB_MANUAL_DISMISS, > + NULL, NULL, errp); > + if (!s) { > + return; > + } > + > + s->tag = g_strdup(tag); > + s->vmstate = g_strdup(vmstate); > + s->devices = QAPI_CLONE(strList, devices); > + > + job_start(&s->common); > +} > + > +void qmp_snapshot_load(const char *job_id, > + const char *tag, > + const char *vmstate, > + strList *devices, > + Error **errp) > +{ > + SnapshotJob *s; > + > + s = job_create(job_id, &snapshot_load_job_driver, NULL, > + qemu_get_aio_context(), JOB_MANUAL_DISMISS, > + NULL, NULL, errp); > + if (!s) { > + return; > + } > + > + s->tag = g_strdup(tag); > + s->vmstate = g_strdup(vmstate); > + s->devices = QAPI_CLONE(strList, devices); > + > + job_start(&s->common); > +} > + > +void qmp_snapshot_delete(const char *job_id, > + const char *tag, > + strList *devices, > + Error **errp) > +{ > + SnapshotJob *s; > + > + s = job_create(job_id, &snapshot_delete_job_driver, NULL, > + qemu_get_aio_context(), JOB_MANUAL_DISMISS, > + NULL, NULL, errp); > + if (!s) { > + return; > + } > + > + s->tag = g_strdup(tag); > + s->devices = QAPI_CLONE(strList, devices); > + > + job_start(&s->common); > +} > diff --git a/qapi/job.json b/qapi/job.json > index 280c2f76f1..1a6ef03451 100644 > --- a/qapi/job.json > +++ b/qapi/job.json > @@ -22,10 +22,17 @@ > # > # @amend: image options amend job type, see "x-blockdev-amend" (since 5.1) > # > +# @snapshot-load: snapshot load job type, see "snapshot-load" (since 6.0) > +# > +# @snapshot-save: snapshot save job type, see "snapshot-save" (since 6.0) > +# > +# @snapshot-delete: snapshot delete job type, see "snapshot-delete" (since 6.0) > +# > # Since: 1.7 > ## > { 'enum': 'JobType', > - 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend'] } > + 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend', > + 'snapshot-load', 'snapshot-save', 'snapshot-delete'] } > > ## > # @JobStatus: > diff --git a/qapi/migration.json b/qapi/migration.json > index d1d9632c2a..5ca0ff9bed 100644 > --- a/qapi/migration.json > +++ b/qapi/migration.json > @@ -1843,3 +1843,176 @@ > # Since: 5.2 > ## > { 'command': 'query-dirty-rate', 'returns': 'DirtyRateInfo' } > + > +## > +# @snapshot-save: > +# > +# Save a VM snapshot > +# > +# @job-id: identifier for the newly created job > +# @tag: name of the snapshot to create > +# @vmstate: block device node name to save vmstate to > +# @devices: list of block device node names to save a snapshot to > +# > +# Applications should not assume that the snapshot save is complete > +# when this command returns. The job commands / events must be used > +# to determine completion and to fetch details of any errors that arise. > +# > +# Note that execution of the guest CPUs may be stopped during the > +# time it takes to save the snapshot. A future version of QEMU > +# may ensure CPUs are executing continuously. > +# > +# It is strongly recommended that @devices contain all writable > +# block device nodes if a consistent snapshot is required. > +# > +# If @tag already exists, an error will be reported > +# > +# Returns: nothing > +# > +# Example: > +# > +# -> { "execute": "snapshot-save", > +# "data": { > +# "job-id": "snapsave0", > +# "tag": "my-snap", > +# "vmstate": "disk0", > +# "devices": ["disk0", "disk1"] > +# } > +# } > +# <- { "return": { } } > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "created", "id": "snapsave0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "running", "id": "snapsave0"}} > +# <- {"event": "STOP"} > +# <- {"event": "RESUME"} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "waiting", "id": "snapsave0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "pending", "id": "snapsave0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "concluded", "id": "snapsave0"}} > +# -> {"execute": "query-jobs"} > +# <- {"return": [{"current-progress": 1, > +# "status": "concluded", > +# "total-progress": 1, > +# "type": "snapshot-save", > +# "id": "snapsave0"}]} > +# > +# Since: 6.0 > +## > +{ 'command': 'snapshot-save', > + 'data': { 'job-id': 'str', > + 'tag': 'str', > + 'vmstate': 'str', > + 'devices': ['str'] } } > + > +## > +# @snapshot-load: > +# > +# Load a VM snapshot > +# > +# @job-id: identifier for the newly created job > +# @tag: name of the snapshot to load. > +# @vmstate: block device node name to load vmstate from > +# @devices: list of block device node names to load a snapshot from > +# > +# Applications should not assume that the snapshot load is complete > +# when this command returns. The job commands / events must be used > +# to determine completion and to fetch details of any errors that arise. > +# > +# Note that execution of the guest CPUs will be stopped during the > +# time it takes to load the snapshot. > +# > +# It is strongly recommended that @devices contain all writable > +# block device nodes that can have changed since the original > +# @snapshot-save command execution. > +# > +# Returns: nothing > +# > +# Example: > +# > +# -> { "execute": "snapshot-load", > +# "data": { > +# "job-id": "snapload0", > +# "tag": "my-snap", > +# "vmstate": "disk0", > +# "devices": ["disk0", "disk1"] > +# } > +# } > +# <- { "return": { } } > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "created", "id": "snapload0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "running", "id": "snapload0"}} > +# <- {"event": "STOP"} > +# <- {"event": "RESUME"} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "waiting", "id": "snapload0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "pending", "id": "snapload0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "concluded", "id": "snapload0"}} > +# -> {"execute": "query-jobs"} > +# <- {"return": [{"current-progress": 1, > +# "status": "concluded", > +# "total-progress": 1, > +# "type": "snapshot-load", > +# "id": "snapload0"}]} > +# > +# Since: 6.0 > +## > +{ 'command': 'snapshot-load', > + 'data': { 'job-id': 'str', > + 'tag': 'str', > + 'vmstate': 'str', > + 'devices': ['str'] } } > + > +## > +# @snapshot-delete: > +# > +# Delete a VM snapshot > +# > +# @job-id: identifier for the newly created job > +# @tag: name of the snapshot to delete. > +# @devices: list of block device node names to delete a snapshot from > +# > +# Applications should not assume that the snapshot delete is complete > +# when this command returns. The job commands / events must be used > +# to determine completion and to fetch details of any errors that arise. > +# > +# Returns: nothing > +# > +# Example: > +# > +# -> { "execute": "snapshot-delete", > +# "data": { > +# "job-id": "snapdelete0", > +# "tag": "my-snap", > +# "devices": ["disk0", "disk1"] > +# } > +# } > +# <- { "return": { } } > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "created", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "running", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "waiting", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "pending", "id": "snapdelete0"}} > +# <- {"event": "JOB_STATUS_CHANGE", > +# "data": {"status": "concluded", "id": "snapdelete0"}} > +# -> {"execute": "query-jobs"} > +# <- {"return": [{"current-progress": 1, > +# "status": "concluded", > +# "total-progress": 1, > +# "type": "snapshot-delete", > +# "id": "snapdelete0"}]} > +# > +# Since: 6.0 > +## > +{ 'command': 'snapshot-delete', > + 'data': { 'job-id': 'str', > + 'tag': 'str', > + 'devices': ['str'] } } > diff --git a/tests/qemu-iotests/tests/internal-snapshots-qapi b/tests/qemu-iotests/tests/internal-snapshots-qapi > new file mode 100755 > index 0000000000..6467eaaac0 > --- /dev/null > +++ b/tests/qemu-iotests/tests/internal-snapshots-qapi > @@ -0,0 +1,386 @@ > +#!/usr/bin/env bash > +# group: rw auto quick snapshot > +# > +# Test which nodes are involved in internal snapshots > +# > +# Copyright (C) 2020-2021 Red Hat, Inc. > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 2 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see <http://www.gnu.org/licenses/>. > +# > + > +# creator > +owner=berrange@redhat.com > + > +seq=`basename $0` > +echo "QA output created by $seq" > + > +status=1 # failure is the default! > + > +_cleanup() > +{ > + _cleanup_qemu > + _cleanup_test_img > + TEST_IMG="$TEST_IMG.alt1" _cleanup_test_img > + TEST_IMG="$TEST_IMG.alt2" _cleanup_test_img > + rm -f "$SOCK_DIR/nbd" > +} > +trap "_cleanup; exit \$status" 0 1 2 3 15 > + > +# get standard environment, filters and checks > +. ../common.rc > +. ../common.filter > +. ../common.qemu > + > +_supported_fmt qcow2 > +_supported_proto file > +_supported_os Linux > +_require_drivers copy-on-read > + > +# Internal snapshots are (currently) impossible with refcount_bits=1, > +# and generally impossible with external data files > +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file > + > +_require_devices virtio-blk > + > + > +size=128M > + > +if [ -n "$BACKING_FILE" ]; then > + _make_test_img -b "$BACKING_FILE" -F $IMGFMT $size > +else > + _make_test_img $size > +fi > +TEST_IMG="$TEST_IMG.alt1" _make_test_img $size > +IMGOPTS= IMGFMT=raw TEST_IMG="$TEST_IMG.alt2" _make_test_img $size > + > +export capture_events="JOB_STATUS_CHANGE STOP RESUME" > + > +wait_job() > +{ > + local job=$1 > + shift > + > + # All jobs start with two events... > + # > + # created > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > + # running > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > + > + # Next events vary depending on job type and > + # whether it succeeds or not. > + for evname in $@ > + do > + _wait_event $QEMU_HANDLE $evname > + done > + > + # All jobs finish off with two more events... > + # concluded > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"query-jobs\"}" "return" > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"job-dismiss\", \"arguments\": {\"id\": \"$job\"}}" "return" > + # null > + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" > +} > + > +run_save() > +{ > + local job=$1 > + local vmstate=$2 > + local devices=$3 > + local fail=$4 > + > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-save\", > + \"arguments\": { > + \"job-id\": \"$job\", > + \"tag\": \"snap0\", > + \"vmstate\": \"$vmstate\", > + \"devices\": $devices}}" "return" > + > + if [ $fail = 0 ]; then > + # job status: waiting, pending > + wait_job $job "STOP" "RESUME" "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" > + else > + # job status: aborting > + wait_job $job "JOB_STATUS_CHANGE" > + fi > +} > + > +run_load() > +{ > + local job=$1 > + local vmstate=$2 > + local devices=$3 > + local fail=$4 > + > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-load\", > + \"arguments\": { > + \"job-id\": \"$job\", > + \"tag\": \"snap0\", > + \"vmstate\": \"$vmstate\", > + \"devices\": $devices}}" "return" > + if [ $fail = 0 ]; then > + # job status: waiting, pending > + wait_job $job "STOP" "RESUME" "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" > + else > + # job status: aborting > + wait_job $job "STOP" "JOB_STATUS_CHANGE" > + fi > +} > + > +run_delete() > +{ > + local job=$1 > + local devices=$2 > + local fail=$3 > + > + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-delete\", > + \"arguments\": { > + \"job-id\": \"$job\", > + \"tag\": \"snap0\", > + \"devices\": $devices}}" "return" > + if [ $fail = 0 ]; then > + # job status: waiting, pending > + wait_job $job "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" > + else > + # job status: aborting > + wait_job $job "JOB_STATUS_CHANGE" > + fi > +} > + > +start_qemu() > +{ > + keep_stderr=y > + _launch_qemu -nodefaults -nographic "$@" > + > + _send_qemu_cmd $QEMU_HANDLE '{"execute": "qmp_capabilities"}' 'return' > +} > + > +stop_qemu() > +{ > + _send_qemu_cmd $QEMU_HANDLE '{"execute": "quit"}' 'return' > + > + wait=1 _cleanup_qemu > +} > + > + > +echo > +echo "===== Snapshot single qcow2 image =====" > +echo > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-simple" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_load "load-simple" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_delete "delete-simple" "[\"diskfmt0\"]" 0 > +stop_qemu > + > + > +echo > +echo "===== Snapshot no image =====" > +echo > + > +# When snapshotting we need to pass at least one writable disk > +# otherwise there's no work to do > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-no-image" "diskfmt0" "[]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot missing image =====" > +echo > + > +# The block node names we pass need to actually exist > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-missing-image" "diskfmt1729" "[\"diskfmt1729\"]" 1 > +stop_qemu > + > +echo > +echo "===== Snapshot vmstate not in devices list =====" > +echo > + > +# The node name referred to for vmstate must be one of the nodes > +# being included in the snapshot, otherwise the vmstate that is > +# captured is liable to be overwritten making subsequent load > +# impossible > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" > +run_save "save-excluded-vmstate" "diskfmt0" "[\"diskfmt1\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot protocol instead of format =====" > +echo > + > +# The snapshot has to be done against the qcow2 format layer > +# not the underlying file protocol layer > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_save "save-proto-not-fmt" "disk0" "[\"disk0\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot dual qcow2 image =====" > +echo > + > +# We can snapshot multiple qcow2 disks at the same time > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" > +run_save "save-dual-image" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_load "load-dual-image" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_delete "delete-dual-image" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +stop_qemu > + > + > +echo > +echo "===== Snapshot error with raw image =====" > +echo > + > +# If we're snapshotting multiple disks, all must be capable > +# of supporting snapshots. A raw disk in the list must cause > +# an error. > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt2','node-name':'disk2'}" \ > + -blockdev "{'driver':'raw','file':'disk2','node-name':'diskfmt2'}" > +run_save "save-raw-fmt" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\", \"diskfmt2\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot with raw image excluded =====" > +echo > + > +# If we're snapshotting multiple disks, all must be capable > +# of supporting snapshots. A writable raw disk can be excluded > +# from the snapshot, though it means its data won't be restored > +# by later snapshot load operation. > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ > + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt2','node-name':'disk2'}" \ > + -blockdev "{'driver':'raw','file':'disk2','node-name':'diskfmt2'}" > +run_save "save-skip-raw" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_load "load-skip-raw" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +run_delete "delete-skip-raw" "[\"diskfmt0\", \"diskfmt1\"]" 0 > +stop_qemu > + > +echo > +echo "===== Snapshot bad error reporting to stderr =====" > +echo > + > +# This demonstrates that we're not capturing vmstate loading failures > +# into QMP errors, they're ending up in stderr instead. vmstate needs > +# to report errors via Error object but that is a major piece of work > +# for the future. This test case's expected output log will need > +# adjusting when that is done. > + > +start_qemu \ > + -device virtio-rng \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > + > +run_save "save-err-stderr" "diskfmt0" "[\"diskfmt0\"]" 0 > +stop_qemu > + > +# leave off virtio-rng to provoke vmstate failure > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > + > +run_load "load-err-stderr" "diskfmt0" "[\"diskfmt0\"]" 1 > +run_delete "delete-err-stderr" "[\"diskfmt0\"]" 0 > + > +stop_qemu > + > + > +echo > +echo "===== Snapshot reuse same tag =====" > +echo > + > +# Validates that we get an error when reusing a snapshot tag that > +# already exists > + > +start_qemu \ > + -device virtio-rng \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > + > +run_save "save-err-stderr-initial" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_save "save-err-stderr-repeat1" "diskfmt0" "[\"diskfmt0\"]" 1 > +run_delete "delete-err-stderr" "[\"diskfmt0\"]" 0 > +run_save "save-err-stderr-repeat2" "diskfmt0" "[\"diskfmt0\"]" 0 > +run_delete "delete-err-stderr-repeat2" "[\"diskfmt0\"]" 0 > + > +stop_qemu > + > +echo > +echo "===== Snapshot load does not exist =====" > +echo > + > +# Validates that we get an error when loading a snapshot that does > +# not exist > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_load "load-missing-snapshot" "diskfmt0" "[\"diskfmt0\"]" 1 > +stop_qemu > + > + > +echo > +echo "===== Snapshot delete does not exist =====" > +echo > + > +# Validates that we don't get an error when deleting a snapshot that > +# does not exist > + > +start_qemu \ > + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ > + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" > +run_delete "delete-missing-snapshot" "[\"diskfmt0\"]" 0 > +stop_qemu > + > + > +# success, all done > +echo "*** done" > +rm -f $seq.full > +status=0 > diff --git a/tests/qemu-iotests/tests/internal-snapshots-qapi.out b/tests/qemu-iotests/tests/internal-snapshots-qapi.out > new file mode 100644 > index 0000000000..26ff4a838c > --- /dev/null > +++ b/tests/qemu-iotests/tests/internal-snapshots-qapi.out > @@ -0,0 +1,520 @@ > +QA output created by internal-snapshots-qapi > +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 > +Formatting 'TEST_DIR/t.IMGFMT.alt1', fmt=IMGFMT size=134217728 > +Formatting 'TEST_DIR/t.qcow2.alt2', fmt=IMGFMT size=134217728 > + > +===== Snapshot single qcow2 image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-simple", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-simple"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-simple"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-simple"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-simple"}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-simple", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-simple"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-simple"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-simple"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-simple"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-simple", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-simple"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-simple"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-simple"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-simple"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-simple"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot no image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-no-image", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": []}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-no-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-no-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-no-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-no-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-no-image", "error": "At least one device is required for snapshot"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-no-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-no-image"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot missing image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-missing-image", > + "tag": "snap0", > + "vmstate": "diskfmt1729", > + "devices": ["diskfmt1729"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-missing-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-missing-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-missing-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-missing-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-missing-image", "error": "No block device node 'diskfmt1729'"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-missing-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-missing-image"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot vmstate not in devices list ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-excluded-vmstate", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-excluded-vmstate"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-excluded-vmstate"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-excluded-vmstate"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-excluded-vmstate"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-excluded-vmstate", "error": "vmstate block device 'diskfmt0' does not exist"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-excluded-vmstate"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-excluded-vmstate"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot protocol instead of format ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-proto-not-fmt", > + "tag": "snap0", > + "vmstate": "disk0", > + "devices": ["disk0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-proto-not-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-proto-not-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-proto-not-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-proto-not-fmt"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-proto-not-fmt", "error": "Device 'disk0' is writable but does not support snapshots"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-proto-not-fmt"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-proto-not-fmt"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot dual qcow2 image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-dual-image", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-dual-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-dual-image"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-dual-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-dual-image"}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-dual-image", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-dual-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-dual-image"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-dual-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-dual-image"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-dual-image", > + "tag": "snap0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-dual-image"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-dual-image"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-dual-image"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-dual-image"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-dual-image"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot error with raw image ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-raw-fmt", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1", "diskfmt2"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-raw-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-raw-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-raw-fmt"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-raw-fmt"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-raw-fmt", "error": "Device 'diskfmt2' is writable but does not support snapshots"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-raw-fmt"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-raw-fmt"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot with raw image excluded ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-skip-raw", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-skip-raw"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-skip-raw"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-skip-raw"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-skip-raw"}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-skip-raw", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-skip-raw"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-skip-raw"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-skip-raw"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-skip-raw"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-skip-raw", > + "tag": "snap0", > + "devices": ["diskfmt0", "diskfmt1"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-skip-raw"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-skip-raw"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-skip-raw"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-skip-raw"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-skip-raw"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot bad error reporting to stderr ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-err-stderr", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-err-stderr"}} > +qemu-system-x86_64: Unknown savevm section or instance '0000:00:02.0/virtio-rng' 0. Make sure that your current VM setup matches your saved VM setup, including any hotplugged devices > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-err-stderr", "error": "Error -22 while loading VM state"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-err-stderr"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-err-stderr", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot reuse same tag ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr-initial", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr-initial"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-initial"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-initial"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-initial"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-initial"}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr-repeat1", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-repeat1"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-repeat1"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-err-stderr-repeat1"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-repeat1"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-repeat1", "error": "Snapshot 'snap0' already exists in one or more devices"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-repeat1"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-repeat1"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-err-stderr", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr"}} > +{"execute": "snapshot-save", > + "arguments": { > + "job-id": "save-err-stderr-repeat2", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-repeat2"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-repeat2"}]} > +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-repeat2"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-repeat2"}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-err-stderr-repeat2", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr-repeat2"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr-repeat2"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr-repeat2"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr-repeat2"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr-repeat2"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot load does not exist ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-load", > + "arguments": { > + "job-id": "load-missing-snapshot", > + "tag": "snap0", > + "vmstate": "diskfmt0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-missing-snapshot"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-missing-snapshot", "error": "Snapshot 'snap0' does not exist in one or more devices"}]} > +{"execute": "job-dismiss", "arguments": {"id": "load-missing-snapshot"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-missing-snapshot"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > + > +===== Snapshot delete does not exist ===== > + > +{"execute": "qmp_capabilities"} > +{"return": {}} > +{"execute": "snapshot-delete", > + "arguments": { > + "job-id": "delete-missing-snapshot", > + "tag": "snap0", > + "devices": ["diskfmt0"]}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-missing-snapshot"}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-missing-snapshot"}} > +{"execute": "query-jobs"} > +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-missing-snapshot"}]} > +{"execute": "job-dismiss", "arguments": {"id": "delete-missing-snapshot"}} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-missing-snapshot"}} > +{"execute": "quit"} > +{"return": {}} > +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} > +*** done >
diff --git a/migration/savevm.c b/migration/savevm.c index 48186918a3..6b320423c7 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -3077,3 +3077,187 @@ bool vmstate_check_only_migratable(const VMStateDescription *vmsd) return !(vmsd && vmsd->unmigratable); } + +typedef struct SnapshotJob { + Job common; + char *tag; + char *vmstate; + strList *devices; + Coroutine *co; + Error **errp; + bool ret; +} SnapshotJob; + +static void qmp_snapshot_job_free(SnapshotJob *s) +{ + g_free(s->tag); + g_free(s->vmstate); + qapi_free_strList(s->devices); +} + + +static void snapshot_load_job_bh(void *opaque) +{ + Job *job = opaque; + SnapshotJob *s = container_of(job, SnapshotJob, common); + int orig_vm_running; + + job_progress_set_remaining(&s->common, 1); + + orig_vm_running = runstate_is_running(); + vm_stop(RUN_STATE_RESTORE_VM); + + s->ret = load_snapshot(s->tag, s->vmstate, true, s->devices, s->errp); + if (s->ret && orig_vm_running) { + vm_start(); + } + + job_progress_update(&s->common, 1); + + qmp_snapshot_job_free(s); + aio_co_wake(s->co); +} + +static void snapshot_save_job_bh(void *opaque) +{ + Job *job = opaque; + SnapshotJob *s = container_of(job, SnapshotJob, common); + + job_progress_set_remaining(&s->common, 1); + s->ret = save_snapshot(s->tag, false, s->vmstate, + true, s->devices, s->errp); + job_progress_update(&s->common, 1); + + qmp_snapshot_job_free(s); + aio_co_wake(s->co); +} + +static void snapshot_delete_job_bh(void *opaque) +{ + Job *job = opaque; + SnapshotJob *s = container_of(job, SnapshotJob, common); + + job_progress_set_remaining(&s->common, 1); + s->ret = delete_snapshot(s->tag, true, s->devices, s->errp); + job_progress_update(&s->common, 1); + + qmp_snapshot_job_free(s); + aio_co_wake(s->co); +} + +static int coroutine_fn snapshot_save_job_run(Job *job, Error **errp) +{ + SnapshotJob *s = container_of(job, SnapshotJob, common); + s->errp = errp; + s->co = qemu_coroutine_self(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + snapshot_save_job_bh, job); + qemu_coroutine_yield(); + return s->ret ? 0 : -1; +} + +static int coroutine_fn snapshot_load_job_run(Job *job, Error **errp) +{ + SnapshotJob *s = container_of(job, SnapshotJob, common); + s->errp = errp; + s->co = qemu_coroutine_self(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + snapshot_load_job_bh, job); + qemu_coroutine_yield(); + return s->ret ? 0 : -1; +} + +static int coroutine_fn snapshot_delete_job_run(Job *job, Error **errp) +{ + SnapshotJob *s = container_of(job, SnapshotJob, common); + s->errp = errp; + s->co = qemu_coroutine_self(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + snapshot_delete_job_bh, job); + qemu_coroutine_yield(); + return s->ret ? 0 : -1; +} + + +static const JobDriver snapshot_load_job_driver = { + .instance_size = sizeof(SnapshotJob), + .job_type = JOB_TYPE_SNAPSHOT_LOAD, + .run = snapshot_load_job_run, +}; + +static const JobDriver snapshot_save_job_driver = { + .instance_size = sizeof(SnapshotJob), + .job_type = JOB_TYPE_SNAPSHOT_SAVE, + .run = snapshot_save_job_run, +}; + +static const JobDriver snapshot_delete_job_driver = { + .instance_size = sizeof(SnapshotJob), + .job_type = JOB_TYPE_SNAPSHOT_DELETE, + .run = snapshot_delete_job_run, +}; + + +void qmp_snapshot_save(const char *job_id, + const char *tag, + const char *vmstate, + strList *devices, + Error **errp) +{ + SnapshotJob *s; + + s = job_create(job_id, &snapshot_save_job_driver, NULL, + qemu_get_aio_context(), JOB_MANUAL_DISMISS, + NULL, NULL, errp); + if (!s) { + return; + } + + s->tag = g_strdup(tag); + s->vmstate = g_strdup(vmstate); + s->devices = QAPI_CLONE(strList, devices); + + job_start(&s->common); +} + +void qmp_snapshot_load(const char *job_id, + const char *tag, + const char *vmstate, + strList *devices, + Error **errp) +{ + SnapshotJob *s; + + s = job_create(job_id, &snapshot_load_job_driver, NULL, + qemu_get_aio_context(), JOB_MANUAL_DISMISS, + NULL, NULL, errp); + if (!s) { + return; + } + + s->tag = g_strdup(tag); + s->vmstate = g_strdup(vmstate); + s->devices = QAPI_CLONE(strList, devices); + + job_start(&s->common); +} + +void qmp_snapshot_delete(const char *job_id, + const char *tag, + strList *devices, + Error **errp) +{ + SnapshotJob *s; + + s = job_create(job_id, &snapshot_delete_job_driver, NULL, + qemu_get_aio_context(), JOB_MANUAL_DISMISS, + NULL, NULL, errp); + if (!s) { + return; + } + + s->tag = g_strdup(tag); + s->devices = QAPI_CLONE(strList, devices); + + job_start(&s->common); +} diff --git a/qapi/job.json b/qapi/job.json index 280c2f76f1..1a6ef03451 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -22,10 +22,17 @@ # # @amend: image options amend job type, see "x-blockdev-amend" (since 5.1) # +# @snapshot-load: snapshot load job type, see "snapshot-load" (since 6.0) +# +# @snapshot-save: snapshot save job type, see "snapshot-save" (since 6.0) +# +# @snapshot-delete: snapshot delete job type, see "snapshot-delete" (since 6.0) +# # Since: 1.7 ## { 'enum': 'JobType', - 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend'] } + 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend', + 'snapshot-load', 'snapshot-save', 'snapshot-delete'] } ## # @JobStatus: diff --git a/qapi/migration.json b/qapi/migration.json index d1d9632c2a..5ca0ff9bed 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1843,3 +1843,176 @@ # Since: 5.2 ## { 'command': 'query-dirty-rate', 'returns': 'DirtyRateInfo' } + +## +# @snapshot-save: +# +# Save a VM snapshot +# +# @job-id: identifier for the newly created job +# @tag: name of the snapshot to create +# @vmstate: block device node name to save vmstate to +# @devices: list of block device node names to save a snapshot to +# +# Applications should not assume that the snapshot save is complete +# when this command returns. The job commands / events must be used +# to determine completion and to fetch details of any errors that arise. +# +# Note that execution of the guest CPUs may be stopped during the +# time it takes to save the snapshot. A future version of QEMU +# may ensure CPUs are executing continuously. +# +# It is strongly recommended that @devices contain all writable +# block device nodes if a consistent snapshot is required. +# +# If @tag already exists, an error will be reported +# +# Returns: nothing +# +# Example: +# +# -> { "execute": "snapshot-save", +# "data": { +# "job-id": "snapsave0", +# "tag": "my-snap", +# "vmstate": "disk0", +# "devices": ["disk0", "disk1"] +# } +# } +# <- { "return": { } } +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "created", "id": "snapsave0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "running", "id": "snapsave0"}} +# <- {"event": "STOP"} +# <- {"event": "RESUME"} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "waiting", "id": "snapsave0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "pending", "id": "snapsave0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "concluded", "id": "snapsave0"}} +# -> {"execute": "query-jobs"} +# <- {"return": [{"current-progress": 1, +# "status": "concluded", +# "total-progress": 1, +# "type": "snapshot-save", +# "id": "snapsave0"}]} +# +# Since: 6.0 +## +{ 'command': 'snapshot-save', + 'data': { 'job-id': 'str', + 'tag': 'str', + 'vmstate': 'str', + 'devices': ['str'] } } + +## +# @snapshot-load: +# +# Load a VM snapshot +# +# @job-id: identifier for the newly created job +# @tag: name of the snapshot to load. +# @vmstate: block device node name to load vmstate from +# @devices: list of block device node names to load a snapshot from +# +# Applications should not assume that the snapshot load is complete +# when this command returns. The job commands / events must be used +# to determine completion and to fetch details of any errors that arise. +# +# Note that execution of the guest CPUs will be stopped during the +# time it takes to load the snapshot. +# +# It is strongly recommended that @devices contain all writable +# block device nodes that can have changed since the original +# @snapshot-save command execution. +# +# Returns: nothing +# +# Example: +# +# -> { "execute": "snapshot-load", +# "data": { +# "job-id": "snapload0", +# "tag": "my-snap", +# "vmstate": "disk0", +# "devices": ["disk0", "disk1"] +# } +# } +# <- { "return": { } } +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "created", "id": "snapload0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "running", "id": "snapload0"}} +# <- {"event": "STOP"} +# <- {"event": "RESUME"} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "waiting", "id": "snapload0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "pending", "id": "snapload0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "concluded", "id": "snapload0"}} +# -> {"execute": "query-jobs"} +# <- {"return": [{"current-progress": 1, +# "status": "concluded", +# "total-progress": 1, +# "type": "snapshot-load", +# "id": "snapload0"}]} +# +# Since: 6.0 +## +{ 'command': 'snapshot-load', + 'data': { 'job-id': 'str', + 'tag': 'str', + 'vmstate': 'str', + 'devices': ['str'] } } + +## +# @snapshot-delete: +# +# Delete a VM snapshot +# +# @job-id: identifier for the newly created job +# @tag: name of the snapshot to delete. +# @devices: list of block device node names to delete a snapshot from +# +# Applications should not assume that the snapshot delete is complete +# when this command returns. The job commands / events must be used +# to determine completion and to fetch details of any errors that arise. +# +# Returns: nothing +# +# Example: +# +# -> { "execute": "snapshot-delete", +# "data": { +# "job-id": "snapdelete0", +# "tag": "my-snap", +# "devices": ["disk0", "disk1"] +# } +# } +# <- { "return": { } } +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "created", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "running", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "waiting", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "pending", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "data": {"status": "concluded", "id": "snapdelete0"}} +# -> {"execute": "query-jobs"} +# <- {"return": [{"current-progress": 1, +# "status": "concluded", +# "total-progress": 1, +# "type": "snapshot-delete", +# "id": "snapdelete0"}]} +# +# Since: 6.0 +## +{ 'command': 'snapshot-delete', + 'data': { 'job-id': 'str', + 'tag': 'str', + 'devices': ['str'] } } diff --git a/tests/qemu-iotests/tests/internal-snapshots-qapi b/tests/qemu-iotests/tests/internal-snapshots-qapi new file mode 100755 index 0000000000..6467eaaac0 --- /dev/null +++ b/tests/qemu-iotests/tests/internal-snapshots-qapi @@ -0,0 +1,386 @@ +#!/usr/bin/env bash +# group: rw auto quick snapshot +# +# Test which nodes are involved in internal snapshots +# +# Copyright (C) 2020-2021 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=berrange@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + TEST_IMG="$TEST_IMG.alt1" _cleanup_test_img + TEST_IMG="$TEST_IMG.alt2" _cleanup_test_img + rm -f "$SOCK_DIR/nbd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter +. ../common.qemu + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux +_require_drivers copy-on-read + +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file + +_require_devices virtio-blk + + +size=128M + +if [ -n "$BACKING_FILE" ]; then + _make_test_img -b "$BACKING_FILE" -F $IMGFMT $size +else + _make_test_img $size +fi +TEST_IMG="$TEST_IMG.alt1" _make_test_img $size +IMGOPTS= IMGFMT=raw TEST_IMG="$TEST_IMG.alt2" _make_test_img $size + +export capture_events="JOB_STATUS_CHANGE STOP RESUME" + +wait_job() +{ + local job=$1 + shift + + # All jobs start with two events... + # + # created + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" + # running + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" + + # Next events vary depending on job type and + # whether it succeeds or not. + for evname in $@ + do + _wait_event $QEMU_HANDLE $evname + done + + # All jobs finish off with two more events... + # concluded + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"query-jobs\"}" "return" + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"job-dismiss\", \"arguments\": {\"id\": \"$job\"}}" "return" + # null + _wait_event $QEMU_HANDLE "JOB_STATUS_CHANGE" +} + +run_save() +{ + local job=$1 + local vmstate=$2 + local devices=$3 + local fail=$4 + + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-save\", + \"arguments\": { + \"job-id\": \"$job\", + \"tag\": \"snap0\", + \"vmstate\": \"$vmstate\", + \"devices\": $devices}}" "return" + + if [ $fail = 0 ]; then + # job status: waiting, pending + wait_job $job "STOP" "RESUME" "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" + else + # job status: aborting + wait_job $job "JOB_STATUS_CHANGE" + fi +} + +run_load() +{ + local job=$1 + local vmstate=$2 + local devices=$3 + local fail=$4 + + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-load\", + \"arguments\": { + \"job-id\": \"$job\", + \"tag\": \"snap0\", + \"vmstate\": \"$vmstate\", + \"devices\": $devices}}" "return" + if [ $fail = 0 ]; then + # job status: waiting, pending + wait_job $job "STOP" "RESUME" "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" + else + # job status: aborting + wait_job $job "STOP" "JOB_STATUS_CHANGE" + fi +} + +run_delete() +{ + local job=$1 + local devices=$2 + local fail=$3 + + _send_qemu_cmd $QEMU_HANDLE "{\"execute\": \"snapshot-delete\", + \"arguments\": { + \"job-id\": \"$job\", + \"tag\": \"snap0\", + \"devices\": $devices}}" "return" + if [ $fail = 0 ]; then + # job status: waiting, pending + wait_job $job "JOB_STATUS_CHANGE" "JOB_STATUS_CHANGE" + else + # job status: aborting + wait_job $job "JOB_STATUS_CHANGE" + fi +} + +start_qemu() +{ + keep_stderr=y + _launch_qemu -nodefaults -nographic "$@" + + _send_qemu_cmd $QEMU_HANDLE '{"execute": "qmp_capabilities"}' 'return' +} + +stop_qemu() +{ + _send_qemu_cmd $QEMU_HANDLE '{"execute": "quit"}' 'return' + + wait=1 _cleanup_qemu +} + + +echo +echo "===== Snapshot single qcow2 image =====" +echo + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" +run_save "save-simple" "diskfmt0" "[\"diskfmt0\"]" 0 +run_load "load-simple" "diskfmt0" "[\"diskfmt0\"]" 0 +run_delete "delete-simple" "[\"diskfmt0\"]" 0 +stop_qemu + + +echo +echo "===== Snapshot no image =====" +echo + +# When snapshotting we need to pass at least one writable disk +# otherwise there's no work to do + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" +run_save "save-no-image" "diskfmt0" "[]" 1 +stop_qemu + + +echo +echo "===== Snapshot missing image =====" +echo + +# The block node names we pass need to actually exist + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" +run_save "save-missing-image" "diskfmt1729" "[\"diskfmt1729\"]" 1 +stop_qemu + +echo +echo "===== Snapshot vmstate not in devices list =====" +echo + +# The node name referred to for vmstate must be one of the nodes +# being included in the snapshot, otherwise the vmstate that is +# captured is liable to be overwritten making subsequent load +# impossible + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" +run_save "save-excluded-vmstate" "diskfmt0" "[\"diskfmt1\"]" 1 +stop_qemu + + +echo +echo "===== Snapshot protocol instead of format =====" +echo + +# The snapshot has to be done against the qcow2 format layer +# not the underlying file protocol layer + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" +run_save "save-proto-not-fmt" "disk0" "[\"disk0\"]" 1 +stop_qemu + + +echo +echo "===== Snapshot dual qcow2 image =====" +echo + +# We can snapshot multiple qcow2 disks at the same time + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" +run_save "save-dual-image" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 +run_load "load-dual-image" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 +run_delete "delete-dual-image" "[\"diskfmt0\", \"diskfmt1\"]" 0 +stop_qemu + + +echo +echo "===== Snapshot error with raw image =====" +echo + +# If we're snapshotting multiple disks, all must be capable +# of supporting snapshots. A raw disk in the list must cause +# an error. + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" \ + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt2','node-name':'disk2'}" \ + -blockdev "{'driver':'raw','file':'disk2','node-name':'diskfmt2'}" +run_save "save-raw-fmt" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\", \"diskfmt2\"]" 1 +stop_qemu + + +echo +echo "===== Snapshot with raw image excluded =====" +echo + +# If we're snapshotting multiple disks, all must be capable +# of supporting snapshots. A writable raw disk can be excluded +# from the snapshot, though it means its data won't be restored +# by later snapshot load operation. + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" \ + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt1','node-name':'disk1'}" \ + -blockdev "{'driver':'qcow2','file':'disk1','node-name':'diskfmt1'}" \ + -blockdev "{'driver':'file','filename':'$TEST_IMG.alt2','node-name':'disk2'}" \ + -blockdev "{'driver':'raw','file':'disk2','node-name':'diskfmt2'}" +run_save "save-skip-raw" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 +run_load "load-skip-raw" "diskfmt0" "[\"diskfmt0\", \"diskfmt1\"]" 0 +run_delete "delete-skip-raw" "[\"diskfmt0\", \"diskfmt1\"]" 0 +stop_qemu + +echo +echo "===== Snapshot bad error reporting to stderr =====" +echo + +# This demonstrates that we're not capturing vmstate loading failures +# into QMP errors, they're ending up in stderr instead. vmstate needs +# to report errors via Error object but that is a major piece of work +# for the future. This test case's expected output log will need +# adjusting when that is done. + +start_qemu \ + -device virtio-rng \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" + +run_save "save-err-stderr" "diskfmt0" "[\"diskfmt0\"]" 0 +stop_qemu + +# leave off virtio-rng to provoke vmstate failure +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" + +run_load "load-err-stderr" "diskfmt0" "[\"diskfmt0\"]" 1 +run_delete "delete-err-stderr" "[\"diskfmt0\"]" 0 + +stop_qemu + + +echo +echo "===== Snapshot reuse same tag =====" +echo + +# Validates that we get an error when reusing a snapshot tag that +# already exists + +start_qemu \ + -device virtio-rng \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" + +run_save "save-err-stderr-initial" "diskfmt0" "[\"diskfmt0\"]" 0 +run_save "save-err-stderr-repeat1" "diskfmt0" "[\"diskfmt0\"]" 1 +run_delete "delete-err-stderr" "[\"diskfmt0\"]" 0 +run_save "save-err-stderr-repeat2" "diskfmt0" "[\"diskfmt0\"]" 0 +run_delete "delete-err-stderr-repeat2" "[\"diskfmt0\"]" 0 + +stop_qemu + +echo +echo "===== Snapshot load does not exist =====" +echo + +# Validates that we get an error when loading a snapshot that does +# not exist + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" +run_load "load-missing-snapshot" "diskfmt0" "[\"diskfmt0\"]" 1 +stop_qemu + + +echo +echo "===== Snapshot delete does not exist =====" +echo + +# Validates that we don't get an error when deleting a snapshot that +# does not exist + +start_qemu \ + -blockdev "{'driver':'file','filename':'$TEST_IMG','node-name':'disk0'}" \ + -blockdev "{'driver':'qcow2','file':'disk0','node-name':'diskfmt0'}" +run_delete "delete-missing-snapshot" "[\"diskfmt0\"]" 0 +stop_qemu + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/internal-snapshots-qapi.out b/tests/qemu-iotests/tests/internal-snapshots-qapi.out new file mode 100644 index 0000000000..26ff4a838c --- /dev/null +++ b/tests/qemu-iotests/tests/internal-snapshots-qapi.out @@ -0,0 +1,520 @@ +QA output created by internal-snapshots-qapi +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT.alt1', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.qcow2.alt2', fmt=IMGFMT size=134217728 + +===== Snapshot single qcow2 image ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-simple", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-simple"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-simple"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-simple"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-simple"}} +{"execute": "snapshot-load", + "arguments": { + "job-id": "load-simple", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-simple"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-simple"}]} +{"execute": "job-dismiss", "arguments": {"id": "load-simple"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-simple"}} +{"execute": "snapshot-delete", + "arguments": { + "job-id": "delete-simple", + "tag": "snap0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-simple"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-simple"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-simple"}]} +{"execute": "job-dismiss", "arguments": {"id": "delete-simple"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-simple"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot no image ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-no-image", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": []}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-no-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-no-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-no-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-no-image"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-no-image", "error": "At least one device is required for snapshot"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-no-image"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-no-image"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot missing image ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-missing-image", + "tag": "snap0", + "vmstate": "diskfmt1729", + "devices": ["diskfmt1729"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-missing-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-missing-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-missing-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-missing-image"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-missing-image", "error": "No block device node 'diskfmt1729'"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-missing-image"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-missing-image"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot vmstate not in devices list ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-excluded-vmstate", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt1"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-excluded-vmstate"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-excluded-vmstate"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-excluded-vmstate"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-excluded-vmstate"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-excluded-vmstate", "error": "vmstate block device 'diskfmt0' does not exist"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-excluded-vmstate"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-excluded-vmstate"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot protocol instead of format ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-proto-not-fmt", + "tag": "snap0", + "vmstate": "disk0", + "devices": ["disk0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-proto-not-fmt"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-proto-not-fmt"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-proto-not-fmt"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-proto-not-fmt"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-proto-not-fmt", "error": "Device 'disk0' is writable but does not support snapshots"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-proto-not-fmt"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-proto-not-fmt"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot dual qcow2 image ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-dual-image", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0", "diskfmt1"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-dual-image"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-dual-image"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-dual-image"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-dual-image"}} +{"execute": "snapshot-load", + "arguments": { + "job-id": "load-dual-image", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0", "diskfmt1"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-dual-image"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-dual-image"}]} +{"execute": "job-dismiss", "arguments": {"id": "load-dual-image"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-dual-image"}} +{"execute": "snapshot-delete", + "arguments": { + "job-id": "delete-dual-image", + "tag": "snap0", + "devices": ["diskfmt0", "diskfmt1"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-dual-image"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-dual-image"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-dual-image"}]} +{"execute": "job-dismiss", "arguments": {"id": "delete-dual-image"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-dual-image"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot error with raw image ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-raw-fmt", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0", "diskfmt1", "diskfmt2"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-raw-fmt"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-raw-fmt"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-raw-fmt"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-raw-fmt"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-raw-fmt", "error": "Device 'diskfmt2' is writable but does not support snapshots"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-raw-fmt"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-raw-fmt"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot with raw image excluded ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-skip-raw", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0", "diskfmt1"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-skip-raw"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-skip-raw"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-skip-raw"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-skip-raw"}} +{"execute": "snapshot-load", + "arguments": { + "job-id": "load-skip-raw", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0", "diskfmt1"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "load-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "load-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-skip-raw"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-skip-raw"}]} +{"execute": "job-dismiss", "arguments": {"id": "load-skip-raw"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-skip-raw"}} +{"execute": "snapshot-delete", + "arguments": { + "job-id": "delete-skip-raw", + "tag": "snap0", + "devices": ["diskfmt0", "diskfmt1"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-skip-raw"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-skip-raw"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-skip-raw"}]} +{"execute": "job-dismiss", "arguments": {"id": "delete-skip-raw"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-skip-raw"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot bad error reporting to stderr ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-err-stderr", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-load", + "arguments": { + "job-id": "load-err-stderr", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-err-stderr"}} +qemu-system-x86_64: Unknown savevm section or instance '0000:00:02.0/virtio-rng' 0. Make sure that your current VM setup matches your saved VM setup, including any hotplugged devices +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-err-stderr"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-err-stderr", "error": "Error -22 while loading VM state"}]} +{"execute": "job-dismiss", "arguments": {"id": "load-err-stderr"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-err-stderr"}} +{"execute": "snapshot-delete", + "arguments": { + "job-id": "delete-err-stderr", + "tag": "snap0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr"}]} +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot reuse same tag ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-err-stderr-initial", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-initial"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-initial"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr-initial"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr-initial"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-initial"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-initial"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-initial"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-initial"}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-err-stderr-repeat1", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-repeat1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-repeat1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "save-err-stderr-repeat1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-repeat1"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-repeat1", "error": "Snapshot 'snap0' already exists in one or more devices"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-repeat1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-repeat1"}} +{"execute": "snapshot-delete", + "arguments": { + "job-id": "delete-err-stderr", + "tag": "snap0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr"}]} +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr"}} +{"execute": "snapshot-save", + "arguments": { + "job-id": "save-err-stderr-repeat2", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "save-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "save-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "save-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "save-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "save-err-stderr-repeat2"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-save", "id": "save-err-stderr-repeat2"}]} +{"execute": "job-dismiss", "arguments": {"id": "save-err-stderr-repeat2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "save-err-stderr-repeat2"}} +{"execute": "snapshot-delete", + "arguments": { + "job-id": "delete-err-stderr-repeat2", + "tag": "snap0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-err-stderr-repeat2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-err-stderr-repeat2"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-err-stderr-repeat2"}]} +{"execute": "job-dismiss", "arguments": {"id": "delete-err-stderr-repeat2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-err-stderr-repeat2"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot load does not exist ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-load", + "arguments": { + "job-id": "load-missing-snapshot", + "tag": "snap0", + "vmstate": "diskfmt0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "load-missing-snapshot"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "load-missing-snapshot"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "STOP"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "load-missing-snapshot"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "load-missing-snapshot"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-load", "id": "load-missing-snapshot", "error": "Snapshot 'snap0' does not exist in one or more devices"}]} +{"execute": "job-dismiss", "arguments": {"id": "load-missing-snapshot"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "load-missing-snapshot"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +===== Snapshot delete does not exist ===== + +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "snapshot-delete", + "arguments": { + "job-id": "delete-missing-snapshot", + "tag": "snap0", + "devices": ["diskfmt0"]}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "delete-missing-snapshot"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "delete-missing-snapshot"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "delete-missing-snapshot"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "delete-missing-snapshot"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "delete-missing-snapshot"}} +{"execute": "query-jobs"} +{"return": [{"current-progress": 1, "status": "concluded", "total-progress": 1, "type": "snapshot-delete", "id": "delete-missing-snapshot"}]} +{"execute": "job-dismiss", "arguments": {"id": "delete-missing-snapshot"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "delete-missing-snapshot"}} +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +*** done