[PATCH 0/4] qemu: add support for RBD namespace

After recent inquiry on libvirt-users I've necromanced this already very old series that I still had laying around. This series: - turns virStorageSource's 'protocol' to real enum - removes virStorageSource's 'volume' - wires in the RBD support Han Han (1): qemu: Add support for RBD namespace. Peter Krempa (3): conf: Turn 'protocol' field of virStorageSource into proper enum type virStorageFileBackendGlusterInit: Refactor cleanup virStorageSource: Eliminate 'volume' field docs/formatdomain.rst | 6 ++ src/conf/domain_conf.c | 36 ++------- src/conf/domain_validate.c | 2 +- src/conf/storage_source_conf.c | 79 ++++++++++++++++++- src/conf/storage_source_conf.h | 10 ++- src/libvirt_private.syms | 1 + src/libxl/libxl_conf.c | 4 +- src/libxl/xen_xl.c | 4 +- src/qemu/qemu_block.c | 41 ++++++---- src/qemu/qemu_domain.c | 2 +- src/qemu/qemu_snapshot.c | 4 +- .../storage_file_backend_gluster.c | 63 ++++++++------- src/storage_file/storage_source.c | 2 - .../storage_source_backingstore.c | 56 ++++--------- .../disk-network-rbd.x86_64-latest.args | 4 +- .../disk-network-rbd.x86_64-latest.xml | 4 +- tests/qemuxmlconfdata/disk-network-rbd.xml | 4 +- 17 files changed, 185 insertions(+), 137 deletions(-) -- 2.49.0

