[libvirt] [RFC PATCH 0/8]: Domain Snapshot API

Hello, Here is the first set of RFC patches for the snapshot API. Note that this definitely isn't finished; in particular, we are going to be changing the semantic for virDomainCreateFromSnapshot, and there are still a couple of known outstanding bugs. Nevertheless, that will probably be pretty minor in terms of code, so we'd like to get review on the rest of these patches for now. I've broken the series down along lines that I believe will compile independently, though I haven't yet done a git-bisect to confirm. Unfortunately it leaves patch 3/8 as a monster patch implementing the skeleton and remote driver. The good news is that it is mostly mechanical. Suggestions for how to better split it up are welcome. Thanks go to Jirka for lots of code contribution to this series, and all of the others who took part in the discussion of this API. Comments are welcome. Chris Lalancette

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 14 +++++++------- src/conf/domain_conf.h | 8 -------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0ad2e4a..e260dce 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -6080,13 +6080,13 @@ cleanup: } -virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, - virDomainObjListPtr doms, - const char *configDir, - const char *autostartDir, - const char *name, - virDomainLoadConfigNotify notify, - void *opaque) +static virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, + virDomainObjListPtr doms, + const char *configDir, + const char *autostartDir, + const char *name, + virDomainLoadConfigNotify notify, + void *opaque) { char *configFile = NULL, *autostartLink = NULL; virDomainDefPtr def = NULL; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fdc6d44..023196f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -950,14 +950,6 @@ typedef void (*virDomainLoadConfigNotify)(virDomainObjPtr dom, int newDomain, void *opaque); -virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, - virDomainObjListPtr doms, - const char *configDir, - const char *autostartDir, - const char *name, - virDomainLoadConfigNotify notify, - void *opaque); - int virDomainLoadAllConfigs(virCapsPtr caps, virDomainObjListPtr doms, const char *configDir, -- 1.6.6.1

2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 14 +++++++------- src/conf/domain_conf.h | 8 -------- 2 files changed, 7 insertions(+), 15 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0ad2e4a..e260dce 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -6080,13 +6080,13 @@ cleanup: }
-virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, - virDomainObjListPtr doms, - const char *configDir, - const char *autostartDir, - const char *name, - virDomainLoadConfigNotify notify, - void *opaque) +static virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, + virDomainObjListPtr doms, + const char *configDir, + const char *autostartDir, + const char *name, + virDomainLoadConfigNotify notify, + void *opaque) { char *configFile = NULL, *autostartLink = NULL; virDomainDefPtr def = NULL; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fdc6d44..023196f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -950,14 +950,6 @@ typedef void (*virDomainLoadConfigNotify)(virDomainObjPtr dom, int newDomain, void *opaque);
-virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, - virDomainObjListPtr doms, - const char *configDir, - const char *autostartDir, - const char *name, - virDomainLoadConfigNotify notify, - void *opaque); - int virDomainLoadAllConfigs(virCapsPtr caps, virDomainObjListPtr doms, const char *configDir, -- 1.6.6.1
ACK. Matthias

On 04/02/2010 09:22 AM, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 14 +++++++------- src/conf/domain_conf.h | 8 -------- 2 files changed, 7 insertions(+), 15 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0ad2e4a..e260dce 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -6080,13 +6080,13 @@ cleanup: }
-virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, - virDomainObjListPtr doms, - const char *configDir, - const char *autostartDir, - const char *name, - virDomainLoadConfigNotify notify, - void *opaque) +static virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, + virDomainObjListPtr doms, + const char *configDir, + const char *autostartDir, + const char *name, + virDomainLoadConfigNotify notify, + void *opaque) { char *configFile = NULL, *autostartLink = NULL; virDomainDefPtr def = NULL; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fdc6d44..023196f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -950,14 +950,6 @@ typedef void (*virDomainLoadConfigNotify)(virDomainObjPtr dom, int newDomain, void *opaque);
-virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, - virDomainObjListPtr doms, - const char *configDir, - const char *autostartDir, - const char *name, - virDomainLoadConfigNotify notify, - void *opaque); - int virDomainLoadAllConfigs(virCapsPtr caps, virDomainObjListPtr doms, const char *configDir, -- 1.6.6.1
ACK.
Thanks. Since this is just an unrelated bugfix, I pushed it. -- Chris Lalancette

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/util/virterror.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/util/virterror.c b/src/util/virterror.c index 12dfeb7..2537110 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -180,6 +180,7 @@ static const char *virErrorDomainName(virErrorDomain domain) { break; case VIR_FROM_NWFILTER: dom = "Network Filter"; + break; case VIR_FROM_HOOK: dom = "Sync Hook "; break; -- 1.6.6.1

2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/util/virterror.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/util/virterror.c b/src/util/virterror.c index 12dfeb7..2537110 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -180,6 +180,7 @@ static const char *virErrorDomainName(virErrorDomain domain) { break; case VIR_FROM_NWFILTER: dom = "Network Filter"; + break; case VIR_FROM_HOOK: dom = "Sync Hook "; break; -- 1.6.6.1
ACK. Matthias

On 04/02/2010 09:22 AM, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/util/virterror.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/util/virterror.c b/src/util/virterror.c index 12dfeb7..2537110 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -180,6 +180,7 @@ static const char *virErrorDomainName(virErrorDomain domain) { break; case VIR_FROM_NWFILTER: dom = "Network Filter"; + break; case VIR_FROM_HOOK: dom = "Sync Hook "; break; -- 1.6.6.1
ACK.
Thanks. Since this is an unrelated bugfix, I pushed it. -- Chris Lalancette

On Fri, Apr 02, 2010 at 02:22:41PM +0100, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/util/virterror.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/util/virterror.c b/src/util/virterror.c index 12dfeb7..2537110 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -180,6 +180,7 @@ static const char *virErrorDomainName(virErrorDomain domain) { break; case VIR_FROM_NWFILTER: dom = "Network Filter"; + break; case VIR_FROM_HOOK: dom = "Sync Hook "; break; -- 1.6.6.1
ACK.
Oops, my bad, that's mine ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- daemon/remote.c | 311 ++++++++++++++++++++++++ daemon/remote_dispatch_args.h | 9 + daemon/remote_dispatch_prototypes.h | 72 ++++++ daemon/remote_dispatch_ret.h | 7 + daemon/remote_dispatch_table.h | 45 ++++ include/libvirt/libvirt.h.in | 61 +++++ include/libvirt/virterror.h | 5 +- src/datatypes.c | 122 ++++++++++ src/datatypes.h | 25 ++ src/driver.h | 47 ++++ src/esx/esx_driver.c | 9 + src/libvirt.c | 448 +++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/libvirt_public.syms | 10 + src/lxc/lxc_driver.c | 9 + src/opennebula/one_driver.c | 9 + src/openvz/openvz_driver.c | 9 + src/phyp/phyp_driver.c | 9 + src/qemu/qemu_driver.c | 9 + src/remote/remote_driver.c | 309 ++++++++++++++++++++++++ src/remote/remote_protocol.c | 181 ++++++++++++++ src/remote/remote_protocol.h | 145 +++++++++++ src/remote/remote_protocol.x | 97 ++++++++- src/test/test_driver.c | 9 + src/uml/uml_driver.c | 9 + src/util/virterror.c | 15 ++ src/vbox/vbox_tmpl.c | 9 + src/xen/xen_driver.c | 9 + src/xenapi/xenapi_driver.c | 9 + 29 files changed, 2007 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 67162d5..daedfd3 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -67,6 +67,7 @@ static virStoragePoolPtr get_nonnull_storage_pool (virConnectPtr conn, remote_no static virStorageVolPtr get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol); static virSecretPtr get_nonnull_secret (virConnectPtr conn, remote_nonnull_secret secret); static virNWFilterPtr get_nonnull_nwfilter (virConnectPtr conn, remote_nonnull_nwfilter nwfilter); +static virDomainSnapshotPtr get_nonnull_domain_snapshot (virConnectPtr conn, remote_nonnull_domain_snapshot snapshot); static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src); static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src); static void make_nonnull_interface (remote_nonnull_interface *interface_dst, virInterfacePtr interface_src); @@ -75,6 +76,7 @@ static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virSt static void make_nonnull_node_device (remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src); static void make_nonnull_secret (remote_nonnull_secret *secret_dst, virSecretPtr secret_src); static void make_nonnull_nwfilter (remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src); +static void make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src); #include "remote_dispatch_prototypes.h" @@ -5766,6 +5768,298 @@ remoteDispatchDomainMigrateSetMaxDowntime(struct qemud_server *server ATTRIBUTE_ return 0; } +static int +remoteDispatchDomainSnapshotCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_snapshot_create_xml_args *args, + remote_domain_snapshot_create_xml_ret *ret) +{ + virDomainSnapshotPtr snapshot; + virDomainPtr domain; + + domain = get_nonnull_domain(conn, args->domain); + if (domain == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + snapshot = virDomainSnapshotCreateXML(domain, args->xml_desc, args->flags); + if (snapshot == NULL) { + virDomainFree(domain); + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_domain_snapshot(&ret->snap, snapshot); + + virDomainSnapshotFree(snapshot); + virDomainFree(domain); + + return 0; +} + +static int +remoteDispatchDomainSnapshotDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_snapshot_dump_xml_args *args, + remote_domain_snapshot_dump_xml_ret *ret) +{ + virDomainSnapshotPtr snapshot; + + snapshot = get_nonnull_domain_snapshot(conn, args->snap); + if (snapshot == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + /* remoteDispatchClientRequest will free this. */ + ret->xml = virDomainSnapshotGetXMLDesc(snapshot, args->flags); + if (!ret->xml) { + virDomainSnapshotFree(snapshot); + remoteDispatchConnError(rerr, conn); + return -1; + } + + virDomainSnapshotFree(snapshot); + + return 0; +} + +static int +remoteDispatchDomainSnapshotNum (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_snapshot_num_args *args, + remote_domain_snapshot_num_ret *ret) +{ + virDomainPtr domain; + + domain = get_nonnull_domain(conn, args->domain); + if (domain == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + ret->num = virDomainSnapshotNum(domain, args->flags); + if (ret->num == -1) { + virDomainFree(domain); + remoteDispatchConnError(rerr, conn); + return -1; + } + + virDomainFree(domain); + + return 0; +} + +static int +remoteDispatchDomainSnapshotListNames (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_snapshot_list_names_args *args, + remote_domain_snapshot_list_names_ret *ret) +{ + virDomainPtr domain; + + if (args->nameslen > REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX) { + remoteDispatchFormatError (rerr, "%s", + _("nameslen > REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX")); + return -1; + } + + domain = get_nonnull_domain(conn, args->domain); + if (domain == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + /* Allocate return buffer. */ + if (VIR_ALLOC_N(ret->names.names_val, args->nameslen) < 0) { + virDomainFree(domain); + remoteDispatchOOMError(rerr); + return -1; + } + + ret->names.names_len = virDomainSnapshotListNames(domain, + ret->names.names_val, + args->nameslen, + args->flags); + if (ret->names.names_len == -1) { + virDomainFree(domain); + VIR_FREE(ret->names.names_val); + remoteDispatchConnError(rerr, conn); + return -1; + } + + virDomainFree(domain); + + return 0; +} + +static int +remoteDispatchDomainSnapshotLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_snapshot_lookup_by_name_args *args, + remote_domain_snapshot_lookup_by_name_ret *ret) +{ + virDomainSnapshotPtr snapshot; + virDomainPtr domain; + + domain = get_nonnull_domain(conn, args->domain); + if (domain == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + snapshot = virDomainSnapshotLookupByName(domain, args->name, args->flags); + if (snapshot == NULL) { + virDomainFree(domain); + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_domain_snapshot (&ret->snap, snapshot); + + virDomainSnapshotFree(snapshot); + virDomainFree(domain); + + return 0; +} + +static int +remoteDispatchDomainHasCurrentSnapshot(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_has_current_snapshot_args *args, + remote_domain_has_current_snapshot_ret *ret) +{ + virDomainPtr domain; + int result; + + domain = get_nonnull_domain(conn, args->domain); + if (domain == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + result = virDomainHasCurrentSnapshot(domain, args->flags); + if (result < 0) { + virDomainFree(domain); + remoteDispatchConnError(rerr, conn); + return -1; + } + + ret->result = result; + + virDomainFree(domain); + + return 0; +} + +static int +remoteDispatchDomainSnapshotCurrent(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_snapshot_current_args *args, + remote_domain_snapshot_current_ret *ret) +{ + virDomainSnapshotPtr snapshot; + virDomainPtr domain; + + domain = get_nonnull_domain(conn, args->domain); + if (domain == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + snapshot = virDomainSnapshotCurrent(domain, args->flags); + if (snapshot == NULL) { + virDomainFree(domain); + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_domain_snapshot(&ret->snap, snapshot); + + virDomainSnapshotFree(snapshot); + virDomainFree(domain); + + return 0; +} + +static int +remoteDispatchDomainCreateFromSnapshot (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_create_from_snapshot_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virDomainSnapshotPtr snapshot; + + snapshot = get_nonnull_domain_snapshot(conn, args->snap); + if (snapshot == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virDomainCreateFromSnapshot(snapshot, args->flags) == -1) { + virDomainSnapshotFree(snapshot); + remoteDispatchConnError(rerr, conn); + return -1; + } + + virDomainSnapshotFree(snapshot); + + return 0; +} + +static int +remoteDispatchDomainSnapshotDelete (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_snapshot_delete_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virDomainSnapshotPtr snapshot; + + snapshot = get_nonnull_domain_snapshot(conn, args->snap); + if (snapshot == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virDomainSnapshotDelete(snapshot, args->flags) == -1) { + virDomainSnapshotFree(snapshot); + remoteDispatchConnError(rerr, conn); + return -1; + } + + virDomainSnapshotFree(snapshot); + + return 0; +} + static int remoteDispatchDomainEventsRegisterAny (struct qemud_server *server ATTRIBUTE_UNUSED, @@ -6076,6 +6370,16 @@ get_nonnull_nwfilter (virConnectPtr conn, remote_nonnull_nwfilter nwfilter) return virGetNWFilter (conn, nwfilter.name, BAD_CAST nwfilter.uuid); } +static virDomainSnapshotPtr +get_nonnull_domain_snapshot (virConnectPtr conn, remote_nonnull_domain_snapshot snapshot) +{ + virDomainPtr domain; + domain = get_nonnull_domain(conn, snapshot.domain); + if (domain == NULL) + return NULL; + return virGetDomainSnapshot(domain, snapshot.name); +} + /* Make remote_nonnull_domain and remote_nonnull_network. */ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src) @@ -6135,3 +6439,10 @@ make_nonnull_nwfilter (remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwf nwfilter_dst->name = strdup (nwfilter_src->name); memcpy (nwfilter_dst->uuid, nwfilter_src->uuid, VIR_UUID_BUFLEN); } + +static void +make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src) +{ + snapshot_dst->name = strdup(snapshot_src->name); + make_nonnull_domain(&snapshot_dst->domain, snapshot_src->domain); +} diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h index b188e6e..ae78e2e 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -151,3 +151,12 @@ remote_list_nwfilters_args val_remote_list_nwfilters_args; remote_nwfilter_define_xml_args val_remote_nwfilter_define_xml_args; remote_nwfilter_undefine_args val_remote_nwfilter_undefine_args; + remote_domain_snapshot_create_xml_args val_remote_domain_snapshot_create_xml_args; + remote_domain_snapshot_dump_xml_args val_remote_domain_snapshot_dump_xml_args; + remote_domain_snapshot_num_args val_remote_domain_snapshot_num_args; + remote_domain_snapshot_list_names_args val_remote_domain_snapshot_list_names_args; + remote_domain_snapshot_lookup_by_name_args val_remote_domain_snapshot_lookup_by_name_args; + remote_domain_has_current_snapshot_args val_remote_domain_has_current_snapshot_args; + remote_domain_snapshot_current_args val_remote_domain_snapshot_current_args; + remote_domain_create_from_snapshot_args val_remote_domain_create_from_snapshot_args; + remote_domain_snapshot_delete_args val_remote_domain_snapshot_delete_args; diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index e155c69..c91e8a3 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -122,6 +122,14 @@ static int remoteDispatchDomainCreate( remote_error *err, remote_domain_create_args *args, void *ret); +static int remoteDispatchDomainCreateFromSnapshot( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_create_from_snapshot_args *args, + void *ret); static int remoteDispatchDomainCreateXml( struct qemud_server *server, struct qemud_client *client, @@ -282,6 +290,14 @@ static int remoteDispatchDomainGetVcpus( remote_error *err, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret); +static int remoteDispatchDomainHasCurrentSnapshot( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_has_current_snapshot_args *args, + remote_domain_has_current_snapshot_ret *ret); static int remoteDispatchDomainInterfaceStats( struct qemud_server *server, struct qemud_client *client, @@ -490,6 +506,62 @@ static int remoteDispatchDomainShutdown( remote_error *err, remote_domain_shutdown_args *args, void *ret); +static int remoteDispatchDomainSnapshotCreateXml( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_snapshot_create_xml_args *args, + remote_domain_snapshot_create_xml_ret *ret); +static int remoteDispatchDomainSnapshotCurrent( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_snapshot_current_args *args, + remote_domain_snapshot_current_ret *ret); +static int remoteDispatchDomainSnapshotDelete( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_snapshot_delete_args *args, + void *ret); +static int remoteDispatchDomainSnapshotDumpXml( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_snapshot_dump_xml_args *args, + remote_domain_snapshot_dump_xml_ret *ret); +static int remoteDispatchDomainSnapshotListNames( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_snapshot_list_names_args *args, + remote_domain_snapshot_list_names_ret *ret); +static int remoteDispatchDomainSnapshotLookupByName( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_snapshot_lookup_by_name_args *args, + remote_domain_snapshot_lookup_by_name_ret *ret); +static int remoteDispatchDomainSnapshotNum( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_snapshot_num_args *args, + remote_domain_snapshot_num_ret *ret); static int remoteDispatchDomainSuspend( struct qemud_server *server, struct qemud_client *client, diff --git a/daemon/remote_dispatch_ret.h b/daemon/remote_dispatch_ret.h index b134f25..358bc58 100644 --- a/daemon/remote_dispatch_ret.h +++ b/daemon/remote_dispatch_ret.h @@ -125,3 +125,10 @@ remote_num_of_nwfilters_ret val_remote_num_of_nwfilters_ret; remote_list_nwfilters_ret val_remote_list_nwfilters_ret; remote_nwfilter_define_xml_ret val_remote_nwfilter_define_xml_ret; + remote_domain_snapshot_create_xml_ret val_remote_domain_snapshot_create_xml_ret; + remote_domain_snapshot_dump_xml_ret val_remote_domain_snapshot_dump_xml_ret; + remote_domain_snapshot_num_ret val_remote_domain_snapshot_num_ret; + remote_domain_snapshot_list_names_ret val_remote_domain_snapshot_list_names_ret; + remote_domain_snapshot_lookup_by_name_ret val_remote_domain_snapshot_lookup_by_name_ret; + remote_domain_has_current_snapshot_ret val_remote_domain_has_current_snapshot_ret; + remote_domain_snapshot_current_ret val_remote_domain_snapshot_current_ret; diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 24756d7..ab317b5 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -912,3 +912,48 @@ .args_filter = (xdrproc_t) xdr_remote_nwfilter_undefine_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* DomainSnapshotCreateXml => 182 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotCreateXml, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_create_xml_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_snapshot_create_xml_ret, +}, +{ /* DomainSnapshotDumpXml => 183 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotDumpXml, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_dump_xml_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_snapshot_dump_xml_ret, +}, +{ /* DomainSnapshotNum => 184 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotNum, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_num_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_snapshot_num_ret, +}, +{ /* DomainSnapshotListNames => 185 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotListNames, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_list_names_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_snapshot_list_names_ret, +}, +{ /* DomainSnapshotLookupByName => 186 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotLookupByName, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_lookup_by_name_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_snapshot_lookup_by_name_ret, +}, +{ /* DomainHasCurrentSnapshot => 187 */ + .fn = (dispatch_fn) remoteDispatchDomainHasCurrentSnapshot, + .args_filter = (xdrproc_t) xdr_remote_domain_has_current_snapshot_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_has_current_snapshot_ret, +}, +{ /* DomainSnapshotCurrent => 188 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotCurrent, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_current_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_snapshot_current_ret, +}, +{ /* DomainCreateFromSnapshot => 189 */ + .fn = (dispatch_fn) remoteDispatchDomainCreateFromSnapshot, + .args_filter = (xdrproc_t) xdr_remote_domain_create_from_snapshot_args, + .ret_filter = (xdrproc_t) xdr_void, +}, +{ /* DomainSnapshotDelete => 190 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotDelete, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_delete_args, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cb483e..a8a97e3 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1861,6 +1861,67 @@ int virDomainGetJobInfo(virDomainPtr dom, virDomainJobInfoPtr info); int virDomainAbortJob(virDomainPtr dom); +/** + * virDomainSnapshot: + * + * a virDomainSnapshot is a private structure representing a snapshot of + * a domain. + */ +typedef struct _virDomainSnapshot virDomainSnapshot; + +/** + * virDomainSnapshotPtr: + * + * a virDomainSnapshotPtr is pointer to a virDomainSnapshot private structure, + * and is the type used to reference a domain snapshot in the API. + */ +typedef virDomainSnapshot *virDomainSnapshotPtr; + +/* Take a snapshot of the current VM state */ +virDomainSnapshotPtr virDomainSnapshotCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags); + +/* Dump the XML of a snapshot */ +char *virDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, + unsigned int flags); + +/* Return the number of snapshots for this domain */ +int virDomainSnapshotNum(virDomainPtr domain, unsigned int flags); + +/* Get the names of all snapshots for this domain */ +int virDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen, + unsigned int flags); + +/* Get a handle to a named snapshot */ +virDomainSnapshotPtr virDomainSnapshotLookupByName(virDomainPtr domain, + const char *name, + unsigned int flags); + +/* Check whether a domain has a snapshot which is currently used */ +int virDomainHasCurrentSnapshot(virDomainPtr domain, unsigned flags); + +/* Get a handle to the current snapshot */ +virDomainSnapshotPtr virDomainSnapshotCurrent(virDomainPtr domain, + unsigned int flags); + +/* Set this snapshot as the current one for a domain, to be + * used next time domain is started */ +int virDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags); + +/* Deactivate a snapshot */ +typedef enum { + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE = (1 << 0), + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE = (1 << 1), + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD = (1 << 2), + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE = (1 << 3), +} virDomainSnapshotDeleteFlags; + +int virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags); + +int virDomainSnapshotFree(virDomainSnapshotPtr snapshot); /* A generic callback definition. Specific events usually have a customization * with extra parameters */ diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index f69d07e..3bbb293 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -71,7 +71,8 @@ typedef enum { VIR_FROM_CPU, /* Error from CPU driver */ VIR_FROM_XENAPI, /* Error from XenAPI */ VIR_FROM_NWFILTER, /* Error from network filter driver */ - VIR_FROM_HOOK /* Error from Synchronous hooks */ + VIR_FROM_HOOK, /* Error from Synchronous hooks */ + VIR_FROM_DOMAIN_SNAPSHOT, /* Error from domain snapshot */ } virErrorDomain; @@ -183,6 +184,8 @@ typedef enum { VIR_ERR_MIGRATE_PERSIST_FAILED, /* a migration worked, but making the VM persist on the dest host failed */ VIR_ERR_HOOK_SCRIPT_FAILED, /* a synchronous hook script failed */ + VIR_ERR_INVALID_DOMAIN_SNAPSHOT, /* invalid domain snapshot */ + VIR_ERR_NO_DOMAIN_SNAPSHOT, /* domain snapshot not found */ } virErrorNumber; /** diff --git a/src/datatypes.c b/src/datatypes.c index a361da6..c514f41 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -129,6 +129,20 @@ virSecretFreeName(void *secret_, const char *name ATTRIBUTE_UNUSED) } /** + * virDomainSnapshotFreeName: + * @snapshot: a domain snapshotobject + * + * Destroy the domain snapshot object, this is just used by the domain hash callback. + * + * Returns 0 in case of success and -1 in case of failure. + */ +static int +virDomainSnapshotFreeName(virDomainSnapshotPtr snapshot, const char *name ATTRIBUTE_UNUSED) +{ + return (virUnrefDomainSnapshot(snapshot)); +} + +/** * virGetConnect: * * Allocates a new hypervisor connection structure @@ -337,6 +351,7 @@ virGetDomain(virConnectPtr conn, const char *name, const unsigned char *uuid) { ret->id = -1; if (uuid != NULL) memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); + ret->snapshots = virHashCreate(20); if (virHashAddEntry(conn->domains, name, ret) < 0) { virMutexUnlock(&conn->lock); @@ -389,6 +404,8 @@ virReleaseDomain(virDomainPtr domain) { domain->magic = -1; domain->id = -1; VIR_FREE(domain->name); + if (domain->snapshots != NULL) + virHashFree(domain->snapshots, (virHashDeallocator) virDomainSnapshotFreeName); VIR_FREE(domain); if (conn) { @@ -1504,3 +1521,108 @@ virUnrefNWFilter(virNWFilterPtr pool) { virMutexUnlock(&pool->conn->lock); return (refs); } + + +virDomainSnapshotPtr +virGetDomainSnapshot(virDomainPtr domain, const char *name) +{ + virDomainSnapshotPtr ret = NULL; + + if ((!VIR_IS_DOMAIN(domain)) || (name == NULL)) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + virMutexLock(&domain->conn->lock); + + ret = (virDomainSnapshotPtr) virHashLookup(domain->snapshots, name); + if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { + virMutexUnlock(&domain->conn->lock); + virReportOOMError(); + goto error; + } + ret->name = strdup(name); + if (ret->name == NULL) { + virMutexUnlock(&domain->conn->lock); + virReportOOMError(); + goto error; + } + ret->magic = VIR_SNAPSHOT_MAGIC; + ret->domain = domain; + + if (virHashAddEntry(domain->snapshots, name, ret) < 0) { + virMutexUnlock(&domain->conn->lock); + virLibConnError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to add snapshot to domain hash table")); + goto error; + } + domain->refs++; + DEBUG("New hash entry %p", ret); + } else { + DEBUG("Existing hash entry %p: refs now %d", ret, ret->refs+1); + } + ret->refs++; + virMutexUnlock(&domain->conn->lock); + return(ret); + + error: + if (ret != NULL) { + VIR_FREE(ret->name); + VIR_FREE(ret); + } + return(NULL); +} + + +static void +virReleaseDomainSnapshot(virDomainSnapshotPtr snapshot) +{ + virDomainPtr domain = snapshot->domain; + DEBUG("release snapshot %p %s", snapshot, snapshot->name); + + if (virHashRemoveEntry(domain->snapshots, snapshot->name, NULL) < 0) { + virMutexUnlock(&domain->conn->lock); + virLibConnError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("snapshot missing from domain hash table")); + domain = NULL; + } + + snapshot->magic = -1; + VIR_FREE(snapshot->name); + VIR_FREE(snapshot); + + if (domain) { + DEBUG("unref domain %p %d", domain, domain->refs); + domain->refs--; + if (domain->refs == 0) { + virReleaseDomain(domain); + /* Already unlocked mutex */ + return; + } + virMutexUnlock(&domain->conn->lock); + } +} + +int +virUnrefDomainSnapshot(virDomainSnapshotPtr snapshot) +{ + int refs; + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + virMutexLock(&snapshot->domain->conn->lock); + DEBUG("unref snapshot %p %s %d", snapshot, snapshot->name, snapshot->refs); + snapshot->refs--; + refs = snapshot->refs; + if (refs == 0) { + virReleaseDomainSnapshot(snapshot); + /* Already unlocked mutex */ + return (0); + } + + virMutexUnlock(&snapshot->domain->conn->lock); + return (refs); +} diff --git a/src/datatypes.h b/src/datatypes.h index 4663c9c..bbeb7cf 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -130,6 +130,15 @@ # define VIR_IS_NWFILTER(obj) ((obj) && (obj)->magic==VIR_NWFILTER_MAGIC) # define VIR_IS_CONNECTED_NWFILTER(obj) (VIR_IS_NWFILTER(obj) && VIR_IS_CONNECT((obj)->conn)) +/** + * VIR_SNAPSHOT_MAGIC: + * + * magic value used to protect the API when pointers to snapshot structures + * are passed down by the users. + */ +# define VIR_SNAPSHOT_MAGIC 0x6666DEAD +# define VIR_IS_SNAPSHOT(obj) ((obj) && (obj)->magic==VIR_SNAPSHOT_MAGIC) +# define VIR_IS_DOMAIN_SNAPSHOT(obj) (VIR_IS_SNAPSHOT(obj) && VIR_IS_DOMAIN((obj)->domain)) /** * _virConnect: @@ -202,6 +211,7 @@ struct _virDomain { char *name; /* the domain external name */ int id; /* the domain ID */ unsigned char uuid[VIR_UUID_BUFLEN]; /* the domain unique identifier */ + virHashTablePtr snapshots; /* hash table for known snapshots */ }; /** @@ -304,6 +314,17 @@ struct _virStream { void *privateData; }; +/** + * _virDomainSnapshot + * + * Internal structure associated with a domain snapshot + */ +struct _virDomainSnapshot { + unsigned int magic; + int refs; + char *name; + virDomainPtr domain; +}; /************************************************************************ * * @@ -368,4 +389,8 @@ virNWFilterPtr virGetNWFilter(virConnectPtr conn, const unsigned char *uuid); int virUnrefNWFilter(virNWFilterPtr pool); +virDomainSnapshotPtr virGetDomainSnapshot(virDomainPtr domain, + const char *name); +int virUnrefDomainSnapshot(virDomainSnapshotPtr pool); + #endif diff --git a/src/driver.h b/src/driver.h index 8f86463..1f257c0 100644 --- a/src/driver.h +++ b/src/driver.h @@ -402,6 +402,44 @@ typedef int (*virDrvDomainEventDeregisterAny)(virConnectPtr conn, int callbackID); +typedef virDomainSnapshotPtr + (*virDrvDomainSnapshotCreateXML)(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags); + +typedef char * + (*virDrvDomainSnapshotDumpXML)(virDomainSnapshotPtr snapshot, + unsigned int flags); + +typedef int + (*virDrvDomainSnapshotNum)(virDomainPtr domain, unsigned int flags); + +typedef int + (*virDrvDomainSnapshotListNames)(virDomainPtr domain, char **names, + int nameslen, + unsigned int flags); + +typedef virDomainSnapshotPtr + (*virDrvDomainSnapshotLookupByName)(virDomainPtr domain, + const char *name, + unsigned int flags); + +typedef int + (*virDrvDomainHasCurrentSnapshot)(virDomainPtr domain, unsigned int flags); + +typedef virDomainSnapshotPtr + (*virDrvDomainSnapshotCurrent)(virDomainPtr domain, + unsigned int flags); + +typedef int + (*virDrvDomainCreateFromSnapshot)(virDomainSnapshotPtr snapshot, + unsigned int flags); + +typedef int + (*virDrvDomainSnapshotDelete)(virDomainSnapshotPtr snapshot, + unsigned int flags); + + /** * _virDriver: * @@ -499,6 +537,15 @@ struct _virDriver { virDrvDomainMigrateSetMaxDowntime domainMigrateSetMaxDowntime; virDrvDomainEventRegisterAny domainEventRegisterAny; virDrvDomainEventDeregisterAny domainEventDeregisterAny; + virDrvDomainSnapshotCreateXML domainSnapshotCreateXML; + virDrvDomainSnapshotDumpXML domainSnapshotDumpXML; + virDrvDomainSnapshotNum domainSnapshotNum; + virDrvDomainSnapshotListNames domainSnapshotListNames; + virDrvDomainSnapshotLookupByName domainSnapshotLookupByName; + virDrvDomainHasCurrentSnapshot domainHasCurrentSnapshot; + virDrvDomainSnapshotCurrent domainSnapshotCurrent; + virDrvDomainCreateFromSnapshot domainCreateFromSnapshot; + virDrvDomainSnapshotDelete domainSnapshotDelete; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 7a92c4d..38846c3 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3389,6 +3389,15 @@ static virDriver esxDriver = { NULL, /* domainMigrateSetMaxDowntime */ NULL, /* domainEventRegisterAny */ NULL, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; diff --git a/src/libvirt.c b/src/libvirt.c index 5247fe7..edb3084 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -683,6 +683,31 @@ virLibNWFilterError(virNWFilterPtr pool, virErrorNumber error, } /** + * virLibDomainSnapshotError: + * @snapshot: the snapshot if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the secret level + */ +static void +virLibDomainSnapshotError(virDomainSnapshotPtr snapshot, virErrorNumber error, const char *info) +{ + virConnectPtr conn = NULL; + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = virErrorMsg(error, info); + if (error != VIR_ERR_INVALID_DOMAIN_SNAPSHOT) + conn = snapshot->domain->conn; + + virRaiseError(conn, NULL, NULL, VIR_FROM_DOMAIN_SNAPSHOT, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** * virRegisterNetworkDriver: * @driver: pointer to a network driver block * @@ -12136,3 +12161,426 @@ error: virDispatchError(conn); return -1; } + +/** + * virDomainSnapshotCreateXML: + * @domain: a domain object + * @xmlDesc: string containing an XML description of the domain + * @flags: unused flag parameters; callers should pass 0 + * + * Creates a new snapshot of a domain based on the snapshot xml + * contained in xmlDesc. + * + * Returns an (opaque) virDomainSnapshotPtr on success, NULL on failure. + */ +virDomainSnapshotPtr +virDomainSnapshotCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("domain=%p, xmlDesc=%s, flags=%u", domain, xmlDesc, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return NULL; + } + + conn = domain->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainSnapshotCreateXML) { + virDomainSnapshotPtr ret; + ret = conn->driver->domainSnapshotCreateXML(domain, xmlDesc, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return NULL; +} + +/** + * virDomainSnapshotGetXMLDesc: + * @snapshot: a domain snapshot object + * @flags: unused flag parameters; callers should pass 0 + * + * Provide an XML description of the domain snapshot. + * + * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error. + * the caller must free() the returned value. + */ +char * +virDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("snapshot=%p, flags=%d", snapshot, flags); + + virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + + conn = snapshot->domain->conn; + + if ((conn->flags & VIR_CONNECT_RO) && (flags & VIR_DOMAIN_XML_SECURE)) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, + _("virDomainSnapshotGetXMLDesc with secure flag")); + goto error; + } + + flags &= VIR_DOMAIN_XML_FLAGS_MASK; + + if (conn->driver->domainSnapshotDumpXML) { + char *ret; + ret = conn->driver->domainSnapshotDumpXML(snapshot, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return NULL; +} + +/** + * virDomainSnapshotNum: + * @domain: a domain object + * @flags: unused flag parameters; callers should pass 0 + * + * Provides the number of domain snapshots for this domain.. + * + * Returns the number of domain snapshost found or -1 in case of error. + */ +int +virDomainSnapshotNum(virDomainPtr domain, unsigned int flags) +{ + virConnectPtr conn; + DEBUG("domain=%p", domain); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + if (conn->driver->domainSnapshotNum) { + int ret = conn->driver->domainSnapshotNum(domain, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainSnapshotListNames: + * @domain: a domain object + * @names: array to collect the list of names of snapshots + * @nameslen: size of @names + * @flags: unused flag parameters; callers should pass 0 + * + * Collect the list of domain snapshots for the given domain, and store + * their names in @names. + * + * Returns the number of domain snapshots found or -1 in case of error. + */ +int +virDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen, + unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("domain=%p, names=%p, nameslen=%d, flags=%u", + domain, names, nameslen, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + if ((names == NULL) || (nameslen < 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->driver->domainSnapshotListNames) { + int ret = conn->driver->domainSnapshotListNames(domain, names, nameslen, + flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainSnapshotLookupByName: + * @domain: a domain object + * @name: name for the domain snapshot + * @flags: unused flag parameters; callers should pass 0 + * + * Try to lookup a domain snapshot based on its name. + * + * Returns a domain snapshot object or NULL in case of failure. If the + * domain snapshot cannot be found, then the VIR_ERR_NO_DOMAIN_SNAPSHOT + * error is raised. + */ +virDomainSnapshotPtr +virDomainSnapshotLookupByName(virDomainPtr domain, + const char *name, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("domain=%p, name=%s, flags=%u", domain, name, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + + conn = domain->conn; + + if (name == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->driver->domainSnapshotLookupByName) { + virDomainSnapshotPtr dom; + dom = conn->driver->domainSnapshotLookupByName(domain, name, flags); + if (!dom) + goto error; + return dom; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return NULL; +} + +/** + * virDomainHasCurrentSnapshot: + * @domain: pointer to the domain object + * @flags: unused flag parameters; callers should pass 0 + * + * Determine if the domain has a current snapshot. + * + * Returns 1 if such snapshot exists, 0 if it doesn't, -1 on error. + */ +int +virDomainHasCurrentSnapshot(virDomainPtr domain, unsigned int flags) +{ + virConnectPtr conn; + DEBUG("domain=%p, flags=%u", domain, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + if (conn->driver->domainHasCurrentSnapshot) { + int ret = conn->driver->domainHasCurrentSnapshot(domain, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainSnapshotCurrent: + * @domain: a domain object + * @flags: unused flag parameters; callers should pass 0 + * + * Get the current snapshot for a domain, if any. + * + * Returns a domain snapshot object or NULL in case of failure. If the + * current domain snapshot cannot be found, then the VIR_ERR_NO_DOMAIN_SNAPSHOT + * error is raised. + */ +virDomainSnapshotPtr +virDomainSnapshotCurrent(virDomainPtr domain, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG("domain=%p, flags=%u", domain, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + + conn = domain->conn; + + if (conn->driver->domainSnapshotCurrent) { + virDomainSnapshotPtr snap; + snap = conn->driver->domainSnapshotCurrent(domain, flags); + if (!snap) + goto error; + return snap; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return NULL; +} + +/** + * virDomainCreateFromSnapshot + * @snapshot: a domain snapshot object + * @flags: unused flag parameters; callers should pass 0 + * + * Start the domain from the given snapshot. + * + * Returns 0 if the creation is successful, -1 on error. + */ +int +virDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("snapshot=%p, flags=%u", snapshot, flags); + + virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = snapshot->domain->conn; + + if (conn->driver->domainCreateFromSnapshot) { + int ret = conn->driver->domainCreateFromSnapshot(snapshot, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainCreateFromSnapshot + * @snapshot: a domain snapshot object + * @flags: flag parameters + * + * Delete the snapshot. + * + * Returns 0 if the snapshot was successfully deleted, -1 on error. + */ +int +virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("snapshot=%p, flags=%u", snapshot, flags); + + /* FIXME: make sure only one of the flags is set */ + + virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = snapshot->domain->conn; + + if (conn->driver->domainSnapshotDelete) { + int ret = conn->driver->domainSnapshotDelete(snapshot, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainFree: + * @snapshot: a domain snapshot object + * + * Free the domain snapshot object. The snapshot itself is not modified. + * The data structure is freed and should not be used thereafter. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainSnapshotFree(virDomainSnapshotPtr snapshot) +{ + DEBUG("snapshot=%p", snapshot); + + virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (virUnrefDomainSnapshot(snapshot) < 0) { + virDispatchError(NULL); + return -1; + } + return 0; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index edb23c2..c402720 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -109,6 +109,7 @@ virGetStream; virUnrefStream; virGetNWFilter; virUnrefNWFilter; +virGetDomainSnapshot; # domain_conf.h diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 2f812a1..5d33c31 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -378,6 +378,16 @@ LIBVIRT_0.7.8 { virNWFilterRef; virNWFilterDefineXML; virNWFilterUndefine; + virDomainSnapshotCreateXML; + virDomainSnapshotGetXMLDesc; + virDomainSnapshotNum; + virDomainSnapshotListNames; + virDomainSnapshotLookupByName; + virDomainHasCurrentSnapshot; + virDomainSnapshotCurrent; + virDomainCreateFromSnapshot; + virDomainSnapshotDelete; + virDomainSnapshotFree; } LIBVIRT_0.7.7; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 9caefa1..ba373f8 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2538,6 +2538,15 @@ static virDriver lxcDriver = { NULL, /* domainMigrateSetMaxDowntime */ lxcDomainEventRegisterAny, /* domainEventRegisterAny */ lxcDomainEventDeregisterAny, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; static virStateDriver lxcStateDriver = { diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index e901a03..85245d8 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -792,6 +792,15 @@ static virDriver oneDriver = { NULL, /* domainMigrateSetMaxDowntime */ NULL, /* domainEventRegisterAny */ NULL, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; static virStateDriver oneStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 6176577..7458521 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1544,6 +1544,15 @@ static virDriver openvzDriver = { NULL, /* domainMigrateSetMaxDowntime */ NULL, /* domainEventRegisterAny */ NULL, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 4f7efdb..272a746 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -1651,6 +1651,15 @@ virDriver phypDriver = { NULL, /* domainMigrateSetMaxDowntime */ NULL, /* domainEventRegisterAny */ NULL, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; int diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4291bc7..695e5ba 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10307,6 +10307,15 @@ static virDriver qemuDriver = { qemuDomainMigrateSetMaxDowntime, /* domainMigrateSetMaxDowntime */ qemuDomainEventRegisterAny, /* domainEventRegisterAny */ qemuDomainEventDeregisterAny, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 3b8be21..a8f48cf 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -254,6 +254,7 @@ static virStoragePoolPtr get_nonnull_storage_pool (virConnectPtr conn, remote_no static virStorageVolPtr get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol); static virNodeDevicePtr get_nonnull_node_device (virConnectPtr conn, remote_nonnull_node_device dev); static virSecretPtr get_nonnull_secret (virConnectPtr conn, remote_nonnull_secret secret); +static virDomainSnapshotPtr get_nonnull_domain_snapshot (virDomainPtr domain, remote_nonnull_domain_snapshot snapshot); static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src); static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src); static void make_nonnull_interface (remote_nonnull_interface *interface_dst, virInterfacePtr interface_src); @@ -261,6 +262,7 @@ static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, vi static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); static void make_nonnull_secret (remote_nonnull_secret *secret_dst, virSecretPtr secret_src); static void make_nonnull_nwfilter (remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src); +static void make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src); void remoteDomainEventFired(int watch, int fd, int event, void *data); void remoteDomainEventQueueFlush(int timer, void *opaque); /*----------------------------------------------------------------------*/ @@ -8301,6 +8303,291 @@ done: return rv; } +static virDomainSnapshotPtr +remoteDomainSnapshotCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags) +{ + virDomainSnapshotPtr snapshot = NULL; + remote_domain_snapshot_create_xml_args args; + remote_domain_snapshot_create_xml_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.domain, domain); + args.xml_desc = (char *) xmlDesc; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML, + (xdrproc_t) xdr_remote_domain_snapshot_create_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_snapshot_create_xml_ret, (char *) &ret) == -1) + goto done; + + snapshot = get_nonnull_domain_snapshot(domain, ret.snap); + xdr_free ((xdrproc_t) &xdr_remote_domain_snapshot_create_xml_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return snapshot; +} + + +static char * +remoteDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot, unsigned int flags) +{ + char *rv = NULL; + remote_domain_snapshot_dump_xml_args args; + remote_domain_snapshot_dump_xml_ret ret; + struct private_data *priv = snapshot->domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain_snapshot(&args.snap, snapshot); + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (snapshot->domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SNAPSHOT_DUMP_XML, + (xdrproc_t) xdr_remote_domain_snapshot_dump_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_snapshot_dump_xml_ret, (char *) &ret) == -1) + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static int +remoteDomainSnapshotNum (virDomainPtr domain, unsigned int flags) +{ + int rv = -1; + remote_domain_snapshot_num_args args; + remote_domain_snapshot_num_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.domain, domain); + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SNAPSHOT_NUM, + (xdrproc_t) xdr_remote_domain_snapshot_num_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_snapshot_num_ret, (char *) &ret) == -1) + goto done; + + rv = ret.num; + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static int +remoteDomainSnapshotListNames (virDomainPtr domain, char **const names, + int nameslen, unsigned int flags) +{ + int rv = -1; + int i; + remote_domain_snapshot_list_names_args args; + remote_domain_snapshot_list_names_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + if (nameslen > REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX) { + errorf (domain->conn, VIR_ERR_RPC, + _("too many remote domain snapshot names: %d > %d"), + nameslen, REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX); + goto done; + } + + make_nonnull_domain(&args.domain, domain); + args.nameslen = nameslen; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_NAMES, + (xdrproc_t) xdr_remote_domain_snapshot_list_names_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_snapshot_list_names_ret, (char *) &ret) == -1) + goto done; + + if (ret.names.names_len > nameslen) { + errorf (domain->conn, VIR_ERR_RPC, + _("too many remote domain snapshots: %d > %d"), + ret.names.names_len, nameslen); + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) { + names[i] = strdup (ret.names.names_val[i]); + + if (names[i] == NULL) { + for (--i; i >= 0; --i) + VIR_FREE(names[i]); + + virReportOOMError(); + goto cleanup; + } + } + + rv = ret.names.names_len; + +cleanup: + xdr_free ((xdrproc_t) xdr_remote_domain_snapshot_list_names_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static virDomainSnapshotPtr +remoteDomainSnapshotLookupByName (virDomainPtr domain, const char *name, + unsigned int flags) +{ + virDomainSnapshotPtr snapshot = NULL; + remote_domain_snapshot_lookup_by_name_args args; + remote_domain_snapshot_lookup_by_name_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.domain, domain); + args.name = (char *) name; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SNAPSHOT_LOOKUP_BY_NAME, + (xdrproc_t) xdr_remote_domain_snapshot_lookup_by_name_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_snapshot_lookup_by_name_ret, (char *) &ret) == -1) + goto done; + + snapshot = get_nonnull_domain_snapshot (domain, ret.snap); + xdr_free ((xdrproc_t) &xdr_remote_domain_snapshot_lookup_by_name_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return snapshot; +} + + +static int +remoteDomainHasCurrentSnapshot(virDomainPtr domain, unsigned int flags) +{ + int rv = -1; + remote_domain_has_current_snapshot_args args; + remote_domain_has_current_snapshot_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.domain, domain); + args.flags = flags; + + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_HAS_CURRENT_SNAPSHOT, + (xdrproc_t) xdr_remote_domain_has_current_snapshot_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_has_current_snapshot_ret, (char *) &ret) == -1) + goto done; + + rv = ret.result; + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static virDomainSnapshotPtr +remoteDomainSnapshotCurrent(virDomainPtr domain, + unsigned int flags) +{ + virDomainSnapshotPtr snapshot = NULL; + remote_domain_snapshot_current_args args; + remote_domain_snapshot_current_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.domain, domain); + args.flags = flags; + + memset(&ret, 0, sizeof ret); + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SNAPSHOT_CURRENT, + (xdrproc_t) xdr_remote_domain_snapshot_current_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_snapshot_current_ret, (char *) &ret) == -1) + goto done; + + snapshot = get_nonnull_domain_snapshot(domain, ret.snap); + xdr_free((xdrproc_t) &xdr_remote_domain_snapshot_current_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return snapshot; +} + + +static int +remoteDomainCreateFromSnapshot (virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + int rv = -1; + remote_domain_create_from_snapshot_args args; + struct private_data *priv = snapshot->domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain_snapshot(&args.snap, snapshot); + args.flags = flags; + + if (call (snapshot->domain->conn, priv, 0, REMOTE_PROC_DOMAIN_CREATE_FROM_SNAPSHOT, + (xdrproc_t) xdr_remote_domain_create_from_snapshot_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static int +remoteDomainSnapshotDelete (virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + int rv = -1; + remote_domain_snapshot_delete_args args; + struct private_data *priv = snapshot->domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain_snapshot(&args.snap, snapshot); + args.flags = flags; + + if (call (snapshot->domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SNAPSHOT_DELETE, + (xdrproc_t) xdr_remote_domain_snapshot_delete_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} static int remoteDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, @@ -9671,6 +9958,12 @@ get_nonnull_nwfilter (virConnectPtr conn, remote_nonnull_nwfilter nwfilter) return virGetNWFilter (conn, nwfilter.name, BAD_CAST nwfilter.uuid); } +static virDomainSnapshotPtr +get_nonnull_domain_snapshot (virDomainPtr domain, remote_nonnull_domain_snapshot snapshot) +{ + return virGetDomainSnapshot(domain, snapshot.name); +} + /* Make remote_nonnull_domain and remote_nonnull_network. */ static void @@ -9726,6 +10019,13 @@ make_nonnull_nwfilter (remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwf memcpy (nwfilter_dst->uuid, nwfilter_src->uuid, VIR_UUID_BUFLEN); } +static void +make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src) +{ + snapshot_dst->name = snapshot_src->name; + make_nonnull_domain(&snapshot_dst->domain, snapshot_src->domain); +} + /*----------------------------------------------------------------------*/ unsigned long remoteVersion(void) @@ -9818,6 +10118,15 @@ static virDriver remote_driver = { remoteDomainMigrateSetMaxDowntime, /* domainMigrateSetMaxDowntime */ remoteDomainEventRegisterAny, /* domainEventRegisterAny */ remoteDomainEventDeregisterAny, /* domainEventDeregisterAny */ + remoteDomainSnapshotCreateXML, /* domainSnapshotCreateXML */ + remoteDomainSnapshotDumpXML, /* domainSnapshotDumpXML */ + remoteDomainSnapshotNum, /* domainSnapshotNum */ + remoteDomainSnapshotListNames, /* domainSnapshotListNames */ + remoteDomainSnapshotLookupByName, /* domainSnapshotLookupByName */ + remoteDomainHasCurrentSnapshot, /* domainHasCurrentSnapshot */ + remoteDomainSnapshotCurrent, /* domainSnapshotCurrent */ + remoteDomainCreateFromSnapshot, /* domainCreateFromSnapshot */ + remoteDomainSnapshotDelete, /* domainSnapshotDelete */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index f252d85..f4c3eb5 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -128,6 +128,17 @@ xdr_remote_nonnull_secret (XDR *xdrs, remote_nonnull_secret *objp) } bool_t +xdr_remote_nonnull_domain_snapshot (XDR *xdrs, remote_nonnull_domain_snapshot *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain (XDR *xdrs, remote_domain *objp) { @@ -3289,6 +3300,176 @@ xdr_remote_domain_event_graphics_msg (XDR *xdrs, remote_domain_event_graphics_ms } bool_t +xdr_remote_domain_snapshot_create_xml_args (XDR *xdrs, remote_domain_snapshot_create_xml_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->xml_desc)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_create_xml_ret (XDR *xdrs, remote_domain_snapshot_create_xml_ret *objp) +{ + + if (!xdr_remote_nonnull_domain_snapshot (xdrs, &objp->snap)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_dump_xml_args (XDR *xdrs, remote_domain_snapshot_dump_xml_args *objp) +{ + + if (!xdr_remote_nonnull_domain_snapshot (xdrs, &objp->snap)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_dump_xml_ret (XDR *xdrs, remote_domain_snapshot_dump_xml_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_num_args (XDR *xdrs, remote_domain_snapshot_num_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_num_ret (XDR *xdrs, remote_domain_snapshot_num_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->num)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_list_names_args (XDR *xdrs, remote_domain_snapshot_list_names_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + if (!xdr_int (xdrs, &objp->nameslen)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_list_names_ret (XDR *xdrs, remote_domain_snapshot_list_names_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->names.names_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->names.names_len, REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX, + sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_lookup_by_name_args (XDR *xdrs, remote_domain_snapshot_lookup_by_name_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_lookup_by_name_ret (XDR *xdrs, remote_domain_snapshot_lookup_by_name_ret *objp) +{ + + if (!xdr_remote_nonnull_domain_snapshot (xdrs, &objp->snap)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_has_current_snapshot_args (XDR *xdrs, remote_domain_has_current_snapshot_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_has_current_snapshot_ret (XDR *xdrs, remote_domain_has_current_snapshot_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->result)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_current_args (XDR *xdrs, remote_domain_snapshot_current_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_current_ret (XDR *xdrs, remote_domain_snapshot_current_ret *objp) +{ + + if (!xdr_remote_nonnull_domain_snapshot (xdrs, &objp->snap)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_create_from_snapshot_args (XDR *xdrs, remote_domain_create_from_snapshot_args *objp) +{ + + if (!xdr_remote_nonnull_domain_snapshot (xdrs, &objp->snap)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_snapshot_delete_args (XDR *xdrs, remote_domain_snapshot_delete_args *objp) +{ + + if (!xdr_remote_nonnull_domain_snapshot (xdrs, &objp->snap)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index d898502..ae1c084 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -42,6 +42,7 @@ typedef remote_nonnull_string *remote_string; #define REMOTE_AUTH_SASL_DATA_MAX 65536 #define REMOTE_AUTH_TYPE_LIST_MAX 20 #define REMOTE_DOMAIN_MEMORY_STATS_MAX 1024 +#define REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX 1024 #define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536 #define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536 #define REMOTE_SECURITY_MODEL_MAX VIR_SECURITY_MODEL_BUFLEN @@ -103,6 +104,12 @@ struct remote_nonnull_secret { }; typedef struct remote_nonnull_secret remote_nonnull_secret; +struct remote_nonnull_domain_snapshot { + remote_nonnull_string name; + remote_nonnull_domain domain; +}; +typedef struct remote_nonnull_domain_snapshot remote_nonnull_domain_snapshot; + typedef remote_nonnull_domain *remote_domain; typedef remote_nonnull_network *remote_network; @@ -1860,6 +1867,101 @@ struct remote_domain_event_graphics_msg { } subject; }; typedef struct remote_domain_event_graphics_msg remote_domain_event_graphics_msg; + +struct remote_domain_snapshot_create_xml_args { + remote_nonnull_domain domain; + remote_nonnull_string xml_desc; + int flags; +}; +typedef struct remote_domain_snapshot_create_xml_args remote_domain_snapshot_create_xml_args; + +struct remote_domain_snapshot_create_xml_ret { + remote_nonnull_domain_snapshot snap; +}; +typedef struct remote_domain_snapshot_create_xml_ret remote_domain_snapshot_create_xml_ret; + +struct remote_domain_snapshot_dump_xml_args { + remote_nonnull_domain_snapshot snap; + int flags; +}; +typedef struct remote_domain_snapshot_dump_xml_args remote_domain_snapshot_dump_xml_args; + +struct remote_domain_snapshot_dump_xml_ret { + remote_nonnull_string xml; +}; +typedef struct remote_domain_snapshot_dump_xml_ret remote_domain_snapshot_dump_xml_ret; + +struct remote_domain_snapshot_num_args { + remote_nonnull_domain domain; + int flags; +}; +typedef struct remote_domain_snapshot_num_args remote_domain_snapshot_num_args; + +struct remote_domain_snapshot_num_ret { + int num; +}; +typedef struct remote_domain_snapshot_num_ret remote_domain_snapshot_num_ret; + +struct remote_domain_snapshot_list_names_args { + remote_nonnull_domain domain; + int nameslen; + int flags; +}; +typedef struct remote_domain_snapshot_list_names_args remote_domain_snapshot_list_names_args; + +struct remote_domain_snapshot_list_names_ret { + struct { + u_int names_len; + remote_nonnull_string *names_val; + } names; +}; +typedef struct remote_domain_snapshot_list_names_ret remote_domain_snapshot_list_names_ret; + +struct remote_domain_snapshot_lookup_by_name_args { + remote_nonnull_domain domain; + remote_nonnull_string name; + int flags; +}; +typedef struct remote_domain_snapshot_lookup_by_name_args remote_domain_snapshot_lookup_by_name_args; + +struct remote_domain_snapshot_lookup_by_name_ret { + remote_nonnull_domain_snapshot snap; +}; +typedef struct remote_domain_snapshot_lookup_by_name_ret remote_domain_snapshot_lookup_by_name_ret; + +struct remote_domain_has_current_snapshot_args { + remote_nonnull_domain domain; + int flags; +}; +typedef struct remote_domain_has_current_snapshot_args remote_domain_has_current_snapshot_args; + +struct remote_domain_has_current_snapshot_ret { + int result; +}; +typedef struct remote_domain_has_current_snapshot_ret remote_domain_has_current_snapshot_ret; + +struct remote_domain_snapshot_current_args { + remote_nonnull_domain domain; + int flags; +}; +typedef struct remote_domain_snapshot_current_args remote_domain_snapshot_current_args; + +struct remote_domain_snapshot_current_ret { + remote_nonnull_domain_snapshot snap; +}; +typedef struct remote_domain_snapshot_current_ret remote_domain_snapshot_current_ret; + +struct remote_domain_create_from_snapshot_args { + remote_nonnull_domain_snapshot snap; + int flags; +}; +typedef struct remote_domain_create_from_snapshot_args remote_domain_create_from_snapshot_args; + +struct remote_domain_snapshot_delete_args { + remote_nonnull_domain_snapshot snap; + int flags; +}; +typedef struct remote_domain_snapshot_delete_args remote_domain_snapshot_delete_args; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -2045,6 +2147,15 @@ enum remote_procedure { REMOTE_PROC_LIST_NWFILTERS = 179, REMOTE_PROC_NWFILTER_DEFINE_XML = 180, REMOTE_PROC_NWFILTER_UNDEFINE = 181, + REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML = 182, + REMOTE_PROC_DOMAIN_SNAPSHOT_DUMP_XML = 183, + REMOTE_PROC_DOMAIN_SNAPSHOT_NUM = 184, + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_NAMES = 185, + REMOTE_PROC_DOMAIN_SNAPSHOT_LOOKUP_BY_NAME = 186, + REMOTE_PROC_DOMAIN_HAS_CURRENT_SNAPSHOT = 187, + REMOTE_PROC_DOMAIN_SNAPSHOT_CURRENT = 188, + REMOTE_PROC_DOMAIN_CREATE_FROM_SNAPSHOT = 189, + REMOTE_PROC_DOMAIN_SNAPSHOT_DELETE = 190, }; typedef enum remote_procedure remote_procedure; @@ -2088,6 +2199,7 @@ extern bool_t xdr_remote_nonnull_storage_pool (XDR *, remote_nonnull_storage_po extern bool_t xdr_remote_nonnull_storage_vol (XDR *, remote_nonnull_storage_vol*); extern bool_t xdr_remote_nonnull_node_device (XDR *, remote_nonnull_node_device*); extern bool_t xdr_remote_nonnull_secret (XDR *, remote_nonnull_secret*); +extern bool_t xdr_remote_nonnull_domain_snapshot (XDR *, remote_nonnull_domain_snapshot*); extern bool_t xdr_remote_domain (XDR *, remote_domain*); extern bool_t xdr_remote_network (XDR *, remote_network*); extern bool_t xdr_remote_nwfilter (XDR *, remote_nwfilter*); @@ -2380,6 +2492,22 @@ extern bool_t xdr_remote_domain_event_io_error_msg (XDR *, remote_domain_event_ extern bool_t xdr_remote_domain_event_graphics_address (XDR *, remote_domain_event_graphics_address*); extern bool_t xdr_remote_domain_event_graphics_identity (XDR *, remote_domain_event_graphics_identity*); extern bool_t xdr_remote_domain_event_graphics_msg (XDR *, remote_domain_event_graphics_msg*); +extern bool_t xdr_remote_domain_snapshot_create_xml_args (XDR *, remote_domain_snapshot_create_xml_args*); +extern bool_t xdr_remote_domain_snapshot_create_xml_ret (XDR *, remote_domain_snapshot_create_xml_ret*); +extern bool_t xdr_remote_domain_snapshot_dump_xml_args (XDR *, remote_domain_snapshot_dump_xml_args*); +extern bool_t xdr_remote_domain_snapshot_dump_xml_ret (XDR *, remote_domain_snapshot_dump_xml_ret*); +extern bool_t xdr_remote_domain_snapshot_num_args (XDR *, remote_domain_snapshot_num_args*); +extern bool_t xdr_remote_domain_snapshot_num_ret (XDR *, remote_domain_snapshot_num_ret*); +extern bool_t xdr_remote_domain_snapshot_list_names_args (XDR *, remote_domain_snapshot_list_names_args*); +extern bool_t xdr_remote_domain_snapshot_list_names_ret (XDR *, remote_domain_snapshot_list_names_ret*); +extern bool_t xdr_remote_domain_snapshot_lookup_by_name_args (XDR *, remote_domain_snapshot_lookup_by_name_args*); +extern bool_t xdr_remote_domain_snapshot_lookup_by_name_ret (XDR *, remote_domain_snapshot_lookup_by_name_ret*); +extern bool_t xdr_remote_domain_has_current_snapshot_args (XDR *, remote_domain_has_current_snapshot_args*); +extern bool_t xdr_remote_domain_has_current_snapshot_ret (XDR *, remote_domain_has_current_snapshot_ret*); +extern bool_t xdr_remote_domain_snapshot_current_args (XDR *, remote_domain_snapshot_current_args*); +extern bool_t xdr_remote_domain_snapshot_current_ret (XDR *, remote_domain_snapshot_current_ret*); +extern bool_t xdr_remote_domain_create_from_snapshot_args (XDR *, remote_domain_create_from_snapshot_args*); +extern bool_t xdr_remote_domain_snapshot_delete_args (XDR *, remote_domain_snapshot_delete_args*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2397,6 +2525,7 @@ extern bool_t xdr_remote_nonnull_storage_pool (); extern bool_t xdr_remote_nonnull_storage_vol (); extern bool_t xdr_remote_nonnull_node_device (); extern bool_t xdr_remote_nonnull_secret (); +extern bool_t xdr_remote_nonnull_domain_snapshot (); extern bool_t xdr_remote_domain (); extern bool_t xdr_remote_network (); extern bool_t xdr_remote_nwfilter (); @@ -2689,6 +2818,22 @@ extern bool_t xdr_remote_domain_event_io_error_msg (); extern bool_t xdr_remote_domain_event_graphics_address (); extern bool_t xdr_remote_domain_event_graphics_identity (); extern bool_t xdr_remote_domain_event_graphics_msg (); +extern bool_t xdr_remote_domain_snapshot_create_xml_args (); +extern bool_t xdr_remote_domain_snapshot_create_xml_ret (); +extern bool_t xdr_remote_domain_snapshot_dump_xml_args (); +extern bool_t xdr_remote_domain_snapshot_dump_xml_ret (); +extern bool_t xdr_remote_domain_snapshot_num_args (); +extern bool_t xdr_remote_domain_snapshot_num_ret (); +extern bool_t xdr_remote_domain_snapshot_list_names_args (); +extern bool_t xdr_remote_domain_snapshot_list_names_ret (); +extern bool_t xdr_remote_domain_snapshot_lookup_by_name_args (); +extern bool_t xdr_remote_domain_snapshot_lookup_by_name_ret (); +extern bool_t xdr_remote_domain_has_current_snapshot_args (); +extern bool_t xdr_remote_domain_has_current_snapshot_ret (); +extern bool_t xdr_remote_domain_snapshot_current_args (); +extern bool_t xdr_remote_domain_snapshot_current_ret (); +extern bool_t xdr_remote_domain_create_from_snapshot_args (); +extern bool_t xdr_remote_domain_snapshot_delete_args (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index bf87c33..49ac935 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -121,6 +121,9 @@ const REMOTE_AUTH_TYPE_LIST_MAX = 20; /* Upper limit on list of memory stats */ const REMOTE_DOMAIN_MEMORY_STATS_MAX = 1024; +/* Upper limit on lists of domain snapshots. */ +const REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX = 1024; + /* Maximum length of a block peek buffer message. * Note applications need to be aware of this limit and issue multiple * requests for large amounts of data. @@ -216,6 +219,12 @@ struct remote_nonnull_secret { remote_nonnull_string usageID; }; +/* A snapshot which may not be NULL. */ +struct remote_nonnull_domain_snapshot { + remote_nonnull_string name; + remote_nonnull_domain domain; +}; + /* A domain or network which may be NULL. */ typedef remote_nonnull_domain *remote_domain; typedef remote_nonnull_network *remote_network; @@ -1647,6 +1656,83 @@ struct remote_domain_event_graphics_msg { remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; +struct remote_domain_snapshot_create_xml_args { + remote_nonnull_domain domain; + remote_nonnull_string xml_desc; + int flags; +}; + +struct remote_domain_snapshot_create_xml_ret { + remote_nonnull_domain_snapshot snap; +}; + +struct remote_domain_snapshot_dump_xml_args { + remote_nonnull_domain_snapshot snap; + int flags; +}; + +struct remote_domain_snapshot_dump_xml_ret { + remote_nonnull_string xml; +}; + +struct remote_domain_snapshot_num_args { + remote_nonnull_domain domain; + int flags; +}; + +struct remote_domain_snapshot_num_ret { + int num; +}; + +struct remote_domain_snapshot_list_names_args { + remote_nonnull_domain domain; + int nameslen; + int flags; +}; + +struct remote_domain_snapshot_list_names_ret { + remote_nonnull_string names<REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX>; +}; + +struct remote_domain_snapshot_lookup_by_name_args { + remote_nonnull_domain domain; + remote_nonnull_string name; + int flags; +}; + +struct remote_domain_snapshot_lookup_by_name_ret { + remote_nonnull_domain_snapshot snap; +}; + +struct remote_domain_has_current_snapshot_args { + remote_nonnull_domain domain; + int flags; +}; + +struct remote_domain_has_current_snapshot_ret { + int result; +}; + +struct remote_domain_snapshot_current_args { + remote_nonnull_domain domain; + int flags; +}; + +struct remote_domain_snapshot_current_ret { + remote_nonnull_domain_snapshot snap; +}; + +struct remote_domain_create_from_snapshot_args { + remote_nonnull_domain_snapshot snap; + int flags; +}; + +struct remote_domain_snapshot_delete_args { + remote_nonnull_domain_snapshot snap; + int flags; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1852,7 +1938,16 @@ enum remote_procedure { REMOTE_PROC_LIST_NWFILTERS = 179, REMOTE_PROC_NWFILTER_DEFINE_XML = 180, - REMOTE_PROC_NWFILTER_UNDEFINE = 181 + REMOTE_PROC_NWFILTER_UNDEFINE = 181, + REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML = 182, + REMOTE_PROC_DOMAIN_SNAPSHOT_DUMP_XML = 183, + REMOTE_PROC_DOMAIN_SNAPSHOT_NUM = 184, + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_NAMES = 185, + REMOTE_PROC_DOMAIN_SNAPSHOT_LOOKUP_BY_NAME = 186, + REMOTE_PROC_DOMAIN_HAS_CURRENT_SNAPSHOT = 187, + REMOTE_PROC_DOMAIN_SNAPSHOT_CURRENT = 188, + REMOTE_PROC_DOMAIN_CREATE_FROM_SNAPSHOT = 189, + REMOTE_PROC_DOMAIN_SNAPSHOT_DELETE = 190 /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 646c7db..2a0e2b7 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5306,6 +5306,15 @@ static virDriver testDriver = { NULL, /* domainMigrateSetMaxDowntime */ testDomainEventRegisterAny, /* domainEventRegisterAny */ testDomainEventDeregisterAny, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 08fbf93..4a1bd84 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1937,6 +1937,15 @@ static virDriver umlDriver = { NULL, /* domainMigrateSetMaxDowntime */ NULL, /* domainEventRegisterAny */ NULL, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; diff --git a/src/util/virterror.c b/src/util/virterror.c index 2537110..d29f95b 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -184,6 +184,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_HOOK: dom = "Sync Hook "; break; + case VIR_FROM_DOMAIN_SNAPSHOT: + dom = "Domain Snapshot "; + break; } return(dom); } @@ -1153,6 +1156,18 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("Hook script execution failed: %s"); break; + case VIR_ERR_INVALID_DOMAIN_SNAPSHOT: + if (info == NULL) + errmsg = _("Invalid snapshot"); + else + errmsg = _("Invalid snapshot: %s"); + break; + case VIR_ERR_NO_DOMAIN_SNAPSHOT: + if (info == NULL) + errmsg = _("Domain snapshot not found"); + else + errmsg = _("Domain snapshot not found: %s"); + break; } return (errmsg); } diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 14fdcda..8163d22 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7184,6 +7184,15 @@ virDriver NAME(Driver) = { vboxDomainEventRegisterAny, /* domainEventRegisterAny */ vboxDomainEventDeregisterAny, /* domainEventDeregisterAny */ #endif + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index ebdc600..c277ade 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1980,6 +1980,15 @@ static virDriver xenUnifiedDriver = { NULL, /* domainMigrateSetMaxDowntime */ xenUnifiedDomainEventRegisterAny, /* domainEventRegisterAny */ xenUnifiedDomainEventDeregisterAny, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; /** diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c index dcfdc1e..c0fd8ab 100644 --- a/src/xenapi/xenapi_driver.c +++ b/src/xenapi/xenapi_driver.c @@ -1783,6 +1783,15 @@ static virDriver xenapiDriver = { NULL, /* domainMigrateSetMaxDowntime */ NULL, /* domainEventRegisterAny */ NULL, /* domainEventDeregisterAny */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainCreateFromSnapshot */ + NULL, /* domainSnapshotDelete */ }; /** -- 1.6.6.1

2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- daemon/remote.c | 311 ++++++++++++++++++++++++ daemon/remote_dispatch_args.h | 9 + daemon/remote_dispatch_prototypes.h | 72 ++++++ daemon/remote_dispatch_ret.h | 7 + daemon/remote_dispatch_table.h | 45 ++++ include/libvirt/libvirt.h.in | 61 +++++ include/libvirt/virterror.h | 5 +- src/datatypes.c | 122 ++++++++++ src/datatypes.h | 25 ++ src/driver.h | 47 ++++ src/esx/esx_driver.c | 9 + src/libvirt.c | 448 +++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/libvirt_public.syms | 10 + src/lxc/lxc_driver.c | 9 + src/opennebula/one_driver.c | 9 + src/openvz/openvz_driver.c | 9 + src/phyp/phyp_driver.c | 9 + src/qemu/qemu_driver.c | 9 + src/remote/remote_driver.c | 309 ++++++++++++++++++++++++ src/remote/remote_protocol.c | 181 ++++++++++++++ src/remote/remote_protocol.h | 145 +++++++++++ src/remote/remote_protocol.x | 97 ++++++++- src/test/test_driver.c | 9 + src/uml/uml_driver.c | 9 + src/util/virterror.c | 15 ++ src/vbox/vbox_tmpl.c | 9 + src/xen/xen_driver.c | 9 + src/xenapi/xenapi_driver.c | 9 + 29 files changed, 2007 insertions(+), 2 deletions(-)
diff --git a/src/datatypes.h b/src/datatypes.h index 4663c9c..bbeb7cf 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -130,6 +130,15 @@ # define VIR_IS_NWFILTER(obj) ((obj) && (obj)->magic==VIR_NWFILTER_MAGIC) # define VIR_IS_CONNECTED_NWFILTER(obj) (VIR_IS_NWFILTER(obj) && VIR_IS_CONNECT((obj)->conn))
+/** + * VIR_SNAPSHOT_MAGIC: + * + * magic value used to protect the API when pointers to snapshot structures + * are passed down by the users. + */ +# define VIR_SNAPSHOT_MAGIC 0x6666DEAD +# define VIR_IS_SNAPSHOT(obj) ((obj) && (obj)->magic==VIR_SNAPSHOT_MAGIC) +# define VIR_IS_DOMAIN_SNAPSHOT(obj) (VIR_IS_SNAPSHOT(obj) && VIR_IS_DOMAIN((obj)->domain))
Why do we need to distinguish between a snapshot and a domain snapshot? Matthias

On 04/02/2010 09:23 AM, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- daemon/remote.c | 311 ++++++++++++++++++++++++ daemon/remote_dispatch_args.h | 9 + daemon/remote_dispatch_prototypes.h | 72 ++++++ daemon/remote_dispatch_ret.h | 7 + daemon/remote_dispatch_table.h | 45 ++++ include/libvirt/libvirt.h.in | 61 +++++ include/libvirt/virterror.h | 5 +- src/datatypes.c | 122 ++++++++++ src/datatypes.h | 25 ++ src/driver.h | 47 ++++ src/esx/esx_driver.c | 9 + src/libvirt.c | 448 +++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/libvirt_public.syms | 10 + src/lxc/lxc_driver.c | 9 + src/opennebula/one_driver.c | 9 + src/openvz/openvz_driver.c | 9 + src/phyp/phyp_driver.c | 9 + src/qemu/qemu_driver.c | 9 + src/remote/remote_driver.c | 309 ++++++++++++++++++++++++ src/remote/remote_protocol.c | 181 ++++++++++++++ src/remote/remote_protocol.h | 145 +++++++++++ src/remote/remote_protocol.x | 97 ++++++++- src/test/test_driver.c | 9 + src/uml/uml_driver.c | 9 + src/util/virterror.c | 15 ++ src/vbox/vbox_tmpl.c | 9 + src/xen/xen_driver.c | 9 + src/xenapi/xenapi_driver.c | 9 + 29 files changed, 2007 insertions(+), 2 deletions(-)
diff --git a/src/datatypes.h b/src/datatypes.h index 4663c9c..bbeb7cf 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -130,6 +130,15 @@ # define VIR_IS_NWFILTER(obj) ((obj) && (obj)->magic==VIR_NWFILTER_MAGIC) # define VIR_IS_CONNECTED_NWFILTER(obj) (VIR_IS_NWFILTER(obj) && VIR_IS_CONNECT((obj)->conn))
+/** + * VIR_SNAPSHOT_MAGIC: + * + * magic value used to protect the API when pointers to snapshot structures + * are passed down by the users. + */ +# define VIR_SNAPSHOT_MAGIC 0x6666DEAD +# define VIR_IS_SNAPSHOT(obj) ((obj) && (obj)->magic==VIR_SNAPSHOT_MAGIC) +# define VIR_IS_DOMAIN_SNAPSHOT(obj) (VIR_IS_SNAPSHOT(obj) && VIR_IS_DOMAIN((obj)->domain))
Why do we need to distinguish between a snapshot and a domain snapshot?
To be perfectly honest, I don't think we do. I was just following the template from the rest of the macros in this file; this could easily enough be written as: # define VIR_IS_DOMAIN_SNAPSHOT(obj) ((obj) && (obj)->magic==VIR_SNAPSHOT_MAGIC) && VIR_IS_DOMAIN(obj)->domain) I don't really care either way. -- Chris Lalancette

On Thu, Apr 01, 2010 at 06:07:22PM -0400, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- [...] diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cb483e..a8a97e3 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1861,6 +1861,67 @@ int virDomainGetJobInfo(virDomainPtr dom, virDomainJobInfoPtr info); int virDomainAbortJob(virDomainPtr dom);
+/** + * virDomainSnapshot: + * + * a virDomainSnapshot is a private structure representing a snapshot of + * a domain. + */ +typedef struct _virDomainSnapshot virDomainSnapshot; + +/** + * virDomainSnapshotPtr: + * + * a virDomainSnapshotPtr is pointer to a virDomainSnapshot private structure, + * and is the type used to reference a domain snapshot in the API. + */ +typedef virDomainSnapshot *virDomainSnapshotPtr; + +/* Take a snapshot of the current VM state */ +virDomainSnapshotPtr virDomainSnapshotCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags); + +/* Dump the XML of a snapshot */ +char *virDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, + unsigned int flags); + +/* Return the number of snapshots for this domain */ +int virDomainSnapshotNum(virDomainPtr domain, unsigned int flags); + +/* Get the names of all snapshots for this domain */ +int virDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen, + unsigned int flags); + +/* Get a handle to a named snapshot */ +virDomainSnapshotPtr virDomainSnapshotLookupByName(virDomainPtr domain, + const char *name, + unsigned int flags); + +/* Check whether a domain has a snapshot which is currently used */ +int virDomainHasCurrentSnapshot(virDomainPtr domain, unsigned flags); + +/* Get a handle to the current snapshot */ +virDomainSnapshotPtr virDomainSnapshotCurrent(virDomainPtr domain, + unsigned int flags); + +/* Set this snapshot as the current one for a domain, to be + * used next time domain is started */ +int virDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags); + +/* Deactivate a snapshot */ +typedef enum { + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE = (1 << 0), + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE = (1 << 1), + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD = (1 << 2), + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE = (1 << 3), +} virDomainSnapshotDeleteFlags;
Having a graphical reprentation of what the 4 options do on a snapshot arborescence would be nice but that's for documentation, in the meantime I would comment the enums. Also it seems to me the current set of options are expected to be mutually exclusive so why use bitfields ?
+int virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags); + +int virDomainSnapshotFree(virDomainSnapshotPtr snapshot);
/* A generic callback definition. Specific events usually have a customization * with extra parameters */ [...] diff --git a/src/libvirt.c b/src/libvirt.c index 5247fe7..edb3084 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -683,6 +683,31 @@ virLibNWFilterError(virNWFilterPtr pool, virErrorNumber error, }
/** + * virLibDomainSnapshotError: + * @snapshot: the snapshot if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the secret level
Comment is obviously an old cut and paste :-)
+ */ +static void +virLibDomainSnapshotError(virDomainSnapshotPtr snapshot, virErrorNumber error, const char *info) +{ + virConnectPtr conn = NULL; + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = virErrorMsg(error, info); + if (error != VIR_ERR_INVALID_DOMAIN_SNAPSHOT) + conn = snapshot->domain->conn; + + virRaiseError(conn, NULL, NULL, VIR_FROM_DOMAIN_SNAPSHOT, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** * virRegisterNetworkDriver: * @driver: pointer to a network driver block * @@ -12136,3 +12161,426 @@ error: virDispatchError(conn); return -1; [...] +/** + * virDomainSnapshotListNames: + * @domain: a domain object + * @names: array to collect the list of names of snapshots + * @nameslen: size of @names + * @flags: unused flag parameters; callers should pass 0 + * + * Collect the list of domain snapshots for the given domain, and store + * their names in @names. + * + * Returns the number of domain snapshots found or -1 in case of error. + */
Maybe need to indicate the strategy for freeing the names list. [...]
+/** + * virDomainCreateFromSnapshot
wrong function name :-)
+ * @snapshot: a domain snapshot object + * @flags: flag parameters + * + * Delete the snapshot. + * + * Returns 0 if the snapshot was successfully deleted, -1 on error. + */ +int +virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("snapshot=%p, flags=%u", snapshot, flags); + + /* FIXME: make sure only one of the flags is set */
hence why use a bitfield allocation of the enums at all ?
+ virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = snapshot->domain->conn; + + if (conn->driver->domainSnapshotDelete) { + int ret = conn->driver->domainSnapshotDelete(snapshot, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainFree:
function name error :-)
+ * @snapshot: a domain snapshot object + * + * Free the domain snapshot object. The snapshot itself is not modified. + * The data structure is freed and should not be used thereafter. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainSnapshotFree(virDomainSnapshotPtr snapshot) +{ + DEBUG("snapshot=%p", snapshot); + + virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (virUnrefDomainSnapshot(snapshot) < 0) { + virDispatchError(NULL); + return -1; + } + return 0; +}
Except for those few things this looks fine to me, ACK once fixed Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On 04/02/2010 12:17 PM, Daniel Veillard wrote:
On Thu, Apr 01, 2010 at 06:07:22PM -0400, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- [...] diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cb483e..a8a97e3 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1861,6 +1861,67 @@ int virDomainGetJobInfo(virDomainPtr dom, virDomainJobInfoPtr info); int virDomainAbortJob(virDomainPtr dom);
+/** + * virDomainSnapshot: + * + * a virDomainSnapshot is a private structure representing a snapshot of + * a domain. + */ +typedef struct _virDomainSnapshot virDomainSnapshot; + +/** + * virDomainSnapshotPtr: + * + * a virDomainSnapshotPtr is pointer to a virDomainSnapshot private structure, + * and is the type used to reference a domain snapshot in the API. + */ +typedef virDomainSnapshot *virDomainSnapshotPtr; + +/* Take a snapshot of the current VM state */ +virDomainSnapshotPtr virDomainSnapshotCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags); + +/* Dump the XML of a snapshot */ +char *virDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, + unsigned int flags); + +/* Return the number of snapshots for this domain */ +int virDomainSnapshotNum(virDomainPtr domain, unsigned int flags); + +/* Get the names of all snapshots for this domain */ +int virDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen, + unsigned int flags); + +/* Get a handle to a named snapshot */ +virDomainSnapshotPtr virDomainSnapshotLookupByName(virDomainPtr domain, + const char *name, + unsigned int flags); + +/* Check whether a domain has a snapshot which is currently used */ +int virDomainHasCurrentSnapshot(virDomainPtr domain, unsigned flags); + +/* Get a handle to the current snapshot */ +virDomainSnapshotPtr virDomainSnapshotCurrent(virDomainPtr domain, + unsigned int flags); + +/* Set this snapshot as the current one for a domain, to be + * used next time domain is started */ +int virDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags); + +/* Deactivate a snapshot */ +typedef enum { + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE = (1 << 0), + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE = (1 << 1), + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD = (1 << 2), + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE = (1 << 3), +} virDomainSnapshotDeleteFlags;
Having a graphical reprentation of what the 4 options do on a snapshot arborescence would be nice but that's for documentation, in the meantime I would comment the enums. Also it seems to me the current set of options are expected to be mutually exclusive so why use bitfields ?
Actually, this is the one area where the new set of patches are changing semantics. All of these flags are going to be removed except for just one, which is "DISCARD_CHILDREN". I'll add documentation for that.
+int virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags); + +int virDomainSnapshotFree(virDomainSnapshotPtr snapshot);
/* A generic callback definition. Specific events usually have a customization * with extra parameters */ [...] diff --git a/src/libvirt.c b/src/libvirt.c index 5247fe7..edb3084 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -683,6 +683,31 @@ virLibNWFilterError(virNWFilterPtr pool, virErrorNumber error, }
/** + * virLibDomainSnapshotError: + * @snapshot: the snapshot if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the secret level
Comment is obviously an old cut and paste :-)
Oops, yeah, fixed now.
+ */ +static void +virLibDomainSnapshotError(virDomainSnapshotPtr snapshot, virErrorNumber error, const char *info) +{ + virConnectPtr conn = NULL; + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = virErrorMsg(error, info); + if (error != VIR_ERR_INVALID_DOMAIN_SNAPSHOT) + conn = snapshot->domain->conn; + + virRaiseError(conn, NULL, NULL, VIR_FROM_DOMAIN_SNAPSHOT, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** * virRegisterNetworkDriver: * @driver: pointer to a network driver block * @@ -12136,3 +12161,426 @@ error: virDispatchError(conn); return -1; [...] +/** + * virDomainSnapshotListNames: + * @domain: a domain object + * @names: array to collect the list of names of snapshots + * @nameslen: size of @names + * @flags: unused flag parameters; callers should pass 0 + * + * Collect the list of domain snapshots for the given domain, and store + * their names in @names. + * + * Returns the number of domain snapshots found or -1 in case of error. + */
Maybe need to indicate the strategy for freeing the names list.
Added now.
[...]
+/** + * virDomainCreateFromSnapshot
wrong function name :-)
Yeah, I noticed that after posting the series. Fixed now.
+ * @snapshot: a domain snapshot object + * @flags: flag parameters + * + * Delete the snapshot. + * + * Returns 0 if the snapshot was successfully deleted, -1 on error. + */ +int +virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("snapshot=%p, flags=%u", snapshot, flags); + + /* FIXME: make sure only one of the flags is set */
hence why use a bitfield allocation of the enums at all ?
Yeah, this was silliness. It's all gone now since we only have one flag.
+ virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = snapshot->domain->conn; + + if (conn->driver->domainSnapshotDelete) { + int ret = conn->driver->domainSnapshotDelete(snapshot, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainFree:
function name error :-)
Fixed.
+ * @snapshot: a domain snapshot object + * + * Free the domain snapshot object. The snapshot itself is not modified. + * The data structure is freed and should not be used thereafter. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainSnapshotFree(virDomainSnapshotPtr snapshot) +{ + DEBUG("snapshot=%p", snapshot); + + virResetLastError(); + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(NULL, VIR_ERR_INVALID_DOMAIN_SNAPSHOT, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (virUnrefDomainSnapshot(snapshot) < 0) { + virDispatchError(NULL); + return -1; + } + return 0; +}
Except for those few things this looks fine to me,
ACK once fixed
Great, thanks. -- Chris Lalancette

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 402 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 53 ++++++ src/libvirt_private.syms | 10 ++ 3 files changed, 455 insertions(+), 10 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e260dce..1971b9a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -28,6 +28,7 @@ #include <unistd.h> #include <fcntl.h> #include <dirent.h> +#include <sys/time.h> #include "virterror_internal.h" #include "datatypes.h" @@ -742,6 +743,8 @@ static void virDomainObjFree(virDomainObjPtr dom) virMutexDestroy(&dom->lock); + virDomainSnapshotObjListDeinit(&dom->snapshots); + VIR_FREE(dom); } @@ -794,6 +797,8 @@ static virDomainObjPtr virDomainObjNew(virCapsPtr caps) domain->state = VIR_DOMAIN_SHUTOFF; domain->refs = 1; + virDomainSnapshotObjListInit(&domain->snapshots); + VIR_DEBUG("obj=%p", domain); return domain; } @@ -6096,22 +6101,25 @@ static virDomainObjPtr virDomainLoadConfig(virCapsPtr caps, if ((configFile = virDomainConfigFile(configDir, name)) == NULL) goto error; - if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL) - goto error; - - if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0) - goto error; - if (!(def = virDomainDefParseFile(caps, configFile, VIR_DOMAIN_XML_INACTIVE))) goto error; - if ((dom = virDomainFindByName(doms, def->name))) { - virDomainObjUnlock(dom); - dom = NULL; - newVM = 0; + /* if the domain is already in our hashtable, we don't need to do + * anything further + */ + if ((dom = virDomainFindByUUID(doms, def->uuid))) { + VIR_FREE(configFile); + virDomainDefFree(def); + return dom; } + if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL) + goto error; + + if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0) + goto error; + if (!(dom = virDomainAssignDef(caps, doms, def, false))) goto error; @@ -6509,4 +6517,378 @@ cleanup: return -1; } +/* Snapshot Def functions */ +void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->description); + VIR_FREE(def->parent); + VIR_FREE(def); +} + +virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, + int newSnapshot) +{ + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr root; + virDomainSnapshotDefPtr def = NULL; + virDomainSnapshotDefPtr ret = NULL; + char *creation = NULL, *state = NULL; + struct timeval tv; + struct tm time_info; + char timestr[100]; + + xml = virXMLParse(NULL, xmlStr, "domainsnapshot.xml"); + if (!xml) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s",_("failed to parse snapshot xml document")); + return NULL; + } + + if ((root = xmlDocGetRootElement(xml)) == NULL) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing root element")); + goto cleanup; + } + + if (!xmlStrEqual(root->name, BAD_CAST "domainsnapshot")) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("incorrect root element")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + + def->name = virXPathString("string(./name)", ctxt); + if (def->name == NULL) { + /* make up a name */ + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info); + def->name = strdup(timestr); + } + if (def->name == NULL) { + virReportOOMError(); + goto cleanup; + } + + def->description = virXPathString("string(./description)", ctxt); + + if (!newSnapshot) { + creation = virXPathString("string(./creationTime)", ctxt); + if (creation == NULL) { + /* there was no creation time in an existing snapshot; this + * should never happen + */ + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing creationTime from existing snapshot")); + goto cleanup; + } + + if (strptime(creation, "%Y-%m-%d_%T", &time_info) == NULL) { + /* we saw a creation time we couldn't parse. We shouldn't + * ever see this, so throw an error + */ + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not convert '%s' to a time value"), + creation); + goto cleanup; + } + def->creationTime = mktime(&time_info); + + def->parent = virXPathString("string(./parent/name)", ctxt); + + state = virXPathString("string(./state)", ctxt); + if (state == NULL) { + /* there was no state in an existing snapshot; this + * should never happen + */ + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing state from existing snapshot")); + goto cleanup; + } + def->state = virDomainStateTypeFromString(state); + if (def->state < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid state '%s' in domain snapshot XML"), + state); + goto cleanup; + } + } + else { + gettimeofday(&tv, NULL); + def->creationTime = tv.tv_sec; + } + + ret = def; + +cleanup: + VIR_FREE(creation); + VIR_FREE(state); + if (ctxt) + xmlXPathFreeContext(ctxt); + if (ret == NULL) + virDomainSnapshotDefFree(def); + xmlFreeDoc(xml); + + return ret; +} + +char *virDomainSnapshotDefFormat(char *domain_uuid, + virDomainSnapshotDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char timestr[100]; + struct tm time_info; + + virBufferAddLit(&buf, "<domainsnapshot>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->name); + if (def->description) + virBufferVSprintf(&buf, " <description>%s</description>\n", + def->description); + virBufferVSprintf(&buf, " <state>%s</state>\n", + virDomainStateTypeToString(def->state)); + if (def->parent) { + virBufferAddLit(&buf, " <parent>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->parent); + virBufferAddLit(&buf, " </parent>\n"); + } + localtime_r(&def->creationTime, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info); + virBufferVSprintf(&buf, " <creationTime>%s</creationTime>\n", timestr); + virBufferAddLit(&buf, " <domain>\n"); + virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", domain_uuid); + virBufferAddLit(&buf, " </domain>\n"); + virBufferAddLit(&buf, "</domainsnapshot>\n"); + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + +/* Snapshot Obj functions */ +static virDomainSnapshotObjPtr virDomainSnapshotObjNew(void) +{ + virDomainSnapshotObjPtr snapshot; + + if (VIR_ALLOC(snapshot) < 0) { + virReportOOMError(); + return NULL; + } + + snapshot->refs = 1; + + VIR_DEBUG("obj=%p", snapshot); + + return snapshot; +} + +static void virDomainSnapshotObjFree(virDomainSnapshotObjPtr snapshot) +{ + if (!snapshot) + return; + + VIR_DEBUG("obj=%p", snapshot); + + virDomainSnapshotDefFree(snapshot->def); +} + +int virDomainSnapshotObjUnref(virDomainSnapshotObjPtr snapshot) +{ + snapshot->refs--; + VIR_DEBUG("obj=%p refs=%d", snapshot, snapshot->refs); + if (snapshot->refs == 0) { + virDomainSnapshotObjFree(snapshot); + return 0; + } + return snapshot->refs; +} + +virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots, + const virDomainSnapshotDefPtr def) +{ + virDomainSnapshotObjPtr snap; + + if (virHashLookup(snapshots->objs, def->name) != NULL) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected domain snapshot %s already exists"), + def->name); + return NULL; + } + + if (!(snap = virDomainSnapshotObjNew())) + return NULL; + snap->def = def; + + if (virHashAddEntry(snapshots->objs, snap->def->name, snap) < 0) { + VIR_FREE(snap); + virReportOOMError(); + return NULL; + } + + return snap; +} + +/* Snapshot Obj List functions */ +int virDomainSnapshotObjListInit(virDomainSnapshotObjListPtr snapshots) +{ + snapshots->objs = virHashCreate(50); + if (!snapshots->objs) { + virReportOOMError(); + return -1; + } + return 0; +} + +static void virDomainSnapshotObjListDeallocator(void *payload, + const char *name ATTRIBUTE_UNUSED) +{ + virDomainSnapshotObjPtr obj = payload; + + virDomainSnapshotObjUnref(obj); +} + +void virDomainSnapshotObjListDeinit(virDomainSnapshotObjListPtr snapshots) +{ + if (snapshots->objs) + virHashFree(snapshots->objs, virDomainSnapshotObjListDeallocator); +} + +struct virDomainSnapshotNameData { + int oom; + int numnames; + int maxnames; + char **const names; +}; + +static void virDomainSnapshotObjListCopyNames(void *payload, + const char *name ATTRIBUTE_UNUSED, + void *opaque) +{ + virDomainSnapshotObjPtr obj = payload; + struct virDomainSnapshotNameData *data = opaque; + + if (data->oom) + return; + + if (data->numnames < data->maxnames) { + if (!(data->names[data->numnames] = strdup(obj->def->name))) + data->oom = 1; + else + data->numnames++; + } +} + +int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots, + char **const names, int maxnames) +{ + struct virDomainSnapshotNameData data = { 0, 0, maxnames, names }; + int i; + + virHashForEach(snapshots->objs, virDomainSnapshotObjListCopyNames, &data); + if (data.oom) { + virReportOOMError(); + goto cleanup; + } + + return data.numnames; + +cleanup: + for (i = 0; i < data.numnames; i++) + VIR_FREE(data.names[i]); + return -1; +} + +static void virDomainSnapshotObjListCount(void *payload ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED, + void *data) +{ + int *count = data; + + (*count)++; +} + +int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots) +{ + int count = 0; + + virHashForEach(snapshots->objs, virDomainSnapshotObjListCount, &count); + + return count; +} + +static int virDomainSnapshotObjListSearchName(const void *payload, + const char *name ATTRIBUTE_UNUSED, + const void *data) +{ + virDomainSnapshotObjPtr obj = (virDomainSnapshotObjPtr)payload; + int want = 0; + + if (STREQ(obj->def->name, (const char *)data)) + want = 1; + + return want; +} + +virDomainSnapshotObjPtr virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots, + const char *name) +{ + return virHashSearch(snapshots->objs, virDomainSnapshotObjListSearchName, name); +} + +void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots, + virDomainSnapshotObjPtr snapshot) +{ + virHashRemoveEntry(snapshots->objs, snapshot->def->name, + virDomainSnapshotObjListDeallocator); +} + +struct snapshot_has_children { + char *name; + int number; +}; + +static void virDomainSnapshotCountChildren(void *payload, + const char *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainSnapshotObjPtr obj = payload; + struct snapshot_has_children *curr = data; + + if (obj->def->parent && STREQ(obj->def->parent, curr->name)) + curr->number++; +} + +int virDomainSnapshotHasChildren(virDomainSnapshotObjPtr snap, + virDomainSnapshotObjListPtr snapshots) +{ + struct snapshot_has_children children; + + children.name = snap->def->name; + children.number = 0; + virHashForEach(snapshots->objs, virDomainSnapshotCountChildren, &children); + + return children.number; +} + + #endif /* ! PROXY */ diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 023196f..8d5276b 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -729,6 +729,55 @@ struct _virDomainClockDef { # define VIR_DOMAIN_CPUMASK_LEN 1024 + +/* Snapshot state */ +typedef struct _virDomainSnapshotDef virDomainSnapshotDef; +typedef virDomainSnapshotDef *virDomainSnapshotDefPtr; +struct _virDomainSnapshotDef { + char *name; + char *description; + char *parent; + time_t creationTime; + int state; +}; + +typedef struct _virDomainSnapshotObj virDomainSnapshotObj; +typedef virDomainSnapshotObj *virDomainSnapshotObjPtr; +struct _virDomainSnapshotObj { + int refs; + + virDomainSnapshotDefPtr def; +}; + +typedef struct _virDomainSnapshotObjList virDomainSnapshotObjList; +typedef virDomainSnapshotObjList *virDomainSnapshotObjListPtr; +struct _virDomainSnapshotObjList { + /* name string -> virDomainSnapshotObj mapping + * for O(1), lockless lookup-by-name */ + virHashTable *objs; +}; + +virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, + int newSnapshot); +void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def); +char *virDomainSnapshotDefFormat(char *domain_uuid, + virDomainSnapshotDefPtr def); +virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots, + const virDomainSnapshotDefPtr def); + +int virDomainSnapshotObjListInit(virDomainSnapshotObjListPtr objs); +void virDomainSnapshotObjListDeinit(virDomainSnapshotObjListPtr objs); +int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots, + char **const names, int maxnames); +int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots); +virDomainSnapshotObjPtr virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots, + const char *name); +void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots, + virDomainSnapshotObjPtr snapshot); +int virDomainSnapshotObjUnref(virDomainSnapshotObjPtr snapshot); +int virDomainSnapshotHasChildren(virDomainSnapshotObjPtr snap, + virDomainSnapshotObjListPtr snapshots); + /* Guest VM main configuration */ typedef struct _virDomainDef virDomainDef; typedef virDomainDef *virDomainDefPtr; @@ -798,6 +847,8 @@ struct _virDomainDef { virSecurityLabelDef seclabel; virDomainWatchdogDefPtr watchdog; virCPUDefPtr cpu; + + virDomainSnapshotDefPtr current_snapshot; }; /* Guest VM runtime state */ @@ -816,6 +867,8 @@ struct _virDomainObj { virDomainDefPtr def; /* The current definition */ virDomainDefPtr newDef; /* New definition to activate at shutdown */ + virDomainSnapshotObjList snapshots; + void *privateData; void (*privateDataFreeFunc)(void *); }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c402720..2bdd97b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -206,6 +206,16 @@ virDomainTimerTickpolicyTypeToString; virDomainTimerTickpolicyTypeFromString; virDomainTimerModeTypeToString; virDomainTimerModeTypeFromString; +virDomainSnapshotObjListGetNames; +virDomainSnapshotObjListNum; +virDomainSnapshotFindByName; +virDomainSnapshotObjListAdd; +virDomainSnapshotObjListRemove; +virDomainSnapshotHasChildren; +virDomainSnapshotObjUnref; +virDomainSnapshotDefParseString; +virDomainSnapshotDefFormat; +virDomainSnapshotAssignDef; # domain_event.h -- 1.6.6.1

2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 402 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 53 ++++++ src/libvirt_private.syms | 10 ++ 3 files changed, 455 insertions(+), 10 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e260dce..1971b9a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c
@@ -6096,22 +6101,25 @@ static virDomainObjPtr virDomainLoadConfig(virCapsPtr caps,
if ((configFile = virDomainConfigFile(configDir, name)) == NULL) goto error; - if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL) - goto error; - - if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0) - goto error; - if (!(def = virDomainDefParseFile(caps, configFile, VIR_DOMAIN_XML_INACTIVE))) goto error;
- if ((dom = virDomainFindByName(doms, def->name))) { - virDomainObjUnlock(dom); - dom = NULL; - newVM = 0; + /* if the domain is already in our hashtable, we don't need to do + * anything further + */ + if ((dom = virDomainFindByUUID(doms, def->uuid))) { + VIR_FREE(configFile); + virDomainDefFree(def); + return dom; }
+ if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL) + goto error; + + if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0) + goto error; + if (!(dom = virDomainAssignDef(caps, doms, def, false))) goto error;
What's the reason for this reordering? It seems to be independent from snapshots. Maybe it should go into a separate patch?
+virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, + int newSnapshot) +{ + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr root; + virDomainSnapshotDefPtr def = NULL; + virDomainSnapshotDefPtr ret = NULL; + char *creation = NULL, *state = NULL; + struct timeval tv; + struct tm time_info; + char timestr[100]; + + xml = virXMLParse(NULL, xmlStr, "domainsnapshot.xml"); + if (!xml) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s",_("failed to parse snapshot xml document")); + return NULL; + } + + if ((root = xmlDocGetRootElement(xml)) == NULL) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing root element")); + goto cleanup; + } + + if (!xmlStrEqual(root->name, BAD_CAST "domainsnapshot")) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("incorrect root element")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + + def->name = virXPathString("string(./name)", ctxt); + if (def->name == NULL) { + /* make up a name */ + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info); + def->name = strdup(timestr); + } + if (def->name == NULL) { + virReportOOMError(); + goto cleanup; + } + + def->description = virXPathString("string(./description)", ctxt); + + if (!newSnapshot) { + creation = virXPathString("string(./creationTime)", ctxt);
I think it should be creationtime or creation_time, but not creationTime.
+ if (creation == NULL) { + /* there was no creation time in an existing snapshot; this + * should never happen + */ + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing creationTime from existing snapshot")); + goto cleanup; + } + + if (strptime(creation, "%Y-%m-%d_%T", &time_info) == NULL) { + /* we saw a creation time we couldn't parse. We shouldn't + * ever see this, so throw an error + */ + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not convert '%s' to a time value"), + creation); + goto cleanup; + } + def->creationTime = mktime(&time_info);
mktime converts the time_info into Unix time in UTC and uses the current timezone set for the system, doesn't it? So you'll get different results in different timezones for the same time string 2010-04-02_12:14:42. Client and server in different timezones. With QEMU the time parsing and formating is done on the server side in server local time and this gets transfered to the client without further modification. With ESX I request the snapshots createTime, that's in server local time annotated with a timezone offset. I need to convert that into UTC and assign it to def->creationTime. virDomainSnapshotDefFormat later on will convert it into client local time, because the ESX driver is client side. So you'll get different time string, even if both QEMU and ESX take a snapshot in the same timezone at the same time, if the client is in another timezone.
+ def->parent = virXPathString("string(./parent/name)", ctxt); + + state = virXPathString("string(./state)", ctxt); + if (state == NULL) { + /* there was no state in an existing snapshot; this + * should never happen + */ + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing state from existing snapshot")); + goto cleanup; + } + def->state = virDomainStateTypeFromString(state); + if (def->state < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid state '%s' in domain snapshot XML"), + state); + goto cleanup; + } + } + else { + gettimeofday(&tv, NULL); + def->creationTime = tv.tv_sec; + } + + ret = def; + +cleanup: + VIR_FREE(creation); + VIR_FREE(state); + if (ctxt) + xmlXPathFreeContext(ctxt); + if (ret == NULL) + virDomainSnapshotDefFree(def); + xmlFreeDoc(xml); + + return ret; +} + +char *virDomainSnapshotDefFormat(char *domain_uuid, + virDomainSnapshotDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char timestr[100]; + struct tm time_info; + + virBufferAddLit(&buf, "<domainsnapshot>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->name); + if (def->description) + virBufferVSprintf(&buf, " <description>%s</description>\n", + def->description); + virBufferVSprintf(&buf, " <state>%s</state>\n", + virDomainStateTypeToString(def->state)); + if (def->parent) { + virBufferAddLit(&buf, " <parent>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->parent); + virBufferAddLit(&buf, " </parent>\n"); + } + localtime_r(&def->creationTime, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info);
Again, you handle the time in local time. You could use %F_%T%z to include the local time zone and then handle that in the parsing function to get a correct UTC time back. Maybe a better solution is to just store the Unix time in seconds in UTC (as returned by the time() function) in the XML and let applications do the conversion into a more human readable format and take care of timezone stuff. This way we get rid of the timezone problem at the libvirt level at all.
+ virBufferVSprintf(&buf, " <creationTime>%s</creationTime>\n", timestr);
Same note about the capital T applies here.
+ virBufferAddLit(&buf, " <domain>\n"); + virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", domain_uuid); + virBufferAddLit(&buf, " </domain>\n"); + virBufferAddLit(&buf, "</domainsnapshot>\n"); + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} +
Matthias

On 04/02/2010 09:25 AM, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 402 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 53 ++++++ src/libvirt_private.syms | 10 ++ 3 files changed, 455 insertions(+), 10 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e260dce..1971b9a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c
@@ -6096,22 +6101,25 @@ static virDomainObjPtr virDomainLoadConfig(virCapsPtr caps,
if ((configFile = virDomainConfigFile(configDir, name)) == NULL) goto error; - if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL) - goto error; - - if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0) - goto error; - if (!(def = virDomainDefParseFile(caps, configFile, VIR_DOMAIN_XML_INACTIVE))) goto error;
- if ((dom = virDomainFindByName(doms, def->name))) { - virDomainObjUnlock(dom); - dom = NULL; - newVM = 0; + /* if the domain is already in our hashtable, we don't need to do + * anything further + */ + if ((dom = virDomainFindByUUID(doms, def->uuid))) { + VIR_FREE(configFile); + virDomainDefFree(def); + return dom; }
+ if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL) + goto error; + + if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0) + goto error; + if (!(dom = virDomainAssignDef(caps, doms, def, false))) goto error;
What's the reason for this reordering? It seems to be independent from snapshots. Maybe it should go into a separate patch?
Oops, yeah, I forgot to split that out. It's either a feature of def/newDef that I don't understand, or it's a bugfix. That being said, I'm not sure I even need this bugfix anymore, so I'll retest and split it into a separate patch if I still need it.
+virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, + int newSnapshot) +{ + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr root; + virDomainSnapshotDefPtr def = NULL; + virDomainSnapshotDefPtr ret = NULL; + char *creation = NULL, *state = NULL; + struct timeval tv; + struct tm time_info; + char timestr[100]; + + xml = virXMLParse(NULL, xmlStr, "domainsnapshot.xml"); + if (!xml) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s",_("failed to parse snapshot xml document")); + return NULL; + } + + if ((root = xmlDocGetRootElement(xml)) == NULL) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing root element")); + goto cleanup; + } + + if (!xmlStrEqual(root->name, BAD_CAST "domainsnapshot")) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("incorrect root element")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + + def->name = virXPathString("string(./name)", ctxt); + if (def->name == NULL) { + /* make up a name */ + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info); + def->name = strdup(timestr); + } + if (def->name == NULL) { + virReportOOMError(); + goto cleanup; + } + + def->description = virXPathString("string(./description)", ctxt); + + if (!newSnapshot) { + creation = virXPathString("string(./creationTime)", ctxt);
I think it should be creationtime or creation_time, but not creationTime.
Taking the domain XML as an example, all 3 styles are used (e.g. currentMemory, on_poweroff, and seclabel). I don't really care too much, so I'll do whatever is more comfortable. <snip>
+char *virDomainSnapshotDefFormat(char *domain_uuid, + virDomainSnapshotDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char timestr[100]; + struct tm time_info; + + virBufferAddLit(&buf, "<domainsnapshot>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->name); + if (def->description) + virBufferVSprintf(&buf, " <description>%s</description>\n", + def->description); + virBufferVSprintf(&buf, " <state>%s</state>\n", + virDomainStateTypeToString(def->state)); + if (def->parent) { + virBufferAddLit(&buf, " <parent>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->parent); + virBufferAddLit(&buf, " </parent>\n"); + } + localtime_r(&def->creationTime, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info);
Again, you handle the time in local time. You could use %F_%T%z to include the local time zone and then handle that in the parsing function to get a correct UTC time back.
Maybe a better solution is to just store the Unix time in seconds in UTC (as returned by the time() function) in the XML and let applications do the conversion into a more human readable format and take care of timezone stuff. This way we get rid of the timezone problem at the libvirt level at all.
Well, I was sort of shooting for that by defining the field to be UTC (which I obviously mis-implemented with localtime_r). It would be nice to have a human-readable string in the XML, though, which is why I didn't just use seconds since the Epoch. Can I just use gmtime_r() here to get the time in UTC, and then strptime and mktime above will do the right thing? -- Chris Lalancette

2010/4/2 Chris Lalancette <clalance@redhat.com>:
On 04/02/2010 09:25 AM, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 402 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 53 ++++++ src/libvirt_private.syms | 10 ++ 3 files changed, 455 insertions(+), 10 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e260dce..1971b9a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c
+virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, + int newSnapshot) +{ + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr root; + virDomainSnapshotDefPtr def = NULL; + virDomainSnapshotDefPtr ret = NULL; + char *creation = NULL, *state = NULL; + struct timeval tv; + struct tm time_info; + char timestr[100]; + + xml = virXMLParse(NULL, xmlStr, "domainsnapshot.xml"); + if (!xml) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s",_("failed to parse snapshot xml document")); + return NULL; + } + + if ((root = xmlDocGetRootElement(xml)) == NULL) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing root element")); + goto cleanup; + } + + if (!xmlStrEqual(root->name, BAD_CAST "domainsnapshot")) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("incorrect root element")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + + def->name = virXPathString("string(./name)", ctxt); + if (def->name == NULL) { + /* make up a name */ + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info); + def->name = strdup(timestr); + } + if (def->name == NULL) { + virReportOOMError(); + goto cleanup; + } + + def->description = virXPathString("string(./description)", ctxt); + + if (!newSnapshot) { + creation = virXPathString("string(./creationTime)", ctxt);
I think it should be creationtime or creation_time, but not creationTime.
Taking the domain XML as an example, all 3 styles are used (e.g. currentMemory, on_poweroff, and seclabel). I don't really care too much, so I'll do whatever is more comfortable.
I missed currentMemory and since it's already a mixture of all styles lets just keep creationTime.
+char *virDomainSnapshotDefFormat(char *domain_uuid, + virDomainSnapshotDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char timestr[100]; + struct tm time_info; + + virBufferAddLit(&buf, "<domainsnapshot>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->name); + if (def->description) + virBufferVSprintf(&buf, " <description>%s</description>\n", + def->description); + virBufferVSprintf(&buf, " <state>%s</state>\n", + virDomainStateTypeToString(def->state)); + if (def->parent) { + virBufferAddLit(&buf, " <parent>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->parent); + virBufferAddLit(&buf, " </parent>\n"); + } + localtime_r(&def->creationTime, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info);
Again, you handle the time in local time. You could use %F_%T%z to include the local time zone and then handle that in the parsing function to get a correct UTC time back.
Maybe a better solution is to just store the Unix time in seconds in UTC (as returned by the time() function) in the XML and let applications do the conversion into a more human readable format and take care of timezone stuff. This way we get rid of the timezone problem at the libvirt level at all.
Well, I was sort of shooting for that by defining the field to be UTC (which I obviously mis-implemented with localtime_r). It would be nice to have a human-readable string in the XML, though, which is why I didn't just use seconds since the Epoch. Can I just use gmtime_r() here to get the time in UTC, and then strptime and mktime above will do the right thing?
I think the problem is that struct tm doesn't contain a timezone filed. Therefore, strptime parses a time that is not fixed in a timezone. So even If we would use a time format like 2010-04-02_12:30:58+0200 it won't help with strptime. strptime knows %z for timezone part, but just ignores it. mktime operates in local time. The man page says: "The mktime() function converts a broken-down time structure, expressed as local time, to calendar time representation" So gmtime_r doesn't help. I think we could use seconds since the Epoch from time() as the actual value, and attach a human readable version (from strftime with "%a, %d %b %Y %H:%M:%S %z" in RFC 2822 format) in a comment like this: <creationTime>1270221648</creationTime><!-- Fri, 02 Apr 2010 17:20:48 +0200 --> That way we don't need to deal with the subtle timezone stuff as seconds since the Epoch is in UTC and we have a human readable version in virsh snapshot-dumpxml for example. Applications then can take the seconds since the Epoch value and format it as they like to local time or what ever they prefer. Matthias

On 04/02/2010 11:33 AM, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
On 04/02/2010 09:25 AM, Matthias Bolte wrote:
2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 402 ++++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_conf.h | 53 ++++++ src/libvirt_private.syms | 10 ++ 3 files changed, 455 insertions(+), 10 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e260dce..1971b9a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c
+virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, + int newSnapshot) +{ + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr root; + virDomainSnapshotDefPtr def = NULL; + virDomainSnapshotDefPtr ret = NULL; + char *creation = NULL, *state = NULL; + struct timeval tv; + struct tm time_info; + char timestr[100]; + + xml = virXMLParse(NULL, xmlStr, "domainsnapshot.xml"); + if (!xml) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s",_("failed to parse snapshot xml document")); + return NULL; + } + + if ((root = xmlDocGetRootElement(xml)) == NULL) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing root element")); + goto cleanup; + } + + if (!xmlStrEqual(root->name, BAD_CAST "domainsnapshot")) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("incorrect root element")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + + def->name = virXPathString("string(./name)", ctxt); + if (def->name == NULL) { + /* make up a name */ + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info); + def->name = strdup(timestr); + } + if (def->name == NULL) { + virReportOOMError(); + goto cleanup; + } + + def->description = virXPathString("string(./description)", ctxt); + + if (!newSnapshot) { + creation = virXPathString("string(./creationTime)", ctxt);
I think it should be creationtime or creation_time, but not creationTime.
Taking the domain XML as an example, all 3 styles are used (e.g. currentMemory, on_poweroff, and seclabel). I don't really care too much, so I'll do whatever is more comfortable.
I missed currentMemory and since it's already a mixture of all styles lets just keep creationTime.
+char *virDomainSnapshotDefFormat(char *domain_uuid, + virDomainSnapshotDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char timestr[100]; + struct tm time_info; + + virBufferAddLit(&buf, "<domainsnapshot>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->name); + if (def->description) + virBufferVSprintf(&buf, " <description>%s</description>\n", + def->description); + virBufferVSprintf(&buf, " <state>%s</state>\n", + virDomainStateTypeToString(def->state)); + if (def->parent) { + virBufferAddLit(&buf, " <parent>\n"); + virBufferVSprintf(&buf, " <name>%s</name>\n", def->parent); + virBufferAddLit(&buf, " </parent>\n"); + } + localtime_r(&def->creationTime, &time_info); + strftime(timestr, sizeof(timestr), "%F_%T", &time_info);
Again, you handle the time in local time. You could use %F_%T%z to include the local time zone and then handle that in the parsing function to get a correct UTC time back.
Maybe a better solution is to just store the Unix time in seconds in UTC (as returned by the time() function) in the XML and let applications do the conversion into a more human readable format and take care of timezone stuff. This way we get rid of the timezone problem at the libvirt level at all.
Well, I was sort of shooting for that by defining the field to be UTC (which I obviously mis-implemented with localtime_r). It would be nice to have a human-readable string in the XML, though, which is why I didn't just use seconds since the Epoch. Can I just use gmtime_r() here to get the time in UTC, and then strptime and mktime above will do the right thing?
I think the problem is that struct tm doesn't contain a timezone filed. Therefore, strptime parses a time that is not fixed in a timezone. So even If we would use a time format like
2010-04-02_12:30:58+0200
it won't help with strptime. strptime knows %z for timezone part, but just ignores it.
Ah, I see. That does make it a problem.
mktime operates in local time. The man page says:
"The mktime() function converts a broken-down time structure, expressed as local time, to calendar time representation"
So gmtime_r doesn't help.
I think we could use seconds since the Epoch from time() as the actual value, and attach a human readable version (from strftime with "%a, %d %b %Y %H:%M:%S %z" in RFC 2822 format) in a comment like this:
<creationTime>1270221648</creationTime><!-- Fri, 02 Apr 2010 17:20:48 +0200 -->
That way we don't need to deal with the subtle timezone stuff as seconds since the Epoch is in UTC and we have a human readable version in virsh snapshot-dumpxml for example.
Applications then can take the seconds since the Epoch value and format it as they like to local time or what ever they prefer.
Yeah, I'll change it over to seconds since the epoch. Thanks. -- Chris Lalancette

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/qemu/qemu_conf.c | 9 +- src/qemu/qemu_conf.h | 4 +- src/qemu/qemu_driver.c | 757 ++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_monitor.c | 39 +++ src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_monitor_json.c | 24 ++ src/qemu/qemu_monitor_json.h | 4 + src/qemu/qemu_monitor_text.c | 126 +++++++ src/qemu/qemu_monitor_text.h | 4 + tests/qemuxml2argvtest.c | 2 +- 10 files changed, 951 insertions(+), 22 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 5d0b211..1ebb29a 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3362,7 +3362,9 @@ int qemudBuildCommandLine(virConnectPtr conn, const char ***retenv, int **tapfds, int *ntapfds, - const char *migrateFrom) { + const char *migrateFrom, + int restore_snapshot) +{ int i; char memory[50]; char boot[VIR_DOMAIN_BOOT_LAST]; @@ -4567,6 +4569,11 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG_LIT("virtio"); } + if (def->current_snapshot && restore_snapshot) { + ADD_ARG_LIT("-loadvm"); + ADD_ARG_LIT(def->current_snapshot->name); + } + ADD_ARG(NULL); ADD_ENV(NULL); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 39518ca..30d9d69 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -120,6 +120,7 @@ struct qemud_driver { * the QEMU user/group */ char *libDir; char *cacheDir; + char *snapshotDir; unsigned int vncTLS : 1; unsigned int vncTLSx509verify : 1; unsigned int vncSASL : 1; @@ -198,7 +199,8 @@ int qemudBuildCommandLine (virConnectPtr conn, const char ***retenv, int **tapfds, int *ntapfds, - const char *migrateFrom) + const char *migrateFrom, + int restore_snapshot) ATTRIBUTE_NONNULL(1); /* With vlan == -1, use netdev syntax, else old hostnet */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 695e5ba..7df15a6 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -148,7 +148,8 @@ static int qemudStartVMDaemon(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, const char *migrateFrom, - int stdin_fd); + int stdin_fd, + int restore_snapshot); static void qemudShutdownVMDaemon(struct qemud_driver *driver, virDomainObjPtr vm); @@ -629,7 +630,7 @@ qemuAutostartDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq int ret; virResetLastError(); - ret = qemudStartVMDaemon(data->conn, data->driver, vm, NULL, -1); + ret = qemudStartVMDaemon(data->conn, data->driver, vm, NULL, -1, 0); if (ret < 0) { virErrorPtr err = virGetLastError(); VIR_ERROR(_("Failed to autostart VM '%s': %s"), @@ -1342,6 +1343,89 @@ no_memory: return NULL; } +static void qemuDomainSnapshotLoad(void *payload, + const char *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainObjPtr vm = (virDomainObjPtr)payload; + char *baseDir = (char *)data; + char *snapDir = NULL; + DIR *dir = NULL; + struct dirent *entry; + char *xmlStr; + int ret; + char *fullpath; + virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotDefPtr def = NULL; + + virDomainObjLock(vm); + if (virAsprintf(&snapDir, "%s/%s", baseDir, vm->def->name) < 0) { + VIR_ERROR("Failed to allocate memory for snapshot directory for domain %s", + vm->def->name); + goto cleanup; + } + + VIR_INFO("Scanning for snapshots for domain %s in %s", vm->def->name, + snapDir); + + if (!(dir = opendir(snapDir))) { + if (errno != ENOENT) + VIR_ERROR("Failed to open snapshot directory %s for domain %s: %s", + snapDir, vm->def->name, strerror(errno)); + goto cleanup; + } + + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; + + /* NB: ignoring errors, so one malformed config doesn't + kill the whole process */ + VIR_INFO("Loading snapshot file '%s'", entry->d_name); + + if (virAsprintf(&fullpath, "%s/%s", snapDir, entry->d_name) < 0) + continue; + + ret = virFileReadAll(fullpath, 1024*1024*1, &xmlStr); + VIR_FREE(fullpath); + if (ret < 0) { + /* Nothing we can do here, skip this one */ + VIR_ERROR("Failed to read snapshot file %s: %s", fullpath, + strerror(errno)); + continue; + } + + def = virDomainSnapshotDefParseString(xmlStr, 0); + if (def == NULL) { + /* Nothing we can do here, skip this one */ + VIR_ERROR("Failed to parse snapshot XML from file '%s'", fullpath); + VIR_FREE(xmlStr); + continue; + } + + snap = virDomainSnapshotAssignDef(&vm->snapshots, def); + + VIR_FREE(xmlStr); + } + + /* FIXME: qemu keeps internal track of snapshots. We can get access + * to this info via the "info snapshots" monitor command for running + * domains, or via "qemu-img snapshot -l" for shutoff domains. It would + * be nice to update our internal state based on that, but there is a + * a problem. qemu doesn't track all of the same metadata that we do. + * In particular we wouldn't be able to fill in the <parent>, which is + * pretty important in our metadata. + */ + + virResetLastError(); + +cleanup: + if (dir) + closedir(dir); + VIR_FREE(snapDir); + virDomainObjUnlock(vm); +} + /** * qemudStartup: * @@ -1399,6 +1483,9 @@ qemudStartup(int privileged) { if (virAsprintf(&qemu_driver->cacheDir, "%s/cache/libvirt/qemu", LOCAL_STATE_DIR) == -1) goto out_of_memory; + if (virAsprintf(&qemu_driver->snapshotDir, + "%s/run/libvirt/qemu/snapshot", LOCAL_STATE_DIR) == -1) + goto out_of_memory; } else { uid_t uid = geteuid(); char *userdir = virGetUserDirectory(uid); @@ -1423,6 +1510,8 @@ qemudStartup(int privileged) { goto out_of_memory; if (virAsprintf(&qemu_driver->cacheDir, "%s/qemu/cache", base) == -1) goto out_of_memory; + if (virAsprintf(&qemu_driver->snapshotDir, "%s/qemu/snapshot", base) == -1) + goto out_of_memory; } if (virFileMakePath(qemu_driver->stateDir) != 0) { @@ -1443,6 +1532,12 @@ qemudStartup(int privileged) { qemu_driver->cacheDir, virStrerror(errno, ebuf, sizeof ebuf)); goto error; } + if (virFileMakePath(qemu_driver->snapshotDir) != 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create save dir '%s': %s"), + qemu_driver->snapshotDir, virStrerror(errno, ebuf, sizeof ebuf)); + goto error; + } /* Configuration paths are either ~/.libvirt/qemu/... (session) or * /etc/libvirt/qemu/... (system). @@ -1493,6 +1588,12 @@ qemudStartup(int privileged) { qemu_driver->cacheDir, qemu_driver->user, qemu_driver->group); goto error; } + if (chown(qemu_driver->snapshotDir, qemu_driver->user, qemu_driver->group) < 0) { + virReportSystemError(errno, + _("unable to set ownership of '%s' to %d:%d"), + qemu_driver->snapshotDir, qemu_driver->user, qemu_driver->group); + goto error; + } } /* If hugetlbfs is present, then we need to create a sub-directory within @@ -1543,6 +1644,11 @@ qemudStartup(int privileged) { qemu_driver->autostartDir, 0, NULL, NULL) < 0) goto error; + + + virHashForEach(qemu_driver->domains.objs, qemuDomainSnapshotLoad, + qemu_driver->snapshotDir); + qemuDriverUnlock(qemu_driver); qemudAutostartConfigs(qemu_driver); @@ -1645,6 +1751,7 @@ qemudShutdown(void) { VIR_FREE(qemu_driver->stateDir); VIR_FREE(qemu_driver->libDir); VIR_FREE(qemu_driver->cacheDir); + VIR_FREE(qemu_driver->snapshotDir); VIR_FREE(qemu_driver->vncTLSx509certdir); VIR_FREE(qemu_driver->vncListen); VIR_FREE(qemu_driver->vncPassword); @@ -3012,7 +3119,9 @@ static int qemudStartVMDaemon(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, const char *migrateFrom, - int stdin_fd) { + int stdin_fd, + int restore_snapshot) +{ const char **argv = NULL, **tmp; const char **progenv = NULL; int i, ret; @@ -3175,7 +3284,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, vm->def->id = driver->nextvmid++; if (qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, priv->monJSON, qemuCmdFlags, &argv, &progenv, - &tapfds, &ntapfds, migrateFrom) < 0) + &tapfds, &ntapfds, migrateFrom, restore_snapshot) < 0) goto cleanup; /* now that we know it is about to start call the hook if present */ @@ -3917,7 +4026,7 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) goto cleanup; /* XXXX free the 'vm' we created ? */ - if (qemudStartVMDaemon(conn, driver, vm, NULL, -1) < 0) { + if (qemudStartVMDaemon(conn, driver, vm, NULL, -1, 0) < 0) { if (qemuDomainObjEndJob(vm) > 0) virDomainRemoveInactive(&driver->domains, vm); @@ -5600,7 +5709,7 @@ static int qemudDomainRestore(virConnectPtr conn, } } /* Set the migration source and start it up. */ - ret = qemudStartVMDaemon(conn, driver, vm, "stdio", fd); + ret = qemudStartVMDaemon(conn, driver, vm, "stdio", fd, 0); if (intermediate_pid != -1) { /* Wait for intermediate process to exit */ while (waitpid(intermediate_pid, &childstat, 0) == -1 && @@ -5906,7 +6015,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, &monConfig, 0, qemuCmdFlags, &retargv, &retenv, NULL, NULL, /* Don't want it to create TAP devices */ - NULL) < 0) { + NULL, 0) < 0) { goto cleanup; } @@ -5995,7 +6104,7 @@ static int qemudDomainStart(virDomainPtr dom) { goto endjob; } - ret = qemudStartVMDaemon(dom->conn, driver, vm, NULL, -1); + ret = qemudStartVMDaemon(dom->conn, driver, vm, NULL, -1, 0); if (ret != -1) event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED, @@ -9060,7 +9169,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, /* Start the QEMU daemon, with the same command-line arguments plus * -incoming unix:/path/to/file or exec:nc -U /path/to/file */ - internalret = qemudStartVMDaemon(dconn, driver, vm, migrateFrom, -1); + internalret = qemudStartVMDaemon(dconn, driver, vm, migrateFrom, -1, 0); VIR_FREE(migrateFrom); if (internalret < 0) { /* Note that we don't set an error here because qemudStartVMDaemon @@ -9265,7 +9374,7 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, * -incoming tcp:0.0.0.0:port */ snprintf (migrateFrom, sizeof (migrateFrom), "tcp:0.0.0.0:%d", this_port); - if (qemudStartVMDaemon (dconn, driver, vm, migrateFrom, -1) < 0) { + if (qemudStartVMDaemon (dconn, driver, vm, migrateFrom, -1, 0) < 0) { /* Note that we don't set an error here because qemudStartVMDaemon * should have already done that. */ @@ -10221,6 +10330,616 @@ cleanup: return ret; } +static char *qemuFindQemuImgBinary(void) +{ + char *ret; + + ret = virFindFileInPath("kvm-img"); + if (ret == NULL) + ret = virFindFileInPath("qemu-img"); + if (ret == NULL) + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to find kvm-img or qemu-img")); + + return ret; +} + +static int qemuDomainSnapshotWriteSnapshotMetadata(virDomainObjPtr vm, + virDomainSnapshotDefPtr def, + char *snapshotDir) +{ + int fd = -1; + char *newxml = NULL; + int ret = -1; + char *snapDir = NULL; + char *snapFile = NULL; + int err; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(vm->def->uuid, uuidstr); + newxml = virDomainSnapshotDefFormat(uuidstr, def); + if (newxml == NULL) { + virReportOOMError(); + return -1; + } + + if (virAsprintf(&snapDir, "%s/%s", snapshotDir, vm->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + err = virFileMakePath(snapDir); + if (err < 0) { + virReportSystemError(err, _("cannot create snapshot directory '%s'"), + snapDir); + goto cleanup; + } + + if (virAsprintf(&snapFile, "%s/%s.snap", snapDir, def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + fd = open(snapFile, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); + if (fd < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to create snapshot file '%s'"), snapFile); + goto cleanup; + } + if (safewrite(fd, newxml, strlen(newxml)) != strlen(newxml)) { + virReportSystemError(errno, _("Failed to write snapshot data to %s"), + snapFile); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(snapFile); + VIR_FREE(snapDir); + VIR_FREE(newxml); + if (fd != -1) + close(fd); + return ret; +} + +static int qemuDomainSnapshotIsAllowed(virDomainObjPtr vm) +{ + int i; + + /* FIXME: we need to figure out what else here might succeed; in + * particular, if it's a raw device but on LVM, we could probably make + * that succeed as well + */ + /* FIXME: we also need to handle devices that are using pools */ + for (i = 0; i < vm->def->ndisks; i++) { + if (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK && + (!vm->def->disks[i]->driverType || + STRNEQ(vm->def->disks[i]->driverType, "qcow2"))) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Disk device '%s' does not support snapshotting"), + vm->def->disks[i]->info.alias); + return 0; + } + } + + return 1; +} + +static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotPtr snapshot = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDomainSnapshotDefPtr def; + qemuDomainObjPrivatePtr priv; + const char *qemuimgarg[] = { NULL, "snapshot", "-c", NULL, NULL, NULL }; + int i; + + qemuDriverLock(driver); + virUUIDFormat(domain->uuid, uuidstr); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + /* in a perfect world, we would allow qemu to tell us this. The problem + * is that qemu only does this check device-by-device; so if you had a + * domain that booted from a large qcow2 device, but had a secondary raw + * device attached, you wouldn't find out that you can't snapshot your + * guest until *after* it had spent the time to snapshot the boot device. + * This is probably a bug in qemu, but we'll work around it here for now. + */ + if (!qemuDomainSnapshotIsAllowed(vm)) + goto cleanup; + + if (!(def = virDomainSnapshotDefParseString(xmlDesc, 1))) + goto cleanup; + + if (!(snap = virDomainSnapshotAssignDef(&vm->snapshots, def))) + goto cleanup; + + /* actually do the snapshot */ + if (!virDomainObjIsActive(vm)) { + qemuimgarg[0] = qemuFindQemuImgBinary(); + if (qemuimgarg[0] == NULL) + /* qemuFindQemuImgBinary set the error */ + goto cleanup; + + qemuimgarg[3] = snap->def->name; + + for (i = 0; i < vm->def->ndisks; i++) { + /* FIXME: we also need to handle LVM here */ + /* FIXME: if we fail halfway through this loop, we are in an + * inconsistent state. I'm not quite sure what to do about that + */ + if (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + if (!vm->def->disks[i]->driverType || + STRNEQ(vm->def->disks[i]->driverType, "qcow2")) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Disk device '%s' does not support snapshotting"), + vm->def->disks[i]->info.alias); + goto cleanup; + } + + qemuimgarg[4] = vm->def->disks[i]->src; + + if (virRun(qemuimgarg, NULL) < 0) { + virReportSystemError(errno, + _("Failed to run '%s' to create snapshot '%s' from disk '%s'"), + qemuimgarg[0], snap->def->name, + vm->def->disks[i]->src); + goto cleanup; + } + } + } + } + else { + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorCreateSnapshot(priv->mon, def->name) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto cleanup; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + } + + snap->def->state = vm->state; + + /* FIXME: if we fail after this point, there's not a whole lot we can + * do; we've successfully taken the snapshot, and we are now running + * on it, so we have to go forward the best we can + */ + + if (vm->def->current_snapshot) { + def->parent = strdup(vm->def->current_snapshot->name); + if (def->parent == NULL) { + virReportOOMError(); + goto cleanup; + } + } + + /* Now we set the new current_snapshot for the domain */ + vm->def->current_snapshot = def; + /* FIXME: Do we have to take an additional reference? */ + + if (qemuDomainSnapshotWriteSnapshotMetadata(vm, def, + driver->snapshotDir) < 0) + /* qemuDomainSnapshotWriteSnapshotMetadata set the error */ + goto cleanup; + + snapshot = virGetDomainSnapshot(domain, snap->def->name); + +cleanup: + VIR_FREE(qemuimgarg[0]); + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return snapshot; +} + +static int qemuDomainSnapshotListNames(virDomainPtr domain, char **names, + int nameslen, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int n = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + n = virDomainSnapshotObjListGetNames(&vm->snapshots, names, nameslen); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return n; +} + +static int qemuDomainSnapshotNum(virDomainPtr domain, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int n = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + n = virDomainSnapshotObjListNum(&vm->snapshots); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return n; +} + +static virDomainSnapshotPtr qemuDomainSnapshotLookupByName(virDomainPtr domain, + const char *name, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm; + virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotPtr snapshot = NULL; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + snap = virDomainSnapshotFindByName(&vm->snapshots, name); + if (!snap) { + qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("no snapshot with matching name '%s'"), name); + goto cleanup; + } + + snapshot = virGetDomainSnapshot(domain, snap->def->name); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return snapshot; +} + +static int qemuDomainHasCurrentSnapshot(virDomainPtr domain, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + ret = (vm->def->current_snapshot != NULL); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + +static virDomainSnapshotPtr qemuDomainSnapshotCurrent(virDomainPtr domain, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm; + virDomainSnapshotPtr snapshot = NULL; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!vm->def->current_snapshot) { + qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, "%s", + _("the domain does not have a current snapshot")); + } + + snapshot = virGetDomainSnapshot(domain, vm->def->current_snapshot->name); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return snapshot; +} + +static char *qemuDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = snapshot->domain->conn->privateData; + virDomainObjPtr vm = NULL; + char *xml = NULL; + virDomainSnapshotObjPtr snap = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + qemuDriverLock(driver); + virUUIDFormat(snapshot->domain->uuid, uuidstr); + vm = virDomainFindByUUID(&driver->domains, snapshot->domain->uuid); + if (!vm) { + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); + if (!snap) { + qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("no domain snapshot with matching name '%s'"), + snapshot->name); + goto cleanup; + } + + xml = virDomainSnapshotDefFormat(uuidstr, snap->def); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return xml; +} + +static int qemuDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = snapshot->domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + virDomainSnapshotObjPtr snap = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDomainEventPtr event = NULL; + qemuDomainObjPrivatePtr priv; + + qemuDriverLock(driver); + virUUIDFormat(snapshot->domain->uuid, uuidstr); + vm = virDomainFindByUUID(&driver->domains, snapshot->domain->uuid); + if (!vm) { + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); + if (!snap) { + qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("no domain snapshot with matching name '%s'"), + snapshot->name); + goto cleanup; + } + + vm->def->current_snapshot = snap->def; + + if (virDomainObjIsActive(vm)) { + if (snap->def->state != VIR_DOMAIN_RUNNING + && snap->def->state != VIR_DOMAIN_PAUSED) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is running, but snapshot was taken while domain was off")); + goto cleanup; + } + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorLoadSnapshot(priv->mon, snap->def->name) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto cleanup; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + ret = 0; + } + else { + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + ret = qemudStartVMDaemon(snapshot->domain->conn, driver, vm, NULL, -1, 1); + if (ret != -1) + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); + if (qemuDomainObjEndJob(vm) == 0) + vm = NULL; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + + return ret; +} + +static int qemuDomainSnapshotDiscard(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainSnapshotObjPtr snap, + int delete_children ATTRIBUTE_UNUSED) +{ + const char *qemuimgarg[] = { NULL, "snapshot", "-d", NULL, NULL, NULL }; + char *snapFile = NULL; + int ret = -1; + int i; + qemuDomainObjPrivatePtr priv; + virDomainSnapshotObjPtr parentsnap = NULL; + + if (!virDomainObjIsActive(vm)) { + qemuimgarg[0] = qemuFindQemuImgBinary(); + if (qemuimgarg[0] == NULL) + /* qemuFindQemuImgBinary set the error */ + goto cleanup; + + qemuimgarg[3] = snap->def->name; + + for (i = 0; i < vm->def->ndisks; i++) { + /* FIXME: we also need to handle LVM here */ + /* FIXME: if we fail halfway through this loop, we are in an + * inconsistent state. I'm not quite sure what to do about that + */ + if (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + if (!vm->def->disks[i]->driverType || + STRNEQ(vm->def->disks[i]->driverType, "qcow2")) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Disk device '%s' does not support snapshotting"), + vm->def->disks[i]->info.alias); + goto cleanup; + } + + qemuimgarg[4] = vm->def->disks[i]->src; + + if (virRun(qemuimgarg, NULL) < 0) { + virReportSystemError(errno, + _("Failed to run '%s' to delete snapshot '%s' from disk '%s'"), + qemuimgarg[0], snap->def->name, + vm->def->disks[i]->src); + goto cleanup; + } + } + } + } + else { + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorDeleteSnapshot(priv->mon, snap->def->name) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto cleanup; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + + if (snap->def == vm->def->current_snapshot) { + if (snap->def->parent) { + parentsnap = virDomainSnapshotFindByName(&vm->snapshots, + snap->def->parent); + if (!parentsnap) { + qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("no domain snapshot parent with matching name '%s'"), + snap->def->parent); + goto cleanup; + } + + /* Now we set the new current_snapshot for the domain */ + vm->def->current_snapshot = parentsnap->def; + } + else + vm->def->current_snapshot = NULL; + } + + if (virAsprintf(&snapFile, "%s/%s/%s.snap", driver->snapshotDir, + vm->def->name, snap->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + unlink(snapFile); + + virDomainSnapshotObjListRemove(&vm->snapshots, snap); + + ret = 0; + +cleanup: + VIR_FREE(snapFile); + VIR_FREE(qemuimgarg[0]); + + return ret; +} + +static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + struct qemud_driver *driver = snapshot->domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + virDomainSnapshotObjPtr snap = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + qemuDriverLock(driver); + virUUIDFormat(snapshot->domain->uuid, uuidstr); + vm = virDomainFindByUUID(&driver->domains, snapshot->domain->uuid); + if (!vm) { + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); + if (!snap) { + qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("no domain snapshot with matching name '%s'"), + snapshot->name); + goto cleanup; + } + + if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD) { + if (qemuDomainSnapshotDiscard(driver, vm, snap, 0) < 0) + goto cleanup; + } + else if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE) { + if (qemuDomainSnapshotDiscard(driver, vm, snap, 1) < 0) + goto cleanup; + } + else if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_MERGE) { + /* FIXME: is this even possible with qemu? */ + if (virDomainSnapshotHasChildren(snap, &vm->snapshots)) { + /* FIXME: we should come up with a list of snapshots that would + * be merged/removed + */ + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("merging snapshot '%s' would also remove children snapshots"), + snap->def->name); + goto cleanup; + } + } + else if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE) { + /* FIXME: is this even possible with qemu? */ + } + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} static virDriver qemuDriver = { VIR_DRV_QEMU, @@ -10307,15 +11026,15 @@ static virDriver qemuDriver = { qemuDomainMigrateSetMaxDowntime, /* domainMigrateSetMaxDowntime */ qemuDomainEventRegisterAny, /* domainEventRegisterAny */ qemuDomainEventDeregisterAny, /* domainEventDeregisterAny */ - NULL, /* domainSnapshotCreateXML */ - NULL, /* domainSnapshotDumpXML */ - NULL, /* domainSnapshotNum */ - NULL, /* domainSnapshotListNames */ - NULL, /* domainSnapshotLookupByName */ - NULL, /* domainHasCurrentSnapshot */ - NULL, /* domainSnapshotCurrent */ - NULL, /* domainCreateFromSnapshot */ - NULL, /* domainSnapshotDelete */ + qemuDomainSnapshotCreateXML, /* domainSnapshotCreateXML */ + qemuDomainSnapshotDumpXML, /* domainSnapshotDumpXML */ + qemuDomainSnapshotNum, /* domainSnapshotNum */ + qemuDomainSnapshotListNames, /* domainSnapshotListNames */ + qemuDomainSnapshotLookupByName, /* domainSnapshotLookupByName */ + qemuDomainHasCurrentSnapshot, /* domainHasCurrentSnapshot */ + qemuDomainSnapshotCurrent, /* domainSnapshotCurrent */ + qemuDomainCreateFromSnapshot, /* domainCreateFromSnapshot */ + qemuDomainSnapshotDelete, /* domainSnapshotDelete */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 64779ac..7011821 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1491,3 +1491,42 @@ int qemuMonitorSetDrivePassphrase(qemuMonitorPtr mon, ret = qemuMonitorTextSetDrivePassphrase(mon, alias, passphrase); return ret; } + +int qemuMonitorCreateSnapshot(qemuMonitorPtr mon, const char *name) +{ + int ret; + + DEBUG("mon=%p, name=%s",mon,name); + + if (mon->json) + ret = qemuMonitorJSONCreateSnapshot(mon, name); + else + ret = qemuMonitorTextCreateSnapshot(mon, name); + return ret; +} + +int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name) +{ + int ret; + + DEBUG("mon=%p, name=%s",mon,name); + + if (mon->json) + ret = qemuMonitorJSONLoadSnapshot(mon, name); + else + ret = qemuMonitorTextLoadSnapshot(mon, name); + return ret; +} + +int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name) +{ + int ret; + + DEBUG("mon=%p, name=%s",mon,name); + + if (mon->json) + ret = qemuMonitorJSONDeleteSnapshot(mon, name); + else + ret = qemuMonitorTextDeleteSnapshot(mon, name); + return ret; +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 07773bd..21b8989 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -344,4 +344,8 @@ int qemuMonitorSetDrivePassphrase(qemuMonitorPtr mon, const char *alias, const char *passphrase); +int qemuMonitorCreateSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name); + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index eac3aca..1b00f22 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2156,3 +2156,27 @@ int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon, virJSONValueFree(reply); return ret; } + +int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED) +{ + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("savevm not suppported in JSON mode")); + return -1; +} + +int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED) +{ + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("loadvm not suppported in JSON mode")); + return -1; +} + +int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED) +{ + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("delvm not suppported in JSON mode")); + return -1; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index fc05153..e7baf84 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -175,4 +175,8 @@ int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon, const char *alias, const char *passphrase); +int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name); + #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index a199de7..5d7d124 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2290,3 +2290,129 @@ cleanup: VIR_FREE(safe_str); return ret; } + +int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name) +{ + char *cmd; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "savevm \"%s\"", name) < 0) { + virReportOOMError(); + return -1; + } + + if (qemuMonitorCommand(mon, cmd, &reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to take snapshot using command '%s'"), cmd); + goto cleanup; + } + + if (strstr(reply, "Error while creating snapshot") != NULL) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to take snapshot: %s"), reply); + goto cleanup; + } + + /* If there is no valid drive on which to store snapshots, this is the + * string that qemu will reply with */ + if (strstr(reply, "No block device can accept snapshots") != NULL) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("this domain does not have a device to take snapshots")); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(cmd); + VIR_FREE(reply); + return ret; +} + +int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name) +{ + char *cmd; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "loadvm \"%s\"", name) < 0) { + virReportOOMError(); + return -1; + } + + if (qemuMonitorCommand(mon, cmd, &reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to restore snapshot using command '%s'"), + cmd); + goto cleanup; + } + + if (strstr(reply, "No block device supports snapshots") != NULL) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("this domain does not have a device to load snapshots")); + goto cleanup; + } + else if (strstr(reply, "Could not find snapshot") != NULL) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("the snapshot '%s' does not exist, and was not loaded"), + name); + goto cleanup; + } + else if (strstr(reply, "Snapshots not supported on device") != NULL) { + /* FIXME: we really should parse out which device it was */ + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("snapshots are not supported on one of the devices")); + goto cleanup; + } + /* FIXME: there is also an "Error %d while activating snapshot" that we + * should handle + */ + + ret = 0; + +cleanup: + VIR_FREE(cmd); + VIR_FREE(reply); + return ret; +} + +int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name) +{ + char *cmd; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "delvm \"%s\"", name) < 0) { + virReportOOMError(); + return -1; + } + if (qemuMonitorCommand(mon, cmd, &reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to delete snapshot using command '%s'"), + cmd); + goto cleanup; + } + + if (strstr(reply, "No block device supports snapshots") != NULL) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("this domain does not have a device to delete snapshots")); + goto cleanup; + } + else if (strstr(reply, "Snapshots not supported on device") != NULL) { + /* FIXME: we really should parse out which device it was */ + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("snapshots are not supported on one of the devices")); + goto cleanup; + } + /* FIXME: there is also an "Error %d while deleting snapshot" that we + * should handle + */ + + ret = 0; + +cleanup: + VIR_FREE(cmd); + VIR_FREE(reply); + return ret; +} diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 4e1939c..fb7d08b 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -177,4 +177,8 @@ int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon, const char *alias, const char *passphrase); +int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name); + #endif /* QEMU_MONITOR_TEXT_H */ diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index c98de19..4ee56cd 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -83,7 +83,7 @@ static int testCompareXMLToArgvFiles(const char *xml, if (qemudBuildCommandLine(conn, &driver, vmdef, &monitor_chr, 0, flags, &argv, &qenv, - NULL, NULL, migrateFrom) < 0) + NULL, NULL, migrateFrom, 0) < 0) goto fail; len = 1; /* for trailing newline */ -- 1.6.6.1

