Skip to contentSkip to conversation

qapi: Convert snapshot's commands

RFC PATCH In review Pavel Hrdina 4 participants 19 messages Jul 12, 2012 – Jul 24, 2012

lore
Share & tools
lore
#0 lore raw

cover qapi: Convert snapshot's commands

This patch-series converts savevm, loadvm, delvm and info snapshots from HMP into QMP as savevm, loadvm, delvm and query-snapshots.

All comments are welcome.

Pavel

Pavel Hrdina (4):
qapi: Convert savevm
qapi: Convert loadvm
qapi: Convert delvm
qapi: Convert info snapshots

 hmp-commands.hx  |    6 +--
 hmp.c            |   64 +++++++++++++++++++++++
 hmp.h            |    4 ++
 monitor.c        |   14 +----
 qapi-schema.json |   96 ++++++++++++++++++++++++++++++++++
 qerror.c         |   44 ++++++++++++++++
 qerror.h         |   33 ++++++++++++
 qmp-commands.hx  |  106 +++++++++++++++++++++++++++++++++++++
 savevm.c         |  154 ++++++++++++++++++++++++++++--------------------------
 sysemu.h         |    5 --
 vl.c             |    7 ++-
 11 files changed, 437 insertions(+), 96 deletions(-)
Signature · 1 line
1.7.10.4
#1 lore raw

RFC PATCH 1/4 qapi: Convert savevm

---
 hmp-commands.hx  |    2 +-
 hmp.c            |   10 ++++++++++
 hmp.h            |    1 +
 qapi-schema.json |   22 ++++++++++++++++++++++
 qerror.c         |   24 ++++++++++++++++++++++++
 qerror.h         |   18 ++++++++++++++++++
 qmp-commands.hx  |   26 ++++++++++++++++++++++++++
 savevm.c         |   29 ++++++++++++++---------------
 sysemu.h         |    1 -
 9 files changed, 116 insertions(+), 17 deletions(-)
hmp-commands.hx +1 -1
index f5d9d91..e8c5325 100644
@@ -267,7 +267,7 @@ ETEXI
         .args_type  = "name:s?",
         .params     = "[tag|id]",
         .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
-        .mhandler.cmd = do_savevm,
+        .mhandler.cmd = hmp_savevm,
     },
 
 STEXI
hmp.c +10 -0
index 4c6d4ae..491599d 100644
@@ -1002,3 +1002,13 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
     qmp_netdev_del(id, &err);
     hmp_handle_error(mon, &err);
 }
+
+void hmp_savevm(Monitor *mon, const QDict *qdict)
+{
+    const char *name = qdict_get_try_str(qdict, "name");
+    Error *err = NULL;
+
+    qmp_savevm(!!name, name, &err);
+
+    hmp_handle_error(mon, &err);
+}
hmp.h +1 -0
index 79d138d..dc6410b 100644
@@ -64,5 +64,6 @@ void hmp_device_del(Monitor *mon, const QDict *qdict);
 void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
 void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
+void hmp_savevm(Monitor *mon, const QDict *qdict);
 
 #endif
qapi-schema.json +22 -0
index 1ab5dbd..4db1447 100644
@@ -1868,3 +1868,25 @@
 # Since: 0.14.0
 ##
 { 'command': 'netdev_del', 'data': {'id': 'str'} }
+
+##
+# @savevm:
+#
+# Create a snapshot of the whole virtual machine. If 'tag' is provided,
+# it is used as human readable identifier. If there is already a snapshot
+# with the same tag or ID, it is replaced.
+#
+# @name: tag or id of new or existing snapshot
+#
+# Returns: Nothing on success
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If an error occures while creating a snapshot, SnapshotCreateFailed
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error uccures while writing vm state, SnapshotWriteFailed
+#          If delete snapshot with same 'name' fail, SnapshotDeleteFailed
+#
+# Since: 1.2
+##
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file
qerror.c +24 -0
index 92c4eff..4e6efa1 100644
@@ -309,6 +309,30 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Could not start VNC server on %(target)",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_CREATE_FAILED,
+        .desc      = "Error '%(errno)' while creating snapshot on '%(device)'",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_DELETE_FAILED,
+        .desc      = "Error '%(errno)' while deleting snapshot on '%(device)'",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_NOT_ACCEPTED,
+        .desc      = "No block device can accept snapshots",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_NOT_SUPPORTED,
+        .desc      = "Device '%(device)' is writable but does not support snapshots",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_OPEN_FAILED,
+        .desc      = "Error while opening snapshot on '%(device)'",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_WRITE_FAILED,
+        .desc      = "Error '%(errno)' while writing snapshot on '%(device)'",
+    },
+    {
         .error_fmt = QERR_SOCKET_CONNECT_IN_PROGRESS,
         .desc      = "Connection can not be completed immediately",
     },
qerror.h +18 -0
index b4c8758..bc47f4a 100644
@@ -251,6 +251,24 @@ QError *qobject_to_qerror(const QObject *obj);
 #define QERR_VNC_SERVER_FAILED \
     "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"
 
+#define QERR_SNAPSHOT_CREATE_FAILED \
+    "{ 'class': 'SnapshotCreateFailed', 'data': { 'device': %s, 'errno': %d } }"
+
+#define QERR_SNAPSHOT_DELETE_FAILED \
+    "{ 'class': 'SnapshotDeleteFailed', 'data': { 'device': %s, 'errno': %d } }"
+
+#define QERR_SNAPSHOT_NOT_ACCEPTED \
+    "{ 'class': 'SnapshotNotAccepted', 'data': {} }"
+
+#define QERR_SNAPSHOT_NOT_SUPPORTED \
+    "{ 'class': 'SnapshotNotSupported', 'data': { 'device': %s } }"
+
+#define QERR_SNAPSHOT_OPEN_FAILED \
+    "{ 'class': 'SnapshotOpenFailed', 'data': { 'device': %s } }"
+
+#define QERR_SNAPSHOT_WRITE_FAILED \
+    "{ 'class': 'SnapshotWriteFailed', 'data': { 'device': %s, 'errno': %d } }"
+
 #define QERR_SOCKET_CONNECT_IN_PROGRESS \
     "{ 'class': 'SockConnectInprogress', 'data': {} }"
 
qmp-commands.hx +26 -0
index 2e1a38e..a1c82f7 100644
@@ -1061,6 +1061,32 @@ Example:
 
 EQMP
     {
+        .name       = "savevm",
+        .args_type  = "name:s?",
+        .params     = "name",
+        .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+        .mhandler.cmd_new = qmp_marshal_input_savevm
+    },
+
+SQMP
+savevm
+------
+
+Create a snapshot of the whole virtual machine. If 'tag' is provided,
+it is used as human readable identifier. If there is already a snapshot
+with the same tag or ID, it is replaced.
+
+Arguments:
+
+- "name": tag or id of new or existing snapshot
+
+Example:
+
+-> { "execute": "savevm", "arguments": { "name": "my_snapshot" } }
+<- { "return": {} }
+
+EQMP
+    {
         .name       = "qmp_capabilities",
         .args_type  = "",
         .params     = "",
savevm.c +14 -15
index a15c163..9fc1b53 100644
@@ -2033,7 +2033,7 @@ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
 /*
  * Deletes snapshots of a given name in all opened images.
  */
-static int del_existing_snapshots(Monitor *mon, const char *name)
+static int del_existing_snapshots(Error **errp, const char *name)
 {
     BlockDriverState *bs;
     QEMUSnapshotInfo sn1, *snapshot = &sn1;
@@ -2046,9 +2046,8 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
         {
             ret = bdrv_snapshot_delete(bs, name);
             if (ret < 0) {
-                monitor_printf(mon,
-                               "Error while deleting snapshot on '%s'\n",
-                               bdrv_get_device_name(bs));
+                error_set(errp, QERR_SNAPSHOT_DELETE_FAILED,
+                          bdrv_get_device_name(bs), ret);
                 return -1;
             }
         }
@@ -2057,7 +2056,7 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
     return 0;
 }
 
-void do_savevm(Monitor *mon, const QDict *qdict)
+void qmp_savevm(bool has_name, const char *name, Error **errp)
 {
     BlockDriverState *bs, *bs1;
     QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
@@ -2072,7 +2071,6 @@ void do_savevm(Monitor *mon, const QDict *qdict)
     struct timeval tv;
     struct tm tm;
 #endif
-    const char *name = qdict_get_try_str(qdict, "name");
 
     /* Verify if there is a device that doesn't support snapshots and is writable */
     bs = NULL;
@@ -2083,15 +2081,15 @@ void do_savevm(Monitor *mon, const QDict *qdict)
         }
 
         if (!bdrv_can_snapshot(bs)) {
-            monitor_printf(mon, "Device '%s' is writable but does not support snapshots.\n",
-                               bdrv_get_device_name(bs));
+            error_set(errp, QERR_SNAPSHOT_NOT_SUPPORTED,
+                      bdrv_get_device_name(bs));
             return;
         }
     }
 
     bs = bdrv_snapshots();
     if (!bs) {
-        monitor_printf(mon, "No block device can accept snapshots\n");
+        error_set(errp, QERR_SNAPSHOT_NOT_ACCEPTED);
         return;
     }
 
@@ -2112,7 +2110,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
 #endif
     sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock);
 
