--- include/libvirt/virterror.h | 2 + src/Makefile.am | 2 + src/datatypes.c | 60 +++++++++++++ src/datatypes.h | 29 ++++++ src/driver-hypervisor.h | 5 ++ src/libvirt-domain-backup.c | 209 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 10 +++ src/util/virerror.c | 6 ++ 9 files changed, 325 insertions(+) create mode 100644 src/libvirt-domain-backup.c diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 2efee8f..c9ae10e 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -132,6 +132,7 @@ typedef enum { VIR_FROM_PERF = 65, /* Error from perf */ VIR_FROM_LIBSSH = 66, /* Error from libssh connection transport */ + VIR_FROM_DOMAIN_BACKUP = 67,/* Error from domain backup */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST @@ -319,6 +320,7 @@ typedef enum { VIR_ERR_AGENT_UNSYNCED = 97, /* guest agent replies with wrong id to guest-sync command */ VIR_ERR_LIBSSH = 98, /* error in libssh transport driver */ + VIR_ERR_INVALID_DOMAIN_BACKUP = 99, /* invalid domain backup */ } virErrorNumber; /** diff --git a/src/Makefile.am b/src/Makefile.am index f95f30e..c4ffc2f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -287,6 +287,7 @@ DRIVER_SOURCES = \ libvirt.c libvirt_internal.h \ libvirt-domain.c \ libvirt-domain-snapshot.c \ + libvirt-domain-backup.c \ libvirt-host.c \ libvirt-interface.c \ libvirt-network.c \ @@ -2682,6 +2683,7 @@ libvirt_setuid_rpc_client_la_SOURCES = \ libvirt.c \ libvirt-domain.c \ libvirt-domain-snapshot.c \ + libvirt-domain-backup.c \ libvirt-host.c \ libvirt-interface.c \ libvirt-network.c \ diff --git a/src/datatypes.c b/src/datatypes.c index 59ba956..365687c 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -37,6 +37,7 @@ virClassPtr virConnectClass; virClassPtr virConnectCloseCallbackDataClass; virClassPtr virDomainClass; virClassPtr virDomainSnapshotClass; +virClassPtr virDomainBackupClass; virClassPtr virInterfaceClass; virClassPtr virNetworkClass; virClassPtr virNodeDeviceClass; @@ -50,6 +51,7 @@ static void virConnectDispose(void *obj); static void virConnectCloseCallbackDataDispose(void *obj); static void virDomainDispose(void *obj); static void virDomainSnapshotDispose(void *obj); +static void virDomainBackupDispose(void *obj); static void virInterfaceDispose(void *obj); static void virNetworkDispose(void *obj); static void virNodeDeviceDispose(void *obj); @@ -88,6 +90,7 @@ virDataTypesOnceInit(void) DECLARE_CLASS_LOCKABLE(virConnectCloseCallbackData); DECLARE_CLASS(virDomain); DECLARE_CLASS(virDomainSnapshot); + DECLARE_CLASS(virDomainBackup); DECLARE_CLASS(virInterface); DECLARE_CLASS(virNetwork); DECLARE_CLASS(virNodeDevice); @@ -891,6 +894,63 @@ virDomainSnapshotDispose(void *obj) } +/** + * virGetDomainBackup: + * @domain: the domain to backup + * @name: pointer to the domain backup name + * + * Allocates a new domain backup object. When the object is no longer needed, + * virObjectUnref() must be called in order to not leak data. + * + * Returns a pointer to the domain backup object, or NULL on error. + */ +virDomainBackupPtr +virGetDomainBackup(virDomainPtr domain, const char *name) +{ + virDomainBackupPtr ret = NULL; + + if (virDataTypesInitialize() < 0) + return NULL; + + virCheckDomainGoto(domain, error); + virCheckNonNullArgGoto(name, error); + + if (!(ret = virObjectNew(virDomainBackupClass))) + goto error; + if (VIR_STRDUP(ret->name, name) < 0) + goto error; + + ret->domain = virObjectRef(domain); + + return ret; + + error: + virObjectUnref(ret); + return NULL; +} + + +/** + * virDomainBackupDispose: + * @obj: the domain backup to release + * + * Unconditionally release all memory associated with a backup. + * The backup object must not be used once this method returns. + * + * It will also unreference the associated connection object, + * which may also be released if its ref count hits zero. + */ +static void +virDomainBackupDispose(void *obj) +{ + virDomainBackupPtr backup = obj; + VIR_DEBUG("release backup %p %s", backup, backup->name); + + VIR_FREE(backup->name); + virObjectUnref(backup->domain); +} + + virAdmConnectPtr virAdmConnectNew(void) { diff --git a/src/datatypes.h b/src/datatypes.h index 288e057..54006ef 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -32,6 +32,7 @@ extern virClassPtr virConnectClass; extern virClassPtr virDomainClass; extern virClassPtr virDomainSnapshotClass; +extern virClassPtr virDomainBackupClass; extern virClassPtr virInterfaceClass; extern virClassPtr virNetworkClass; extern virClassPtr virNodeDeviceClass; @@ -292,6 +293,21 @@ extern virClassPtr virAdmClientClass; } \ } while (0) +# define virCheckDomainBackupReturn(obj, retval) \ + do { \ + virDomainBackupPtr _back = (obj); \ + if (!virObjectIsClass(_back, virDomainBackupClass) || \ + !virObjectIsClass(_back->domain, virDomainClass) || \ + !virObjectIsClass(_back->domain->conn, virConnectClass)) { \ + virReportErrorHelper(VIR_FROM_DOMAIN_BACKUP, \ + VIR_ERR_INVALID_DOMAIN_BACKUP, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + virDispatchError(NULL); \ + return retval; \ + } \ + } while (0) + /* Helper macros to implement VIR_DOMAIN_DEBUG using just C99. This * assumes you pass fewer than 15 arguments to VIR_DOMAIN_DEBUG, but @@ -675,6 +691,17 @@ struct _virNWFilter { unsigned char uuid[VIR_UUID_BUFLEN]; /* the network filter unique identifier */ }; +/** + * _virDomainBackup + * + * Internal structure associated with a domain backup + */ +struct _virDomainBackup { + virObject object; + char *name; + virDomainPtr domain; +}; + /* * Helper APIs for allocating new object instances @@ -714,6 +741,8 @@ virNWFilterPtr virGetNWFilter(virConnectPtr conn, const unsigned char *uuid); virDomainSnapshotPtr virGetDomainSnapshot(virDomainPtr domain, const char *name); +virDomainBackupPtr virGetDomainBackup(virDomainPtr domain, + const char *name); virAdmConnectPtr virAdmConnectNew(void); diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 3053d7a..9c1756f 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1263,6 +1263,10 @@ typedef int unsigned long long threshold, unsigned int flags); +typedef virDomainBackupPtr +(*virDrvDomainBackupCreateXML)(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags); typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1504,6 +1508,7 @@ struct _virHypervisorDriver { virDrvDomainSetGuestVcpus domainSetGuestVcpus; virDrvDomainSetVcpu domainSetVcpu; virDrvDomainSetBlockThreshold domainSetBlockThreshold; + virDrvDomainBackupCreateXML domainBackupCreateXML; }; diff --git a/src/libvirt-domain-backup.c b/src/libvirt-domain-backup.c new file mode 100644 index 0000000..f019d2c --- /dev/null +++ b/src/libvirt-domain-backup.c @@ -0,0 +1,209 @@ +/* + * libvirt-domain-backup.c: entry points for virDomainBackupPtr APIs + + * Copyright (C) 2017 Parallels International GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "datatypes.h" +#include "virlog.h" + +VIR_LOG_INIT("libvirt.domain-backup"); + +#define VIR_FROM_THIS VIR_FROM_DOMAIN_BACKUP + +/** + * virDomainBackupGetName: + * @backup: a backup object + * + * Get the public name for that backup + * + * Returns a pointer to the name or NULL, the string need not be deallocated + * as its lifetime will be the same as the backup object. + */ +const char * +virDomainBackupGetName(virDomainBackupPtr backup) +{ + VIR_DEBUG("backup=%p", backup); + + virResetLastError(); + + virCheckDomainBackupReturn(backup, NULL); + + return backup->name; +} + + +/** + * virDomainBackupGetDomain: + * @backup: a backup object + * + * Provides the domain pointer associated with a backup. The + * reference counter on the domain is not increased by this + * call. + * + * WARNING: When writing libvirt bindings in other languages, do not use this + * function. Instead, store the domain and the backup object together. + * + * Returns the domain or NULL. + */ +virDomainPtr +virDomainBackupGetDomain(virDomainBackupPtr backup) +{ + VIR_DEBUG("backup=%p", backup); + + virResetLastError(); + + virCheckDomainBackupReturn(backup, NULL); + + return backup->domain; +} + + +/** + * virDomainBackupGetConnect: + * @backup: a backup object + * + * Provides the connection pointer associated with a backup. The + * reference counter on the connection is not increased by this + * call. + * + * WARNING: When writing libvirt bindings in other languages, do not use this + * function. Instead, store the connection and the backup object together. + * + * Returns the connection or NULL. + */ +virConnectPtr +virDomainBackupGetConnect(virDomainBackupPtr backup) +{ + VIR_DEBUG("backup=%p", backup); + + virResetLastError(); + + virCheckDomainBackupReturn(backup, NULL); + + return backup->domain->conn; +} + + +/** + * virDomainBackupCreateXML: + * @domain: a domain object + * @xmlDesc: domain backup XML description + * @flags: reserved, must be 0 + * + * Starts creating of the domain disks backup based on the xml description in + * @xmlDesc. Backup is a copy of the specified domain disks at the moment of + * operation start. + * + * Backup creates a blockjob for every specified disk hence the backup + * status can be tracked thru blockjob event API and the backup progress + * is given by per blockjob virDomainBlockJobInfo. Backup can be cancelled by + * cancelling any of its still active blockjobs via virDomainBlockJobAbort. + * + * Known issues. In case libvirt connection is lost and restored back and all + * backup blockjobs are already gone then currenly it is not possible to know + * whether backup is completed or failed. + * + * Returns an (opaque) virDomainBackupPtr on success, NULL on failure. + */ +virDomainBackupPtr +virDomainBackupCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "xmlDesc=%s, flags=%x", xmlDesc, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, NULL); + conn = domain->conn; + + virCheckNonNullArgGoto(xmlDesc, error); + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->driver->domainBackupCreateXML) { + virDomainBackupPtr ret; + ret = conn->driver->domainBackupCreateXML(domain, xmlDesc, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virDomainBackupRef: + * @backup: the backup to hold a reference on + * + * Increment the reference count on the backup. For each + * additional call to this method, there shall be a corresponding + * call to virDomainBackupFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection and domain remain open until all threads have finished + * using the backup. ie, each new thread using a backup would + * increment the reference count. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainBackupRef(virDomainBackupPtr backup) +{ + VIR_DEBUG("backup=%p, refs=%d", backup, + backup ? backup->object.u.s.refs : 0); + + virResetLastError(); + + virCheckDomainBackupReturn(backup, -1); + + virObjectRef(backup); + return 0; +} + + +/** + * virDomainBackupFree: + * @backup: a domain backup object + * + * Free the domain backup object. The backup 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 +virDomainBackupFree(virDomainBackupPtr backup) +{ + VIR_DEBUG("backup=%p", backup); + + virResetLastError(); + + virCheckDomainBackupReturn(backup, -1); + + virObjectUnref(backup); + return 0; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index afb9100..2b40d34 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1068,10 +1068,12 @@ virConnectCloseCallbackDataClass; virConnectCloseCallbackDataGetCallback; virConnectCloseCallbackDataRegister; virConnectCloseCallbackDataUnregister; +virDomainBackupClass; virDomainClass; virDomainSnapshotClass; virGetConnect; virGetDomain; +virGetDomainBackup; virGetDomainSnapshot; virGetInterface; virGetNetwork; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 428cf2e..e3ee083 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -759,4 +759,14 @@ LIBVIRT_3.1.0 { virDomainSetVcpu; } LIBVIRT_3.0.0; +LIBVIRT_3.4.0 { + global: + virDomainBackupRef; + virDomainBackupFree; + virDomainBackupGetName; + virDomainBackupGetDomain; + virDomainBackupGetConnect; + virDomainBackupCreateXML; +} LIBVIRT_3.1.0; + # .... define new API here using predicted next version number .... diff --git a/src/util/virerror.c b/src/util/virerror.c index ef17fb5..f666e6e 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -139,6 +139,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Perf", /* 65 */ "Libssh transport layer", + "Domain Backup", ) @@ -1400,6 +1401,11 @@ virErrorMsg(virErrorNumber error, const char *info) errmsg = _("guest agent replied with wrong id to guest-sync command"); else errmsg = _("guest agent replied with wrong id to guest-sync command: %s"); + case VIR_ERR_INVALID_DOMAIN_BACKUP: + if (info == NULL) + errmsg = _("Invalid backup"); + else + errmsg = _("Invalid backup: %s"); break; case VIR_ERR_LIBSSH: if (info == NULL) -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list