diff --git a/daemon/remote.c b/daemon/remote.c index 780d53614f..149176fadc 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" @@ -5845,6 +5847,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 +remoteDispatchDomainRevertToSnapshot (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_revert_to_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 (virDomainRevertToSnapshot(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, @@ -6155,6 +6449,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) @@ -6214,3 +6518,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 51c1675953..85ebedfc24 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -154,3 +154,12 @@ remote_domain_managed_save_args val_remote_domain_managed_save_args; remote_domain_has_managed_save_image_args val_remote_domain_has_managed_save_image_args; remote_domain_managed_save_remove_args val_remote_domain_managed_save_remove_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_revert_to_snapshot_args val_remote_domain_revert_to_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 c33c1440c6..5ce2873ef2 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -514,6 +514,78 @@ 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 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 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 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 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 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 remoteDispatchDomainRevertToSnapshot( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_revert_to_snapshot_args *args, + void *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 remoteDispatchDomainSuspend( struct qemud_server *server, struct qemud_client *client, diff --git a/daemon/remote_dispatch_ret.h b/daemon/remote_dispatch_ret.h index 84f401b362..895b511e11 100644 --- a/daemon/remote_dispatch_ret.h +++ b/daemon/remote_dispatch_ret.h @@ -126,3 +126,10 @@ remote_list_nwfilters_ret val_remote_list_nwfilters_ret; remote_nwfilter_define_xml_ret val_remote_nwfilter_define_xml_ret; remote_domain_has_managed_save_image_ret val_remote_domain_has_managed_save_image_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 bda7af21f4..ebe8da41ce 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -927,3 +927,48 @@ .args_filter = (xdrproc_t) xdr_remote_domain_managed_save_remove_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* DomainSnapshotCreateXml => 185 */ + .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 => 186 */ + .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 => 187 */ + .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 => 188 */ + .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 => 189 */ + .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 => 190 */ + .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 => 191 */ + .fn = (dispatch_fn) remoteDispatchDomainSnapshotCurrent, + .args_filter = (xdrproc_t) xdr_remote_domain_snapshot_current_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_snapshot_current_ret, +}, +{ /* DomainRevertToSnapshot => 192 */ + .fn = (dispatch_fn) remoteDispatchDomainRevertToSnapshot, + .args_filter = (xdrproc_t) xdr_remote_domain_revert_to_snapshot_args, + .ret_filter = (xdrproc_t) xdr_void, +}, +{ /* DomainSnapshotDelete => 193 */ + .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 a9be8798f6..4addc62b6f 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1384,6 +1384,7 @@ typedef enum { VIR_DOMAIN_EVENT_STARTED_BOOTED = 0, /* Normal startup from boot */ VIR_DOMAIN_EVENT_STARTED_MIGRATED = 1, /* Incoming migration from another host */ VIR_DOMAIN_EVENT_STARTED_RESTORED = 2, /* Restored from a state file */ + VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT = 3, /* Restored from snapshot */ } virDomainEventStartedDetailType; /** @@ -1420,6 +1421,7 @@ typedef enum { VIR_DOMAIN_EVENT_STOPPED_MIGRATED = 3, /* Migrated off to another host */ VIR_DOMAIN_EVENT_STOPPED_SAVED = 4, /* Saved to a state file */ VIR_DOMAIN_EVENT_STOPPED_FAILED = 5, /* Host emulator/mgmt failed */ + VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT = 6, /* offline snapshot loaded */ } virDomainEventStoppedDetailType; @@ -1871,6 +1873,66 @@ 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); + +/* Revert the domain to a point-in-time snapshot. The + * state of the guest after this call will be the state + * of the guest when the snapshot in question was taken + */ +int virDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags); + +/* Delete a snapshot */ +typedef enum { + VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN = (1 << 0), +} 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 f69d07e74a..3bbb293136 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/python/generator.py b/python/generator.py index a24e122165..cb9f3d9119 100755 --- a/python/generator.py +++ b/python/generator.py @@ -241,6 +241,8 @@ py_types = { 'const virStreamPtr': ('O', "virStream", "virStreamPtr", "virStreamPtr"), 'virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"), 'const virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"), + + 'virDomainSnapshotPtr': ('O', "virDomainSnapshot", "virDomainSnapshotPtr", "virDomainSnapshotPtr"), } py_return_types = { @@ -317,6 +319,7 @@ skip_impl = ( 'virNodeListDevices', 'virNodeDeviceListCaps', 'virConnectBaselineCPU', + 'virDomainSnapshotListNames', ) diff --git a/python/typewrappers.c b/python/typewrappers.c index b33822c212..d0f703c77c 100644 --- a/python/typewrappers.c +++ b/python/typewrappers.c @@ -227,6 +227,21 @@ libvirt_virStreamPtrWrap(virStreamPtr node) return (ret); } +PyObject * +libvirt_virDomainSnapshotPtrWrap(virDomainSnapshotPtr node) +{ + PyObject *ret; + + if (node == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCObject_FromVoidPtrAndDesc((void *) node, (char *) "virDomainSnapshotPtr", + NULL); + return (ret); +} + PyObject * libvirt_virEventHandleCallbackWrap(virEventHandleCallback node) { diff --git a/python/typewrappers.h b/python/typewrappers.h index 8e1998e4d9..c0e69d8b91 100644 --- a/python/typewrappers.h +++ b/python/typewrappers.h @@ -101,6 +101,15 @@ typedef struct { } PyvirStream_Object; +#define PyvirDomainSnapshot_Get(v) (((v) == Py_None) ? NULL : \ + (((PyvirDomainSnapshot_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + virDomainSnapshotPtr obj; +} PyvirDomainSnapshot_Object; + + #define PyvirEventHandleCallback_Get(v) (((v) == Py_None) ? NULL : \ (((PyvirEventHandleCallback_Object *)(v))->obj)) @@ -155,6 +164,7 @@ PyObject * libvirt_virVoidPtrWrap(void* node); PyObject * libvirt_virNodeDevicePtrWrap(virNodeDevicePtr node); PyObject * libvirt_virSecretPtrWrap(virSecretPtr node); PyObject * libvirt_virStreamPtrWrap(virStreamPtr node); +PyObject * libvirt_virDomainSnapshotPtrWrap(virDomainSnapshotPtr node); /* Provide simple macro statement wrappers (adapted from GLib, in turn from Perl): diff --git a/src/datatypes.c b/src/datatypes.c index a361da6cd4..c514f41b3a 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -128,6 +128,20 @@ virSecretFreeName(void *secret_, const char *name ATTRIBUTE_UNUSED) virUnrefSecret(secret); } +/** + * 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: * @@ -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 4663c9c6f3..bbeb7cf23e 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 7e2536d3c4..f8db9c1d3d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -411,6 +411,44 @@ typedef int typedef int (*virDrvDomainManagedSaveRemove)(virDomainPtr domain, unsigned int flags); +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 + (*virDrvDomainRevertToSnapshot)(virDomainSnapshotPtr snapshot, + unsigned int flags); + +typedef int + (*virDrvDomainSnapshotDelete)(virDomainSnapshotPtr snapshot, + unsigned int flags); + + /** * _virDriver: * @@ -511,6 +549,15 @@ struct _virDriver { virDrvDomainManagedSave domainManagedSave; virDrvDomainHasManagedSaveImage domainHasManagedSaveImage; virDrvDomainManagedSaveRemove domainManagedSaveRemove; + virDrvDomainSnapshotCreateXML domainSnapshotCreateXML; + virDrvDomainSnapshotDumpXML domainSnapshotDumpXML; + virDrvDomainSnapshotNum domainSnapshotNum; + virDrvDomainSnapshotListNames domainSnapshotListNames; + virDrvDomainSnapshotLookupByName domainSnapshotLookupByName; + virDrvDomainHasCurrentSnapshot domainHasCurrentSnapshot; + virDrvDomainSnapshotCurrent domainSnapshotCurrent; + virDrvDomainRevertToSnapshot domainRevertToSnapshot; + virDrvDomainSnapshotDelete domainSnapshotDelete; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index e55c948724..785f98b285 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3392,6 +3392,15 @@ static virDriver esxDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; diff --git a/src/libvirt.c b/src/libvirt.c index fb683c0a73..731fcebd88 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -682,6 +682,31 @@ virLibNWFilterError(virNWFilterPtr pool, virErrorNumber error, errmsg, info, NULL, 0, 0, errmsg, info); } +/** + * virLibDomainSnapshotError: + * @snapshot: the snapshot if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the domain snapshot 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 @@ -12276,3 +12301,432 @@ 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; + } + + 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. Caller is responsible for freeing each member + * of the array. + * + * 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; +} + +/** + * virDomainRevertToSnapshot + * @snapshot: a domain snapshot object + * @flags: unused flag parameters; callers should pass 0 + * + * Revert the domain to a given snapshot. + * + * Returns 0 if the creation is successful, -1 on error. + */ +int +virDomainRevertToSnapshot(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->domainRevertToSnapshot) { + int ret = conn->driver->domainRevertToSnapshot(snapshot, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainSnapshotDelete + * @snapshot: a domain snapshot object + * @flags: flag parameters + * + * Delete the snapshot. + * + * If @flags is 0, then just this snapshot is deleted, and changes from + * this snapshot are automatically merged into children snapshots. If + * flags is VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, then this snapshot + * and any children snapshots are deleted. + * + * 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); + + 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; +} + +/** + * virDomainSnapshotFree: + * @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 878eda3bd4..86aacd71b0 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 24a422fc63..babe6bce61 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -381,6 +381,16 @@ LIBVIRT_0.7.8 { virDomainManagedSave; virDomainHasManagedSaveImage; virDomainManagedSaveRemove; + virDomainSnapshotCreateXML; + virDomainSnapshotGetXMLDesc; + virDomainSnapshotNum; + virDomainSnapshotListNames; + virDomainSnapshotLookupByName; + virDomainHasCurrentSnapshot; + virDomainSnapshotCurrent; + virDomainRevertToSnapshot; + virDomainSnapshotDelete; + virDomainSnapshotFree; } LIBVIRT_0.7.7; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 01330ec352..b5d422c543 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2541,6 +2541,15 @@ static virDriver lxcDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; static virStateDriver lxcStateDriver = { diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 91d7459cd9..4554d950b0 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -795,6 +795,15 @@ static virDriver oneDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; static virStateDriver oneStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 32bc3c2e70..6b1b0464ce 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1547,6 +1547,15 @@ static virDriver openvzDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 0e1d35fa01..28e17ffdab 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -1654,6 +1654,15 @@ virDriver phypDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; int diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index eea49bb124..4d1fabb56b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10531,6 +10531,15 @@ static virDriver qemuDriver = { qemuDomainManagedSave, /* domainManagedSave */ qemuDomainHasManagedSaveImage, /* domainHasManagedSaveImage */ qemuDomainManagedSaveRemove, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ede00e8920..022e3ccccb 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); /*----------------------------------------------------------------------*/ @@ -8374,6 +8376,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 +remoteDomainRevertToSnapshot (virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + int rv = -1; + remote_domain_revert_to_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_REVERT_TO_SNAPSHOT, + (xdrproc_t) xdr_remote_domain_revert_to_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, @@ -9744,6 +10031,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 @@ -9799,6 +10092,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) @@ -9894,6 +10194,15 @@ static virDriver remote_driver = { remoteDomainManagedSave, /* domainManagedSave */ remoteDomainHasManagedSaveImage, /* domainHasManagedSaveImage */ remoteDomainManagedSaveRemove, /* domainManagedSaveRemove */ + remoteDomainSnapshotCreateXML, /* domainSnapshotCreateXML */ + remoteDomainSnapshotDumpXML, /* domainSnapshotDumpXML */ + remoteDomainSnapshotNum, /* domainSnapshotNum */ + remoteDomainSnapshotListNames, /* domainSnapshotListNames */ + remoteDomainSnapshotLookupByName, /* domainSnapshotLookupByName */ + remoteDomainHasCurrentSnapshot, /* domainHasCurrentSnapshot */ + remoteDomainSnapshotCurrent, /* domainSnapshotCurrent */ + remoteDomainRevertToSnapshot, /* domainRevertToSnapshot */ + remoteDomainSnapshotDelete, /* domainSnapshotDelete */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 68b827a1f1..c9816dd93b 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -127,6 +127,17 @@ xdr_remote_nonnull_secret (XDR *xdrs, remote_nonnull_secret *objp) return TRUE; } +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) { @@ -3330,6 +3341,176 @@ xdr_remote_domain_managed_save_remove_args (XDR *xdrs, remote_domain_managed_sav return TRUE; } +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_revert_to_snapshot_args (XDR *xdrs, remote_domain_revert_to_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 6558948feb..57ed123751 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; @@ -1883,6 +1890,101 @@ struct remote_domain_managed_save_remove_args { u_int flags; }; typedef struct remote_domain_managed_save_remove_args remote_domain_managed_save_remove_args; + +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_revert_to_snapshot_args { + remote_nonnull_domain_snapshot snap; + int flags; +}; +typedef struct remote_domain_revert_to_snapshot_args remote_domain_revert_to_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 @@ -2071,6 +2173,15 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MANAGED_SAVE = 182, REMOTE_PROC_DOMAIN_HAS_MANAGED_SAVE_IMAGE = 183, REMOTE_PROC_DOMAIN_MANAGED_SAVE_REMOVE = 184, + REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML = 185, + REMOTE_PROC_DOMAIN_SNAPSHOT_DUMP_XML = 186, + REMOTE_PROC_DOMAIN_SNAPSHOT_NUM = 187, + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_NAMES = 188, + REMOTE_PROC_DOMAIN_SNAPSHOT_LOOKUP_BY_NAME = 189, + REMOTE_PROC_DOMAIN_HAS_CURRENT_SNAPSHOT = 190, + REMOTE_PROC_DOMAIN_SNAPSHOT_CURRENT = 191, + REMOTE_PROC_DOMAIN_REVERT_TO_SNAPSHOT = 192, + REMOTE_PROC_DOMAIN_SNAPSHOT_DELETE = 193, }; typedef enum remote_procedure remote_procedure; @@ -2114,6 +2225,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*); @@ -2410,6 +2522,22 @@ extern bool_t xdr_remote_domain_managed_save_args (XDR *, remote_domain_managed extern bool_t xdr_remote_domain_has_managed_save_image_args (XDR *, remote_domain_has_managed_save_image_args*); extern bool_t xdr_remote_domain_has_managed_save_image_ret (XDR *, remote_domain_has_managed_save_image_ret*); extern bool_t xdr_remote_domain_managed_save_remove_args (XDR *, remote_domain_managed_save_remove_args*); +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_revert_to_snapshot_args (XDR *, remote_domain_revert_to_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*); @@ -2427,6 +2555,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 (); @@ -2723,6 +2852,22 @@ extern bool_t xdr_remote_domain_managed_save_args (); extern bool_t xdr_remote_domain_has_managed_save_image_args (); extern bool_t xdr_remote_domain_has_managed_save_image_ret (); extern bool_t xdr_remote_domain_managed_save_remove_args (); +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_revert_to_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 0374f9a9dc..61b4bc1635 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; @@ -1666,6 +1675,83 @@ struct remote_domain_managed_save_remove_args { unsigned flags; }; +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; +}; + +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_revert_to_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. */ @@ -1874,7 +1960,16 @@ enum remote_procedure { REMOTE_PROC_NWFILTER_UNDEFINE = 181, REMOTE_PROC_DOMAIN_MANAGED_SAVE = 182, REMOTE_PROC_DOMAIN_HAS_MANAGED_SAVE_IMAGE = 183, - REMOTE_PROC_DOMAIN_MANAGED_SAVE_REMOVE = 184 + REMOTE_PROC_DOMAIN_MANAGED_SAVE_REMOVE = 184, + REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML = 185, + REMOTE_PROC_DOMAIN_SNAPSHOT_DUMP_XML = 186, + REMOTE_PROC_DOMAIN_SNAPSHOT_NUM = 187, + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_NAMES = 188, + REMOTE_PROC_DOMAIN_SNAPSHOT_LOOKUP_BY_NAME = 189, + REMOTE_PROC_DOMAIN_HAS_CURRENT_SNAPSHOT = 190, + REMOTE_PROC_DOMAIN_SNAPSHOT_CURRENT = 191, + REMOTE_PROC_DOMAIN_REVERT_TO_SNAPSHOT = 192, + REMOTE_PROC_DOMAIN_SNAPSHOT_DELETE = 193 /* * 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 105f8250c4..91e197f86b 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5309,6 +5309,15 @@ static virDriver testDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 443d82ed90..dfa5de5d9e 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1940,6 +1940,15 @@ static virDriver umlDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; diff --git a/src/util/virterror.c b/src/util/virterror.c index 2537110dcb..d29f95ba2b 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 8450266003..89337ad996 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7187,6 +7187,15 @@ virDriver NAME(Driver) = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index a5d58d0418..7c4eb5ef36 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1983,6 +1983,15 @@ static virDriver xenUnifiedDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; /** diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c index 240aa16415..75796d6177 100644 --- a/src/xenapi/xenapi_driver.c +++ b/src/xenapi/xenapi_driver.c @@ -1786,6 +1786,15 @@ static virDriver xenapiDriver = { NULL, /* domainManagedSave */ NULL, /* domainHasManagedSaveImage */ NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ }; /**