-    if (name) {
+    if (has_name) {
         ret = bdrv_snapshot_find(bs, old_sn, name);
         if (ret >= 0) {
             pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
@@ -2133,21 +2131,22 @@ void do_savevm(Monitor *mon, const QDict *qdict)
     }
 
     /* Delete old snapshots of the same name */
-    if (name && del_existing_snapshots(mon, name) < 0) {
+    if (has_name && del_existing_snapshots(errp, name) < 0) {
         goto the_end;
     }
 
     /* save the VM state */
     f = qemu_fopen_bdrv(bs, 1);
     if (!f) {
-        monitor_printf(mon, "Could not open VM state file\n");
+        error_set(errp, QERR_SNAPSHOT_OPEN_FAILED, bdrv_get_device_name(bs));
         goto the_end;
     }
     ret = qemu_savevm_state(f);
     vm_state_size = qemu_ftell(f);
     qemu_fclose(f);
     if (ret < 0) {
-        monitor_printf(mon, "Error %d while writing VM\n", ret);
+        error_set(errp, QERR_SNAPSHOT_WRITE_FAILED,
+                  bdrv_get_device_name(bs), ret);
         goto the_end;
     }
 
@@ -2160,8 +2159,8 @@ void do_savevm(Monitor *mon, const QDict *qdict)
             sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
             ret = bdrv_snapshot_create(bs1, sn);
             if (ret < 0) {
-                monitor_printf(mon, "Error while creating snapshot on '%s'\n",
-                               bdrv_get_device_name(bs1));
+                error_set(errp, QERR_SNAPSHOT_CREATE_FAILED,
+                          bdrv_get_device_name(bs1), ret);
             }
         }
     }
sysemu.h +0 -1
index 6540c79..95d1207 100644
@@ -69,7 +69,6 @@ void qemu_remove_exit_notifier(Notifier *notify);
 
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 
-void do_savevm(Monitor *mon, const QDict *qdict);
 int load_vmstate(const char *name);
 void do_delvm(Monitor *mon, const QDict *qdict);
 void do_info_snapshots(Monitor *mon);
Signature · 1 line
1.7.10.4
#2 lore raw

Re: [Qemu-devel] [RFC PATCH 1/4] qapi: Convert savevm

Quoted 7 lines · Pavel Hrdina

---

+++ b/qapi-schema.json

qapi-schema.json +0 -0
@@ -1868,3 +1868,25 @@
 # Since: 0.14.0
 ##

qapi-schema.json line 1879

Quoted 10 lines
qapi-schema.json +9 -0
 { 'command': 'netdev_del', 'data': {'id': 'str'} }
+
+##
+# @savevm:
+#
+# Create a snapshot of the whole virtual machine. If 'tag' is provided,
+# it is used as human readable identifier. If there is already a snapshot
+# with the same tag or ID, it is replaced.
+#
+# @name: tag or id of new or existing snapshot

Needs an #optional designation, given the syntax below.

qapi-schema.json line 1885

Quoted 6 lines
qapi-schema.json +6 -0
+#
+# Returns: Nothing on success
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If an error occures while creating a snapshot, SnapshotCreateFailed

s/occures/occurs/

qapi-schema.json line 1887

Quoted 2 lines
qapi-schema.json +2 -0
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error uccures while writing vm state, SnapshotWriteFailed

s/uccures/occurs/

qapi-schema.json line 1888

Quoted 1 line
qapi-schema.json +1 -0
+#          If delete snapshot with same 'name' fail, SnapshotDeleteFailed

The notion of blindly overwriting the existing snapshot of the same name seems a bit dangerous; should we take this opportunity to enhance the command, and add a force flag, where things fail if the flag is false but the name already exists, and where the reuse only happens if the flag is present? (In fact, it would make my life in libvirt easier, as I have an action item to make libvirt reject attempts to create a snapshot with tag named '1' if an existing snapshot already has an id of '1'.)

qapi-schema.json line 1892

Quoted 5 lines
qapi-schema.json +4 -0
+#
+# Since: 1.2
+##
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file

Fix that.

qmp-commands.hx Example: · line 1072

Quoted 13 lines
qmp-commands.hx +9 -0
@@ -1061,6 +1061,32 @@ Example:
 
 EQMP
     {
+        .name       = "savevm",
+        .args_type  = "name:s?",
+        .params     = "name",
+        .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+        .mhandler.cmd_new = qmp_marshal_input_savevm
+    },
+
+SQMP
+savevm

I know the HMP command is short, for ease of typing; but since 'savevm' is not an English word, should we name the QMP command 'save-vm'?

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#3 lore raw

Re: [Qemu-devel] [RFC PATCH 1/4] qapi: Convert savevm

Quoted 32 lines · Eric Blake
Quoted 6 lines · Pavel Hrdina

---
+++ b/qapi-schema.json

qapi-schema.json +0 -0
@@ -1868,3 +1868,25 @@
  # Since: 0.14.0
  ##

qapi-schema.json line 1879

Quoted 10 lines
qapi-schema.json +9 -0
  { 'command': 'netdev_del', 'data': {'id': 'str'} }
+
+##
+# @savevm:
+#
+# Create a snapshot of the whole virtual machine. If 'tag' is provided,
+# it is used as human readable identifier. If there is already a snapshot
+# with the same tag or ID, it is replaced.
+#
+# @name: tag or id of new or existing snapshot

Needs an #optional designation, given the syntax below.

qapi-schema.json line 1885

Quoted 6 lines
qapi-schema.json +6 -0
+#
+# Returns: Nothing on success
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If an error occures while creating a snapshot, SnapshotCreateFailed

s/occures/occurs/

qapi-schema.json line 1887

Quoted 2 lines
qapi-schema.json +2 -0
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error uccures while writing vm state, SnapshotWriteFailed

s/uccures/occurs/

qapi-schema.json line 1888

Quoted 1 line
qapi-schema.json +1 -0
+#          If delete snapshot with same 'name' fail, SnapshotDeleteFailed
Quoted 8 lines

The notion of blindly overwriting the existing snapshot of the same name seems a bit dangerous; should we take this opportunity to enhance the command, and add a force flag, where things fail if the flag is false but the name already exists, and where the reuse only happens if the flag is present? (In fact, it would make my life in libvirt easier, as I have an action item to make libvirt reject attempts to create a snapshot with tag named '1' if an existing snapshot already has an id of '1'.)

This sounds reasonable and I think that this could be also good for HMP, not only for QMP. If there isn't anyone who disagree, I'll implement it.

Pavel

Quoted 23 lines

qapi-schema.json line 1892

Quoted 5 lines
qapi-schema.json +4 -0
+#
+# Since: 1.2
+##
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file

Fix that.

qmp-commands.hx Example: · line 1072

Quoted 13 lines
qmp-commands.hx +9 -0
@@ -1061,6 +1061,32 @@ Example:
 
  EQMP
      {
+        .name       = "savevm",
+        .args_type  = "name:s?",
+        .params     = "name",
+        .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+        .mhandler.cmd_new = qmp_marshal_input_savevm
+    },
+
+SQMP
+savevm

I know the HMP command is short, for ease of typing; but since 'savevm' is not an English word, should we name the QMP command 'save-vm'?

#4 lore raw

Re: [Qemu-devel] [RFC PATCH 1/4] qapi: Convert savevm

Quoted 41 lines · On Mon, 16 Jul 2012 10:12:08 +0200 Pavel Hrdina <[email protected]> wrote:
Quoted 32 lines · Eric Blake
Quoted 6 lines · Pavel Hrdina

---
+++ b/qapi-schema.json

qapi-schema.json +0 -0
@@ -1868,3 +1868,25 @@
  # Since: 0.14.0
  ##

qapi-schema.json line 1879

Quoted 10 lines
qapi-schema.json +9 -0
  { 'command': 'netdev_del', 'data': {'id': 'str'} }
+
+##
+# @savevm:
+#
+# Create a snapshot of the whole virtual machine. If 'tag' is provided,
+# it is used as human readable identifier. If there is already a snapshot
+# with the same tag or ID, it is replaced.
+#
+# @name: tag or id of new or existing snapshot

Needs an #optional designation, given the syntax below.

qapi-schema.json line 1885

Quoted 6 lines
qapi-schema.json +6 -0
+#
+# Returns: Nothing on success
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If an error occures while creating a snapshot, SnapshotCreateFailed

s/occures/occurs/

qapi-schema.json line 1887

Quoted 2 lines
qapi-schema.json +2 -0
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error uccures while writing vm state, SnapshotWriteFailed

s/uccures/occurs/

qapi-schema.json line 1888

Quoted 1 line
qapi-schema.json +1 -0
+#          If delete snapshot with same 'name' fail, SnapshotDeleteFailed
Quoted 8 lines

The notion of blindly overwriting the existing snapshot of the same name seems a bit dangerous; should we take this opportunity to enhance the command, and add a force flag, where things fail if the flag is false but the name already exists, and where the reuse only happens if the flag is present? (In fact, it would make my life in libvirt easier, as I have an action item to make libvirt reject attempts to create a snapshot with tag named '1' if an existing snapshot already has an id of '1'.)

Quoted 3 lines

This sounds reasonable and I think that this could be also good for HMP, not only for QMP.
If there isn't anyone who disagree, I'll implement it.

I agree with it.

Quoted 28 lines

Pavel

Quoted 23 lines

qapi-schema.json line 1892

Quoted 5 lines
qapi-schema.json +4 -0
+#
+# Since: 1.2
+##
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file

Fix that.

qmp-commands.hx Example: · line 1072

Quoted 13 lines
qmp-commands.hx +9 -0
@@ -1061,6 +1061,32 @@ Example:
 
  EQMP
      {
+        .name       = "savevm",
+        .args_type  = "name:s?",
+        .params     = "name",
+        .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+        .mhandler.cmd_new = qmp_marshal_input_savevm
+    },
+
+SQMP
+savevm

I know the HMP command is short, for ease of typing; but since 'savevm' is not an English word, should we name the QMP command 'save-vm'?

#5 lore raw

Re: [Qemu-devel] [RFC PATCH 1/4] qapi: Convert savevm

In reply to Pavel Hrdina
Quoted 60 lines · On Thu, 12 Jul 2012 18:55:15 +0200 Pavel Hrdina <[email protected]> wrote:
---
 hmp-commands.hx  |    2 +-
 hmp.c            |   10 ++++++++++
 hmp.h            |    1 +
 qapi-schema.json |   22 ++++++++++++++++++++++
 qerror.c         |   24 ++++++++++++++++++++++++
 qerror.h         |   18 ++++++++++++++++++
 qmp-commands.hx  |   26 ++++++++++++++++++++++++++
 savevm.c         |   29 ++++++++++++++---------------
 sysemu.h         |    1 -
 9 files changed, 116 insertions(+), 17 deletions(-)
hmp-commands.hx +1 -1
index f5d9d91..e8c5325 100644
@@ -267,7 +267,7 @@ ETEXI
         .args_type  = "name:s?",
         .params     = "[tag|id]",
         .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
-        .mhandler.cmd = do_savevm,
+        .mhandler.cmd = hmp_savevm,
     },
 
 STEXI