2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/qemu/qemu_conf.c | 9 +- src/qemu/qemu_conf.h | 4 +- src/qemu/qemu_driver.c | 757 ++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_monitor.c | 39 +++ src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_monitor_json.c | 24 ++ src/qemu/qemu_monitor_json.h | 4 + src/qemu/qemu_monitor_text.c | 126 +++++++ src/qemu/qemu_monitor_text.h | 4 + tests/qemuxml2argvtest.c | 2 +- 10 files changed, 951 insertions(+), 22 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 5d0b211..1ebb29a 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c
+ +static int qemuDomainSnapshotWriteSnapshotMetadata(virDomainObjPtr vm, + virDomainSnapshotDefPtr def, + char *snapshotDir) +{ + int fd = -1; + char *newxml = NULL; + int ret = -1; + char *snapDir = NULL; + char *snapFile = NULL; + int err; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(vm->def->uuid, uuidstr); + newxml = virDomainSnapshotDefFormat(uuidstr, def); + if (newxml == NULL) { + virReportOOMError(); + return -1; + } + + if (virAsprintf(&snapDir, "%s/%s", snapshotDir, vm->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + err = virFileMakePath(snapDir); + if (err < 0) { + virReportSystemError(err, _("cannot create snapshot directory '%s'"), + snapDir); + goto cleanup; + } + + if (virAsprintf(&snapFile, "%s/%s.snap", snapDir, def->name) < 0) {
Maybe use .xml instead of .snap, because the file will contain XML.
+ virReportOOMError(); + goto cleanup; + } + fd = open(snapFile, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); + if (fd < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to create snapshot file '%s'"), snapFile); + goto cleanup; + } + if (safewrite(fd, newxml, strlen(newxml)) != strlen(newxml)) { + virReportSystemError(errno, _("Failed to write snapshot data to %s"), + snapFile); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(snapFile); + VIR_FREE(snapDir); + VIR_FREE(newxml); + if (fd != -1) + close(fd); + return ret; +} +
ACK. Matthias

--- src/vbox/vbox_tmpl.c | 983 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 974 insertions(+), 9 deletions(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 8163d22..5827446 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -5007,6 +5007,971 @@ static int vboxDomainDetachDeviceFlags(virDomainPtr dom, const char *xml, return vboxDomainDetachDevice(dom, xml); } +static int +vboxDomainSnapshotGetAll(virDomainPtr dom, + IMachine *machine, + ISnapshot ***snapshots) +{ + ISnapshot **list = NULL; + PRUint32 count; + nsresult rc; + unsigned int next; + unsigned int top; + + rc = machine->vtbl->GetSnapshotCount(machine, &count); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get snapshot count for domain %s"), + dom->name); + goto error; + } + + if (count == 0) + goto out; + + if (VIR_ALLOC_N(list, count) < 0) { + virReportOOMError(); + goto error; + } + + rc = machine->vtbl->GetSnapshot(machine, NULL, list); + if (NS_FAILED(rc) || !list[0]) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get root snapshot for domain %s"), + dom->name); + goto error; + } + + /* BFS walk through snapshot tree */ + top = 1; + for (next = 0; next < count; next++) { + PRUint32 childrenCount = 0; + ISnapshot **children = NULL; + unsigned int i; + + if (!list[next]) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected number of snapshots < %u"), count); + goto error; + } + + rc = list[next]->vtbl->GetChildren(list[next], &childrenCount, + &children); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("could not get children snapshots")); + goto error; + } + for (i = 0; i < childrenCount; i++) { + if (!children[i]) + continue; + if (top == count) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected number of snapshots > %u"), count); + goto error; + } + list[top++] = children[i]; + } + } + +out: + *snapshots = list; + return count; + +error: + if (list) { + for (next = 0; next < count; next++) + VBOX_RELEASE(list[next]); + } + VIR_FREE(list); + + return -1; +} + +static ISnapshot * +vboxDomainSnapshotGet(vboxGlobalData *data, + virDomainPtr dom, + IMachine *machine, + const char *name) +{ + ISnapshot **snapshots = NULL; + ISnapshot *snapshot = NULL; + nsresult rc; + int count = 0; + int i; + + if ((count = vboxDomainSnapshotGetAll(dom, machine, &snapshots)) < 0) + goto cleanup; + + for (i = 0; i < count; i++) { + PRUnichar *nameUtf16; + char *nameUtf8; + + rc = snapshots[i]->vtbl->GetName(snapshots[i], &nameUtf16); + if (NS_FAILED(rc) || !nameUtf16) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("could not get snapshot name")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(nameUtf16, &nameUtf8); + VBOX_UTF16_FREE(nameUtf16); + if (STREQ(name, nameUtf8)) + snapshot = snapshots[i]; + VBOX_UTF8_FREE(nameUtf8); + + if (snapshot) + break; + } + + if (!snapshot) { + vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, + _("domain %s has no snapshots with name %s"), + dom->name, name); + goto cleanup; + } + +cleanup: + if (count > 0) { + for (i = 0; i < count; i++) { + if (snapshots[i] != snapshot) + VBOX_RELEASE(snapshots[i]); + } + } + VIR_FREE(snapshots); + return snapshot; +} + +static virDomainSnapshotPtr +vboxDomainSnapshotCreateXML(virDomainPtr dom, + const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED) +{ + VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL); + virDomainSnapshotDefPtr def = NULL; + vboxIID *domiid = NULL; + IMachine *machine = NULL; + IConsole *console = NULL; + IProgress *progress = NULL; + ISnapshot *snapshot = NULL; + PRUnichar *name = NULL; + PRUnichar *description = NULL; + PRUint32 state; + nsresult rc; +#if VBOX_API_VERSION == 2002 + nsresult result; +#else + PRInt32 result; +#endif + + if (!(def = virDomainSnapshotDefParseString(xmlDesc, 1))) + goto cleanup; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(domiid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, domiid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, domiid, &machine); + if (NS_FAILED(rc) || !machine) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + rc = machine->vtbl->GetState(machine, &state); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get domain state")); + goto cleanup; + } + + if ((state >= MachineState_FirstOnline) + && (state <= MachineState_LastOnline)) { + rc = data->vboxObj->vtbl->OpenExistingSession(data->vboxObj, + data->vboxSession, + domiid); + } else { + rc = data->vboxObj->vtbl->OpenSession(data->vboxObj, + data->vboxSession, + domiid); + } + if (NS_SUCCEEDED(rc)) + rc = data->vboxSession->vtbl->GetConsole(data->vboxSession, &console); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not open VirtualBox session with domain %s"), + dom->name); + goto cleanup; + } + + VBOX_UTF8_TO_UTF16(def->name, &name); + if (!name) { + virReportOOMError(); + goto cleanup; + } + + if (def->description) { + VBOX_UTF8_TO_UTF16(def->description, &description); + if (!description) { + virReportOOMError(); + goto cleanup; + } + } + + rc = console->vtbl->TakeSnapshot(console, name, description, &progress); + if (NS_FAILED(rc) || !progress) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not take snapshot of domain %s"), dom->name); + goto cleanup; + } + + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &result); + if (NS_FAILED(result)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not take snapshot of domain %s"), dom->name); + goto cleanup; + } + + rc = machine->vtbl->GetCurrentSnapshot(machine, &snapshot); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get current snapshot of domain %s"), + dom->name); + goto cleanup; + } + + ret = virGetDomainSnapshot(dom, def->name); + +cleanup: + VBOX_RELEASE(progress); + VBOX_UTF16_FREE(description); + VBOX_UTF16_FREE(name); + VBOX_RELEASE(console); + data->vboxSession->vtbl->Close(data->vboxSession); + VBOX_RELEASE(machine); + vboxIIDFree(domiid); + virDomainSnapshotDefFree(def); + return ret; +} + +static char * +vboxDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, char *, NULL); + vboxIID *domiid = NULL; + IMachine *machine = NULL; + ISnapshot *snap = NULL; + ISnapshot *parent = NULL; + nsresult rc; + virDomainSnapshotDefPtr def = NULL; + PRUnichar *str16; + char *str8; + PRInt64 timestamp; + PRBool online = PR_FALSE; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(domiid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, domiid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, domiid, &machine); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) + goto cleanup; + + if (VIR_ALLOC(def) < 0 + || !(def->name = strdup(snapshot->name))) + goto no_memory; + + rc = snap->vtbl->GetDescription(snap, &str16); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get description of snapshot %s"), + snapshot->name); + goto cleanup; + } + if (str16) { + VBOX_UTF16_TO_UTF8(str16, &str8); + VBOX_UTF16_FREE(str16); + def->description = strdup(str8); + VBOX_UTF8_FREE(str8); + if (!def->description) + goto no_memory; + } + + rc = snap->vtbl->GetTimeStamp(snap, ×tamp); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get creation time of snapshot %s"), + snapshot->name); + goto cleanup; + } + /* timestamp is in milliseconds while creationTime in seconds */ + def->creationTime = timestamp / 1000; + + rc = snap->vtbl->GetParent(snap, &parent); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get parent of snapshot %s"), + snapshot->name); + goto cleanup; + } + if (parent) { + rc = parent->vtbl->GetName(parent, &str16); + if (NS_FAILED(rc) || !str16) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get name of parent of snapshot %s"), + snapshot->name); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(str16, &str8); + VBOX_UTF16_FREE(str16); + def->parent = strdup(str8); + VBOX_UTF8_FREE(str8); + if (!def->parent) + goto no_memory; + } + + rc = snap->vtbl->GetOnline(snap, &online); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get online state of snapshot %s"), + snapshot->name); + goto cleanup; + } + if (online == PR_TRUE) + def->state = VIR_DOMAIN_RUNNING; + else + def->state = VIR_DOMAIN_SHUTOFF; + + virUUIDFormat(dom->uuid, uuidstr); + ret = virDomainSnapshotDefFormat(uuidstr, def); + +cleanup: + virDomainSnapshotDefFree(def); + VBOX_RELEASE(parent); + VBOX_RELEASE(snap); + VBOX_RELEASE(machine); + vboxIIDFree(domiid); + return ret; + +no_memory: + virReportOOMError(); + goto cleanup; +} + +static int +vboxDomainSnapshotNum(virDomainPtr dom, + unsigned int flags ATTRIBUTE_UNUSED) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID *iid = NULL; + IMachine *machine = NULL; + nsresult rc; + PRUint32 snapshotCount; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(iid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, iid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, iid, &machine); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + rc = machine->vtbl->GetSnapshotCount(machine, &snapshotCount); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get snapshot count for domain %s"), + dom->name); + goto cleanup; + } + + ret = snapshotCount; + +cleanup: + VBOX_RELEASE(machine); + vboxIIDFree(iid); + return ret; +} + +static int +vboxDomainSnapshotListNames(virDomainPtr dom, + char **names, + int nameslen, + unsigned int flags ATTRIBUTE_UNUSED) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID *iid = NULL; + IMachine *machine = NULL; + nsresult rc; + ISnapshot **snapshots = NULL; + int count = 0; + int i; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(iid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, iid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, iid, &machine); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + if ((count = vboxDomainSnapshotGetAll(dom, machine, &snapshots)) < 0) + goto cleanup; + + for (i = 0; i < nameslen; i++) { + PRUnichar *nameUtf16; + char *name; + + if (i >= count) + break; + + rc = snapshots[i]->vtbl->GetName(snapshots[i], &nameUtf16); + if (NS_FAILED(rc) || !nameUtf16) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("could not get snapshot name")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(nameUtf16, &name); + VBOX_UTF16_FREE(nameUtf16); + names[i] = strdup(name); + VBOX_UTF8_FREE(name); + if (!names[i]) { + virReportOOMError(); + goto cleanup; + } + } + + if (count <= nameslen) + ret = count; + else + ret = nameslen; + +cleanup: + if (count > 0) { + for (i = 0; i < count; i++) + VBOX_RELEASE(snapshots[i]); + } + VIR_FREE(snapshots); + VBOX_RELEASE(machine); + vboxIIDFree(iid); + return ret; +} + +static virDomainSnapshotPtr +vboxDomainSnapshotLookupByName(virDomainPtr dom, + const char *name, + unsigned int flags ATTRIBUTE_UNUSED) +{ + VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL); + vboxIID *iid = NULL; + IMachine *machine = NULL; + ISnapshot *snapshot = NULL; + nsresult rc; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(iid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, iid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, iid, &machine); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + if (!(snapshot = vboxDomainSnapshotGet(data, dom, machine, name))) + goto cleanup; + + ret = virGetDomainSnapshot(dom, name); + +cleanup: + VBOX_RELEASE(snapshot); + VBOX_RELEASE(machine); + vboxIIDFree(iid); + return ret; +} + +static int +vboxDomainHasCurrentSnapshot(virDomainPtr dom, + unsigned int flags ATTRIBUTE_UNUSED) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID *iid = NULL; + IMachine *machine = NULL; + ISnapshot *snapshot = NULL; + nsresult rc; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(iid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, iid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, iid, &machine); + if (NS_FAILED(rc) || !machine) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + rc = machine->vtbl->GetCurrentSnapshot(machine, &snapshot); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get current snapshot")); + goto cleanup; + } + + if (snapshot) + ret = 1; + else + ret = 0; + +cleanup: + VBOX_RELEASE(machine); + vboxIIDFree(iid); + return ret; +} + +static virDomainSnapshotPtr +vboxDomainSnapshotCurrent(virDomainPtr dom, + unsigned int flags ATTRIBUTE_UNUSED) +{ + VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL); + vboxIID *iid = NULL; + IMachine *machine = NULL; + ISnapshot *snapshot = NULL; + PRUnichar *nameUtf16 = NULL; + char *name = NULL; + nsresult rc; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(iid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, iid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, iid, &machine); + if (NS_FAILED(rc) || !machine) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + rc = machine->vtbl->GetCurrentSnapshot(machine, &snapshot); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get current snapshot")); + goto cleanup; + } + + if (!snapshot) { + vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, "%s", + _("domain has no snapshots")); + goto cleanup; + } + + rc = snapshot->vtbl->GetName(snapshot, &nameUtf16); + if (NS_FAILED(rc) || !nameUtf16) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get current snapshot name")); + goto cleanup; + } + + VBOX_UTF16_TO_UTF8(nameUtf16, &name); + if (!name) { + virReportOOMError(); + goto cleanup; + } + + ret = virGetDomainSnapshot(dom, name); + +cleanup: + VBOX_UTF8_FREE(name); + VBOX_UTF16_FREE(nameUtf16); + VBOX_RELEASE(snapshot); + VBOX_RELEASE(machine); + vboxIIDFree(iid); + return ret; +} + +#if VBOX_API_VERSION < 3001 +static int +vboxDomainSnapshotRestore(virDomainPtr dom, + IMachine *machine, + ISnapshot *snapshot) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID *iid = NULL; + nsresult rc; + + rc = snapshot->vtbl->GetId(snapshot, &iid); + if (NS_FAILED(rc) || !iid) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get snapshot UUID")); + goto cleanup; + } + + rc = machine->vtbl->SetCurrentSnapshot(machine, iid); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not restore snapshot for domain %s"), dom->name); + goto cleanup; + } + + ret = 0; + +cleanup: + vboxIIDUnalloc(iid); + return ret; +} +#else +static int +vboxDomainSnapshotRestore(virDomainPtr dom, + IMachine *machine, + ISnapshot *snapshot) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + IConsole *console = NULL; + IProgress *progress = NULL; + PRUint32 state; + nsresult rc; + PRInt32 result; + vboxIID *domiid; + + rc = machine->vtbl->GetId(machine, &domiid); + if (NS_FAILED(rc) || !domiid) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get domain UUID")); + goto cleanup; + } + + rc = machine->vtbl->GetState(machine, &state); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get domain state")); + goto cleanup; + } + + if ((state >= MachineState_FirstOnline) + && (state <= MachineState_LastOnline)) { + vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, + _("domain %s is already running"), dom->name); + goto cleanup; + } + + rc = data->vboxObj->vtbl->OpenSession(data->vboxObj, data->vboxSession, + domiid); + if (NS_SUCCEEDED(rc)) + rc = data->vboxSession->vtbl->GetConsole(data->vboxSession, &console); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not open VirtualBox session with domain %s"), + dom->name); + goto cleanup; + } + + rc = console->vtbl->RestoreSnapshot(console, snapshot, &progress); + if (NS_FAILED(rc) || !progress) { + if (rc == VBOX_E_INVALID_VM_STATE) { + vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, "%s", + _("cannot restore domain snapshot for running domain")); + } else { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not restore snapshot for domain %s"), + dom->name); + } + goto cleanup; + } + + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &result); + if (NS_FAILED(result)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not restore snapshot for domain %s"), dom->name); + goto cleanup; + } + + ret = 0; + +cleanup: + VBOX_RELEASE(progress); + VBOX_RELEASE(console); + data->vboxSession->vtbl->Close(data->vboxSession); + return ret; +} +#endif + +static int +vboxDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID *domiid = NULL; + IMachine *machine = NULL; + ISnapshot *newSnapshot = NULL; + ISnapshot *prevSnapshot = NULL; + nsresult rc; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(domiid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + vboxIIDFromUUID(dom->uuid, domiid); + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, domiid, &machine); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + newSnapshot = vboxDomainSnapshotGet(data, dom, machine, snapshot->name); + if (!newSnapshot) + goto cleanup; + + rc = machine->vtbl->GetCurrentSnapshot(machine, &prevSnapshot); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not get current snapshot of domain %s"), + dom->name); + goto cleanup; + } + + if (vboxDomainSnapshotRestore(dom, machine, newSnapshot)) + goto cleanup; + + ret = vboxDomainCreate(dom); + if (!ret) + vboxDomainSnapshotRestore(dom, machine, prevSnapshot); + +cleanup: + VBOX_RELEASE(prevSnapshot); + VBOX_RELEASE(newSnapshot); + vboxIIDUnalloc(domiid); + return ret; +} + +static int +vboxDomainSnapshotDeleteSingle(vboxGlobalData *data, + IConsole *console, + ISnapshot *snapshot) +{ + IProgress *progress = NULL; + vboxIID *iid = NULL; + int ret = -1; + nsresult rc; +#if VBOX_API_VERSION == 2002 + nsresult result; +#else + PRInt32 result; +#endif + + rc = snapshot->vtbl->GetId(snapshot, &iid); + if (NS_FAILED(rc) || !iid) { + vboxError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get snapshot UUID")); + goto cleanup; + } + +#if VBOX_API_VERSION < 3001 + rc = console->vtbl->DiscardSnapshot(console, iid, &progress); +#else + rc = console->vtbl->DeleteSnapshot(console, iid, &progress); +#endif + if (NS_FAILED(rc) || !progress) { + if (rc == VBOX_E_INVALID_VM_STATE) { + vboxError(NULL, VIR_ERR_OPERATION_INVALID, "%s", + _("cannot delete domain snapshot for running domain")); + } else { + vboxError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not delete snapshot")); + } + goto cleanup; + } + + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &result); + if (NS_FAILED(result)) { + vboxError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not delete snapshot")); + goto cleanup; + } + + ret = 0; + +cleanup: + VBOX_RELEASE(progress); + vboxIIDUnalloc(iid); + return ret; +} + +static int +vboxDomainSnapshotDeleteTree(vboxGlobalData *data, + IConsole *console, + ISnapshot *snapshot) +{ + PRUint32 childrenCount = 0; + ISnapshot **children = NULL; + int ret = -1; + nsresult rc; + unsigned int i; + + rc = snapshot->vtbl->GetChildren(snapshot, &childrenCount, &children); + if (NS_FAILED(rc)) { + vboxError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get children snapshots")); + goto cleanup; + } + + if (childrenCount > 0) { + for (i = 0; i < childrenCount; i++) { + if (vboxDomainSnapshotDeleteTree(data, console, children[i])) + goto cleanup; + } + } + + ret = vboxDomainSnapshotDeleteSingle(data, console, snapshot); + +cleanup: + for (i = 0; i < childrenCount; i++) + VBOX_RELEASE(children[i]); + return ret; +} + +static int +vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID *domiid = NULL; + IMachine *machine = NULL; + ISnapshot *snap = NULL; + IConsole *console = NULL; + PRUint32 childrenCount = 0; + ISnapshot **children = NULL; + unsigned int i; + nsresult rc; + +#if VBOX_API_VERSION == 2002 + if (VIR_ALLOC(domiid) < 0) { + virReportOOMError(); + goto cleanup; + } +#endif + + if (!(flags & (VIR_DOMAIN_SNAPSHOT_DELETE_MERGE | + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE | + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD | + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE))) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("none of the supported flags passed")); + return -1; + } + + vboxIIDFromUUID(dom->uuid, domiid); + + rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, domiid, &machine); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name); + if (!snap) + goto cleanup; + + rc = snap->vtbl->GetChildren(snap, &childrenCount, &children); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("could not get children snapshots")); + goto cleanup; + } + for (i = 0; i < childrenCount; i++) + VBOX_RELEASE(children[i]); + + if ((flags & (VIR_DOMAIN_SNAPSHOT_DELETE_MERGE | + VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE)) + && childrenCount == 0) { + vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, "%s", + _("operation would discard snapshot contents")); + goto cleanup; + } + + if ((flags & VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD) + && childrenCount > 0) { + vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, "%s", + _("operation would discard additional snapshots")); + goto cleanup; + } + + rc = data->vboxObj->vtbl->OpenSession(data->vboxObj, data->vboxSession, + domiid); + if (NS_SUCCEEDED(rc)) + rc = data->vboxSession->vtbl->GetConsole(data->vboxSession, &console); + if (NS_FAILED(rc)) { + vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, + _("could not open VirtualBox session with domain %s"), + dom->name); + goto cleanup; + } + + if ((flags & (VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD | + VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE))) + ret = vboxDomainSnapshotDeleteTree(data, console, snap); + else + ret = vboxDomainSnapshotDeleteSingle(data, console, snap); + +cleanup: + VBOX_RELEASE(console); + VBOX_RELEASE(snap); + vboxIIDUnalloc(domiid); + data->vboxSession->vtbl->Close(data->vboxSession); + return ret; +} + #if VBOX_API_VERSION == 2002 /* No Callback support for VirtualBox 2.2.* series */ #else /* !(VBOX_API_VERSION == 2002) */ @@ -7184,15 +8149,15 @@ virDriver NAME(Driver) = { vboxDomainEventRegisterAny, /* domainEventRegisterAny */ vboxDomainEventDeregisterAny, /* domainEventDeregisterAny */ #endif - NULL, /* domainSnapshotCreateXML */ - NULL, /* domainSnapshotDumpXML */ - NULL, /* domainSnapshotNum */ - NULL, /* domainSnapshotListNames */ - NULL, /* domainSnapshotLookupByName */ - NULL, /* domainHasCurrentSnapshot */ - NULL, /* domainSnapshotCurrent */ - NULL, /* domainCreateFromSnapshot */ - NULL, /* domainSnapshotDelete */ + vboxDomainSnapshotCreateXML, /* domainSnapshotCreateXML */ + vboxDomainSnapshotDumpXML, /* domainSnapshotDumpXML */ + vboxDomainSnapshotNum, /* domainSnapshotNum */ + vboxDomainSnapshotListNames, /* domainSnapshotListNames */ + vboxDomainSnapshotLookupByName, /* domainSnapshotLookupByName */ + vboxDomainHasCurrentSnapshot, /* domainHasCurrentSnapshot */ + vboxDomainSnapshotCurrent, /* domainSnapshotCurrent */ + vboxDomainCreateFromSnapshot, /* domainCreateFromSnapshot */ + vboxDomainSnapshotDelete, /* domainSnapshotDelete */ }; virNetworkDriver NAME(NetworkDriver) = { -- 1.6.6.1

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- tools/virsh.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 367 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 5c56fa6..1eb4bef 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -49,6 +49,7 @@ #include "console.h" #include "util.h" #include "memory.h" +#include "ignore-value.h" static char *progname; @@ -8137,6 +8138,364 @@ cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } /* + * "snapshot-create" command + */ +static const vshCmdInfo info_snapshot_create[] = { + {"help", N_("Create a snapshot")}, + {"desc", N_("Snapshot create")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_snapshot_create[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"file", VSH_OT_DATA, 0, N_("domain snapshot XML")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = FALSE; + char *from; + char *buffer = NULL; + virDomainSnapshotPtr snapshot; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + from = vshCommandOptString(cmd, "file", NULL); + if (from == NULL) + buffer = strdup("<domainsnapshot/>"); + else + ignore_value(virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer)); + if (buffer == NULL) + goto cleanup; + + snapshot = virDomainSnapshotCreateXML(dom, buffer, 0); + if (snapshot == NULL) + goto cleanup; + + virDomainSnapshotFree(snapshot); + + ret = TRUE; + +cleanup: + VIR_FREE(buffer); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* + * "snapshot-current" command + */ +static const vshCmdInfo info_snapshot_current[] = { + {"help", N_("Get the current snapshot")}, + {"desc", N_("Get the current snapshot")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_snapshot_current[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSnapshotCurrent(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = FALSE; + int current; + virDomainSnapshotPtr snapshot = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + current = virDomainHasCurrentSnapshot(dom, 0); + if (current < 0) + goto cleanup; + else if (current) { + char *xml; + + if (!(snapshot = virDomainSnapshotCurrent(dom, 0))) + goto cleanup; + + xml = virDomainSnapshotGetXMLDesc(snapshot, 0); + if (!xml) + goto cleanup; + + vshPrint(ctl, "%s", xml); + VIR_FREE(xml); + } + + ret = TRUE; + +cleanup: + if (snapshot) + virDomainSnapshotFree(snapshot); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* + * "snapshot-list" command + */ +static const vshCmdInfo info_snapshot_list[] = { + {"help", N_("List snapshots for a domain")}, + {"desc", N_("Snapshot List")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_snapshot_list[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSnapshotList(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = FALSE; + int numsnaps; + char **names = NULL; + int actual; + int i; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + numsnaps = virDomainSnapshotNum(dom, 0); + + if (numsnaps < 0) + goto cleanup; + + if (numsnaps) { + if (VIR_ALLOC_N(names, numsnaps) < 0) + goto cleanup; + + actual = virDomainSnapshotListNames(dom, names, numsnaps, 0); + if (actual < 0) + goto cleanup; + + for (i = 0; i < actual; i++) + vshPrint(ctl, "%d: %s\n", i, names[i]); + } + + ret = TRUE; + +cleanup: + VIR_FREE(names); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* + * "snapshot-dumpxml" command + */ +static const vshCmdInfo info_snapshot_dumpxml[] = { + {"help", N_("Dump XML for a domain snapshot")}, + {"desc", N_("Snapshot Dump XML")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_snapshot_dumpxml[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("snapshot name")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = FALSE; + char *name; + virDomainSnapshotPtr snapshot = NULL; + char *xml = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + name = vshCommandOptString(cmd, "name", NULL); + if (name == NULL) + goto cleanup; + + snapshot = virDomainSnapshotLookupByName(dom, name, 0); + if (snapshot == NULL) + goto cleanup; + + xml = virDomainSnapshotGetXMLDesc(snapshot, 0); + if (!xml) + goto cleanup; + + printf("%s", xml); + + ret = TRUE; + +cleanup: + VIR_FREE(xml); + if (snapshot) + virDomainSnapshotFree(snapshot); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* + * "start-from-snapshot" command + */ +static const vshCmdInfo info_start_from_snapshot[] = { + {"help", N_("Start a domain from a snapshot")}, + {"desc", N_("Domain start from snapshot")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_start_from_snapshot[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("snapshot name")}, +#ifndef WIN32 + {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")}, +#endif + {NULL, 0, 0, NULL} +}; + +static int +cmdDomainStartFromSnapshot(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = FALSE; + char *name; + virDomainSnapshotPtr snapshot = NULL; +#ifndef WIN32 + int console = vshCommandOptBool(cmd, "console"); +#endif + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + name = vshCommandOptString(cmd, "name", NULL); + if (name == NULL) + goto cleanup; + + snapshot = virDomainSnapshotLookupByName(dom, name, 0); + if (snapshot == NULL) + goto cleanup; + + if (virDomainCreateFromSnapshot(snapshot, 0) < 0) + goto cleanup; + +#ifndef WIN32 + if (console) + cmdRunConsole(ctl, dom); +#endif + + ret = TRUE; + +cleanup: + if (snapshot) + virDomainSnapshotFree(snapshot); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* + * "snapshot-delete" command + */ +static const vshCmdInfo info_snapshot_delete[] = { + {"help", N_("Delete a domain snapshot")}, + {"desc", N_("Snapshot Delete")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_snapshot_delete[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("snapshot name")}, + {"merge", VSH_OT_BOOL, 0, N_("merge snapshot")}, + {"delete", VSH_OT_BOOL, 0, N_("delete snapshot")}, + {"merge-force", VSH_OT_BOOL, 0, N_("merge snapshot, discard children")}, + {"delete-force", VSH_OT_BOOL, 0, N_("delete snapshot and all children")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSnapshotDelete(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = FALSE; + char *name; + virDomainSnapshotPtr snapshot = NULL; + unsigned int flags = 0; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + name = vshCommandOptString(cmd, "name", NULL); + if (name == NULL) + goto cleanup; + + if (vshCommandOptBool(cmd, "merge")) + flags |= VIR_DOMAIN_SNAPSHOT_DELETE_MERGE; + if (vshCommandOptBool(cmd, "delete")) + flags |= VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD; + if (vshCommandOptBool(cmd, "merge-force")) + flags |= VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE; + if (vshCommandOptBool(cmd, "delete-force")) + flags |= VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE; + + /* FIXME: throw an error if more than one flag is passed */ + + snapshot = virDomainSnapshotLookupByName(dom, name, 0); + if (snapshot == NULL) + goto cleanup; + + if (virDomainSnapshotDelete(snapshot, flags) < 0) + goto cleanup; + + ret = TRUE; + +cleanup: + if (snapshot) + virDomainSnapshotFree(snapshot); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* * Commands */ static const vshCmdDef commands[] = { @@ -8288,6 +8647,14 @@ static const vshCmdDef commands[] = { {"vcpupin", cmdVcpupin, opts_vcpupin, info_vcpupin}, {"version", cmdVersion, NULL, info_version}, {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay}, + + {"snapshot-create", cmdSnapshotCreate, opts_snapshot_create, info_snapshot_create}, + {"snapshot-current", cmdSnapshotCurrent, opts_snapshot_current, info_snapshot_current}, + {"snapshot-delete", cmdSnapshotDelete, opts_snapshot_delete, info_snapshot_delete}, + {"snapshot-dumpxml", cmdSnapshotDumpXML, opts_snapshot_dumpxml, info_snapshot_dumpxml}, + {"snapshot-list", cmdSnapshotList, opts_snapshot_list, info_snapshot_list}, + {"start-from-snapshot", cmdDomainStartFromSnapshot, opts_start_from_snapshot, info_start_from_snapshot}, + {NULL, NULL, NULL, NULL} }; -- 1.6.6.1

2010/4/2 Chris Lalancette <clalance@redhat.com>:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- tools/virsh.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 367 insertions(+), 0 deletions(-)
ACK. Matthias

None of the other flags made sense anymore, so remove them all. DELETE_CHILDREN means delete *this* snapshot and any children of this snapshot. Signed-off-by: Chris Lalancette <clalance@redhat.com> --- include/libvirt/libvirt.h.in | 5 +-- src/qemu/qemu_driver.c | 93 +++++++++++++++++++++++------------------ src/vbox/vbox_tmpl.c | 27 +------------ tools/virsh.c | 17 +------ 4 files changed, 57 insertions(+), 85 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index a8a97e3..9aec351 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1912,10 +1912,7 @@ int virDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot, /* Deactivate a snapshot */ typedef enum { - VIR_DOMAIN_SNAPSHOT_DELETE_MERGE = (1 << 0), - VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE = (1 << 1), - VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD = (1 << 2), - VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE = (1 << 3), + VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN = (1 << 0), } virDomainSnapshotDeleteFlags; int virDomainSnapshotDelete(virDomainSnapshotPtr snapshot, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7df15a6..65b499c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10792,15 +10792,14 @@ cleanup: static int qemuDomainSnapshotDiscard(struct qemud_driver *driver, virDomainObjPtr vm, - virDomainSnapshotObjPtr snap, - int delete_children ATTRIBUTE_UNUSED) + virDomainSnapshotObjPtr snap) { const char *qemuimgarg[] = { NULL, "snapshot", "-d", NULL, NULL, NULL }; char *snapFile = NULL; int ret = -1; int i; qemuDomainObjPrivatePtr priv; - virDomainSnapshotObjPtr parentsnap = NULL; + virDomainSnapshotObjPtr parentsnap; if (!virDomainObjIsActive(vm)) { qemuimgarg[0] = qemuFindQemuImgBinary(); @@ -10812,26 +10811,22 @@ static int qemuDomainSnapshotDiscard(struct qemud_driver *driver, for (i = 0; i < vm->def->ndisks; i++) { /* FIXME: we also need to handle LVM here */ - /* FIXME: if we fail halfway through this loop, we are in an - * inconsistent state. I'm not quite sure what to do about that - */ if (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) { if (!vm->def->disks[i]->driverType || STRNEQ(vm->def->disks[i]->driverType, "qcow2")) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - _("Disk device '%s' does not support snapshotting"), - vm->def->disks[i]->info.alias); - goto cleanup; + /* we continue on even in the face of error, since other + * disks in this VM may have this snapshot in place + */ + continue; } qemuimgarg[4] = vm->def->disks[i]->src; if (virRun(qemuimgarg, NULL) < 0) { - virReportSystemError(errno, - _("Failed to run '%s' to delete snapshot '%s' from disk '%s'"), - qemuimgarg[0], snap->def->name, - vm->def->disks[i]->src); - goto cleanup; + /* we continue on even in the face of error, since other + * disks in this VM may have this snapshot in place + */ + continue; } } } @@ -10839,10 +10834,8 @@ static int qemuDomainSnapshotDiscard(struct qemud_driver *driver, else { priv = vm->privateData; qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorDeleteSnapshot(priv->mon, snap->def->name) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - goto cleanup; - } + /* we continue on even in the face of error */ + qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); qemuDomainObjExitMonitorWithDriver(driver, vm); } @@ -10882,6 +10875,36 @@ cleanup: return ret; } +struct snap_remove { + struct qemud_driver *driver; + virDomainObjPtr vm; + char *parent; + int err; +}; + +static void qemuDomainSnapshotDiscardChildren(void *payload, + const char *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainSnapshotObjPtr snap = payload; + struct snap_remove *curr = data; + struct snap_remove this; + + if (snap->def->parent && STREQ(snap->def->parent, curr->parent)) { + this.driver = curr->driver; + this.vm = curr->vm; + this.parent = snap->def->name; + this.err = 0; + virHashForEach(curr->vm->snapshots.objs, + qemuDomainSnapshotDiscardChildren, &this); + + if (this.err) + curr->err = this.err; + else + this.err = qemuDomainSnapshotDiscard(curr->driver, curr->vm, snap); + } +} + static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) { @@ -10890,6 +10913,7 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, int ret = -1; virDomainSnapshotObjPtr snap = NULL; char uuidstr[VIR_UUID_STRING_BUFLEN]; + struct snap_remove rem; qemuDriverLock(driver); virUUIDFormat(snapshot->domain->uuid, uuidstr); @@ -10908,31 +10932,18 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, goto cleanup; } - if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD) { - if (qemuDomainSnapshotDiscard(driver, vm, snap, 0) < 0) + if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN) { + rem.driver = driver; + rem.vm = vm; + rem.parent = snap->def->name; + rem.err = 0; + virHashForEach(vm->snapshots.objs, qemuDomainSnapshotDiscardChildren, + &rem); + if (rem.err < 0) goto cleanup; } - else if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE) { - if (qemuDomainSnapshotDiscard(driver, vm, snap, 1) < 0) - goto cleanup; - } - else if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_MERGE) { - /* FIXME: is this even possible with qemu? */ - if (virDomainSnapshotHasChildren(snap, &vm->snapshots)) { - /* FIXME: we should come up with a list of snapshots that would - * be merged/removed - */ - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("merging snapshot '%s' would also remove children snapshots"), - snap->def->name); - goto cleanup; - } - } - else if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE) { - /* FIXME: is this even possible with qemu? */ - } - ret = 0; + ret = qemuDomainSnapshotDiscard(driver, vm, snap); cleanup: if (vm) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 5827446..185c941 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -5901,15 +5901,6 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, } #endif - if (!(flags & (VIR_DOMAIN_SNAPSHOT_DELETE_MERGE | - VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE | - VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD | - VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE))) { - vboxError(dom->conn, VIR_ERR_INTERNAL_ERROR, "%s", - _("none of the supported flags passed")); - return -1; - } - vboxIIDFromUUID(dom->uuid, domiid); rc = data->vboxObj->vtbl->GetMachine(data->vboxObj, domiid, &machine); @@ -5932,21 +5923,6 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, for (i = 0; i < childrenCount; i++) VBOX_RELEASE(children[i]); - if ((flags & (VIR_DOMAIN_SNAPSHOT_DELETE_MERGE | - VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE)) - && childrenCount == 0) { - vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, "%s", - _("operation would discard snapshot contents")); - goto cleanup; - } - - if ((flags & VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD) - && childrenCount > 0) { - vboxError(dom->conn, VIR_ERR_OPERATION_INVALID, "%s", - _("operation would discard additional snapshots")); - goto cleanup; - } - rc = data->vboxObj->vtbl->OpenSession(data->vboxObj, data->vboxSession, domiid); if (NS_SUCCEEDED(rc)) @@ -5958,8 +5934,7 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, goto cleanup; } - if ((flags & (VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD | - VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE))) + if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN) ret = vboxDomainSnapshotDeleteTree(data, console, snap); else ret = vboxDomainSnapshotDeleteSingle(data, console, snap); diff --git a/tools/virsh.c b/tools/virsh.c index 1eb4bef..2a03442 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -8439,10 +8439,7 @@ static const vshCmdInfo info_snapshot_delete[] = { static const vshCmdOptDef opts_snapshot_delete[] = { {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("snapshot name")}, - {"merge", VSH_OT_BOOL, 0, N_("merge snapshot")}, - {"delete", VSH_OT_BOOL, 0, N_("delete snapshot")}, - {"merge-force", VSH_OT_BOOL, 0, N_("merge snapshot, discard children")}, - {"delete-force", VSH_OT_BOOL, 0, N_("delete snapshot and all children")}, + {"children", VSH_OT_BOOL, 0, N_("delete snapshot and all children")}, {NULL, 0, 0, NULL} }; @@ -8466,16 +8463,8 @@ cmdSnapshotDelete(vshControl *ctl, const vshCmd *cmd) if (name == NULL) goto cleanup; - if (vshCommandOptBool(cmd, "merge")) - flags |= VIR_DOMAIN_SNAPSHOT_DELETE_MERGE; - if (vshCommandOptBool(cmd, "delete")) - flags |= VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD; - if (vshCommandOptBool(cmd, "merge-force")) - flags |= VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE; - if (vshCommandOptBool(cmd, "delete-force")) - flags |= VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE; - - /* FIXME: throw an error if more than one flag is passed */ + if (vshCommandOptBool(cmd, "children")) + flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN; snapshot = virDomainSnapshotLookupByName(dom, name, 0); if (snapshot == NULL) -- 1.6.6.1

2010/4/2 Chris Lalancette <clalance@redhat.com>:
None of the other flags made sense anymore, so remove them all. DELETE_CHILDREN means delete *this* snapshot and any children of this snapshot.
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- include/libvirt/libvirt.h.in | 5 +-- src/qemu/qemu_driver.c | 93 +++++++++++++++++++++++------------------ src/vbox/vbox_tmpl.c | 27 +------------ tools/virsh.c | 17 +------ 4 files changed, 57 insertions(+), 85 deletions(-)
ACK. Matthias
participants (3)
-
Chris Lalancette
-
Daniel Veillard
-
Matthias Bolte