[libvirt] [PATCH v9 05/10] backup: Parse and output backup XML
Peter Krempa
pkrempa at redhat.com
Tue Jul 9 15:56:22 UTC 2019
On Mon, Jul 08, 2019 at 11:55:48 -0500, Eric Blake wrote:
> Accept XML describing a generic block job, and output it again as
> needed. This may still need a few tweaks to match the documented XML
> and RNG schema.
>
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
[...]
> diff --git a/src/conf/backup_conf.h b/src/conf/backup_conf.h
> new file mode 100644
> index 0000000000..1714315a1f
> --- /dev/null
> +++ b/src/conf/backup_conf.h
> @@ -0,0 +1,94 @@
[...]
> +/* Stores disk-backup information */
> +typedef struct _virDomainBackupDiskDef virDomainBackupDiskDef;
> +typedef virDomainBackupDiskDef *virDomainBackupDiskDefPtr;
> +struct _virDomainBackupDiskDef {
> + char *name; /* name matching the <target dev='...' of the domain */
> + int idx; /* index within checkpoint->dom->disks that matches name */
> +
> + /* details of target for push-mode, or of the scratch file for pull-mode */
> + 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 == PULL */
> +
> + size_t ndisks; /* should not exceed dom->ndisks */
> + virDomainBackupDiskDef *disks;
> +};
[...]
> diff --git a/src/conf/backup_conf.c b/src/conf/backup_conf.c
> new file mode 100644
> index 0000000000..2bd94c1d73
> --- /dev/null
> +++ b/src/conf/backup_conf.c
> @@ -0,0 +1,546 @@
[...]
> +static void
> +virDomainBackupDiskDefClear(virDomainBackupDiskDefPtr disk)
> +{
> + VIR_FREE(disk->name);
> + virStorageSourceClear(disk->store);
> + disk->store = NULL;
This leaks disk->store
> +}
[...]
> +static int
> +virDomainBackupDiskDefParseXML(xmlNodePtr node,
> + xmlXPathContextPtr ctxt,
> + virDomainBackupDiskDefPtr def,
> + bool push, bool internal,
> + virDomainXMLOptionPtr xmlopt)
> +{
> + int ret = -1;
> + // char *backup = NULL; /* backup="yes|no"? */
> + char *type = NULL;
> + char *driver = NULL;
> + xmlNodePtr cur;
> + xmlNodePtr saved = ctxt->node;
> +
> + ctxt->node = node;
> +
> + if (VIR_ALLOC(def->store) < 0)
virStorageSource MUST be allocated using virStorageSourceNew since it's
an virObject
> + goto cleanup;
> +
> + def->name = 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 = virXMLPropString(node, "backup");
> + if (backup) {
> + def->type = virDomainCheckpointTypeFromString(checkpoint);
> + if (def->type <= 0) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("unknown disk checkpoint setting '%s'"),
> + checkpoint);
> + goto cleanup;
> + }
> + }
> +#endif
> +
> + if ((type = virXMLPropString(node, "type"))) {
> + if ((def->store->type = virStorageTypeFromString(type)) <= 0 ||
> + def->store->type == VIR_STORAGE_TYPE_VOLUME ||
> + def->store->type == VIR_STORAGE_TYPE_DIR) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("unknown disk backup type '%s'"), type);
> + goto cleanup;
> + }
> + } else {
> + def->store->type = VIR_STORAGE_TYPE_FILE;
> + }
> +
> + if ((cur = virXPathNode(push ? "./target" : "./scratch", ctxt)) &&
> + virDomainStorageSourceParse(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 = detected;
> + def->store->nodeformat = virXPathString("string(./node)", ctxt);
> + }
> +
> + if ((driver = virXPathString("string(./driver/@type)", ctxt))) {
> + def->store->format = virStorageFileFormatTypeFromString(driver);
> + if (def->store->format <= 0) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("unknown disk backup driver '%s'"), driver);
> + goto cleanup;
> + } else if (!push && def->store->format != VIR_STORAGE_FILE_QCOW2) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("pull mode requires qcow2 driver, not '%s'"),
Why is this qemuism validated in the main parser?
> + 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 = 0;
> + cleanup:
> + ctxt->node = saved;
> +
> + VIR_FREE(driver);
> +// VIR_FREE(backup);
...
> + VIR_FREE(type);
> + if (ret < 0)
> + virDomainBackupDiskDefClear(def);
> + return ret;
> +}
[...]
> +static int
> +virDomainBackupDiskDefFormat(virBufferPtr buf,
> + virDomainBackupDiskDefPtr disk,
> + bool push, bool internal)
> +{
> + int type = disk->store->type;
> + virBuffer attrBuf = VIR_BUFFER_INITIALIZER;
> + virBuffer childBuf = VIR_BUFFER_INITIALIZER;
> + int ret = -1;
> +
> + if (!disk->name)
> + return 0;
> +
> + virBufferEscapeString(buf, "<disk name='%s'", disk->name);
> + /* TODO: per-disk backup=off? */
> +
> + virBufferAsprintf(buf, " type='%s'>\n", virStorageTypeToString(type));
> + virBufferAdjustIndent(buf, 2);
> +
> + if (disk->store->format > 0)
> + virBufferEscapeString(buf, "<driver type='%s'/>\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, "<node detected='%s'",
> + disk->store->detected ? "1" : "0");
> + virBufferEscapeString(buf, ">%s</node>\n", disk->store->nodeformat);
private data?
> + }
> +
> + if (virDomainDiskSourceFormat(buf, disk->store, push ? "target" : "scratch",
> + 0, false, 0, NULL) < 0)
> + goto cleanup;
> + virBufferAdjustIndent(buf, -2);
> + virBufferAddLit(buf, "</disk>\n");
> +
> + ret = 0;
> +
> + cleanup:
> + virBufferFreeAndReset(&attrBuf);
> + virBufferFreeAndReset(&childBuf);
> + return ret;
> +}
> +
[...]
> +static int
> +virDomainBackupDefAssignStore(virDomainBackupDiskDefPtr disk,
> + virStorageSourcePtr src,
> + const char *suffix)
> +{
> + int ret = -1;
Please note in the comment that disk->store is used to switch behaviour
when all_disks is true.
> +
> + 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) == VIR_STORAGE_TYPE_FILE) {
> + if (VIR_ALLOC(disk->store) < 0)
virStorageSourceNew.
> + goto cleanup;
> + disk->store->type = VIR_STORAGE_TYPE_FILE;
> + if (virAsprintf(&disk->store->path, "%s.%s", src->path,
> + suffix) < 0)
> + goto cleanup;
> + disk->store->detected = true;
I already commented once that this should not be abused.
> + } else {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("refusing to generate file name for disk '%s'"),
> + disk->name);
> + goto cleanup;
> + }
> + }
> + ret = 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 = -1;
> + virBitmapPtr map = NULL;
> + size_t i;
> + int ndisks;
> + bool alloc_all = 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 = virBitmapNew(dom->ndisks)))
> + goto cleanup;
> +
> + /* Double check requested disks. */
> + for (i = 0; i < def->ndisks; i++) {
> + virDomainBackupDiskDefPtr disk = &def->disks[i];
> + int idx = 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 = 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 = NULL;
This will leak disk->store.
> + }
> + if (virDomainBackupDefAssignStore(disk, dom->disks[idx]->src,
> + suffix) < 0)
> + goto cleanup;
> + }
> +
> + /* Provide fillers for all remaining disks, for easier iteration. */
> + if (!def->ndisks)
> + alloc_all = true;
> + ndisks = def->ndisks;
> + if (VIR_EXPAND_N(def->disks, def->ndisks,
> + dom->ndisks - def->ndisks) < 0)
> + goto cleanup;
> +
> + for (i = 0; i < dom->ndisks; i++) {
> + virDomainBackupDiskDefPtr disk;
> +
> + if (virBitmapIsBitSet(map, i))
> + continue;
> + disk = &def->disks[ndisks++];
> + if (VIR_STRDUP(disk->name, dom->disks[i]->dst) < 0)
> + goto cleanup;
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20190709/9c14c35e/attachment-0001.sig>
More information about the libvir-list
mailing list