Re: [PATCH v4 12/20] wip: backup: Parse and output backup XML

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 2/6/19 2:18 PM, Eric Blake wrote:
> 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 <eblake@xxxxxxxxxx>
> ---
>  src/conf/checkpoint_conf.h |  65 +++++
>  src/conf/domain_conf.h     |   3 +
>  src/conf/checkpoint_conf.c | 503 +++++++++++++++++++++++++++++++++++++
>  src/libvirt_private.syms   |   8 +-
>  4 files changed, 578 insertions(+), 1 deletion(-)
> 

I think this should be it's own module src/conf/backup_conf.{c,h}

> diff --git a/src/conf/checkpoint_conf.h b/src/conf/checkpoint_conf.h
> index 994a8bd083..abaeaaad52 100644
> --- a/src/conf/checkpoint_conf.h
> +++ b/src/conf/checkpoint_conf.h
> @@ -147,4 +147,69 @@ int virDomainCheckpointRedefinePrep(virDomainPtr domain,
> 
>  VIR_ENUM_DECL(virDomainCheckpoint);
> 
> +/* Items related to incremental backup state */
> +
> +typedef enum {
> +    VIR_DOMAIN_BACKUP_TYPE_DEFAULT = 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 = 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 */
> +} virDomainBackupDiskState;
> +
> +/* 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;
> +};

Like checkpoint, the "struct _vir*" probably should go with in the .c
files and accessors created.

> +
> +VIR_ENUM_DECL(virDomainBackup);
> +
> +typedef enum {
> +    VIR_DOMAIN_BACKUP_PARSE_INTERNAL = 1 << 0,
> +} virDomainBackupParseFlags;
> +
> +virDomainBackupDefPtr virDomainBackupDefParseString(const char *xmlStr,
> +                                                    virDomainXMLOptionPtr xmlopt,
> +                                                    unsigned int flags);
> +virDomainBackupDefPtr virDomainBackupDefParseNode(xmlDocPtr xml,
> +                                                  xmlNodePtr root,
> +                                                  virDomainXMLOptionPtr xmlopt,
> +                                                  unsigned int flags);
> +void virDomainBackupDefFree(virDomainBackupDefPtr def);
> +int virDomainBackupDefFormat(virBufferPtr buf,
> +                             virDomainBackupDefPtr def,
> +                             bool internal);
> +int virDomainBackupAlignDisks(virDomainBackupDefPtr backup,
> +                              virDomainDefPtr dom, const char *suffix);
> +

Similar to previous patch.

Could also have the

VIR_DEFINE_AUTOPTR_FUNC(virDomainBackupDef, virDomainBackupDefFree);

>  #endif /* LIBVIRT_CHECKPOINT_CONF_H */
> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
> index d31c45427e..fe818a2b27 100644
> --- a/src/conf/domain_conf.h
> +++ b/src/conf/domain_conf.h
> @@ -125,6 +125,9 @@ typedef virDomainCheckpointObj *virDomainCheckpointObjPtr;
>  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 c0840a96b2..4b148827c1 100644
> --- a/src/conf/checkpoint_conf.c
> +++ b/src/conf/checkpoint_conf.c
> @@ -1028,3 +1028,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 = NULL;

Initialize idx and state?


> +}

Similar to previous w/ 2 blank lines.

> +
> +void
> +virDomainBackupDefFree(virDomainBackupDefPtr def)
> +{
> +    size_t i;
> +
> +    if (!def)
> +        return;
> +
> +    VIR_FREE(def->incremental);
> +    VIR_FREE(def->server); // FIXME which struct

True...  You'll need some what to Free only what you've allocated or if
there's a generic Server*Free function to use it.


> +    for (i = 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 = -1;
> +    //    char *backup = NULL; /* backup="yes|no"? */
> +    char *type = NULL;
> +    char *driver = NULL;
> +    xmlNodePtr cur;
> +    xmlNodePtr saved = ctxt->node;

    VIR_AUTOFREE(char *) type = NULL;
    VIR_AUTOFREE(char *) driver = NULL;

> +
> +    ctxt->node = node;
> +
> +    if (VIR_ALLOC(def->store) < 0)
> +        goto cleanup;

Rather than this you could have a @store which would stolen at the end
before cleanup if everything has passed through parsing. I'm in the
processing of adding a VIR_AUTOPTR(virStorageSource) too.

> +
> +    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

Still need to decide...

> +
> +    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)) &&
> +        virDomainDiskSourceParse(cur, ctxt, def->store, 0, xmlopt) < 0)
> +        goto cleanup;
> +
> +    if (internal) {

Is this another way of using FLAGS to determine the parse mode?  IOW, is
this only needed when parsing the status/running XML.

> +        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'"),
> +                           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);

VIR_FREE's won't be necessary w/ autofree stuff.

> +    if (ret < 0)
> +        virDomainBackupDiskDefClear(def);

Unnecessary since the caller will do this on error with
virDomainBackupDefFree

> +    return ret;
> +}
> +
> +static virDomainBackupDefPtr
> +virDomainBackupDefParse(xmlXPathContextPtr ctxt,
> +                        virDomainXMLOptionPtr xmlopt,
> +                        unsigned int flags)
> +{
> +    virDomainBackupDefPtr def = NULL;
> +    virDomainBackupDefPtr ret = NULL;
> +    xmlNodePtr *nodes = NULL;
> +    xmlNodePtr node = NULL;
> +    char *mode = NULL;
> +    bool push;
> +    size_t i;
> +    int n;

    VIR_AUTOPTR(virDomainBackupDef) def = NULL;
    VIR_AUTOFREE(char *) mode = NULL;
    VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;

> +
> +    if (VIR_ALLOC(def) < 0)
> +        goto cleanup;
> +
> +    mode = virXMLPropString(ctxt->node, "mode");
> +    if (mode) {
> +        def->type = virDomainBackupTypeFromString(mode);
> +        if (def->type <= 0) {
> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> +                           _("unknown backup mode '%s'"), mode);
> +            goto cleanup;
> +        }
> +    } else {
> +        def->type = VIR_DOMAIN_BACKUP_TYPE_PUSH;
> +    }
> +    push = def->type == VIR_DOMAIN_BACKUP_TYPE_PUSH;
> +
> +    if (flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL) {
> +        char *tmp = virXMLPropString(ctxt->node, "id");

You can use VIR_AUTOFREE(char *) tmp too...

Ahh... so this is where it's referenced... I recall this from RNG...

> +        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 = virXPathString("string(./incremental)", ctxt);
> +
> +    node = virXPathNode("./server", ctxt);
> +    if (node) {
> +        if (def->type != VIR_DOMAIN_BACKUP_TYPE_PULL) {
> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                           _("use of <server> 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 == VIR_STORAGE_NET_HOST_TRANS_RDMA) {
> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                           _("transport rdma is not supported for <server>"));

If transport == FOO is ever added and not supported, then you're in
trouble... Go with

    if (def->server->transport != VIR_STORAGE_NET_HOST_TRANS_...)
{TCP|UNIX}

and use the virStorageHostNetTransportTypeToString to translate what is
not supported...

> +            goto cleanup;
> +        }
> +    }
> +
> +    if ((n = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
> +        goto cleanup;
> +    if (n && VIR_ALLOC_N(def->disks, n) < 0)
> +        goto cleanup;
> +    def->ndisks = n;
> +    for (i = 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);

Autofree removes the need for cleanup here, so we could return NULL
directly on failure paths.
> +
> +    return ret;
> +}
> +
> +virDomainBackupDefPtr
> +virDomainBackupDefParseString(const char *xmlStr,
> +                              virDomainXMLOptionPtr xmlopt,
> +                              unsigned int flags)
> +{
> +    virDomainBackupDefPtr ret = NULL;
> +    xmlDocPtr xml;
> +    int keepBlanksDefault = xmlKeepBlanksDefault(0);
> +
> +    if ((xml = virXMLParse(NULL, xmlStr, _("(domain_backup)")))) {
> +        xmlKeepBlanksDefault(keepBlanksDefault);
> +        ret = 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 = NULL;
> +    virDomainBackupDefPtr def = NULL;
> +
> +    if (!virXMLNodeNameEqual(root, "domainbackup")) {
> +        virReportError(VIR_ERR_XML_ERROR, "%s", _("domainbackup"));
> +        goto cleanup;

    return NULL;

> +    }
> +
> +    ctxt = xmlXPathNewContext(xml);
> +    if (ctxt == NULL) {
> +        virReportOOMError();
> +        goto cleanup;

    return NULL;

> +    }
> +
> +    ctxt->node = root;
> +    def = virDomainBackupDefParse(ctxt, xmlopt, flags);
> + cleanup:
> +    xmlXPathFreeContext(ctxt);
> +    return def;
> +}
> +
> +static int
> +virDomainBackupDiskDefFormat(virBufferPtr buf,
> +                             virDomainBackupDiskDefPtr disk,
> +                             bool push, bool internal)

one arg per line

Should @internal make use of flags instead?

> +{
> +    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? */

... I assume this is the <backup> noted earlier.

> +
> +    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? */

Do we really want to store them in two places?  I think it's been hard
enough with just one.

Hazards of a design where the checkpoint and/or backup are not
subelements of the disk I suppose.

> +    if (internal) {
> +        virBufferEscapeString(buf, "<node detected='%s'",
> +                              disk->store->detected ? "1" : "0");
> +        virBufferEscapeString(buf, ">%s</node>\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, "</disk>\n");
> +
> +    ret = 0;
> +
> + cleanup:
> +    virBufferFreeAndReset(&attrBuf);
> +    virBufferFreeAndReset(&childBuf);
> +    return ret;
> +}
> +
> +int
> +virDomainBackupDefFormat(virBufferPtr buf, virDomainBackupDefPtr def,
> +                         bool internal)
> +{
> +    size_t i;
> +
> +    virBufferAsprintf(buf, "<domainbackup mode='%s'",
> +                      virDomainBackupTypeToString(def->type));
> +    if (def->id)
> +        virBufferAsprintf(buf, " id='%d'", def->id);> +    virBufferAddLit(buf, ">\n");
> +    virBufferAdjustIndent(buf, 2);
> +
> +    virBufferEscapeString(buf, "<incremental>%s</incremental>\n",
> +                          def->incremental);
> +    if (def->server) {
> +        virBufferAsprintf(buf, "<server transport='%s'",
> +                          virStorageNetHostTransportTypeToString(def->server->transport));
> +        virBufferEscapeString(buf, " name='%s'", def->server->name);
> +        if (def->server->port)
> +            virBufferAsprintf(buf, " port='%u'", def->server->port);
> +        virBufferEscapeString(buf, " socket='%s'", def->server->socket);
> +        virBufferAddLit(buf, "/>\n");
> +    }
> +
> +    if (def->ndisks) {
> +        virBufferAddLit(buf, "<disks>\n");
> +        virBufferAdjustIndent(buf, 2);
> +        for (i = 0; i < def->ndisks; i++) {
> +            if (!def->disks[i].store)
> +                continue;
> +            if (virDomainBackupDiskDefFormat(buf, &def->disks[i],
> +                                             def->type == VIR_DOMAIN_BACKUP_TYPE_PUSH,
> +                                             internal) < 0)
> +                return -1;
> +        }
> +        virBufferAdjustIndent(buf, -2);
> +        virBufferAddLit(buf, "</disks>\n");
> +    }
> +
> +    virBufferAdjustIndent(buf, -2);
> +    virBufferAddLit(buf, "</domainbackup>\n");
> +
> +    return virBufferCheckError(buf);
> +}
> +
> +
> +static int
> +virDomainBackupCompareDiskIndex(const void *a, const void *b)
> +{
> +    const virDomainBackupDiskDef *diska = a;
> +    const virDomainBackupDiskDef *diskb = 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 = -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) == VIR_STORAGE_TYPE_FILE) {
> +            if (VIR_ALLOC(disk->store) < 0)
> +                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;
> +        } else {
> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> +                           _("refusing to generate file name for disk '%s'"),
> +                           disk->name);
> +            goto cleanup;
> +        }
> +    }

does there need to be an else here ? e.g. can disk->store be set and
@src not assigned there?  The caller I assume knows to manage @src
afterwards.

> +    ret = 0;
> + cleanup:
> +    return ret;

Since there's no cleanup to be done, we could just return directly.

> +}
> +
> +/* 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. */

Similar locking concerns as previously - that is locking domain object.

This is really familiar code too - perhaps some code sharability is
possible.

> +int
> +virDomainBackupAlignDisks(virDomainBackupDefPtr def, virDomainDefPtr dom,
> +                          const char *suffix)
> +{
> +    int ret = -1;
> +    virBitmapPtr map = NULL;
> +    size_t i;
> +    int ndisks;
> +    bool alloc_all = false;

    VIR_AUTOPTR(virBitmap) map = NULL;

(could be useful other times too, but I just noticed it).

> +
> +    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)) {

Trying to picture how this happens... should this use STRNEQ_NULLABLE?

> +            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);

