mirror of
https://github.com/libvirt/libvirt.git
synced 2025-02-25 18:55:26 -06:00
qemu: block: Add support for creating 'format' layer for blockdev-add
When using blockdev-add and friends, libvirt will need to create also properties for the qcow2/raw/... format handler in qemu. This patch adds the infrastructure and implements all formats known to libvirt including all properties which are expressed at the format level in qemu. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
This commit is contained in:
parent
0e6b60dcac
commit
e17193acd2
@ -1176,3 +1176,300 @@ qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
|
|||||||
virJSONValueFree(fileprops);
|
virJSONValueFree(fileprops);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuBlockStorageSourceGetFormatRawProps(virStorageSourcePtr src,
|
||||||
|
virJSONValuePtr props)
|
||||||
|
{
|
||||||
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
||||||
|
const char *driver = "raw";
|
||||||
|
const char *secretalias = NULL;
|
||||||
|
|
||||||
|
if (src->encryption &&
|
||||||
|
src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
|
||||||
|
srcPriv &&
|
||||||
|
srcPriv->encinfo) {
|
||||||
|
driver = "luks";
|
||||||
|
secretalias = srcPriv->encinfo->s.aes.alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* currently unhandled properties for the 'raw' driver:
|
||||||
|
* 'offset'
|
||||||
|
* 'size'
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (virJSONValueObjectAdd(props,
|
||||||
|
"s:driver", driver,
|
||||||
|
"S:key-secret", secretalias, NULL) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuBlockStorageSourceGetCryptoProps(virStorageSourcePtr src,
|
||||||
|
virJSONValuePtr *encprops)
|
||||||
|
{
|
||||||
|
qemuDomainStorageSourcePrivatePtr srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
||||||
|
const char *encformat = NULL;
|
||||||
|
|
||||||
|
*encprops = NULL;
|
||||||
|
|
||||||
|
/* qemu requires encrypted secrets regardless of encryption method used when
|
||||||
|
* passed using the blockdev infrastructure, thus only
|
||||||
|
* VIR_DOMAIN_SECRET_INFO_TYPE_AES works here. The correct type needs to be
|
||||||
|
* instantiated elsewhere. */
|
||||||
|
if (!src->encryption ||
|
||||||
|
!srcpriv ||
|
||||||
|
!srcpriv->encinfo ||
|
||||||
|
srcpriv->encinfo->type != VIR_DOMAIN_SECRET_INFO_TYPE_AES)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch ((virStorageEncryptionFormatType) src->encryption->format) {
|
||||||
|
case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW:
|
||||||
|
encformat = "aes";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS:
|
||||||
|
encformat = "luks";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT:
|
||||||
|
case VIR_STORAGE_ENCRYPTION_FORMAT_LAST:
|
||||||
|
default:
|
||||||
|
virReportEnumRangeError(virStorageEncryptionFormatType,
|
||||||
|
src->encryption->format);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return virJSONValueObjectCreate(encprops,
|
||||||
|
"s:format", encformat,
|
||||||
|
"s:key-secret", srcpriv->encinfo->s.aes.alias,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuBlockStorageSourceGetFormatQcowGenericProps(virStorageSourcePtr src,
|
||||||
|
const char *format,
|
||||||
|
virJSONValuePtr props)
|
||||||
|
{
|
||||||
|
virJSONValuePtr encprops = NULL;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (qemuBlockStorageSourceGetCryptoProps(src, &encprops) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (virJSONValueObjectAdd(props,
|
||||||
|
"s:driver", format,
|
||||||
|
"A:encrypt", &encprops, NULL) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
virJSONValueFree(encprops);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuBlockStorageSourceGetFormatQcow2Props(virStorageSourcePtr src,
|
||||||
|
virJSONValuePtr props)
|
||||||
|
{
|
||||||
|
/* currently unhandled qcow2 props:
|
||||||
|
*
|
||||||
|
* 'lazy-refcounts'
|
||||||
|
* 'pass-discard-request'
|
||||||
|
* 'pass-discard-snapshot'
|
||||||
|
* 'pass-discard-other'
|
||||||
|
* 'overlap-check'
|
||||||
|
* 'l2-cache-size'
|
||||||
|
* 'l2-cache-entry-size'
|
||||||
|
* 'refcount-cache-size'
|
||||||
|
* 'cache-clean-interval'
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (qemuBlockStorageSourceGetFormatQcowGenericProps(src, "qcow2", props) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static virJSONValuePtr
|
||||||
|
qemuBlockStorageSourceGetBlockdevFormatCommonProps(virStorageSourcePtr src)
|
||||||
|
{
|
||||||
|
const char *detectZeroes = NULL;
|
||||||
|
const char *discard = NULL;
|
||||||
|
int detectZeroesMode = virDomainDiskGetDetectZeroesMode(src->discard,
|
||||||
|
src->detect_zeroes);
|
||||||
|
virJSONValuePtr props = NULL;
|
||||||
|
virJSONValuePtr ret = NULL;
|
||||||
|
|
||||||
|
if (qemuBlockNodeNameValidate(src->nodeformat) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (src->discard)
|
||||||
|
discard = virDomainDiskDiscardTypeToString(src->discard);
|
||||||
|
|
||||||
|
if (detectZeroesMode)
|
||||||
|
detectZeroes = virDomainDiskDetectZeroesTypeToString(detectZeroesMode);
|
||||||
|
|
||||||
|
/* currently unhandled global properties:
|
||||||
|
* '*force-share': 'bool'
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (virJSONValueObjectCreate(&props,
|
||||||
|
"s:node-name", src->nodeformat,
|
||||||
|
"b:read-only", src->readonly,
|
||||||
|
"S:discard", discard,
|
||||||
|
"S:detect-zeroes", detectZeroes,
|
||||||
|
NULL) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (qemuBlockStorageSourceGetBlockdevGetCacheProps(src, props) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
VIR_STEAL_PTR(ret, props);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
virJSONValueFree(props);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static virJSONValuePtr
|
||||||
|
qemuBlockStorageSourceGetBlockdevFormatProps(virStorageSourcePtr src)
|
||||||
|
{
|
||||||
|
const char *driver = NULL;
|
||||||
|
virJSONValuePtr props = NULL;
|
||||||
|
virJSONValuePtr ret = NULL;
|
||||||
|
|
||||||
|
if (!(props = qemuBlockStorageSourceGetBlockdevFormatCommonProps(src)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
switch ((virStorageFileFormat) src->format) {
|
||||||
|
case VIR_STORAGE_FILE_FAT:
|
||||||
|
/* The fat layer is emulated by the storage access layer, so we need to
|
||||||
|
* put a raw layer on top */
|
||||||
|
case VIR_STORAGE_FILE_RAW:
|
||||||
|
if (qemuBlockStorageSourceGetFormatRawProps(src, props) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_FILE_QCOW2:
|
||||||
|
if (qemuBlockStorageSourceGetFormatQcow2Props(src, props) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_FILE_QCOW:
|
||||||
|
if (qemuBlockStorageSourceGetFormatQcowGenericProps(src, "qcow", props) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* formats without any special parameters */
|
||||||
|
case VIR_STORAGE_FILE_PLOOP:
|
||||||
|
driver = "parallels";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_FILE_VHD:
|
||||||
|
driver = "vhdx";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_FILE_BOCHS:
|
||||||
|
case VIR_STORAGE_FILE_CLOOP:
|
||||||
|
case VIR_STORAGE_FILE_DMG:
|
||||||
|
case VIR_STORAGE_FILE_VDI:
|
||||||
|
case VIR_STORAGE_FILE_VPC:
|
||||||
|
case VIR_STORAGE_FILE_QED:
|
||||||
|
case VIR_STORAGE_FILE_VMDK:
|
||||||
|
driver = virStorageFileFormatTypeToString(src->format);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_FILE_AUTO_SAFE:
|
||||||
|
case VIR_STORAGE_FILE_AUTO:
|
||||||
|
case VIR_STORAGE_FILE_NONE:
|
||||||
|
case VIR_STORAGE_FILE_COW:
|
||||||
|
case VIR_STORAGE_FILE_ISO:
|
||||||
|
case VIR_STORAGE_FILE_DIR:
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("mishandled storage format '%s'"),
|
||||||
|
virStorageFileFormatTypeToString(src->format));
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
case VIR_STORAGE_FILE_LAST:
|
||||||
|
default:
|
||||||
|
virReportEnumRangeError(virStorageFileFormat, src->format);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver &&
|
||||||
|
virJSONValueObjectAdd(props, "s:driver", driver, NULL) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
VIR_STEAL_PTR(ret, props);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
virJSONValueFree(props);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qemuBlockStorageSourceGetBlockdevProps:
|
||||||
|
*
|
||||||
|
* @src: storage source to format
|
||||||
|
*
|
||||||
|
* Formats @src into a JSON object which can be used with blockdev-add or
|
||||||
|
* -blockdev. The formatted object contains both the storage and format layer
|
||||||
|
* in nested form including link to the backing chain layer if necessary.
|
||||||
|
*/
|
||||||
|
virJSONValuePtr
|
||||||
|
qemuBlockStorageSourceGetBlockdevProps(virStorageSourcePtr src)
|
||||||
|
{
|
||||||
|
bool backingSupported = src->format >= VIR_STORAGE_FILE_BACKING;
|
||||||
|
virJSONValuePtr storage = NULL;
|
||||||
|
virJSONValuePtr props = NULL;
|
||||||
|
virJSONValuePtr ret = NULL;
|
||||||
|
|
||||||
|
if (virStorageSourceHasBacking(src) && !backingSupported) {
|
||||||
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||||
|
_("storage format '%s' does not support backing store"),
|
||||||
|
virStorageFileFormatTypeToString(src->format));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(props = qemuBlockStorageSourceGetBlockdevFormatProps(src)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (!(storage = qemuBlockStorageSourceGetBackendProps(src, false)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (virJSONValueObjectAppend(props, "file", storage) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
storage = NULL;
|
||||||
|
|
||||||
|
if (src->backingStore && backingSupported) {
|
||||||
|
if (virStorageSourceHasBacking(src)) {
|
||||||
|
if (virJSONValueObjectAppendString(props, "backing",
|
||||||
|
src->backingStore->nodeformat) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
} else {
|
||||||
|
/* chain is terminated, indicate that no detection should happen
|
||||||
|
* in qemu */
|
||||||
|
if (virJSONValueObjectAppendNull(props, "backing") < 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_STEAL_PTR(ret, props);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
virJSONValueFree(storage);
|
||||||
|
virJSONValueFree(props);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -64,4 +64,7 @@ qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
|
|||||||
virURIPtr
|
virURIPtr
|
||||||
qemuBlockStorageSourceGetURI(virStorageSourcePtr src);
|
qemuBlockStorageSourceGetURI(virStorageSourcePtr src);
|
||||||
|
|
||||||
|
virJSONValuePtr
|
||||||
|
qemuBlockStorageSourceGetBlockdevProps(virStorageSourcePtr src);
|
||||||
|
|
||||||
#endif /* __QEMU_BLOCK_H__ */
|
#endif /* __QEMU_BLOCK_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user