From: Peter Krempa <pkrempa@redhat.com> Convert the member to the appropriate type, fix few offending parse calls and remove explicit typecasts in switch(). Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/domain_conf.c | 5 +---- src/conf/domain_validate.c | 2 +- src/conf/storage_source_conf.h | 2 +- src/libxl/libxl_conf.c | 2 +- src/libxl/xen_xl.c | 2 +- src/qemu/qemu_block.c | 7 +++---- src/qemu/qemu_domain.c | 2 +- src/qemu/qemu_snapshot.c | 4 ++-- .../storage_source_backingstore.c | 21 ++++++++++++------- 9 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1e24e41a48..f1057b406c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7368,15 +7368,12 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node, virStorageSource *src, unsigned int flags) { - virStorageNetProtocol protocol; xmlNodePtr tmpnode; if (virXMLPropEnum(node, "protocol", virStorageNetProtocolTypeFromString, - VIR_XML_PROP_REQUIRED, &protocol) < 0) + VIR_XML_PROP_REQUIRED, &src->protocol) < 0) return -1; - src->protocol = protocol; - if (!(src->path = virXMLPropString(node, "name")) && src->protocol != VIR_STORAGE_NET_PROTOCOL_NBD) { virReportError(VIR_ERR_XML_ERROR, "%s", diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index b28af7fa56..87fbefe4f1 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -494,7 +494,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src) return -1; } - switch ((virStorageNetProtocol) src->protocol) { + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_ISCSI: case VIR_STORAGE_NET_PROTOCOL_HTTP: case VIR_STORAGE_NET_PROTOCOL_HTTPS: diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index ebddf28cd6..362897f058 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -301,7 +301,7 @@ struct _virStorageSource { virStorageType type; char *path; char *fdgroup; /* name of group of file descriptors the user wishes to use instead of 'path' */ - int protocol; /* virStorageNetProtocol */ + virStorageNetProtocol protocol; char *volume; /* volume name for remote storage */ char *snapshot; /* for storage systems supporting internal snapshots */ char *configFile; /* some storage systems use config file as part of diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index bdd30dd65a..36fba4a555 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -1068,7 +1068,7 @@ libxlMakeNetworkDiskSrcStr(virStorageSource *src, g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; size_t i; - switch ((virStorageNetProtocol) src->protocol) { + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_HTTP: case VIR_STORAGE_NET_PROTOCOL_HTTPS: diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c index ec8de30c01..b83ed45c46 100644 --- a/src/libxl/xen_xl.c +++ b/src/libxl/xen_xl.c @@ -1448,7 +1448,7 @@ xenFormatXLDiskSrcNet(virStorageSource *src) g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; size_t i; - switch ((virStorageNetProtocol) src->protocol) { + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_HTTP: case VIR_STORAGE_NET_PROTOCOL_HTTPS: diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 32568d4ae6..a59c0a0a03 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -1046,14 +1046,13 @@ qemuBlockStorageSourceGetBackendProps(virStorageSource *src, break; } - switch ((virStorageNetProtocol) src->protocol) { + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_GLUSTER: driver = "gluster"; if (!(fileprops = qemuBlockStorageSourceGetGlusterProps(src, onlytarget))) return NULL; break; - case VIR_STORAGE_NET_PROTOCOL_HTTP: case VIR_STORAGE_NET_PROTOCOL_HTTPS: case VIR_STORAGE_NET_PROTOCOL_FTP: @@ -1969,7 +1968,7 @@ qemuBlockGetBackingStoreString(virStorageSource *src, src->readahead == 0 && src->reconnectDelay == 0) { - switch ((virStorageNetProtocol) src->protocol) { + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_HTTP: case VIR_STORAGE_NET_PROTOCOL_HTTPS: @@ -2352,7 +2351,7 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src, break; case VIR_STORAGE_TYPE_NETWORK: - switch ((virStorageNetProtocol) src->protocol) { + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_GLUSTER: driver = "gluster"; if (!(location = qemuBlockStorageSourceGetGlusterProps(src, false))) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0d2548d8d4..cf5083de34 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -8943,7 +8943,7 @@ qemuDomainPrepareStorageSourceTLS(virStorageSource *src, if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) return 0; - switch ((virStorageNetProtocol) src->protocol) { + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_VXHS: /* vxhs is no longer supported */ break; diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 8128154749..764aafda4d 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -658,7 +658,7 @@ qemuSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDef *snapdisk, break; case VIR_STORAGE_TYPE_NETWORK: - switch ((virStorageNetProtocol) domdisk->src->protocol) { + switch (domdisk->src->protocol) { case VIR_STORAGE_NET_PROTOCOL_NONE: case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_RBD: @@ -883,7 +883,7 @@ qemuSnapshotPrepareDiskInternal(virDomainDiskDef *disk, return 0; case VIR_STORAGE_TYPE_NETWORK: - switch ((virStorageNetProtocol) disk->src->protocol) { + switch (disk->src->protocol) { case VIR_STORAGE_NET_PROTOCOL_NONE: case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_RBD: diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c index 80681924ea..4a273fe46c 100644 --- a/src/storage_file/storage_source_backingstore.c +++ b/src/storage_file/storage_source_backingstore.c @@ -44,6 +44,7 @@ virStorageSourceParseBackingURI(virStorageSource *src, const char *path = NULL; int transport = 0; g_auto(GStrv) scheme = NULL; + int protocol; if (!(uri = virURIParse(uristr))) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -59,12 +60,13 @@ virStorageSourceParseBackingURI(virStorageSource *src, return -1; if (!scheme[0] || - (src->protocol = virStorageNetProtocolTypeFromString(scheme[0])) < 0) { + (protocol = virStorageNetProtocolTypeFromString(scheme[0])) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid backing protocol '%1$s'"), NULLSTR(scheme[0])); return -1; } + src->protocol = protocol; if (scheme[1]) { if ((transport = virStorageNetHostTransportTypeFromString(scheme[1])) < 0) { @@ -370,7 +372,8 @@ virStorageSourceParseBackingColon(virStorageSource *src, const char *path) { const char *p; - g_autofree char *protocol = NULL; + g_autofree char *protocol_str = NULL; + int protocol; if (!(p = strchr(path, ':'))) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -379,16 +382,18 @@ virStorageSourceParseBackingColon(virStorageSource *src, return -1; } - protocol = g_strndup(path, p - path); + protocol_str = g_strndup(path, p - path); - if ((src->protocol = virStorageNetProtocolTypeFromString(protocol)) < 0) { + if ((protocol = virStorageNetProtocolTypeFromString(protocol_str)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid backing protocol '%1$s'"), - protocol); + protocol_str); return -1; } - switch ((virStorageNetProtocol) src->protocol) { + src->protocol = protocol; + + switch (src->protocol) { case VIR_STORAGE_NET_PROTOCOL_NBD: if (virStorageSourceParseNBDColonString(path, src) < 0) return -1; @@ -404,7 +409,7 @@ virStorageSourceParseBackingColon(virStorageSource *src, case VIR_STORAGE_NET_PROTOCOL_NONE: virReportError(VIR_ERR_INTERNAL_ERROR, _("backing store parser is not implemented for protocol %1$s"), - protocol); + protocol_str); return -1; case VIR_STORAGE_NET_PROTOCOL_HTTP: @@ -419,7 +424,7 @@ virStorageSourceParseBackingColon(virStorageSource *src, case VIR_STORAGE_NET_PROTOCOL_NFS: virReportError(VIR_ERR_INTERNAL_ERROR, _("malformed backing store path for protocol %1$s"), - protocol); + protocol_str); return -1; } -- 2.49.0

From: Peter Krempa <pkrempa@redhat.com> Automatically free 'priv' and call 'glfs_fini()' directly from the two error paths. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- .../storage_file_backend_gluster.c | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/storage_file/storage_file_backend_gluster.c b/src/storage_file/storage_file_backend_gluster.c index df4df0f128..abb1c47309 100644 --- a/src/storage_file/storage_file_backend_gluster.c +++ b/src/storage_file/storage_file_backend_gluster.c @@ -97,7 +97,7 @@ static int virStorageFileBackendGlusterInit(virStorageSource *src) { virStorageDriverData *drv = src->drv; - virStorageFileBackendGlusterPriv *priv = NULL; + g_autofree virStorageFileBackendGlusterPriv *priv = NULL; size_t i; if (!src->volume) { @@ -117,31 +117,27 @@ virStorageFileBackendGlusterInit(virStorageSource *src) if (!(priv->vol = glfs_new(src->volume))) { virReportError(VIR_ERR_OPERATION_FAILED, _("failed to create glfs object for '%1$s'"), src->volume); - goto error; + return -1; } for (i = 0; i < src->nhosts; i++) { - if (virStorageFileBackendGlusterInitServer(priv, src->hosts + i) < 0) - goto error; + if (virStorageFileBackendGlusterInitServer(priv, src->hosts + i) < 0) { + glfs_fini(priv->vol); + return -1; + } } if (glfs_init(priv->vol) < 0) { virReportSystemError(errno, _("failed to initialize gluster connection (src=%1$p priv=%2$p)"), src, priv); - goto error; + glfs_fini(priv->vol); + return -1; } - drv->priv = priv; + drv->priv = g_steal_pointer(&priv); return 0; - - error: - if (priv->vol) - glfs_fini(priv->vol); - VIR_FREE(priv); - - return -1; } -- 2.49.0

From: Peter Krempa <pkrempa@redhat.com> While historically we've stored the 'pool' and 'image' properties of RBD and gluster images in separate fields but they are presented in a single field in the XML. This creates multiple points where they need to be separated and combined. Introduce helper 'virStorageSourceNetworkProtocolPathSplit' which will do that at the point of use rather than everywhere in the code. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/domain_conf.c | 30 ++-------- src/conf/storage_source_conf.c | 56 ++++++++++++++++++- src/conf/storage_source_conf.h | 7 ++- src/libvirt_private.syms | 1 + src/libxl/libxl_conf.c | 2 +- src/libxl/xen_xl.c | 2 +- src/qemu/qemu_block.c | 32 +++++++---- .../storage_file_backend_gluster.c | 41 +++++++------- src/storage_file/storage_source.c | 2 - .../storage_source_backingstore.c | 35 ++---------- 10 files changed, 113 insertions(+), 95 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f1057b406c..c7bad53ae6 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7381,6 +7381,9 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node, return -1; } + if (virStorageSourceNetworkProtocolPathSplit(src->path, src->protocol, NULL, NULL) < 0) + return -1; + if (virXMLPropTristateBool(node, "tls", VIR_XML_PROP_NONE, &src->haveTLS) < 0) return -1; @@ -7404,27 +7407,6 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node, } } - /* for historical reasons we store the volume and image name in one XML - * element although it complicates thing when attempting to access them. */ - if (src->path && - (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER || - src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { - char *tmp; - if (!(tmp = strchr(src->path, '/')) || - tmp == src->path) { - virReportError(VIR_ERR_XML_ERROR, - _("can't split path '%1$s' into pool name and image name"), - src->path); - return -1; - } - - src->volume = src->path; - - src->path = g_strdup(tmp + 1); - - tmp[0] = '\0'; - } - /* snapshot currently works only for remote disks */ src->snapshot = virXPathString("string(./snapshot/@name)", ctxt); @@ -23172,15 +23154,11 @@ virDomainDiskSourceFormatNetwork(virBuffer *attrBuf, unsigned int flags) { size_t n; - g_autofree char *path = NULL; virBufferAsprintf(attrBuf, " protocol='%s'", virStorageNetProtocolTypeToString(src->protocol)); - if (src->volume) - path = g_strdup_printf("%s/%s", src->volume, src->path); - - virBufferEscapeString(attrBuf, " name='%s'", path ? path : src->path); + virBufferEscapeString(attrBuf, " name='%s'", src->path); virBufferEscapeString(attrBuf, " query='%s'", src->query); if (src->haveTLS != VIR_TRISTATE_BOOL_ABSENT && diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 8a063be244..9014fa37e6 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -820,7 +820,6 @@ virStorageSourceCopy(const virStorageSource *src, def->path = g_strdup(src->path); def->fdgroup = g_strdup(src->fdgroup); - def->volume = g_strdup(src->volume); def->relPath = g_strdup(src->relPath); def->backingStoreRaw = g_strdup(src->backingStoreRaw); def->backingStoreRawFormat = src->backingStoreRawFormat; @@ -945,7 +944,6 @@ virStorageSourceIsSameLocation(virStorageSource *a, return false; if (STRNEQ_NULLABLE(a->path, b->path) || - STRNEQ_NULLABLE(a->volume, b->volume) || STRNEQ_NULLABLE(a->snapshot, b->snapshot)) return false; @@ -1152,7 +1150,6 @@ virStorageSourceClear(virStorageSource *def) VIR_FREE(def->path); VIR_FREE(def->fdgroup); - VIR_FREE(def->volume); VIR_FREE(def->vdpadev); VIR_FREE(def->snapshot); VIR_FREE(def->configFile); @@ -1445,3 +1442,56 @@ virStorageSourceFDTupleNew(void) { return g_object_new(vir_storage_source_fd_tuple_get_type(), NULL); } + + +/** + * virStorageSourceNetworkProtocolPathSplit: + * @path: path to split + * @protocol: protocol + * @pool: filled with pool name (may be NULL) + * @image: filled with image name (may be NULL) + * + * Historically libvirt accepted the specification of Gluster's volume and + * RBD's pool as part of the 'path' field, but internally many places require + * individual components. + * + * This helper validates and splits the path as appropriate for given protocol. + * It's useful for 'gluster' and 'rbd' protocol but for validation can be called + * with any protocol. + */ +int +virStorageSourceNetworkProtocolPathSplit(const char *path, + virStorageNetProtocol protocol, + char **pool, + char **image) +{ + + g_autofree char *pathcopy = g_strdup(path); + char *tmp; + + if (protocol != VIR_STORAGE_NET_PROTOCOL_GLUSTER && + protocol != VIR_STORAGE_NET_PROTOCOL_RBD) { + + if (image) + *image = g_steal_pointer(&pathcopy); + + return 0; + } + + if (!(tmp = strchr(pathcopy, '/')) || tmp == pathcopy) { + virReportError(VIR_ERR_XML_ERROR, + _("can't split path '%1$s' into pool name and image name"), + pathcopy); + return -1; + } + + tmp[0] = '\0'; + + if (pool) + *pool = g_steal_pointer(&pathcopy); + + if (image) + *image = g_strdup(tmp + 1); + + return 0; +} diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index 362897f058..798ca20ac4 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -302,7 +302,6 @@ struct _virStorageSource { char *path; char *fdgroup; /* name of group of file descriptors the user wishes to use instead of 'path' */ virStorageNetProtocol protocol; - char *volume; /* volume name for remote storage */ char *snapshot; /* for storage systems supporting internal snapshots */ char *configFile; /* some storage systems use config file as part of the source definition */ @@ -597,3 +596,9 @@ void virStorageSourceInitiatorClear(virStorageSourceInitiatorDef *initiator); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageAuthDef, virStorageAuthDefFree); + +int +virStorageSourceNetworkProtocolPathSplit(const char *path, + virStorageNetProtocol protocol, + char **pool, + char **image); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a8ebf9efd8..01a13048c5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1186,6 +1186,7 @@ virStorageSourceIsRelative; virStorageSourceIsSameLocation; virStorageSourceNetCookiesValidate; virStorageSourceNetworkAssignDefaultPorts; +virStorageSourceNetworkProtocolPathSplit; virStorageSourceNew; virStorageSourceNVMeDefFree; virStorageSourcePoolDefFree; diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index 36fba4a555..9d8301169b 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -1096,7 +1096,7 @@ libxlMakeNetworkDiskSrcStr(virStorageSource *src, return NULL; } - virBufferStrcat(&buf, "rbd:", src->volume, "/", src->path, NULL); + virBufferStrcat(&buf, "rbd:", src->path, NULL); if (username) { virBufferEscape(&buf, '\\', ":", ":id=%s", username); diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c index b83ed45c46..b2ff0edcf2 100644 --- a/src/libxl/xen_xl.c +++ b/src/libxl/xen_xl.c @@ -1476,7 +1476,7 @@ xenFormatXLDiskSrcNet(virStorageSource *src) return NULL; } - virBufferStrcat(&buf, "rbd:", src->volume, "/", src->path, NULL); + virBufferStrcat(&buf, "rbd:", src->path, NULL); virBufferAddLit(&buf, ":auth_supported=none"); diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index a59c0a0a03..9b370d4c7c 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -213,13 +213,9 @@ qemuBlockStorageSourceGetURI(virStorageSource *src) } if (src->path) { - if (src->volume) { - uri->path = g_strdup_printf("/%s/%s", src->volume, src->path); - } else { - uri->path = g_strdup_printf("%s%s", - g_path_is_absolute(src->path) ? "" : "/", - src->path); - } + uri->path = g_strdup_printf("%s%s", + g_path_is_absolute(src->path) ? "" : "/", + src->path); } uri->query = g_strdup(src->query); @@ -401,10 +397,17 @@ qemuBlockStorageSourceGetGlusterProps(virStorageSource *src, { g_autoptr(virJSONValue) servers = NULL; g_autoptr(virJSONValue) props = NULL; + g_autofree char *volume = NULL; + g_autofree char *path = NULL; if (!(servers = qemuBlockStorageSourceBuildHostsJSONSocketAddress(src))) return NULL; + if (virStorageSourceNetworkProtocolPathSplit(src->path, + VIR_STORAGE_NET_PROTOCOL_GLUSTER, + &volume, &path) < 0) + return NULL; + /* { driver:"gluster", * volume:"testvol", * path:"/a.img", @@ -412,8 +415,8 @@ qemuBlockStorageSourceGetGlusterProps(virStorageSource *src, * {type:"unix", socket:"/tmp/glusterd.socket"}, ...]} */ if (virJSONValueObjectAdd(&props, - "s:volume", src->volume, - "s:path", src->path, + "s:volume", volume, + "s:path", path, "a:server", &servers, NULL) < 0) return NULL; @@ -662,6 +665,13 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src, const char *username = NULL; g_autoptr(virJSONValue) authmodes = NULL; const char *keysecret = NULL; + g_autofree char *pool = NULL; + g_autofree char *image = NULL; + + if (virStorageSourceNetworkProtocolPathSplit(src->path, + VIR_STORAGE_NET_PROTOCOL_RBD, + &pool, &image) < 0) + return NULL; if (src->nhosts > 0 && !(servers = qemuBlockStorageSourceBuildHostsJSONInetSocketAddress(src))) @@ -715,8 +725,8 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src, } if (virJSONValueObjectAdd(&ret, - "s:pool", src->volume, - "s:image", src->path, + "s:pool", pool, + "s:image", image, "S:snapshot", src->snapshot, "S:conf", src->configFile, "A:server", &servers, diff --git a/src/storage_file/storage_file_backend_gluster.c b/src/storage_file/storage_file_backend_gluster.c index abb1c47309..8778995b6c 100644 --- a/src/storage_file/storage_file_backend_gluster.c +++ b/src/storage_file/storage_file_backend_gluster.c @@ -37,6 +37,7 @@ VIR_LOG_INIT("storage.storage_file_gluster"); typedef struct _virStorageFileBackendGlusterPriv virStorageFileBackendGlusterPriv; struct _virStorageFileBackendGlusterPriv { glfs_t *vol; + char *image; }; static void @@ -45,12 +46,13 @@ virStorageFileBackendGlusterDeinit(virStorageSource *src) virStorageDriverData *drv = src->drv; virStorageFileBackendGlusterPriv *priv = drv->priv; - VIR_DEBUG("deinitializing gluster storage file %p (gluster://%s:%u/%s%s)", - src, src->hosts->name, src->hosts->port, src->volume, src->path); + VIR_DEBUG("deinitializing gluster storage file %p (gluster://%s:%u/%s)", + src, src->hosts->name, src->hosts->port, src->path); if (priv->vol) glfs_fini(priv->vol); + VIR_FREE(priv->image); VIR_FREE(priv); drv->priv = NULL; } @@ -98,25 +100,25 @@ virStorageFileBackendGlusterInit(virStorageSource *src) { virStorageDriverData *drv = src->drv; g_autofree virStorageFileBackendGlusterPriv *priv = NULL; + g_autofree char *volume = NULL; + g_autofree char *image = NULL; size_t i; - if (!src->volume) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing gluster volume name for path '%1$s'"), - src->path); + if (virStorageSourceNetworkProtocolPathSplit(src->path, + VIR_STORAGE_NET_PROTOCOL_GLUSTER, + &volume, &image) < 0) return -1; - } priv = g_new0(virStorageFileBackendGlusterPriv, 1); VIR_DEBUG("initializing gluster storage file %p " "(priv='%p' volume='%s' path='%s') as [%u:%u]", - src, priv, src->volume, src->path, + src, priv, volume, image, (unsigned int)drv->uid, (unsigned int)drv->gid); - if (!(priv->vol = glfs_new(src->volume))) { + if (!(priv->vol = glfs_new(volume))) { virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to create glfs object for '%1$s'"), src->volume); + _("failed to create glfs object for '%1$s'"), volume); return -1; } @@ -135,6 +137,7 @@ virStorageFileBackendGlusterInit(virStorageSource *src) return -1; } + priv->image = g_steal_pointer(&image); drv->priv = g_steal_pointer(&priv); return 0; @@ -148,7 +151,7 @@ virStorageFileBackendGlusterCreate(virStorageSource *src) virStorageFileBackendGlusterPriv *priv = drv->priv; glfs_fd_t *fd = NULL; - if (!(fd = glfs_creat(priv->vol, src->path, + if (!(fd = glfs_creat(priv->vol, priv->image, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR))) return -1; @@ -163,7 +166,7 @@ virStorageFileBackendGlusterUnlink(virStorageSource *src) virStorageDriverData *drv = src->drv; virStorageFileBackendGlusterPriv *priv = drv->priv; - return glfs_unlink(priv->vol, src->path); + return glfs_unlink(priv->vol, priv->image); } @@ -174,7 +177,7 @@ virStorageFileBackendGlusterStat(virStorageSource *src, virStorageDriverData *drv = src->drv; virStorageFileBackendGlusterPriv *priv = drv->priv; - return glfs_stat(priv->vol, src->path, st); + return glfs_stat(priv->vol, priv->image, st); } @@ -193,15 +196,15 @@ virStorageFileBackendGlusterRead(virStorageSource *src, *buf = NULL; - if (!(fd = glfs_open(priv->vol, src->path, O_RDONLY))) { + if (!(fd = glfs_open(priv->vol, priv->image, O_RDONLY))) { virReportSystemError(errno, _("Failed to open file '%1$s'"), - src->path); + priv->image); return -1; } if (offset > 0) { if (glfs_lseek(fd, offset, SEEK_SET) == (off_t) -1) { - virReportSystemError(errno, _("cannot seek into '%1$s'"), src->path); + virReportSystemError(errno, _("cannot seek into '%1$s'"), priv->image); goto cleanup; } } @@ -216,7 +219,7 @@ virStorageFileBackendGlusterRead(virStorageSource *src, continue; if (r < 0) { VIR_FREE(*buf); - virReportSystemError(errno, _("unable to read '%1$s'"), src->path); + virReportSystemError(errno, _("unable to read '%1$s'"), priv->image); return r; } if (r == 0) @@ -243,7 +246,7 @@ virStorageFileBackendGlusterAccess(virStorageSource *src, virStorageDriverData *drv = src->drv; virStorageFileBackendGlusterPriv *priv = drv->priv; - return glfs_access(priv->vol, src->path, mode); + return glfs_access(priv->vol, priv->image, mode); } static int @@ -254,7 +257,7 @@ virStorageFileBackendGlusterChown(const virStorageSource *src, virStorageDriverData *drv = src->drv; virStorageFileBackendGlusterPriv *priv = drv->priv; - return glfs_chown(priv->vol, src->path, uid, gid); + return glfs_chown(priv->vol, priv->image, uid, gid); } diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index fa59949cf2..843910a0d8 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -392,8 +392,6 @@ virStorageSourceNewFromBackingRelative(virStorageSource *parent, def->nhosts = parent->nhosts; } - - def->volume = g_strdup(parent->volume); } else { /* set the type to _FILE, the caller shall update it to the actual type */ def->type = VIR_STORAGE_TYPE_FILE; diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c index 4a273fe46c..700a2f5dcb 100644 --- a/src/storage_file/storage_source_backingstore.c +++ b/src/storage_file/storage_source_backingstore.c @@ -108,27 +108,9 @@ virStorageSourceParseBackingURI(virStorageSource *src, src->path = g_strdup(path); if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) { - char *tmp; - - if (!src->path) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("missing volume name and path for gluster volume")); - return -1; - } - - if (!(tmp = strchr(src->path, '/')) || - tmp == src->path) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("missing volume name or file name in gluster source path '%1$s'"), - src->path); + if (virStorageSourceNetworkProtocolPathSplit(src->path, src->protocol, + NULL, NULL) < 0) return -1; - } - - src->volume = src->path; - - src->path = g_strdup(tmp + 1); - - tmp[0] = '\0'; } src->hosts->port = uri->port; @@ -211,13 +193,6 @@ virStorageSourceParseRBDColonString(const char *rbdstr, *p = '\0'; } - /* pool vs. image name */ - if ((p = strchr(src->path, '/'))) { - src->volume = g_steal_pointer(&src->path); - src->path = g_strdup(p + 1); - *p = '\0'; - } - /* options */ if (!options) return 0; /* all done */ @@ -703,8 +678,7 @@ virStorageSourceParseBackingJSONGluster(virStorageSource *src, src->type = VIR_STORAGE_TYPE_NETWORK; src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER; - src->volume = g_strdup(volume); - src->path = g_strdup(path); + src->path = g_strdup_printf("%s/%s", volume, path); nservers = virJSONValueArraySize(server); if (nservers == 0) { @@ -959,8 +933,7 @@ virStorageSourceParseBackingJSONRBD(virStorageSource *src, return -1; } - src->volume = g_strdup(pool); - src->path = g_strdup(image); + src->path = g_strdup_printf("%s/%s", pool, image); src->snapshot = g_strdup(snapshot); src->configFile = g_strdup(conf); -- 2.49.0

From: Han Han <hhan@redhat.com> Since Nautilus ceph supports separate image namespaces within a pool for tenant isolation and QEMU adds it as a rbd blockdev options from 5.0.0. The source name with format "<pool>/<namespace>/<image>" could be used to access a rbd image with namespace. Add unit tests for this attribute. https://bugzilla.redhat.com/show_bug.cgi?id=1816909 Closes: https://gitlab.com/libvirt/libvirt/-/issues/405 Signed-off-by: Han Han <hhan@redhat.com> Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- docs/formatdomain.rst | 6 +++ src/conf/domain_conf.c | 3 +- src/conf/storage_source_conf.c | 47 ++++++++++++++----- src/conf/storage_source_conf.h | 1 + src/qemu/qemu_block.c | 6 ++- .../storage_file_backend_gluster.c | 2 +- .../storage_source_backingstore.c | 2 +- .../disk-network-rbd.x86_64-latest.args | 4 +- .../disk-network-rbd.x86_64-latest.xml | 4 +- tests/qemuxmlconfdata/disk-network-rbd.xml | 4 +- 10 files changed, 56 insertions(+), 23 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 9a2f065590..e8022e502d 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -2958,6 +2958,12 @@ paravirtualized driver is specified via the ``disk`` element. the optional attribute ``tlsHostname`` can be used to override the expected host name of the NBD server used for TLS certificate verification. + For "rbd", the ``name`` attribute could be two formats: the format of + ``pool_name/image_name`` includes the rbd pool name and image name with + default rbd pool namespace; for the customized namespace, the format is + ``pool_name/namespace/image_name`` ( :since:`Since 11.6.0 and QEMU 5.0` ). + The pool name, namespace and image are separated by slash. + For protocols ``http`` and ``https`` an optional attribute ``query`` specifies the query string. ( :since:`Since 6.2.0` ) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c7bad53ae6..7a8713771c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7381,7 +7381,8 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node, return -1; } - if (virStorageSourceNetworkProtocolPathSplit(src->path, src->protocol, NULL, NULL) < 0) + if (virStorageSourceNetworkProtocolPathSplit(src->path, src->protocol, + NULL, NULL, NULL) < 0) return -1; if (virXMLPropTristateBool(node, "tls", VIR_XML_PROP_NONE, diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 9014fa37e6..dd46ea695b 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -1449,6 +1449,7 @@ virStorageSourceFDTupleNew(void) * @path: path to split * @protocol: protocol * @pool: filled with pool name (may be NULL) + * @namespace: filed with namespace (may be NULL) * @image: filled with image name (may be NULL) * * Historically libvirt accepted the specification of Gluster's volume and @@ -1463,35 +1464,57 @@ int virStorageSourceNetworkProtocolPathSplit(const char *path, virStorageNetProtocol protocol, char **pool, + char **namespace, char **image) { - - g_autofree char *pathcopy = g_strdup(path); - char *tmp; + g_auto(GStrv) tokens = NULL; + int components_max = 2; + size_t ncomponents = 0; + size_t i; if (protocol != VIR_STORAGE_NET_PROTOCOL_GLUSTER && protocol != VIR_STORAGE_NET_PROTOCOL_RBD) { if (image) - *image = g_steal_pointer(&pathcopy); + *image = g_strdup(path); return 0; } - if (!(tmp = strchr(pathcopy, '/')) || tmp == pathcopy) { - virReportError(VIR_ERR_XML_ERROR, - _("can't split path '%1$s' into pool name and image name"), - pathcopy); - return -1; + if (protocol == VIR_STORAGE_NET_PROTOCOL_RBD) { + /* the name of rbd can be <pool>/<image> or <pool>/<namespace>/<image> */ + components_max = 3; } - tmp[0] = '\0'; + if ((tokens = g_strsplit(path, "/", components_max))) + ncomponents = g_strv_length(tokens); + + if (ncomponents < 2) + goto error; + + for (i = 0; i < ncomponents; i++) { + if (*tokens[i] == '\0') + goto error; + } if (pool) - *pool = g_steal_pointer(&pathcopy); + *pool = g_strdup(tokens[0]); + + if (namespace) { + if (ncomponents == 3) + *namespace = g_strdup(tokens[1]); + else + *namespace = NULL; + } if (image) - *image = g_strdup(tmp + 1); + *image = g_strdup(tokens[ncomponents - 1]); return 0; + + error: + virReportError(VIR_ERR_XML_ERROR, + _("failed to split path '%1$s' into 'pool/image' or 'pool/namespace/image' components"), + path); + return -1; } diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index 798ca20ac4..aa4efda9ad 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -601,4 +601,5 @@ int virStorageSourceNetworkProtocolPathSplit(const char *path, virStorageNetProtocol protocol, char **pool, + char **namespace, char **image); diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 9b370d4c7c..daefeb2f45 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -405,7 +405,7 @@ qemuBlockStorageSourceGetGlusterProps(virStorageSource *src, if (virStorageSourceNetworkProtocolPathSplit(src->path, VIR_STORAGE_NET_PROTOCOL_GLUSTER, - &volume, &path) < 0) + &volume, NULL, &path) < 0) return NULL; /* { driver:"gluster", @@ -666,11 +666,12 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src, g_autoptr(virJSONValue) authmodes = NULL; const char *keysecret = NULL; g_autofree char *pool = NULL; + g_autofree char *namespace = NULL; g_autofree char *image = NULL; if (virStorageSourceNetworkProtocolPathSplit(src->path, VIR_STORAGE_NET_PROTOCOL_RBD, - &pool, &image) < 0) + &pool, &namespace, &image) < 0) return NULL; if (src->nhosts > 0 && @@ -726,6 +727,7 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src, if (virJSONValueObjectAdd(&ret, "s:pool", pool, + "S:namespace", namespace, "s:image", image, "S:snapshot", src->snapshot, "S:conf", src->configFile, diff --git a/src/storage_file/storage_file_backend_gluster.c b/src/storage_file/storage_file_backend_gluster.c index 8778995b6c..4682b85903 100644 --- a/src/storage_file/storage_file_backend_gluster.c +++ b/src/storage_file/storage_file_backend_gluster.c @@ -106,7 +106,7 @@ virStorageFileBackendGlusterInit(virStorageSource *src) if (virStorageSourceNetworkProtocolPathSplit(src->path, VIR_STORAGE_NET_PROTOCOL_GLUSTER, - &volume, &image) < 0) + &volume, NULL, &image) < 0) return -1; priv = g_new0(virStorageFileBackendGlusterPriv, 1); diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c index 700a2f5dcb..821378883c 100644 --- a/src/storage_file/storage_source_backingstore.c +++ b/src/storage_file/storage_source_backingstore.c @@ -109,7 +109,7 @@ virStorageSourceParseBackingURI(virStorageSource *src, if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) { if (virStorageSourceNetworkProtocolPathSplit(src->path, src->protocol, - NULL, NULL) < 0) + NULL, NULL, NULL) < 0) return -1; } diff --git a/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.args b/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.args index f344b57371..a15263c884 100644 --- a/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.args +++ b/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.args @@ -36,9 +36,9 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ -blockdev '{"driver":"rbd","pool":"pool","image":"image","snapshot":"foo","conf":"/blah/test.conf","node-name":"libvirt-3-storage","read-only":false}' \ -device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x5","drive":"libvirt-3-storage","id":"virtio-disk3"}' \ -object '{"qom-type":"secret","id":"libvirt-2-storage-auth-secret0","data":"9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \ --blockdev '{"driver":"rbd","pool":"pool","image":"image","server":[{"host":"mon1.example.org","port":"6321"},{"host":"mon2.example.org","port":"6322"},{"host":"mon3.example.org","port":"6322"}],"user":"myname","auth-client-required":["cephx","none"],"key-secret":"libvirt-2-storage-auth-secret0","node-name":"libvirt-2-storage","read-only":false}' \ +-blockdev '{"driver":"rbd","pool":"pool","namespace":"namespace","image":"image","server":[{"host":"mon1.example.org","port":"6321"},{"host":"mon2.example.org","port":"6322"},{"host":"mon3.example.org","port":"6322"}],"user":"myname","auth-client-required":["cephx","none"],"key-secret":"libvirt-2-storage-auth-secret0","node-name":"libvirt-2-storage","read-only":false}' \ -device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x6","drive":"libvirt-2-storage","id":"virtio-disk4"}' \ --blockdev '{"driver":"rbd","pool":"pool","image":"image","server":[{"host":"::1","port":"6321"},{"host":"example.org","port":"6789"},{"host":"ffff:1234:567:abc::0f","port":"6322"},{"host":"2001:db8::ff00:42:8329","port":"6322"}],"node-name":"libvirt-1-storage","read-only":false}' \ +-blockdev '{"driver":"rbd","pool":"pool","namespace":"namespace","image":"image","server":[{"host":"::1","port":"6321"},{"host":"example.org","port":"6789"},{"host":"ffff:1234:567:abc::0f","port":"6322"},{"host":"2001:db8::ff00:42:8329","port":"6322"}],"node-name":"libvirt-1-storage","read-only":false}' \ -device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x7","drive":"libvirt-1-storage","id":"virtio-disk5"}' \ -audiodev '{"id":"audio1","driver":"none"}' \ -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ diff --git a/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.xml b/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.xml index 6da7185d18..cfb10a701c 100644 --- a/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.xml +++ b/tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.xml @@ -60,7 +60,7 @@ <auth username='myname'> <secret type='ceph' usage='mycluster_myname'/> </auth> - <source protocol='rbd' name='pool/image'> + <source protocol='rbd' name='pool/namespace/image'> <host name='mon1.example.org' port='6321'/> <host name='mon2.example.org' port='6322'/> <host name='mon3.example.org' port='6322'/> @@ -70,7 +70,7 @@ </disk> <disk type='network' device='disk'> <driver name='qemu' type='raw'/> - <source protocol='rbd' name='pool/image'> + <source protocol='rbd' name='pool/namespace/image'> <host name='::1' port='6321'/> <host name='example.org' port='6789'/> <host name='ffff:1234:567:abc::0f' port='6322'/> diff --git a/tests/qemuxmlconfdata/disk-network-rbd.xml b/tests/qemuxmlconfdata/disk-network-rbd.xml index c427fbea83..0973f50ecc 100644 --- a/tests/qemuxmlconfdata/disk-network-rbd.xml +++ b/tests/qemuxmlconfdata/disk-network-rbd.xml @@ -53,7 +53,7 @@ <auth username='myname'> <secret type='ceph' usage='mycluster_myname'/> </auth> - <source protocol='rbd' name='pool/image'> + <source protocol='rbd' name='pool/namespace/image'> <host name='mon1.example.org' port='6321'/> <host name='mon2.example.org' port='6322'/> <host name='mon3.example.org' port='6322'/> @@ -62,7 +62,7 @@ </disk> <disk type='network' device='disk'> <driver name='qemu' type='raw'/> - <source protocol='rbd' name='pool/image'> + <source protocol='rbd' name='pool/namespace/image'> <host name='::1' port='6321'/> <host name='example.org' port='6789'/> <host name='ffff:1234:567:abc::0f' port='6322'/> -- 2.49.0

On Wed, Jun 25, 2025 at 17:56:33 +0200, Peter Krempa wrote:
After recent inquiry on libvirt-users I've necromanced this already very old series that I still had laying around.
This series: - turns virStorageSource's 'protocol' to real enum - removes virStorageSource's 'volume' - wires in the RBD support
Han Han (1): qemu: Add support for RBD namespace.
Peter Krempa (3): conf: Turn 'protocol' field of virStorageSource into proper enum type virStorageFileBackendGlusterInit: Refactor cleanup virStorageSource: Eliminate 'volume' field
Ping ;)

On 6/25/25 17:56, Peter Krempa via Devel wrote:
After recent inquiry on libvirt-users I've necromanced this already very old series that I still had laying around.
This series: - turns virStorageSource's 'protocol' to real enum - removes virStorageSource's 'volume' - wires in the RBD support
Han Han (1): qemu: Add support for RBD namespace.
Peter Krempa (3): conf: Turn 'protocol' field of virStorageSource into proper enum type virStorageFileBackendGlusterInit: Refactor cleanup virStorageSource: Eliminate 'volume' field
docs/formatdomain.rst | 6 ++ src/conf/domain_conf.c | 36 ++------- src/conf/domain_validate.c | 2 +- src/conf/storage_source_conf.c | 79 ++++++++++++++++++- src/conf/storage_source_conf.h | 10 ++- src/libvirt_private.syms | 1 + src/libxl/libxl_conf.c | 4 +- src/libxl/xen_xl.c | 4 +- src/qemu/qemu_block.c | 41 ++++++---- src/qemu/qemu_domain.c | 2 +- src/qemu/qemu_snapshot.c | 4 +- .../storage_file_backend_gluster.c | 63 ++++++++------- src/storage_file/storage_source.c | 2 - .../storage_source_backingstore.c | 56 ++++--------- .../disk-network-rbd.x86_64-latest.args | 4 +- .../disk-network-rbd.x86_64-latest.xml | 4 +- tests/qemuxmlconfdata/disk-network-rbd.xml | 4 +- 17 files changed, 185 insertions(+), 137 deletions(-)
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal
participants (2)
-
Michal Prívozník
-
Peter Krempa