conf: implement XML parsing/formating for <dataStore> element of a storage <source>

Introduce parsing and formatting of <dataStore> element. The <dataStore
represents a different storage volume meant for storing the actual
blocks of guest-visible data. The original disk source is then just a
metadata storage for any advanced features.

This currently works only for 'qcow2' images.

Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com>
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
This commit is contained in:
Nikolai Barybin 2024-11-20 18:48:39 +03:00 committed by Peter Krempa
parent 28d88e9fc3
commit b3171cf8da
2 changed files with 113 additions and 0 deletions

View File

@ -7561,6 +7561,53 @@ virDomainStorageSourceParseSlices(virStorageSource *src,
} }
static int
virDomainDiskDataStoreParse(xmlXPathContextPtr ctxt,
virStorageSource *src,
unsigned int flags,
virDomainXMLOption *xmlopt)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
xmlNodePtr source;
g_autofree char *type = NULL;
g_autofree char *format = NULL;
g_autofree char *idx = NULL;
g_autoptr(virStorageSource) dataFileStore = NULL;
if (!(ctxt->node = virXPathNode("./dataStore", ctxt)))
return 0;
if (!(type = virXMLPropStringRequired(ctxt->node, "type")))
return -1;
if (!(format = virXPathString("string(./format/@type)", ctxt))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing data file store format"));
return -1;
}
if (!(source = virXPathNode("./source", ctxt))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing disk data file store source"));
return -1;
}
if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
idx = virXMLPropString(source, "index");
if (!(dataFileStore = virDomainStorageSourceParseBase(type, format, idx)))
return -1;
if (virDomainStorageSourceParse(source, ctxt, dataFileStore, flags, xmlopt) < 0)
return -1;
dataFileStore->readonly = src->readonly;
src->dataFileStore = g_steal_pointer(&dataFileStore);
return 0;
}
/** /**
* virDomainStorageSourceParse: * virDomainStorageSourceParse:
* @node: XML node pointing to the source element to parse * @node: XML node pointing to the source element to parse
@ -7651,6 +7698,9 @@ virDomainStorageSourceParse(xmlNodePtr node,
ctxt, flags) < 0) ctxt, flags) < 0)
return -1; return -1;
if (virDomainDiskDataStoreParse(ctxt, src, flags, xmlopt) < 0)
return -1;
/* People sometimes pass a bogus '' source path when they mean to omit the /* People sometimes pass a bogus '' source path when they mean to omit the
* source element completely (e.g. CDROM without media). This is just a * source element completely (e.g. CDROM without media). This is just a
* little compatibility check to help those broken apps */ * little compatibility check to help those broken apps */
@ -7720,6 +7770,7 @@ virDomainDiskBackingStoreParse(xmlXPathContextPtr ctxt,
backingStore->readonly = true; backingStore->readonly = true;
if (virDomainStorageSourceParse(source, ctxt, backingStore, flags, xmlopt) < 0 || if (virDomainStorageSourceParse(source, ctxt, backingStore, flags, xmlopt) < 0 ||
virDomainDiskDataStoreParse(ctxt, backingStore, flags, xmlopt) < 0 ||
virDomainDiskBackingStoreParse(ctxt, backingStore, flags, xmlopt) < 0) virDomainDiskBackingStoreParse(ctxt, backingStore, flags, xmlopt) < 0)
return -1; return -1;
@ -22762,6 +22813,39 @@ virDomainDiskSourceFormatSlices(virBuffer *buf,
} }
static int
virDomainDiskDataStoreFormat(virBuffer *buf,
virStorageSource *src,
virDomainXMLOption *xmlopt,
unsigned int flags)
{
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) formatAttrBuf = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
bool inactive = flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE;
if (!src->dataFileStore)
return 0;
/* don't write detected data file member to inactive xml */
if (inactive && src->dataFileStore->detected)
return 0;
virBufferAsprintf(&attrBuf, " type='%s'",
virStorageTypeToString(src->dataFileStore->type));
virBufferAsprintf(&formatAttrBuf, " type='%s'", "raw");
virXMLFormatElement(&childBuf, "format", &formatAttrBuf, NULL);
if (virDomainDiskSourceFormat(&childBuf, src->dataFileStore, "source", 0,
true, flags, false, false, xmlopt) < 0)
return -1;
virXMLFormatElement(buf, "dataStore", &attrBuf, &childBuf);
return 0;
}
/** /**
* virDomainDiskSourceFormat: * virDomainDiskSourceFormat:
* @buf: output buffer * @buf: output buffer
@ -22877,6 +22961,9 @@ virDomainDiskSourceFormat(virBuffer *buf,
if (attrIndex && src->id != 0) if (attrIndex && src->id != 0)
virBufferAsprintf(&attrBuf, " index='%u'", src->id); virBufferAsprintf(&attrBuf, " index='%u'", src->id);
if (virDomainDiskDataStoreFormat(&childBuf, src, xmlopt, flags) < 0)
return -1;
if (virDomainDiskSourceFormatPrivateData(&childBuf, src, flags, xmlopt) < 0) if (virDomainDiskSourceFormatPrivateData(&childBuf, src, flags, xmlopt) < 0)
return -1; return -1;

View File

@ -542,6 +542,32 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
} }
} }
if (src->dataFileStore) {
if (src->format != VIR_STORAGE_FILE_QCOW2) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("<dataStore> feature available only with qcow2 images"));
return -1;
}
if (src->dataFileStore->format != VIR_STORAGE_FILE_RAW) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only 'raw' images supported as <dataStore>"));
return -1;
}
if (src->dataFileStore->dataFileStore || src->backingStore) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("The <source> of <dataStore> can't have another nested <dataStore> or <backingStore> element"));
return -1;
}
if (src->dataFileStore->sliceStorage) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("slices are not supported for <dataStore>"));
return -1;
}
}
/* internal snapshots and config files are currently supported only with rbd: */ /* internal snapshots and config files are currently supported only with rbd: */
if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK && if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) { src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {