]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: Add support for RBD namespace.
authorHan Han <hhan@redhat.com>
Wed, 26 May 2021 13:35:11 +0000 (21:35 +0800)
committerPeter Krempa <pkrempa@redhat.com>
Wed, 23 Jul 2025 12:47:25 +0000 (14:47 +0200)
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>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
docs/formatdomain.rst
src/conf/domain_conf.c
src/conf/storage_source_conf.c
src/conf/storage_source_conf.h
src/qemu/qemu_block.c
src/storage_file/storage_file_backend_gluster.c
src/storage_file/storage_source_backingstore.c
tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.args
tests/qemuxmlconfdata/disk-network-rbd.x86_64-latest.xml
tests/qemuxmlconfdata/disk-network-rbd.xml

index 54a809eaf9dc00ecefbcd15a1b0f8e38393ecde7..49d8f3fc149d921b8a2578592a215203838b64da 100644 (file)
@@ -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` )
 
index c6ef3ac2062b6dcf110d4a6d4eb4b63d682843bc..23feb2f4daef327d9fd2be9cf56ebca344343262 100644 (file)
@@ -7370,7 +7370,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,
index 2d57e7e816bbbc0572d26e913e63b19c81f15d52..087de1eaf252537f60984b315d450cc5bc2d7038 100644 (file)
@@ -1451,6 +1451,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
@@ -1465,35 +1466,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;
 }
index 17b5d2a99808d521b13c5a79a69a31b9f5f0a745..fc868b31afa69b17b1c230c90925bc8a3bd46ddb 100644 (file)
@@ -602,4 +602,5 @@ int
 virStorageSourceNetworkProtocolPathSplit(const char *path,
                                          virStorageNetProtocol protocol,
                                          char **pool,
+                                         char **namespace,
                                          char **image);
index 9b370d4c7cce5767e4ebd72fbf6e8e3defb4bec5..daefeb2f45a16ce868237a1310f807589efceb69 100644 (file)
@@ -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,
index 8778995b6cdc1f06cd1cdd6d92969742e6bde6f8..4682b859036633ee46842310c0a4a19acd50ee18 100644 (file)
@@ -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);
index 700a2f5dcb97ed5dd5fa6c63e1466cf4482ab4a1..821378883cb66982f4d0d8e54d9a3d7ffde7d730 100644 (file)
@@ -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;
     }
 
index f344b573710b5486641dd4892055ea18fb4ef048..a15263c884fe19087c1c7d3a76315f109fa090cb 100644 (file)
@@ -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 \
index 6da7185d18cb0383425cc4e7c0e545610981d75c..cfb10a701cdf5c14f0ed01998130f8cd3186aafa 100644 (file)
@@ -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'/>
index c427fbea8397a6449819058afd7eaf859798c161..0973f50ecc76bc20b210dc6da833639ce461cd5b 100644 (file)
@@ -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'/>