hmp.c +10 -0
index 4c6d4ae..491599d 100644
@@ -1002,3 +1002,13 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
     qmp_netdev_del(id, &err);
     hmp_handle_error(mon, &err);
 }
+
+void hmp_savevm(Monitor *mon, const QDict *qdict)
+{
+    const char *name = qdict_get_try_str(qdict, "name");
+    Error *err = NULL;
+
+    qmp_savevm(!!name, name, &err);
+
+    hmp_handle_error(mon, &err);
+}
hmp.h +1 -0
index 79d138d..dc6410b 100644
@@ -64,5 +64,6 @@ void hmp_device_del(Monitor *mon, const QDict *qdict);
 void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
 void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
+void hmp_savevm(Monitor *mon, const QDict *qdict);
 
 #endif
qapi-schema.json +0 -0
index 1ab5dbd..4db1447 100644

qapi-schema.json line 1873

Quoted 6 lines
qapi-schema.json +3 -0
@@ -1868,3 +1868,25 @@
 # Since: 0.14.0
 ##
 { 'command': 'netdev_del', 'data': {'id': 'str'} }
+
+##
+# @savevm:

Let's call it save-vm.

qapi-schema.json line 1877

Quoted 4 lines
qapi-schema.json +4 -0
+#
+# Create a snapshot of the whole virtual machine. If 'tag' is provided,
+# it is used as human readable identifier. If there is already a snapshot
+# with the same tag or ID, it is replaced.

Please, document that the vm is automatically stopped and resumed, and that this can take a long time.

qapi-schema.json line 1879

Quoted 2 lines
qapi-schema.json +2 -0
+#
+# @name: tag or id of new or existing snapshot

I don't like the idea of making 'name' optional in QMP. We could (and should) have it as optional in HMP though. This means that we should move the code that auto-generates it to HMP.

Also, I remember an old discussion about distinguishing between 'tag' and 'id'. I remember it was difficult to do, so we could choose not to do it, but let's re-evaluate this again.

qapi-schema.json line 1888

Quoted 9 lines
qapi-schema.json +9 -0
+#
+# Returns: Nothing on success
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If an error occures while creating a snapshot, SnapshotCreateFailed
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error uccures while writing vm state, SnapshotWriteFailed
+#          If delete snapshot with same 'name' fail, SnapshotDeleteFailed

I don't think we should re-create all these errors, more comments on that below.

Quoted 120 lines
qapi-schema.json +4 -0
+#
+# Since: 1.2
+##
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file
qerror.c +24 -0
index 92c4eff..4e6efa1 100644
@@ -309,6 +309,30 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Could not start VNC server on %(target)",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_CREATE_FAILED,
+        .desc      = "Error '%(errno)' while creating snapshot on '%(device)'",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_DELETE_FAILED,
+        .desc      = "Error '%(errno)' while deleting snapshot on '%(device)'",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_NOT_ACCEPTED,
+        .desc      = "No block device can accept snapshots",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_NOT_SUPPORTED,
+        .desc      = "Device '%(device)' is writable but does not support snapshots",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_OPEN_FAILED,
+        .desc      = "Error while opening snapshot on '%(device)'",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_WRITE_FAILED,
+        .desc      = "Error '%(errno)' while writing snapshot on '%(device)'",
+    },
+    {
         .error_fmt = QERR_SOCKET_CONNECT_IN_PROGRESS,
         .desc      = "Connection can not be completed immediately",
     },
qerror.h +18 -0
index b4c8758..bc47f4a 100644
@@ -251,6 +251,24 @@ QError *qobject_to_qerror(const QObject *obj);
 #define QERR_VNC_SERVER_FAILED \
     "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"
 
+#define QERR_SNAPSHOT_CREATE_FAILED \
+    "{ 'class': 'SnapshotCreateFailed', 'data': { 'device': %s, 'errno': %d } }"
+
+#define QERR_SNAPSHOT_DELETE_FAILED \
+    "{ 'class': 'SnapshotDeleteFailed', 'data': { 'device': %s, 'errno': %d } }"
+
+#define QERR_SNAPSHOT_NOT_ACCEPTED \
+    "{ 'class': 'SnapshotNotAccepted', 'data': {} }"
+
+#define QERR_SNAPSHOT_NOT_SUPPORTED \
+    "{ 'class': 'SnapshotNotSupported', 'data': { 'device': %s } }"
+
+#define QERR_SNAPSHOT_OPEN_FAILED \
+    "{ 'class': 'SnapshotOpenFailed', 'data': { 'device': %s } }"
+
+#define QERR_SNAPSHOT_WRITE_FAILED \
+    "{ 'class': 'SnapshotWriteFailed', 'data': { 'device': %s, 'errno': %d } }"
+
 #define QERR_SOCKET_CONNECT_IN_PROGRESS \
     "{ 'class': 'SockConnectInprogress', 'data': {} }"
 
qmp-commands.hx +26 -0
index 2e1a38e..a1c82f7 100644
@@ -1061,6 +1061,32 @@ Example:
 
 EQMP
     {
+        .name       = "savevm",
+        .args_type  = "name:s?",
+        .params     = "name",
+        .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+        .mhandler.cmd_new = qmp_marshal_input_savevm
+    },
+
+SQMP
+savevm
+------
+
+Create a snapshot of the whole virtual machine. If 'tag' is provided,
+it is used as human readable identifier. If there is already a snapshot
+with the same tag or ID, it is replaced.
+
+Arguments:
+
+- "name": tag or id of new or existing snapshot
+
+Example:
+
+-> { "execute": "savevm", "arguments": { "name": "my_snapshot" } }
+<- { "return": {} }
+
+EQMP
+    {
         .name       = "qmp_capabilities",
         .args_type  = "",
         .params     = "",
savevm.c +1 -1
index a15c163..9fc1b53 100644
@@ -2033,7 +2033,7 @@ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
 /*
  * Deletes snapshots of a given name in all opened images.
  */
-static int del_existing_snapshots(Monitor *mon, const char *name)
+static int del_existing_snapshots(Error **errp, const char *name)
 {
     BlockDriverState *bs;
     QEMUSnapshotInfo sn1, *snapshot = &sn1;

savevm.c static int del_existing_snapshots(Monitor *mon, const char *name) · line 2050

Quoted 8 lines
savevm.c +2 -3
@@ -2046,9 +2046,8 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
         {
             ret = bdrv_snapshot_delete(bs, name);
             if (ret < 0) {
-                monitor_printf(mon,
-                               "Error while deleting snapshot on '%s'\n",
-                               bdrv_get_device_name(bs));
+                error_set(errp, QERR_SNAPSHOT_DELETE_FAILED,
+                          bdrv_get_device_name(bs), ret);

The Right Thing to do here is to change bdrv_snapshot_delete() to take an Error argument and let it set the real error cause. Three considerations:

1. The QMP command shouldn't delete existing snapshots by default. Either, we add a 'force' argument to it or don't delete snapshots in save-vm at all (in which case a mngt app would have to delete the snapshots with the same name manually, I prefer this approach btw)

2. This has to be done in a separate series

3. It's a good idea to wait for my series improving error_set() to be merged before doing this

Quoted 21 lines
savevm.c +1 -2
                 return -1;
             }
         }
@@ -2057,7 +2056,7 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
     return 0;
 }
 
-void do_savevm(Monitor *mon, const QDict *qdict)
+void qmp_savevm(bool has_name, const char *name, Error **errp)
 {
     BlockDriverState *bs, *bs1;
     QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
@@ -2072,7 +2071,6 @@ void do_savevm(Monitor *mon, const QDict *qdict)
     struct timeval tv;
     struct tm tm;
 #endif
-    const char *name = qdict_get_try_str(qdict, "name");
 
     /* Verify if there is a device that doesn't support snapshots and is writable */
     bs = NULL;

savevm.c void do_savevm(Monitor *mon, const QDict *qdict) · line 2085

Quoted 7 lines
savevm.c +2 -2
@@ -2083,15 +2081,15 @@ void do_savevm(Monitor *mon, const QDict *qdict)
         }
 
         if (!bdrv_can_snapshot(bs)) {
-            monitor_printf(mon, "Device '%s' is writable but does not support snapshots.\n",
-                               bdrv_get_device_name(bs));
+            error_set(errp, QERR_SNAPSHOT_NOT_SUPPORTED,
+                      bdrv_get_device_name(bs));

Please, use QERR_NOT_SUPPORTED instead. I know it doesn't allow any arguments today, but I'm working on a series which will allow you to pass any arguments you wish.

savevm.c void do_savevm(Monitor *mon, const QDict *qdict) · line 2092

Quoted 8 lines
savevm.c +1 -1
             return;
         }
     }
 
     bs = bdrv_snapshots();
     if (!bs) {
-        monitor_printf(mon, "No block device can accept snapshots\n");
+        error_set(errp, QERR_SNAPSHOT_NOT_ACCEPTED);

I think we should use QERR_NOT_SUPPORTED here too.

Quoted 13 lines
savevm.c +1 -1
         return;
     }
 
@@ -2112,7 +2110,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
 #endif
     sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock);
 
-    if (name) {
+    if (has_name) {
         ret = bdrv_snapshot_find(bs, old_sn, name);
         if (ret >= 0) {
             pstrcpy(sn->name, sizeof(sn->name), old_sn->name);

savevm.c void do_savevm(Monitor *mon, const QDict *qdict) · line 2136

Quoted 7 lines
savevm.c +1 -1
@@ -2133,21 +2131,22 @@ void do_savevm(Monitor *mon, const QDict *qdict)
     }
 
     /* Delete old snapshots of the same name */
-    if (name && del_existing_snapshots(mon, name) < 0) {
+    if (has_name && del_existing_snapshots(errp, name) < 0) {
         goto the_end;
     }

The QMP command should not delete existing snapshots, as said above.

savevm.c void do_savevm(Monitor *mon, const QDict *qdict) · line 2141

Quoted 6 lines
savevm.c +1 -1
     /* save the VM state */
     f = qemu_fopen_bdrv(bs, 1);
     if (!f) {
-        monitor_printf(mon, "Could not open VM state file\n");
+        error_set(errp, QERR_SNAPSHOT_OPEN_FAILED, bdrv_get_device_name(bs));

Please, use QERR_OPEN_FILE_FAILED instead. The filename should be the backing file name.

savevm.c void do_savevm(Monitor *mon, const QDict *qdict) · line 2149

Quoted 9 lines
savevm.c +2 -1
         goto the_end;
     }
     ret = qemu_savevm_state(f);
     vm_state_size = qemu_ftell(f);
     qemu_fclose(f);
     if (ret < 0) {
-        monitor_printf(mon, "Error %d while writing VM\n", ret);
+        error_set(errp, QERR_SNAPSHOT_WRITE_FAILED,
+                  bdrv_get_device_name(bs), ret);

The Right Thing to do here it to convert qemu_savevm_sstate() to take an Error argument. The same considerations I wrote above about del_existing_snapshots() apply here, except that you can report IOError for most qemu_savevm_state() errors.

savevm.c void do_savevm(Monitor *mon, const QDict *qdict) · line 2163

Quoted 11 lines
savevm.c +2 -2
         goto the_end;
     }
 
@@ -2160,8 +2159,8 @@ void do_savevm(Monitor *mon, const QDict *qdict)
             sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
             ret = bdrv_snapshot_create(bs1, sn);
             if (ret < 0) {
-                monitor_printf(mon, "Error while creating snapshot on '%s'\n",
-                               bdrv_get_device_name(bs1));
+                error_set(errp, QERR_SNAPSHOT_CREATE_FAILED,
+                          bdrv_get_device_name(bs1), ret);

Here too, bdrv_snapshot_create() should be changed to take an Error argument.

Quoted 15 lines
savevm.c +0 -0
             }
         }
     }
sysemu.h +0 -1
index 6540c79..95d1207 100644
@@ -69,7 +69,6 @@ void qemu_remove_exit_notifier(Notifier *notify);
 
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 
-void do_savevm(Monitor *mon, const QDict *qdict);
 int load_vmstate(const char *name);
 void do_delvm(Monitor *mon, const QDict *qdict);
 void do_info_snapshots(Monitor *mon);
#6 lore raw

RFC PATCH 2/4 qapi: Convert loadvm

In reply to Pavel Hrdina
---
 hmp-commands.hx  |    2 +-
 hmp.c            |   10 ++++++++++
 hmp.h            |    1 +
 monitor.c        |   12 ------------
 qapi-schema.json |   24 +++++++++++++++++++++++-
 qerror.c         |   16 ++++++++++++++++
 qerror.h         |   12 ++++++++++++
 qmp-commands.hx  |   25 +++++++++++++++++++++++++
 savevm.c         |   52 +++++++++++++++++++++++++++++++---------------------
 sysemu.h         |    1 -
 vl.c             |    7 ++++++-
 11 files changed, 125 insertions(+), 37 deletions(-)
hmp-commands.hx +1 -1
index e8c5325..b145612 100644
@@ -284,7 +284,7 @@ ETEXI
         .args_type  = "name:s",
         .params     = "tag|id",
         .help       = "restore a VM snapshot from its tag or id",
-        .mhandler.cmd = do_loadvm,
+        .mhandler.cmd = hmp_loadvm,
     },
 
 STEXI
hmp.c +10 -0
index 491599d..6ed4c3f 100644
@@ -1012,3 +1012,13 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
 
     hmp_handle_error(mon, &err);
 }
+
+void hmp_loadvm(Monitor *mon, const QDict *qdict)
+{
+    const char *name = qdict_get_str(qdict, "name");
+    Error *err = NULL;
+
+    qmp_loadvm(name, &err);
+
+    hmp_handle_error(mon, &err);
+}
hmp.h +1 -0
index dc6410b..5623ae7 100644
@@ -65,5 +65,6 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
 void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
 void hmp_savevm(Monitor *mon, const QDict *qdict);
+void hmp_loadvm(Monitor *mon, const QDict *qdict);
 
 #endif
monitor.c +0 -12
index f6107ba..3efbcc8 100644
@@ -2385,18 +2385,6 @@ static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data)
     return -1;
 }
 
-static void do_loadvm(Monitor *mon, const QDict *qdict)
-{
-    int saved_vm_running  = runstate_is_running();
-    const char *name = qdict_get_str(qdict, "name");
-
-    vm_stop(RUN_STATE_RESTORE_VM);
-
-    if (load_vmstate(name) == 0 && saved_vm_running) {
-        vm_start();
-    }
-}
-
 int monitor_get_fd(Monitor *mon, const char *fdname)
 {
     mon_fd_t *monfd;
qapi-schema.json +23 -1
index 4db1447..7df6a90 100644
@@ -1889,4 +1889,26 @@
 #
 # Since: 1.2
 ##
-{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
+
+##
+# @loadvm:
+#
+# Set the whole virtual machine to the snapshot identified by the tag
+# 'tag' or the unique snapshot ID 'id'.
+#
+# @name: tag or id of existing snapshot
+#
+# Returns: Nothing on success
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If no snapshot is found, SnapshotNotFound
+#          If snapshot is only disk snapshot, SnapshotInvalid
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If snapshot activate failed, SnapshotActivateFailed
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error occures while loading vm state, SnapshotLoadFailed
+#
+# Since: 1.2
+##
+{ 'command': 'loadvm', 'data': {'name': 'str'} }
qerror.c +16 -0
index 4e6efa1..b9c7352 100644
@@ -309,6 +309,10 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Could not start VNC server on %(target)",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_ACTIVATE_FAILED,
+        .desc      = "Error '%(errno)' while activating snapshot '%(name)' on '%(device)'",
+    },
+    {
         .error_fmt = QERR_SNAPSHOT_CREATE_FAILED,
         .desc      = "Error '%(errno)' while creating snapshot on '%(device)'",
     },
@@ -317,10 +321,22 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Error '%(errno)' while deleting snapshot on '%(device)'",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_INVALID,
+        .desc      = "Device '%(device)' disk-only snapshot. Revert to it offline using qemu-img.",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_LOAD_FAILED,
+        .desc      = "Error '%(errno)' while loading snapshot for '%(device)'",
+    },
+    {
         .error_fmt = QERR_SNAPSHOT_NOT_ACCEPTED,
         .desc      = "No block device can accept snapshots",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_NOT_FOUND,
+        .desc      = "Device '%(device)' does not have the requested snapshot '%(name)'",
+    },
+    {
         .error_fmt = QERR_SNAPSHOT_NOT_SUPPORTED,
         .desc      = "Device '%(device)' is writable but does not support snapshots",
     },
qerror.h +12 -0
index bc47f4a..da2abdd 100644
@@ -251,15 +251,27 @@ QError *qobject_to_qerror(const QObject *obj);
 #define QERR_VNC_SERVER_FAILED \
     "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"
 
+#define QERR_SNAPSHOT_ACTIVATE_FAILED \
+    "{ 'class': 'SnapshotActivateFailed', 'data': { 'device': %s, 'name': %s, 'errno':%d } }"
+
 #define QERR_SNAPSHOT_CREATE_FAILED \
     "{ 'class': 'SnapshotCreateFailed', 'data': { 'device': %s, 'errno': %d } }"
 
 #define QERR_SNAPSHOT_DELETE_FAILED \
     "{ 'class': 'SnapshotDeleteFailed', 'data': { 'device': %s, 'errno': %d } }"
 
+#define QERR_SNAPSHOT_INVALID \
+    "{ 'class': 'SnapshotInvalid', 'data': { 'device': %s } }"
+
+#define QERR_SNAPSHOT_LOAD_FAILED \
+    "{ 'class': 'SnapshotLoadFailed', 'data': { 'device': %s, 'errno': %d } }"
+
 #define QERR_SNAPSHOT_NOT_ACCEPTED \
     "{ 'class': 'SnapshotNotAccepted', 'data': {} }"
 
+#define QERR_SNAPSHOT_NOT_FOUND \
+    "{ 'class': 'SnapshotNotFound', 'data': { 'device': %s, 'name': %s } }"
+
 #define QERR_SNAPSHOT_NOT_SUPPORTED \
     "{ 'class': 'SnapshotNotSupported', 'data': { 'device': %s } }"
 
qmp-commands.hx +25 -0
index a1c82f7..02550f2 100644
@@ -1087,6 +1087,31 @@ Example:
 
 EQMP
     {
+        .name       = "loadvm",
+        .args_type  = "name:s",
+        .params     = "name",
+        .help       = "restore a VM snapshot from its tag or id",
+        .mhandler.cmd_new = qmp_marshal_input_loadvm
+    },
+
+SQMP
+loadvm
+------
+
+Set the whole virtual machine to the snapshot identified by the tag
+'tag' or the unique snapshot ID 'id'.
+
+Arguments:
+
+- "name": tag or id of existing snapshot
+
+Example:
+
+-> { "execute": "loadvm", "arguments": { "name": "my_snapshot" } }
+<- { "return": {} }
+
+EQMP
+    {
         .name       = "qmp_capabilities",
         .args_type  = "",
         .params     = "",
savevm.c +31 -21

105 lines not on this page · raw message

sysemu.h +0 -1
index 95d1207..8e65651 100644
@@ -69,7 +69,6 @@ void qemu_remove_exit_notifier(Notifier *notify);
 
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 
-int load_vmstate(const char *name);
 void do_delvm(Monitor *mon, const QDict *qdict);
 void do_info_snapshots(Monitor *mon);
 
vl.c +6 -1
index 1329c30..28b982d 100644
@@ -3630,8 +3630,13 @@ int main(int argc, char **argv, char **envp)
 
     qemu_system_reset(VMRESET_SILENT);
     if (loadvm) {
-        if (load_vmstate(loadvm) < 0) {
+        Error *err = NULL;
+
+        qmp_loadvm(loadvm, &err);
+
+        if (error_is_set(&err)) {
             autostart = 0;
+            error_report("%s\n", error_get_pretty(err));
         }
     }
 
Signature · 1 line
1.7.10.4
#7 lore raw

Re: [Qemu-devel] [RFC PATCH 2/4] qapi: Convert loadvm

Quoted 5 lines · Pavel Hrdina

---

+++ b/qapi-schema.json

qapi-schema.json line 1895

Quoted 9 lines
qapi-schema.json +4 -1
@@ -1889,4 +1889,26 @@
 #
 # Since: 1.2
 ##
-{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
+
+##
+# @loadvm:

Similar to my question in 1/4, should this be named 'load-vm'?

Quoted 5 lines
qapi-schema.json +5 -0
+#
+# Set the whole virtual machine to the snapshot identified by the tag
+# 'tag' or the unique snapshot ID 'id'.
+#
+# @name: tag or id of existing snapshot

qapi-schema.json line 1910

Quoted 10 lines
qapi-schema.json +10 -0
+#
+# Returns: Nothing on success
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If no snapshot is found, SnapshotNotFound
+#          If snapshot is only disk snapshot, SnapshotInvalid
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If snapshot activate failed, SnapshotActivateFailed
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error occures while loading vm state, SnapshotLoadFailed

s/occures/occurs/

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#8 lore raw

Re: [Qemu-devel] [RFC PATCH 2/4] qapi: Convert loadvm

Quoted 4 lines · On Thu, 12 Jul 2012 11:56:28 -0600 Eric Blake <[email protected]> wrote:
Quoted 2 lines · Pavel Hrdina

---

Quoted 13 lines

qapi-schema.json line 1895

Quoted 11 lines

+++ b/qapi-schema.json

qapi-schema.json +4 -1
@@ -1889,4 +1889,26 @@
 #
 # Since: 1.2
 ##
-{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
+
+##
+# @loadvm:

Similar to my question in 1/4, should this be named 'load-vm'?

Yes.

Quoted 19 lines
Quoted 6 lines
qapi-schema.json +5 -0
+#
+# Set the whole virtual machine to the snapshot identified by the tag
+# 'tag' or the unique snapshot ID 'id'.
+#
+# @name: tag or id of existing snapshot

qapi-schema.json line 1910

Quoted 10 lines
qapi-schema.json +10 -0
+#
+# Returns: Nothing on success
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If no snapshot is found, SnapshotNotFound
+#          If snapshot is only disk snapshot, SnapshotInvalid
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If snapshot activate failed, SnapshotActivateFailed
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error occures while loading vm state, SnapshotLoadFailed

s/occures/occurs/

#9 lore raw

Re: [Qemu-devel] [RFC PATCH 2/4] qapi: Convert loadvm

In reply to Pavel Hrdina
Quoted 234 lines · On Thu, 12 Jul 2012 18:55:16 +0200 Pavel Hrdina <[email protected]> wrote:
---
 hmp-commands.hx  |    2 +-
 hmp.c            |   10 ++++++++++
 hmp.h            |    1 +
 monitor.c        |   12 ------------
 qapi-schema.json |   24 +++++++++++++++++++++++-
 qerror.c         |   16 ++++++++++++++++
 qerror.h         |   12 ++++++++++++
 qmp-commands.hx  |   25 +++++++++++++++++++++++++
 savevm.c         |   52 +++++++++++++++++++++++++++++++---------------------
 sysemu.h         |    1 -
 vl.c             |    7 ++++++-
 11 files changed, 125 insertions(+), 37 deletions(-)
hmp-commands.hx +1 -1
index e8c5325..b145612 100644
@@ -284,7 +284,7 @@ ETEXI
         .args_type  = "name:s",
         .params     = "tag|id",
         .help       = "restore a VM snapshot from its tag or id",
-        .mhandler.cmd = do_loadvm,
+        .mhandler.cmd = hmp_loadvm,
     },
 
 STEXI
hmp.c +10 -0
index 491599d..6ed4c3f 100644
@@ -1012,3 +1012,13 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
 
     hmp_handle_error(mon, &err);
 }
+
+void hmp_loadvm(Monitor *mon, const QDict *qdict)
+{
+    const char *name = qdict_get_str(qdict, "name");
+    Error *err = NULL;
+
+    qmp_loadvm(name, &err);
+
+    hmp_handle_error(mon, &err);
+}
hmp.h +1 -0
index dc6410b..5623ae7 100644
@@ -65,5 +65,6 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
 void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
 void hmp_savevm(Monitor *mon, const QDict *qdict);
+void hmp_loadvm(Monitor *mon, const QDict *qdict);
 
 #endif
monitor.c +0 -12
index f6107ba..3efbcc8 100644
@@ -2385,18 +2385,6 @@ static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data)
     return -1;
 }
 
-static void do_loadvm(Monitor *mon, const QDict *qdict)
-{
-    int saved_vm_running  = runstate_is_running();
-    const char *name = qdict_get_str(qdict, "name");
-
-    vm_stop(RUN_STATE_RESTORE_VM);
-
-    if (load_vmstate(name) == 0 && saved_vm_running) {
-        vm_start();
-    }
-}
-
 int monitor_get_fd(Monitor *mon, const char *fdname)
 {
     mon_fd_t *monfd;
qapi-schema.json +23 -1
index 4db1447..7df6a90 100644
@@ -1889,4 +1889,26 @@
 #
 # Since: 1.2
 ##
-{ 'command': 'savevm', 'data': {'*name': 'str'} }
\ No newline at end of file
+{ 'command': 'savevm', 'data': {'*name': 'str'} }
+
+##
+# @loadvm:
+#
+# Set the whole virtual machine to the snapshot identified by the tag
+# 'tag' or the unique snapshot ID 'id'.
+#
+# @name: tag or id of existing snapshot
+#
+# Returns: Nothing on success
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If no snapshot is found, SnapshotNotFound
+#          If snapshot is only disk snapshot, SnapshotInvalid
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If snapshot activate failed, SnapshotActivateFailed
+#          If open a block device for vm state fail, SnapshotOpenFailed
+#          If an error occures while loading vm state, SnapshotLoadFailed
+#
+# Since: 1.2
+##
+{ 'command': 'loadvm', 'data': {'name': 'str'} }
qerror.c +16 -0
index 4e6efa1..b9c7352 100644
@@ -309,6 +309,10 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Could not start VNC server on %(target)",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_ACTIVATE_FAILED,
+        .desc      = "Error '%(errno)' while activating snapshot '%(name)' on '%(device)'",
+    },
+    {
         .error_fmt = QERR_SNAPSHOT_CREATE_FAILED,
         .desc      = "Error '%(errno)' while creating snapshot on '%(device)'",
     },
@@ -317,10 +321,22 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Error '%(errno)' while deleting snapshot on '%(device)'",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_INVALID,
+        .desc      = "Device '%(device)' disk-only snapshot. Revert to it offline using qemu-img.",
+    },
+    {
+        .error_fmt = QERR_SNAPSHOT_LOAD_FAILED,
+        .desc      = "Error '%(errno)' while loading snapshot for '%(device)'",
+    },
+    {
         .error_fmt = QERR_SNAPSHOT_NOT_ACCEPTED,
         .desc      = "No block device can accept snapshots",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_NOT_FOUND,
+        .desc      = "Device '%(device)' does not have the requested snapshot '%(name)'",
+    },
+    {
         .error_fmt = QERR_SNAPSHOT_NOT_SUPPORTED,
         .desc      = "Device '%(device)' is writable but does not support snapshots",
     },
qerror.h +12 -0
index bc47f4a..da2abdd 100644
@@ -251,15 +251,27 @@ QError *qobject_to_qerror(const QObject *obj);
 #define QERR_VNC_SERVER_FAILED \
     "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"
 
+#define QERR_SNAPSHOT_ACTIVATE_FAILED \
+    "{ 'class': 'SnapshotActivateFailed', 'data': { 'device': %s, 'name': %s, 'errno':%d } }"
+
 #define QERR_SNAPSHOT_CREATE_FAILED \
     "{ 'class': 'SnapshotCreateFailed', 'data': { 'device': %s, 'errno': %d } }"
 
 #define QERR_SNAPSHOT_DELETE_FAILED \
     "{ 'class': 'SnapshotDeleteFailed', 'data': { 'device': %s, 'errno': %d } }"
 
+#define QERR_SNAPSHOT_INVALID \
+    "{ 'class': 'SnapshotInvalid', 'data': { 'device': %s } }"
+
+#define QERR_SNAPSHOT_LOAD_FAILED \
+    "{ 'class': 'SnapshotLoadFailed', 'data': { 'device': %s, 'errno': %d } }"
+
 #define QERR_SNAPSHOT_NOT_ACCEPTED \
     "{ 'class': 'SnapshotNotAccepted', 'data': {} }"
 
+#define QERR_SNAPSHOT_NOT_FOUND \
+    "{ 'class': 'SnapshotNotFound', 'data': { 'device': %s, 'name': %s } }"
+
 #define QERR_SNAPSHOT_NOT_SUPPORTED \
     "{ 'class': 'SnapshotNotSupported', 'data': { 'device': %s } }"
 
qmp-commands.hx +25 -0
index a1c82f7..02550f2 100644
@@ -1087,6 +1087,31 @@ Example:
 
 EQMP
     {
+        .name       = "loadvm",
+        .args_type  = "name:s",
+        .params     = "name",
+        .help       = "restore a VM snapshot from its tag or id",
+        .mhandler.cmd_new = qmp_marshal_input_loadvm
+    },
+
+SQMP
+loadvm
+------
+
+Set the whole virtual machine to the snapshot identified by the tag
+'tag' or the unique snapshot ID 'id'.
+
+Arguments:
+
+- "name": tag or id of existing snapshot
+
+Example:
+
+-> { "execute": "loadvm", "arguments": { "name": "my_snapshot" } }
+<- { "return": {} }
+
+EQMP
+    {
         .name       = "qmp_capabilities",
         .args_type  = "",
         .params     = "",
savevm.c +1 -1
index 9fc1b53..c113dd4 100644
@@ -2196,27 +2196,30 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp)
     return;
 }
 
-int load_vmstate(const char *name)
+void qmp_loadvm(const char *name, Error **errp)
 {
     BlockDriverState *bs, *bs_vm_state;
     QEMUSnapshotInfo sn;
     QEMUFile *f;

savevm.c void qmp_xen_save_devices_state(const char *filename, Error **errp) · line 2211

Quoted 10 lines
savevm.c +3 -2
     int ret;
+    int saved_vm_running;
 
     bs_vm_state = bdrv_snapshots();
     if (!bs_vm_state) {
-        error_report("No block device supports snapshots");
-        return -ENOTSUP;
+        error_set(errp, QERR_SNAPSHOT_NOT_ACCEPTED);
+        return;
     }

As I said for save-vm, we can use QERR_NOT_SUPPORTED here.

savevm.c void qmp_xen_save_devices_state(const char *filename, Error **errp) · line 2217

Quoted 7 lines
savevm.c +2 -1
     /* Don't even try to load empty VM states */
     ret = bdrv_snapshot_find(bs_vm_state, &sn, name);
     if (ret < 0) {
-        return ret;
+        error_set(errp, QERR_SNAPSHOT_NOT_FOUND,
+                  bdrv_get_device_name(bs_vm_state), name);

This can be QERR_INVALID_PARAMETER.

Quoted 12 lines
savevm.c +4 -3
+        return;
     } else if (sn.vm_state_size == 0) {
-        error_report("This is a disk-only snapshot. Revert to it offline "
-            "using qemu-img.");
-        return -EINVAL;
+        error_set(errp, QERR_SNAPSHOT_INVALID,
+                  bdrv_get_device_name(bs_vm_state));
+        return;
     }
 
     /* Verify if there is any device that doesn't support snapshots and is

savevm.c int load_vmstate(const char *name) · line 2236

Quoted 8 lines
savevm.c +2 -3
@@ -2229,19 +2232,22 @@ int load_vmstate(const char *name)
         }
 
         if (!bdrv_can_snapshot(bs)) {
-            error_report("Device '%s' is writable but does not support snapshots.",
-                               bdrv_get_device_name(bs));
-            return -ENOTSUP;
+            error_set(errp, QERR_SNAPSHOT_NOT_SUPPORTED,
+                      bdrv_get_device_name(bs));

QERR_NOT_SUPPORTED is fine I think.

savevm.c int load_vmstate(const char *name) · line 2243

Quoted 10 lines
savevm.c +3 -3
+            return;
         }
 
         ret = bdrv_snapshot_find(bs, &sn, name);
         if (ret < 0) {
-            error_report("Device '%s' does not have the requested snapshot '%s'",
-                           bdrv_get_device_name(bs), name);
-            return ret;
+            error_set(errp, QERR_SNAPSHOT_NOT_FOUND,
+                      bdrv_get_device_name(bs), name);

QERR_PARAMETER_INVALID.

Quoted 11 lines
savevm.c +4 -0
+            return;
         }
     }
 
+    saved_vm_running  = runstate_is_running();
+    vm_stop(RUN_STATE_RESTORE_VM);
+
     /* Flush all IO requests so they don't interfere with the new state.  */
     bdrv_drain_all();
 

savevm.c int load_vmstate(const char *name) · line 2260

Quoted 8 lines
savevm.c +2 -3
@@ -2250,9 +2256,9 @@ int load_vmstate(const char *name)
         if (bdrv_can_snapshot(bs)) {
             ret = bdrv_snapshot_goto(bs, name);
             if (ret < 0) {
-                error_report("Error %d while activating snapshot '%s' on '%s'",
-                             ret, name, bdrv_get_device_name(bs));
-                return ret;
+                error_set(errp, QERR_SNAPSHOT_ACTIVATE_FAILED,
+                          bdrv_get_device_name(bs), name, ret);

The Right Thing to do here is to convert bdrv_snapshot_goto() to take an Error argument.

Quoted 52 lines
savevm.c +10 -5
+                return;
             }
         }
     }
@@ -2260,8 +2266,9 @@ int load_vmstate(const char *name)
     /* restore the VM state */
     f = qemu_fopen_bdrv(bs_vm_state, 0);
     if (!f) {
-        error_report("Could not open VM state file");
-        return -EINVAL;
+        error_set(errp, QERR_SNAPSHOT_OPEN_FAILED,
+                  bdrv_get_device_name(bs_vm_state));
+        return;
     }
 
     qemu_system_reset(VMRESET_SILENT);
@@ -2269,11 +2276,14 @@ int load_vmstate(const char *name)
 
     qemu_fclose(f);
     if (ret < 0) {
-        error_report("Error %d while loading VM state", ret);
-        return ret;
+        error_set(errp, QERR_SNAPSHOT_LOAD_FAILED,
+                  bdrv_get_device_name(bs_vm_state), ret);
+        return;
     }
 
-    return 0;
+    if (!error_is_set(errp) && saved_vm_running) {
+        vm_start();
+    }
 }
 
 void do_delvm(Monitor *mon, const QDict *qdict)
sysemu.h +0 -1
index 95d1207..8e65651 100644
@@ -69,7 +69,6 @@ void qemu_remove_exit_notifier(Notifier *notify);
 
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 
-int load_vmstate(const char *name);
 void do_delvm(Monitor *mon, const QDict *qdict);
 void do_info_snapshots(Monitor *mon);
 
vl.c +0 -0
index 1329c30..28b982d 100644
@@ -3630,8 +3630,13 @@ int main(int argc, char **argv, char **envp)
 

vl.c int main(int argc, char **argv, char **envp) · line 3639

Quoted 10 lines
vl.c +6 -1
     qemu_system_reset(VMRESET_SILENT);
     if (loadvm) {
-        if (load_vmstate(loadvm) < 0) {
+        Error *err = NULL;
+
+        qmp_loadvm(loadvm, &err);
+
+        if (error_is_set(&err)) {
             autostart = 0;
+            error_report("%s\n", error_get_pretty(err));

You can use fprintf() here, and err has to be freed.

Quoted 3 lines
vl.c +0 -0
         }
     }
 
#10 lore raw

RFC PATCH 3/4 qapi: Convert delvm

In reply to Pavel Hrdina
---
 hmp-commands.hx  |    2 +-
 hmp.c            |   10 ++++++++++
 hmp.h            |    1 +
 qapi-schema.json |   17 +++++++++++++++++
 qmp-commands.hx  |   24 ++++++++++++++++++++++++
 savevm.c         |   21 +++++++++++----------
 sysemu.h         |    1 -
 7 files changed, 64 insertions(+), 12 deletions(-)
hmp-commands.hx +1 -1
index b145612..3be9ad4 100644
@@ -299,7 +299,7 @@ ETEXI
         .args_type  = "name:s",
         .params     = "tag|id",
         .help       = "delete a VM snapshot from its tag or id",
-        .mhandler.cmd = do_delvm,
+        .mhandler.cmd = hmp_delvm,
     },
 
 STEXI
hmp.c +10 -0
index 6ed4c3f..a4668ca 100644
@@ -1022,3 +1022,13 @@ void hmp_loadvm(Monitor *mon, const QDict *qdict)
 
     hmp_handle_error(mon, &err);
 }
+
+void hmp_delvm(Monitor *mon, const QDict *qdict)
+{
+    const char *name = qdict_get_str(qdict, "name");
+    Error *err = NULL;
+
+    qmp_delvm(name, &err);
+
+    hmp_handle_error(mon, &err);
+}
hmp.h +1 -0
index 5623ae7..78710e2 100644
@@ -66,5 +66,6 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
 void hmp_savevm(Monitor *mon, const QDict *qdict);
 void hmp_loadvm(Monitor *mon, const QDict *qdict);
+void hmp_delvm(Monitor *mon, const QDict *qdict);
 
 #endif
qapi-schema.json +17 -0
index 7df6a90..a22d26a 100644
@@ -1912,3 +1912,20 @@
 # Since: 1.2
 ##
 { 'command': 'loadvm', 'data': {'name': 'str'} }
+
+##
+# @delvm:
+#
+# Delete the snapshot identified by 'tag' or 'id'.
+#
+# @name: tag or id of existing snapshot
+#
+# Returns: Nothing on success
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If there is a writable device not supporting snapshots,
+#            SnapshotNotSupported
+#          If delete snapshot with same 'name' fail, SnapshotDeleteFailed
+#
+# Since: 1.2
+##
+{ 'command': 'delvm', 'data': {'name': 'str'} }
qmp-commands.hx +24 -0
index 02550f2..1071bb9 100644
@@ -1112,6 +1112,30 @@ Example:
 
 EQMP
     {
+        .name       = "delvm",
+        .args_type  = "name:s",
+        .params     = "tag|id",
+        .help       = "delete a VM snapshot from its tag or id",
+        .mhandler.cmd_new = qmp_marshal_input_delvm
+    },
+
+SQMP
+delvm
+------
+
+Delete the snapshot identified by 'tag' or 'id'.
+
+Arguments:
+
+- "name": tag or id of existing snapshot
+
+Example:
+
+-> { "execute": "delvm", "arguments": { "name": "my_snapshot" } }
+<- { "return": {} }
+
+EQMP
+    {
         .name       = "qmp_capabilities",
         .args_type  = "",
         .params     = "",
savevm.c +11 -10
index c113dd4..d3e8b07 100644
@@ -2286,15 +2286,14 @@ void qmp_loadvm(const char *name, Error **errp)
     }
 }
 
-void do_delvm(Monitor *mon, const QDict *qdict)
+void qmp_delvm(const char *name, Error **errp)
 {
     BlockDriverState *bs, *bs1;
     int ret;
-    const char *name = qdict_get_str(qdict, "name");
 
     bs = bdrv_snapshots();
     if (!bs) {
-        monitor_printf(mon, "No block device supports snapshots\n");
+        error_set(errp, QERR_SNAPSHOT_NOT_ACCEPTED);
         return;
     }
 
@@ -2303,13 +2302,15 @@ void do_delvm(Monitor *mon, const QDict *qdict)
         if (bdrv_can_snapshot(bs1)) {
             ret = bdrv_snapshot_delete(bs1, name);
             if (ret < 0) {
-                if (ret == -ENOTSUP)
-                    monitor_printf(mon,
-                                   "Snapshots not supported on device '%s'\n",
-                                   bdrv_get_device_name(bs1));
-                else
-                    monitor_printf(mon, "Error %d while deleting snapshot on "
-                                   "'%s'\n", ret, bdrv_get_device_name(bs1));
+                if (ret == -ENOTSUP) {
+                    error_set(errp, QERR_SNAPSHOT_NOT_SUPPORTED,
+                              bdrv_get_device_name(bs1));
+                    return;
+                } else {
+                    error_set(errp, QERR_SNAPSHOT_DELETE_FAILED,
+                              bdrv_get_device_name(bs1), ret);
+                    return;
+                }
             }
         }
     }
sysemu.h +0 -1
index 8e65651..45e071c 100644
@@ -69,7 +69,6 @@ void qemu_remove_exit_notifier(Notifier *notify);
 
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 
-void do_delvm(Monitor *mon, const QDict *qdict);
 void do_info_snapshots(Monitor *mon);
 
 void qemu_announce_self(void);
Signature · 1 line
1.7.10.4
#11 lore raw

Re: [Qemu-devel] [RFC PATCH 3/4] qapi: Convert delvm

Quoted 11 lines · Pavel Hrdina
---
 hmp-commands.hx  |    2 +-
 hmp.c            |   10 ++++++++++
 hmp.h            |    1 +
 qapi-schema.json |   17 +++++++++++++++++
 qmp-commands.hx  |   24 ++++++++++++++++++++++++
 savevm.c         |   21 +++++++++++----------
 sysemu.h         |    1 -
 7 files changed, 64 insertions(+), 12 deletions(-)

qapi-schema.json line 1917

Quoted 3 lines
qapi-schema.json +3 -0
+
+##
+# @delvm:

This name is worse than 'loadvm' or 'savevm', in that we are NOT deleting the entire vm, but a named snapshot stored within the vm. Would naming it 'delete-vm-snapshot' make more sense (in which case the others might make more sense as 'save-vm-snapshot' and 'load-vm-snapshot')?

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#12 lore raw

Re: [Qemu-devel] [RFC PATCH 3/4] qapi: Convert delvm

Quoted 10 lines · Eric Blake
Quoted 9 lines · Pavel Hrdina
---
  hmp-commands.hx  |    2 +-
  hmp.c            |   10 ++++++++++
  hmp.h            |    1 +
  qapi-schema.json |   17 +++++++++++++++++
  qmp-commands.hx  |   24 ++++++++++++++++++++++++
  savevm.c         |   21 +++++++++++----------
  sysemu.h         |    1 -
Quoted 9 lines

qapi-schema.json line 1917

Quoted 4 lines
  7 files changed, 64 insertions(+), 12 deletions(-)
qapi-schema.json +3 -0
+
+##
+# @delvm:

This name is worse than 'loadvm' or 'savevm', in that we are NOT deleting the entire vm, but a named snapshot stored within the vm. Would naming it 'delete-vm-snapshot' make more sense (in which case the others might make more sense as 'save-vm-snapshot' and 'load-vm-snapshot')?

This naming looks nice. I definitely agree that it could be save-vm-snapshot, load-vm-snapshot, delete-vm-snapshot and query-vm-snapshots.

Pavel

#13 lore raw

Re: [Qemu-devel] [RFC PATCH 3/4] qapi: Convert delvm

Quoted 21 lines · Pavel Hrdina
Quoted 11 lines · Eric Blake
Quoted 10 lines · Pavel Hrdina
---
  hmp-commands.hx  |    2 +-
  hmp.c            |   10 ++++++++++
  hmp.h            |    1 +
  qapi-schema.json |   17 +++++++++++++++++
  qmp-commands.hx  |   24 ++++++++++++++++++++++++
  savevm.c         |   21 +++++++++++----------
  sysemu.h         |    1 -
  7 files changed, 64 insertions(+), 12 deletions(-)
Quoted 9 lines

qapi-schema.json line 1917

Quoted 3 lines
qapi-schema.json +3 -0
+
+##
+# @delvm:

This name is worse than 'loadvm' or 'savevm', in that we are NOT deleting the entire vm, but a named snapshot stored within the vm. Would naming it 'delete-vm-snapshot' make more sense (in which case the others might make more sense as 'save-vm-snapshot' and 'load-vm-snapshot')?

Quoted 3 lines

This naming looks nice. I definitely agree that it could be save-vm-snapshot, load-vm-snapshot, delete-vm-snapshot and query-vm-snapshots.

On seeing that spelled out, I wonder if the '-vm' is just noise; where we could use 'query-snapshots' instead of 'query-vm-snapshots'. Then again, we already have 'blockdev-snapshot-sync', which is an entirely different snapshot (just a block device, rather than the entire VM), so dropping -vm is probably a bad idea. Anyone else want to chime in on the bikeshed painting discussion of the best QMP name?

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#14 lore raw

Re: [Qemu-devel] [RFC PATCH 3/4] qapi: Convert delvm

Am 16.07.2012 18:37, schrieb Eric Blake:

Quoted 26 lines · Pavel Hrdina
Quoted 21 lines · Eric Blake
Quoted 11 lines · Pavel Hrdina
Quoted 10 lines
---
  hmp-commands.hx  |    2 +-
  hmp.c            |   10 ++++++++++
  hmp.h            |    1 +
  qapi-schema.json |   17 +++++++++++++++++
  qmp-commands.hx  |   24 ++++++++++++++++++++++++
  savevm.c         |   21 +++++++++++----------
  sysemu.h         |    1 -
  7 files changed, 64 insertions(+), 12 deletions(-)
Quoted 9 lines

qapi-schema.json line 1917

Quoted 3 lines
qapi-schema.json +3 -0
+
+##
+# @delvm:

This name is worse than 'loadvm' or 'savevm', in that we are NOT deleting the entire vm, but a named snapshot stored within the vm. Would naming it 'delete-vm-snapshot' make more sense (in which case the others might make more sense as 'save-vm-snapshot' and 'load-vm-snapshot')?

Quoted 3 lines

This naming looks nice. I definitely agree that it could be save-vm-snapshot, load-vm-snapshot, delete-vm-snapshot and query-vm-snapshots.

Quoted 6 lines

On seeing that spelled out, I wonder if the '-vm' is just noise; where we could use 'query-snapshots' instead of 'query-vm-snapshots'. Then again, we already have 'blockdev-snapshot-sync', which is an entirely different snapshot (just a block device, rather than the entire VM), so dropping -vm is probably a bad idea. Anyone else want to chime in on the bikeshed painting discussion of the best QMP name?

Sure. ;-)

If there is a blockdev-snapshot-sync, then the counterparts for the whole VM shouldn't be called load/save/del-vm-snapshot, but vm-snapshot-load/save/del. And possibly add a -sync as well.

Kevin

#15 lore raw

RFC PATCH 4/4 qapi: Convert info snapshots

In reply to Pavel Hrdina
---
 hmp.c            |   34 ++++++++++++++++++++++++++++++++++
 hmp.h            |    1 +
 monitor.c        |    2 +-
 qapi-schema.json |   35 +++++++++++++++++++++++++++++++++++
 qerror.c         |    4 ++++
 qerror.h         |    3 +++
 qmp-commands.hx  |   31 +++++++++++++++++++++++++++++++
 savevm.c         |   52 ++++++++++++++++++++++++----------------------------
 sysemu.h         |    2 --
 9 files changed, 133 insertions(+), 31 deletions(-)
hmp.c +34 -0
index a4668ca..e611329 100644
@@ -553,6 +553,40 @@ void hmp_info_block_jobs(Monitor *mon)
     }
 }
 
+void hmp_info_snapshots(Monitor *mon)
+{
+    SnapshotInfoList *list;
+    Error *err = NULL;
+    char buf[256];
+    QEMUSnapshotInfo sn;
+
+    list = qmp_query_snapshots(&err);
+
+    if (error_is_set(&err)) {
+        hmp_handle_error(mon, &err);
+        return;
+    }
+
+    if (!list) {
+        monitor_printf(mon, "There is no snapshot available.\n");
+        return;
+    }
+
+    monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+    while (list) {
+        memcpy(&(sn.id_str), list->value->id, sizeof(sn.id_str));
+        memcpy(&(sn.name), list->value->tag, sizeof(sn.name));
+        sn.date_sec = list->value->date;
+        sn.date_nsec = sn.date_nsec * 1000;
+        sn.vm_clock_nsec = list->value->vm_clock;
+        sn.vm_state_size = list->value->vm_size;
+        monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), &sn));
+        list = list->next;
+    }
+
+    qapi_free_SnapshotInfoList(list);
+}
+
 void hmp_quit(Monitor *mon, const QDict *qdict)
 {
     monitor_suspend(mon);
hmp.h +1 -0
index 78710e2..0c13209 100644
@@ -33,6 +33,7 @@ void hmp_info_spice(Monitor *mon);
 void hmp_info_balloon(Monitor *mon);
 void hmp_info_pci(Monitor *mon);
 void hmp_info_block_jobs(Monitor *mon);
+void hmp_info_snapshots(Monitor *mon);
 void hmp_quit(Monitor *mon, const QDict *qdict);
 void hmp_stop(Monitor *mon, const QDict *qdict);
 void hmp_system_reset(Monitor *mon, const QDict *qdict);
monitor.c +1 -1
index 3efbcc8..262f9ea 100644
@@ -2596,7 +2596,7 @@ static mon_cmd_t info_cmds[] = {
         .args_type  = "",
         .params     = "",
         .help       = "show the currently saved VM snapshots",
-        .mhandler.info = do_info_snapshots,
+        .mhandler.info = hmp_info_snapshots,
     },
     {
         .name       = "status",
qapi-schema.json +35 -0
index a22d26a..e66156c 100644
@@ -934,6 +934,41 @@
 { 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] }
 
 ##
+# @SnapshotInfo:
+#
+# Snapshot list.  This structure contains list of snapshots for virtual machine.
+#
+# @id: id of the snapshot.
+#
+# @tag: human readable tag of the snapshot.
+#
+# @vm_size: size of the snapshot in Bytes.
+#
+# @date: date and time of the snapshot as timestamp.
+#
+# @vm_clock: time in the guest in nsecs.
+#
+# Since:  1.2
+##
+{ 'type': 'SnapshotInfo',
+  'data': {'id': 'str', 'tag': 'str', 'vm_size': 'int',
+           'date': 'int', 'vm_clock': 'int'} }
+
+##
+# @query-snapshots:
+#
+# List available snapshots for VM.
+#
+# Returns: a list of @SnapshotInfo describing each snapshot or an empty list
+#            on success
+#          If no block device can accept snapshots, SnapshotNotAccepted
+#          If loading snapshot list failed, SnapshotListFailed
+#
+# Since: 1.2
+##
+{ 'command': 'query-snapshots', 'returns': ['SnapshotInfo'] }
+
+##
 # @quit:
 #
 # This command will cause the QEMU process to exit gracefully.  While every
qerror.c +4 -0
index b9c7352..7c6c0b3 100644
@@ -325,6 +325,10 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Device '%(device)' disk-only snapshot. Revert to it offline using qemu-img.",
     },
     {
+        .error_fmt = QERR_SNAPSHOT_LIST_FAILED,
+        .desc      = "Error '%(errno)' while listing snapshots on '%(device)'",
+    },
+    {
         .error_fmt = QERR_SNAPSHOT_LOAD_FAILED,
         .desc      = "Error '%(errno)' while loading snapshot for '%(device)'",
     },
qerror.h +3 -0
index da2abdd..d602f05 100644
@@ -263,6 +263,9 @@ QError *qobject_to_qerror(const QObject *obj);
 #define QERR_SNAPSHOT_INVALID \
     "{ 'class': 'SnapshotInvalid', 'data': { 'device': %s } }"
 
+#define QERR_SNAPSHOT_LIST_FAILED \
+    "{ 'class': 'SnapshotListFailed', 'data': { 'device': %s, 'errno': %d } }"
+
 #define QERR_SNAPSHOT_LOAD_FAILED \
     "{ 'class': 'SnapshotLoadFailed', 'data': { 'device': %s, 'errno': %d } }"
 
qmp-commands.hx +31 -0
index 1071bb9..949efd2 100644
@@ -2284,3 +2284,34 @@ EQMP
         .args_type  = "implements:s?,abstract:b?",
         .mhandler.cmd_new = qmp_marshal_input_qom_list_types,
     },
+
+SQMP
+query-snapshots
+----------
+
+List available snapshots for VM.
+
+Return a json-object with the following information:
+
+- "id": id of the snapshot.
+
+- "tag": human readable tag of the snapshot.
+
+- "vm_size": size of the snapshot in Bytes.
+
+- "date": date and time of the snapshot as timestamp.
+
+- "vm_clock": time in the guest in nsecs.
+
+Example:
+
+-> { "execute": "query-snapshots" }
+<- {"return": [{"vm_size": 212309067, "tag": "my_snapshot",
+    "vm_clock": 630176706190, "id": "1", "date": 1341999856}]}
+
+EQMP
+    {
+        .name       = "query-snapshots",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_query_snapshots,
+    },
savevm.c +24 -28

81 lines not on this page · raw message

sysemu.h +0 -2
index 45e071c..b79ce73 100644
@@ -69,8 +69,6 @@ void qemu_remove_exit_notifier(Notifier *notify);
 
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 
-void do_info_snapshots(Monitor *mon);
-
 void qemu_announce_self(void);
 
 bool qemu_savevm_state_blocked(Error **errp);
Signature · 1 line
1.7.10.4
#16 lore raw

Re: [Qemu-devel] [RFC PATCH 4/4] qapi: Convert info snapshots

Quoted 5 lines · Pavel Hrdina

---

+++ b/qapi-schema.json

qapi-schema.json line 943

Quoted 10 lines
qapi-schema.json +7 -0
@@ -934,6 +934,41 @@
 { 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] }
 
 ##
+# @SnapshotInfo:
+#
+# Snapshot list.  This structure contains list of snapshots for virtual machine.
+#
+# @id: id of the snapshot.
+#
+# @tag: human readable tag of the snapshot.

Tag was optional on creation, should that field be marked optional and omitted on output when there was no tag on input?

qapi-schema.json line 947

Quoted 4 lines
qapi-schema.json +4 -0
+#
+# @vm_size: size of the snapshot in Bytes.
+#
+# @date: date and time of the snapshot as timestamp.

What type of timestamp? Seconds since Epoch?

Review line 949

Quoted 2 lines
(quoted diff) +2 -0
+#
+# @vm_clock: time in the guest in nsecs.

If vm_clock is in nsecs, should we also be reporting sub-second resolution in date?

qmp-commands.hx EQMP · line 2294

Quoted 7 lines
qmp-commands.hx +7 -0
+SQMP
+query-snapshots
+----------
+
+List available snapshots for VM.
+
+Return a json-object with the following information:

Returns an array of json-objects, each with the following information:

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#17 lore raw

Re: [Qemu-devel] [RFC PATCH 4/4] qapi: Convert info snapshots

Quoted 7 lines · Eric Blake
Quoted 6 lines · Pavel Hrdina

---
+++ b/qapi-schema.json

qapi-schema.json +0 -0
@@ -934,6 +934,41 @@
  { 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] }
 
Quoted 10 lines

qapi-schema.json line 943

Quoted 8 lines
qapi-schema.json +7 -0
  ##
+# @SnapshotInfo:
+#
+# Snapshot list.  This structure contains list of snapshots for virtual machine.
+#
+# @id: id of the snapshot.
+#
+# @tag: human readable tag of the snapshot.

Tag was optional on creation, should that field be marked optional and omitted on output when there was no tag on input?

If you create snapshot using an ID, Tag is created automatically. Even if it is ugly tag, I think that we shouldn't omit it on output.

Quoted 5 lines

qapi-schema.json line 947

Quoted 4 lines
qapi-schema.json +4 -0
+#
+# @vm_size: size of the snapshot in Bytes.
+#
+# @date: date and time of the snapshot as timestamp.

What type of timestamp? Seconds since Epoch?

It is unix timestamp in seconds since Epoch.

Quoted 4 lines

Review line 949

Quoted 2 lines
(quoted diff) +2 -0
+#
+# @vm_clock: time in the guest in nsecs.

If vm_clock is in nsecs, should we also be reporting sub-second resolution in date?

Well, I thought that this could be enough, but we could return the same structure as "QEMUSnapshotInfo".

Quoted 9 lines

qmp-commands.hx EQMP · line 2294

Quoted 7 lines
qmp-commands.hx +7 -0
+SQMP
+query-snapshots
+----------
+
+List available snapshots for VM.
+
+Return a json-object with the following information:

Returns an array of json-objects, each with the following information:

#18 lore raw
In reply to Pavel Hrdina
Quoted 2 lines · On Thu, 12 Jul 2012 18:55:14 +0200 Pavel Hrdina <[email protected]> wrote:

This patch-series converts savevm, loadvm, delvm and info snapshots from HMP into QMP as savevm, loadvm, delvm and query-snapshots.

I think we'll have to start propagating errors correctly as I pointed out in my review, and then put this series on top. Let's wait for my series that will improve error_set().