From nobody Mon Feb 9 15:25:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1551937749241872.2471049215922; Wed, 6 Mar 2019 21:49:09 -0800 (PST) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id A452FC05D3E4; Thu, 7 Mar 2019 05:49:07 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 7009960147; Thu, 7 Mar 2019 05:49:07 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 304273F7D9; Thu, 7 Mar 2019 05:49:07 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x275mUFw004035 for ; Thu, 7 Mar 2019 00:48:30 -0500 Received: by smtp.corp.redhat.com (Postfix) id 023CE5D78D; Thu, 7 Mar 2019 05:48:30 +0000 (UTC) Received: from blue.redhat.com (ovpn-118-35.phx2.redhat.com [10.3.118.35]) by smtp.corp.redhat.com (Postfix) with ESMTP id EFC795D783; Thu, 7 Mar 2019 05:48:28 +0000 (UTC) From: Eric Blake To: libvir-list@redhat.com Date: Wed, 6 Mar 2019 23:47:43 -0600 Message-Id: <20190307054752.19522-12-eblake@redhat.com> In-Reply-To: <20190307054752.19522-1-eblake@redhat.com> References: <20190307054752.19522-1-eblake@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-loop: libvir-list@redhat.com Cc: amureini@redhat.com, derez@redhat.com, vsementsov@virtuozzo.com, zhanhouliang@outlook.com, bharadwaj.rayala@rubrik.com, ydary@redhat.com, nsoffer@redhat.com, jsnow@redhat.com, suman.swaroop@rubrik.com Subject: [libvirt] [PATCH v5 11/20] wip: backup: Parse and output backup XML X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Thu, 07 Mar 2019 05:49:08 +0000 (UTC) Content-Type: text/plain; charset="utf-8" Accept XML describing a generic block job, and output it again as needed. At the moment, it has some qemu-specific hacks, such as storing internal XML for a node name, that might be cleaner once full-tree node-name support goes in. Still not done: decent tests Signed-off-by: Eric Blake --- src/conf/checkpoint_conf.h | 66 +++++ src/conf/domain_conf.h | 3 + src/conf/checkpoint_conf.c | 503 +++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 8 +- 4 files changed, 579 insertions(+), 1 deletion(-) diff --git a/src/conf/checkpoint_conf.h b/src/conf/checkpoint_conf.h index 45ed8b0d29..2d18b7d0fb 100644 --- a/src/conf/checkpoint_conf.h +++ b/src/conf/checkpoint_conf.h @@ -168,4 +168,70 @@ int virDomainCheckpointRedefinePrep(virDomainPtr domai= n, VIR_ENUM_DECL(virDomainCheckpoint); +/* Items related to incremental backup state */ + +typedef enum { + VIR_DOMAIN_BACKUP_TYPE_DEFAULT =3D 0, + VIR_DOMAIN_BACKUP_TYPE_PUSH, + VIR_DOMAIN_BACKUP_TYPE_PULL, + + VIR_DOMAIN_BACKUP_TYPE_LAST +} virDomainBackupType; + +typedef enum { + VIR_DOMAIN_BACKUP_DISK_STATE_DEFAULT =3D 0, /* Initial */ + VIR_DOMAIN_BACKUP_DISK_STATE_CREATED, /* File created */ + VIR_DOMAIN_BACKUP_DISK_STATE_LABEL, /* Security labels applied */ + VIR_DOMAIN_BACKUP_DISK_STATE_READY, /* Handed to guest */ + VIR_DOMAIN_BACKUP_DISK_STATE_BITMAP, /* Associated temp bitmap created= */ + VIR_DOMAIN_BACKUP_DISK_STATE_EXPORT, /* NBD export created */ + VIR_DOMAIN_BACKUP_DISK_STATE_COMPLETE, /* Push job finished */ +} virDomainBackupDiskState; + +/* Stores disk-backup information */ +typedef struct _virDomainBackupDiskDef virDomainBackupDiskDef; +typedef virDomainBackupDiskDef *virDomainBackupDiskDefPtr; +struct _virDomainBackupDiskDef { + char *name; /* name matching the dom->disks that matches na= me */ + + /* details of target for push-mode, or of the scratch file for pull-mo= de */ + virStorageSourcePtr store; + int state; /* virDomainBackupDiskState, not stored in XML */ +}; + +/* Stores the complete backup metadata */ +typedef struct _virDomainBackupDef virDomainBackupDef; +typedef virDomainBackupDef *virDomainBackupDefPtr; +struct _virDomainBackupDef { + /* Public XML. */ + int type; /* virDomainBackupType */ + int id; + char *incremental; + virStorageNetHostDefPtr server; /* only when type =3D=3D PULL */ + + size_t ndisks; /* should not exceed dom->ndisks */ + virDomainBackupDiskDef *disks; +}; + +VIR_ENUM_DECL(virDomainBackup); + +typedef enum { + VIR_DOMAIN_BACKUP_PARSE_INTERNAL =3D 1 << 0, +} virDomainBackupParseFlags; + +virDomainBackupDefPtr virDomainBackupDefParseString(const char *xmlStr, + virDomainXMLOptionPtr = xmlopt, + unsigned int flags); +virDomainBackupDefPtr virDomainBackupDefParseNode(xmlDocPtr xml, + xmlNodePtr root, + virDomainXMLOptionPtr xm= lopt, + unsigned int flags); +void virDomainBackupDefFree(virDomainBackupDefPtr def); +int virDomainBackupDefFormat(virBufferPtr buf, + virDomainBackupDefPtr def, + bool internal); +int virDomainBackupAlignDisks(virDomainBackupDefPtr backup, + virDomainDefPtr dom, const char *suffix); + #endif /* LIBVIRT_CHECKPOINT_CONF_H */ diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index c3d5f53bdd..ea98dd31d9 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -125,6 +125,9 @@ typedef virDomainCheckpointObj *virDomainCheckpointObjP= tr; typedef struct _virDomainCheckpointObjList virDomainCheckpointObjList; typedef virDomainCheckpointObjList *virDomainCheckpointObjListPtr; +typedef struct _virDomainBackupDef virDomainBackupDef; +typedef virDomainBackupDef *virDomainBackupDefPtr; + typedef struct _virDomainSnapshotObj virDomainSnapshotObj; typedef virDomainSnapshotObj *virDomainSnapshotObjPtr; diff --git a/src/conf/checkpoint_conf.c b/src/conf/checkpoint_conf.c index e5e4c5e306..61aeb7f0a1 100644 --- a/src/conf/checkpoint_conf.c +++ b/src/conf/checkpoint_conf.c @@ -1238,3 +1238,506 @@ virDomainCheckpointRedefinePrep(virDomainPtr domain, cleanup: return ret; } + +/* Backup Def functions */ + +VIR_ENUM_IMPL(virDomainBackup, VIR_DOMAIN_BACKUP_TYPE_LAST, + "default", "push", "pull"); + +static void +virDomainBackupDiskDefClear(virDomainBackupDiskDefPtr disk) +{ + VIR_FREE(disk->name); + virStorageSourceClear(disk->store); + disk->store =3D NULL; +} + +void +virDomainBackupDefFree(virDomainBackupDefPtr def) +{ + size_t i; + + if (!def) + return; + + VIR_FREE(def->incremental); + VIR_FREE(def->server); // FIXME which struct + for (i =3D 0; i < def->ndisks; i++) + virDomainBackupDiskDefClear(&def->disks[i]); + VIR_FREE(def->disks); + VIR_FREE(def); +} + +static int +virDomainBackupDiskDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt, + virDomainBackupDiskDefPtr def, + bool push, bool internal, + virDomainXMLOptionPtr xmlopt) +{ + int ret =3D -1; + // char *backup =3D NULL; /* backup=3D"yes|no"? */ + char *type =3D NULL; + char *driver =3D NULL; + xmlNodePtr cur; + xmlNodePtr saved =3D ctxt->node; + + ctxt->node =3D node; + + if (VIR_ALLOC(def->store) < 0) + goto cleanup; + + def->name =3D virXMLPropString(node, "name"); + if (!def->name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing name from disk backup element")); + goto cleanup; + } + + /* Needed? A way for users to list a disk and explicitly mark it + * as not participating, and then output shows all disks rather + * than just active disks */ +#if 0 + backup =3D virXMLPropString(node, "backup"); + if (backup) { + def->type =3D virDomainCheckpointTypeFromString(checkpoint); + if (def->type <=3D 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown disk checkpoint setting '%s'"), + checkpoint); + goto cleanup; + } + } +#endif + + if ((type =3D virXMLPropString(node, "type"))) { + if ((def->store->type =3D virStorageTypeFromString(type)) <=3D 0 || + def->store->type =3D=3D VIR_STORAGE_TYPE_VOLUME || + def->store->type =3D=3D VIR_STORAGE_TYPE_DIR) { + virReportError(VIR_ERR_XML_ERROR, + _("unknown disk backup type '%s'"), type); + goto cleanup; + } + } else { + def->store->type =3D VIR_STORAGE_TYPE_FILE; + } + + if ((cur =3D virXPathNode(push ? "./target" : "./scratch", ctxt)) && + virDomainDiskSourceParse(cur, ctxt, def->store, 0, xmlopt) < 0) + goto cleanup; + + if (internal) { + int detected; + if (virXPathInt("string(./node/@detected)", ctxt, &detected) < 0) + goto cleanup; + def->store->detected =3D detected; + def->store->nodeformat =3D virXPathString("string(./node)", ctxt); + } + + if ((driver =3D virXPathString("string(./driver/@type)", ctxt))) { + def->store->format =3D virStorageFileFormatTypeFromString(driver); + if (def->store->format <=3D 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown disk backup driver '%s'"), driver); + goto cleanup; + } else if (!push && def->store->format !=3D VIR_STORAGE_FILE_QCOW2= ) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("pull mode requires qcow2 driver, not '%s'"), + driver); + goto cleanup; + } + } + + /* validate that the passed path is absolute */ + if (virStorageSourceIsRelative(def->store)) { + virReportError(VIR_ERR_XML_ERROR, + _("disk backup image path '%s' must be absolute"), + def->store->path); + goto cleanup; + } + + ret =3D 0; + cleanup: + ctxt->node =3D saved; + + VIR_FREE(driver); +// VIR_FREE(backup); + VIR_FREE(type); + if (ret < 0) + virDomainBackupDiskDefClear(def); + return ret; +} + +static virDomainBackupDefPtr +virDomainBackupDefParse(xmlXPathContextPtr ctxt, + virDomainXMLOptionPtr xmlopt, + unsigned int flags) +{ + virDomainBackupDefPtr def =3D NULL; + virDomainBackupDefPtr ret =3D NULL; + xmlNodePtr *nodes =3D NULL; + xmlNodePtr node =3D NULL; + char *mode =3D NULL; + bool push; + size_t i; + int n; + + if (VIR_ALLOC(def) < 0) + goto cleanup; + + mode =3D virXMLPropString(ctxt->node, "mode"); + if (mode) { + def->type =3D virDomainBackupTypeFromString(mode); + if (def->type <=3D 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown backup mode '%s'"), mode); + goto cleanup; + } + } else { + def->type =3D VIR_DOMAIN_BACKUP_TYPE_PUSH; + } + push =3D def->type =3D=3D VIR_DOMAIN_BACKUP_TYPE_PUSH; + + if (flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL) { + char *tmp =3D virXMLPropString(ctxt->node, "id"); + if (tmp && virStrToLong_i(tmp, NULL, 10, &def->id) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid 'id' value '%s'"), tmp); + VIR_FREE(tmp); + goto cleanup; + } + VIR_FREE(tmp); + } + + def->incremental =3D virXPathString("string(./incremental)", ctxt); + + node =3D virXPathNode("./server", ctxt); + if (node) { + if (def->type !=3D VIR_DOMAIN_BACKUP_TYPE_PULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("use of requires pull mode backup")); + goto cleanup; + } + if (VIR_ALLOC(def->server) < 0) + goto cleanup; + if (virDomainStorageNetworkParseHost(node, def->server) < 0) + goto cleanup; + if (def->server->transport =3D=3D VIR_STORAGE_NET_HOST_TRANS_RDMA)= { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("transport rdma is not supported for = ")); + goto cleanup; + } + } + + if ((n =3D virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0) + goto cleanup; + if (n && VIR_ALLOC_N(def->disks, n) < 0) + goto cleanup; + def->ndisks =3D n; + for (i =3D 0; i < def->ndisks; i++) { + if (virDomainBackupDiskDefParseXML(nodes[i], ctxt, + &def->disks[i], push, + flags & VIR_DOMAIN_BACKUP_PARSE= _INTERNAL, + xmlopt) < 0) + goto cleanup; + } + VIR_FREE(nodes); + + VIR_STEAL_PTR(ret, def); + + cleanup: + VIR_FREE(mode); + VIR_FREE(nodes); + virDomainBackupDefFree(def); + + return ret; +} + +virDomainBackupDefPtr +virDomainBackupDefParseString(const char *xmlStr, + virDomainXMLOptionPtr xmlopt, + unsigned int flags) +{ + virDomainBackupDefPtr ret =3D NULL; + xmlDocPtr xml; + int keepBlanksDefault =3D xmlKeepBlanksDefault(0); + + if ((xml =3D virXMLParse(NULL, xmlStr, _("(domain_backup)")))) { + xmlKeepBlanksDefault(keepBlanksDefault); + ret =3D virDomainBackupDefParseNode(xml, xmlDocGetRootElement(xml), + xmlopt, flags); + xmlFreeDoc(xml); + } + xmlKeepBlanksDefault(keepBlanksDefault); + + return ret; +} + +virDomainBackupDefPtr +virDomainBackupDefParseNode(xmlDocPtr xml, + xmlNodePtr root, + virDomainXMLOptionPtr xmlopt, + unsigned int flags) +{ + xmlXPathContextPtr ctxt =3D NULL; + virDomainBackupDefPtr def =3D NULL; + + if (!virXMLNodeNameEqual(root, "domainbackup")) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("domainbackup")); + goto cleanup; + } + + ctxt =3D xmlXPathNewContext(xml); + if (ctxt =3D=3D NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node =3D root; + def =3D virDomainBackupDefParse(ctxt, xmlopt, flags); + cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + +static int +virDomainBackupDiskDefFormat(virBufferPtr buf, + virDomainBackupDiskDefPtr disk, + bool push, bool internal) +{ + int type =3D disk->store->type; + virBuffer attrBuf =3D VIR_BUFFER_INITIALIZER; + virBuffer childBuf =3D VIR_BUFFER_INITIALIZER; + int ret =3D -1; + + if (!disk->name) + return 0; + + virBufferEscapeString(buf, "name); + /* TODO: per-disk backup=3Doff? */ + + virBufferAsprintf(buf, " type=3D'%s'>\n", virStorageTypeToString(type)= ); + virBufferAdjustIndent(buf, 2); + + if (disk->store->format > 0) + virBufferEscapeString(buf, "\n", + virStorageFileFormatTypeToString(disk->store= ->format)); + /* TODO: should node names be part of storage file xml, rather + * than a one-off hack for qemu? */ + if (internal) { + virBufferEscapeString(buf, "store->detected ? "1" : "0"); + virBufferEscapeString(buf, ">%s\n", disk->store->nodeformat= ); + } + + virBufferSetChildIndent(&childBuf, buf); + if (virDomainStorageSourceFormat(&attrBuf, &childBuf, disk->store, 0, + false) < 0) + goto cleanup; + if (virXMLFormatElement(buf, push ? "target" : "scratch", + &attrBuf, &childBuf) < 0) + goto cleanup; + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "\n"); + + ret =3D 0; + + cleanup: + virBufferFreeAndReset(&attrBuf); + virBufferFreeAndReset(&childBuf); + return ret; +} + +int +virDomainBackupDefFormat(virBufferPtr buf, virDomainBackupDefPtr def, + bool internal) +{ + size_t i; + + virBufferAsprintf(buf, "type)); + if (def->id) + virBufferAsprintf(buf, " id=3D'%d'", def->id); + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + + virBufferEscapeString(buf, "%s\n", + def->incremental); + if (def->server) { + virBufferAsprintf(buf, "serv= er->transport)); + virBufferEscapeString(buf, " name=3D'%s'", def->server->name); + if (def->server->port) + virBufferAsprintf(buf, " port=3D'%u'", def->server->port); + virBufferEscapeString(buf, " socket=3D'%s'", def->server->socket); + virBufferAddLit(buf, "/>\n"); + } + + if (def->ndisks) { + virBufferAddLit(buf, "\n"); + virBufferAdjustIndent(buf, 2); + for (i =3D 0; i < def->ndisks; i++) { + if (!def->disks[i].store) + continue; + if (virDomainBackupDiskDefFormat(buf, &def->disks[i], + def->type =3D=3D VIR_DOMAIN_B= ACKUP_TYPE_PUSH, + internal) < 0) + return -1; + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "\n"); + + return virBufferCheckError(buf); +} + + +static int +virDomainBackupCompareDiskIndex(const void *a, const void *b) +{ + const virDomainBackupDiskDef *diska =3D a; + const virDomainBackupDiskDef *diskb =3D b; + + /* Integer overflow shouldn't be a problem here. */ + return diska->idx - diskb->idx; +} + +static int +virDomainBackupDefAssignStore(virDomainBackupDiskDefPtr disk, + virStorageSourcePtr src, + const char *suffix) +{ + int ret =3D -1; + + if (virStorageSourceIsEmpty(src)) { + if (disk->store) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk '%s' has no media"), disk->name); + goto cleanup; + } + } else if (src->readonly && disk->store) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("backup of readonly disk '%s' makes no sense"), + disk->name); + goto cleanup; + } else if (!disk->store) { + if (virStorageSourceGetActualType(src) =3D=3D VIR_STORAGE_TYPE_FIL= E) { + if (VIR_ALLOC(disk->store) < 0) + goto cleanup; + disk->store->type =3D VIR_STORAGE_TYPE_FILE; + if (virAsprintf(&disk->store->path, "%s.%s", src->path, + suffix) < 0) + goto cleanup; + disk->store->detected =3D true; + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("refusing to generate file name for disk '%s'= "), + disk->name); + goto cleanup; + } + } + ret =3D 0; + cleanup: + return ret; +} + +/* Align def->disks to domain. Sort the list of def->disks, + * generating storage names using suffix as needed. Convert paths to + * disk targets for uniformity. Issue an error and return -1 if any + * def->disks[n]->name appears more than once or does not map to + * dom->disks. */ +int +virDomainBackupAlignDisks(virDomainBackupDefPtr def, virDomainDefPtr dom, + const char *suffix) +{ + int ret =3D -1; + virBitmapPtr map =3D NULL; + size_t i; + int ndisks; + bool alloc_all =3D false; + + if (def->ndisks > dom->ndisks) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("too many disk backup requests for domain")); + goto cleanup; + } + + /* Unlikely to have a guest without disks but technically possible. */ + if (!dom->ndisks) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("domain must have at least one disk to perform " + "backups")); + goto cleanup; + } + + if (!(map =3D virBitmapNew(dom->ndisks))) + goto cleanup; + + /* Double check requested disks. */ + for (i =3D 0; i < def->ndisks; i++) { + virDomainBackupDiskDefPtr disk =3D &def->disks[i]; + int idx =3D virDomainDiskIndexByName(dom, disk->name, false); + + if (idx < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("no disk named '%s'"), disk->name); + goto cleanup; + } + + if (virBitmapIsBitSet(map, idx)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk '%s' specified twice"), + disk->name); + goto cleanup; + } + ignore_value(virBitmapSetBit(map, idx)); + disk->idx =3D idx; + + if (STRNEQ(disk->name, dom->disks[idx]->dst)) { + VIR_FREE(disk->name); + if (VIR_STRDUP(disk->name, dom->disks[idx]->dst) < 0) + goto cleanup; + } + if (disk->store && !disk->store->path) { + virStorageSourceClear(disk->store); + disk->store =3D NULL; + } + if (virDomainBackupDefAssignStore(disk, dom->disks[i]->src, suffix= ) < 0) + goto cleanup; + } + + /* Provide fillers for all remaining disks, for easier iteration. */ + if (!def->ndisks) + alloc_all =3D true; + ndisks =3D def->ndisks; + if (VIR_EXPAND_N(def->disks, def->ndisks, + dom->ndisks - def->ndisks) < 0) + goto cleanup; + + for (i =3D 0; i < dom->ndisks; i++) { + virDomainBackupDiskDefPtr disk; + + if (virBitmapIsBitSet(map, i)) + continue; + disk =3D &def->disks[ndisks++]; + if (VIR_STRDUP(disk->name, dom->disks[i]->dst) < 0) + goto cleanup; + disk->idx =3D i; + if (alloc_all && + virDomainBackupDefAssignStore(disk, dom->disks[i]->src, suffix= ) < 0) + goto cleanup; + } + + qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]), + virDomainBackupCompareDiskIndex); + + ret =3D 0; + + cleanup: + virBitmapFree(map); + return ret; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 84e5d3dea2..053fa8bc8b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -70,6 +70,13 @@ virCapabilitiesSetNetPrefix; # conf/checkpoint_conf.h +virDomainBackupAlignDisks; +virDomainBackupDefFormat; +virDomainBackupDefFree; +virDomainBackupDefParseNode; +virDomainBackupDefParseString; +virDomainBackupTypeFromString; +virDomainBackupTypeToString; virDomainCheckpointAlignDisks; virDomainCheckpointAssignDef; virDomainCheckpointDefFormat; @@ -92,7 +99,6 @@ virDomainCheckpointTypeToString; virDomainCheckpointUpdateRelations; virDomainListAllCheckpoints; - # conf/cpu_conf.h virCPUCacheModeTypeFromString; virCPUCacheModeTypeToString; --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list