[stable/zed] [ATMOSPHERE-482]fix: Directly import converted image to RBD (#2069)

This is an automated cherry-pick of #2000
/assign ricolin
diff --git a/images/cinder/patches/cinder/0002-Directly-import-converted-image-to-RBD.patch b/images/cinder/patches/cinder/0002-Directly-import-converted-image-to-RBD.patch
new file mode 100644
index 0000000..5466393
--- /dev/null
+++ b/images/cinder/patches/cinder/0002-Directly-import-converted-image-to-RBD.patch
@@ -0,0 +1,135 @@
+From a37e5d80306b4902a65dd3e38501fdaddc7c822a Mon Sep 17 00:00:00 2001
+From: ricolin <rlin@vexxhost.com>
+Date: Wed, 30 Oct 2024 12:54:42 +0800
+Subject: [PATCH] Directly import converted image to RBD
+
+For volume encryption from Glance image case,
+once we cloned the image down, we do convert and import back to RBD.
+
+This patch allows us to avoid another tempfile write and directly
+upload image to RBD.
+
+Related-Bug: #2055517
+Change-Id: Ib5e15eeee6a02e2833d14ac34f6fdeb4a6548a67
+---
+ cinder/volume/drivers/rbd.py | 70 +++++++++++++++++++-----------------
+ 1 file changed, 37 insertions(+), 33 deletions(-)
+
+diff --git a/cinder/volume/drivers/rbd.py b/cinder/volume/drivers/rbd.py
+index 6cc86c2c5..7c7be68ae 100644
+--- a/cinder/volume/drivers/rbd.py
++++ b/cinder/volume/drivers/rbd.py
+@@ -33,7 +33,6 @@ from oslo_log import log as logging
+ from oslo_service import loopingcall
+ from oslo_utils import encodeutils
+ from oslo_utils import excutils
+-from oslo_utils import fileutils
+ from oslo_utils import units
+ try:
+     import rados
+@@ -1911,11 +1910,10 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
+                              image_id: str) -> None:
+         self._copy_image_to_volume(context, volume, image_service, image_id)
+ 
+-    def _encrypt_image(self,
+-                       context: context.RequestContext,
+-                       volume: Volume,
+-                       tmp_dir: str,
+-                       src_image_path: Any) -> None:
++    def _encrypt_image_and_upload(
++        self, context: context.RequestContext,
++        volume: Volume, tmp_dir: str, src_image_path: Any
++    ) -> None:
+         encryption = volume_utils.check_encryption_provider(
+             volume,
+             context)
+@@ -1929,25 +1927,29 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
+         cipher_spec = image_utils.decode_cipher(encryption['cipher'],
+                                                 encryption['key_size'])
+ 
++        _, conf, user_id, _ = self._get_config_tuple()
++        rbd_options = ''
++        if user_id:
++            rbd_options += f':id={user_id}'
++        if conf:
++            rbd_options += f':conf={conf}'
++
++        rbd_dest = 'rbd:%(pool_name)s/%(image_name)s%(rbd_options)s' % {
++            'pool_name': self.configuration.rbd_pool,
++            'image_name': volume.name,
++            'rbd_options': rbd_options
++        }
++
+         tmp_dir = volume_utils.image_conversion_dir()
+ 
+         with tempfile.NamedTemporaryFile(prefix='luks_',
+                                          dir=tmp_dir) as pass_file:
+             with open(pass_file.name, 'w') as f:
+                 f.write(passphrase)
+-
+-            # Convert the raw image to luks
+-            dest_image_path = src_image_path + '.luks'
+-            try:
+-                image_utils.convert_image(src_image_path, dest_image_path,
+-                                          'luks', src_format='raw',
+-                                          cipher_spec=cipher_spec,
+-                                          passphrase_file=pass_file.name)
+-
+-                # Replace the original image with the now encrypted image
+-                os.rename(dest_image_path, src_image_path)
+-            finally:
+-                fileutils.delete_if_exists(dest_image_path)
++            image_utils.convert_image(src_image_path, rbd_dest,
++                                      'luks', src_format='raw',
++                                      cipher_spec=cipher_spec,
++                                      passphrase_file=pass_file.name)
+ 
+     def _copy_image_to_volume(self,
+                               context: context.RequestContext,
+@@ -1964,9 +1966,6 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
+                                      self.configuration.volume_dd_blocksize,
+                                      size=volume.size)
+ 
+-            if encrypted:
+-                self._encrypt_image(context, volume, tmp_dir, tmp.name)
+-
+             @utils.retry(exception.VolumeIsBusy,
+                          self.configuration.rados_connection_interval,
+                          self.configuration.rados_connection_retries)
+@@ -1975,17 +1974,22 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
+ 
+             _delete_volume(volume)
+ 
+-            chunk_size = self.configuration.rbd_store_chunk_size * units.Mi
+-            order = int(math.log(chunk_size, 2))
+-            # keep using the command line import instead of librbd since it
+-            # detects zeroes to preserve sparseness in the image
+-            args = ['rbd', 'import',
+-                    '--pool', self.configuration.rbd_pool,
+-                    '--order', order,
+-                    tmp.name, volume.name,
+-                    '--new-format']
+-            args.extend(self._ceph_args())
+-            self._try_execute(*args)
++            if encrypted:
++                self._encrypt_image_and_upload(
++                    context, volume, tmp_dir, tmp.name)
++            else:
++                chunk_size = self.configuration.rbd_store_chunk_size * units.Mi
++                order = int(math.log(chunk_size, 2))
++                # keep using the command line import instead of librbd since it
++                # detects zeroes to preserve sparseness in the image
++                args = ['rbd', 'import',
++                        '--pool', self.configuration.rbd_pool,
++                        '--order', order,
++                        tmp.name, volume.name,
++                        '--new-format']
++                args.extend(self._ceph_args())
++                self._try_execute(*args)
++
+         self._resize(volume)
+         # We may need to re-enable replication because we have deleted the
+         # original image and created a new one using the command line import.
+-- 
+2.25.1
+