But no VIR_FREE(disk->source) how does that play with the following?

> +            disk->store = NULL;
> +        }
> +        if (virDomainBackupDefAssignStore(disk, dom->disks[i]->src, suffix) < 0)
> +            goto cleanup;
> +    }
> +
> +    /* Provide fillers for all remaining disks, for easier iteration.  */

But is it "hooked up" with hotplug/hotunplug?

Beginning to think/wonder why backup/checkpoint isn't a child of storage
source.


> +    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;
> +        disk->idx = 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 = 0;
> +
> + cleanup:
> +    virBitmapFree(map);

Using autofree stuff means the cleanup is unnecessary and we return
directly.


John

> +    return ret;
> +}
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index fbe7ba2d40..b9b7684494 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -69,6 +69,13 @@ virCapabilitiesSetNetPrefix;
> 
> 
>  # conf/checkpoint_conf.h
> +virDomainBackupAlignDisks;
> +virDomainBackupDefFormat;
> +virDomainBackupDefFree;
> +virDomainBackupDefParseNode;
> +virDomainBackupDefParseString;
> +virDomainBackupTypeFromString;
> +virDomainBackupTypeToString;
>  virDomainCheckpointAlignDisks;
>  virDomainCheckpointAssignDef;
>  virDomainCheckpointDefFormat;
> @@ -88,7 +95,6 @@ virDomainCheckpointTypeToString;
>  virDomainCheckpointUpdateRelations;
>  virDomainListAllCheckpoints;
> 
> -
>  # conf/cpu_conf.h
>  virCPUCacheModeTypeFromString;
>  virCPUCacheModeTypeToString;
> 


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux