Patchwork [RFC,4/4] qapi: Convert info snapshots

login
register
mail settings
Submitter Pavel Hrdina
Date July 12, 2012, 4:55 p.m.
Message ID <eb5bfa38183fc1139c9f26ab03981f106aa30f25.1342092497.git.phrdina@redhat.com>
Download mbox | patch
Permalink /patch/170737/
State New
Headers show

Comments

Pavel Hrdina - July 12, 2012, 4:55 p.m.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
---
 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(-)
Eric Blake - July 12, 2012, 6:03 p.m.
On 07/12/2012 10:55 AM, Pavel Hrdina wrote:
> Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
> ---

> +++ b/qapi-schema.json
> @@ -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?

> +#
> +# @vm_size: size of the snapshot in Bytes.
> +#
> +# @date: date and time of the snapshot as timestamp.

What type of timestamp?  Seconds since Epoch?

> +#
> +# @vm_clock: time in the guest in nsecs.

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

> +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:
Pavel Hrdina - July 16, 2012, 8:15 a.m.
On 07/12/2012 08:03 PM, Eric Blake wrote:
> On 07/12/2012 10:55 AM, Pavel Hrdina wrote:
>> Signed-off-by: Pavel Hrdina<phrdina@redhat.com>
>> ---
>> +++ b/qapi-schema.json
>> @@ -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?
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.
>> +#
>> +# @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.
>> +#
>> +# @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".
>> +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:
>

Patch

diff --git a/hmp.c b/hmp.c
index a4668ca..e611329 100644
--- a/hmp.c
+++ b/hmp.c
@@ -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);
diff --git a/hmp.h b/hmp.h
index 78710e2..0c13209 100644
--- a/hmp.h
+++ b/hmp.h
@@ -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);
diff --git a/monitor.c b/monitor.c
index 3efbcc8..262f9ea 100644
--- a/monitor.c
+++ b/monitor.c
@@ -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",
diff --git a/qapi-schema.json b/qapi-schema.json
index a22d26a..e66156c 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -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
diff --git a/qerror.c b/qerror.c
index b9c7352..7c6c0b3 100644
--- a/qerror.c
+++ b/qerror.c
@@ -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)'",
     },
diff --git a/qerror.h b/qerror.h
index da2abdd..d602f05 100644
--- a/qerror.h
+++ b/qerror.h
@@ -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 } }"
 
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 1071bb9..949efd2 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -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,
+    },
diff --git a/savevm.c b/savevm.c
index d3e8b07..7454bd4 100644
--- a/savevm.c
+++ b/savevm.c
@@ -2316,34 +2316,26 @@  void qmp_delvm(const char *name, Error **errp)
     }
 }
 
-void do_info_snapshots(Monitor *mon)
+SnapshotInfoList *qmp_query_snapshots(Error **errp)
 {
+    SnapshotInfoList *snapshot_list = NULL, *last = NULL;
     BlockDriverState *bs, *bs1;
     QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s;
     int nb_sns, i, ret, available;
-    int total;
-    int *available_snapshots;
-    char buf[256];
 
     bs = bdrv_snapshots();
     if (!bs) {
-        monitor_printf(mon, "No available block device supports snapshots\n");
-        return;
+        error_set(errp, QERR_SNAPSHOT_NOT_ACCEPTED);
+        return NULL;
     }
 
     nb_sns = bdrv_snapshot_list(bs, &sn_tab);
     if (nb_sns < 0) {
-        monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns);
-        return;
-    }
-
-    if (nb_sns == 0) {
-        monitor_printf(mon, "There is no snapshot available.\n");
-        return;
+        error_set(errp, QERR_SNAPSHOT_LIST_FAILED,
+                  bdrv_get_device_name(bs), nb_sns);
+        return NULL;
     }
 
-    available_snapshots = g_malloc0(sizeof(int) * nb_sns);
-    total = 0;
     for (i = 0; i < nb_sns; i++) {
         sn = &sn_tab[i];
         available = 1;
@@ -2360,23 +2352,27 @@  void do_info_snapshots(Monitor *mon)
         }
 
         if (available) {
-            available_snapshots[total] = i;
-            total++;
-        }
-    }
-
-    if (total > 0) {
-        monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
-        for (i = 0; i < total; i++) {
-            sn = &sn_tab[available_snapshots[i]];
-            monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+            SnapshotInfoList *info = g_malloc0(sizeof(*info));
+            info->value = g_malloc0(sizeof(*info->value));
+            info->value->id = g_strdup(sn->id_str);
+            info->value->tag = g_strdup(sn->name);
+            info->value->vm_size = sn->vm_state_size;
+            info->value->date = sn->date_sec;
+            info->value->vm_clock = sn->vm_clock_nsec;
+
+            if (!snapshot_list) {
+                snapshot_list = info;
+                last = info;
+            } else {
+                last->next = info;
+                last = info;
+            }
         }
-    } else {
-        monitor_printf(mon, "There is no suitable snapshot available\n");
     }
 
     g_free(sn_tab);
-    g_free(available_snapshots);
+
+    return snapshot_list;
 
 }
 
diff --git a/sysemu.h b/sysemu.h
index 45e071c..b79ce73 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -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);