From bb358bdd42ce03da8040128bad1f79b2b436f25b Mon Sep 17 00:00:00 2001 From: Vladyslav Drok Date: Mon, 27 Jan 2020 15:31:53 +0100 Subject: [PATCH] Make RBD imagebackend flatten method idempotent If glance and nova are both configured with RBD backend, but glance does not return location information from the API, nova will fail to clone the image from glance pool and will download it from the API. In this case, image will be already flat, and subsequent flatten call will fail. This commit makes flatten call idempotent, so that it ignores already flat images by catching ImageUnacceptable when requesting parent info from ceph. Closes-Bug: 1860990 Change-Id: Ia6c184c31a980e4728b7309b2afaec4d9f494ac3 (cherry picked from commit 65825ebfbd58920adac5e8594891eec8e9cec41f) (cherry picked from commit 03d59e289369df4980bc1e7350e7f52a6f6aa828) (cherry picked from commit dd3c17216cdf2814cbefc83371c712b3dd9d9147) (cherry picked from commit 5d44052fedc9914aed4de4af3dcae4de3a03a856) --- .../unit/virt/libvirt/test_imagebackend.py | 19 ++++++++++++++++++- nova/virt/libvirt/imagebackend.py | 12 +++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/nova/tests/unit/virt/libvirt/test_imagebackend.py b/nova/tests/unit/virt/libvirt/test_imagebackend.py index a76c4f60bd3c..013c73a06b2a 100644 --- a/nova/tests/unit/virt/libvirt/test_imagebackend.py +++ b/nova/tests/unit/virt/libvirt/test_imagebackend.py @@ -1558,11 +1558,28 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase): ["server1:1899", "server2:1920"]), model) + @mock.patch.object(rbd_utils.RBDDriver, 'parent_info') @mock.patch.object(rbd_utils.RBDDriver, 'flatten') - def test_flatten(self, mock_flatten): + def test_flatten(self, mock_flatten, mock_parent_info): image = self.image_class(self.INSTANCE, self.NAME) image.flatten() mock_flatten.assert_called_once_with(image.rbd_name, pool=self.POOL) + mock_parent_info.assert_called_once_with( + image.rbd_name, pool=self.POOL) + + @mock.patch.object(imagebackend, 'LOG') + @mock.patch.object(rbd_utils.RBDDriver, 'parent_info') + @mock.patch.object(rbd_utils.RBDDriver, 'flatten') + def test_flatten_already_flat( + self, mock_flatten, mock_parent_info, mock_log): + mock_parent_info.side_effect = exception.ImageUnacceptable( + image_id=1, reason='foo') + image = self.image_class(self.INSTANCE, self.NAME) + image.flatten() + mock_log.debug.assert_called_once() + mock_flatten.assert_not_called() + mock_parent_info.assert_called_once_with( + image.rbd_name, pool=self.POOL) def test_import_file(self): image = self.image_class(self.INSTANCE, self.NAME) diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py index d185b7e1eb2a..5a7877b77801 100644 --- a/nova/virt/libvirt/imagebackend.py +++ b/nova/virt/libvirt/imagebackend.py @@ -969,7 +969,17 @@ class Rbd(Image): reason=reason) def flatten(self): - self.driver.flatten(self.rbd_name, pool=self.pool) + # NOTE(vdrok): only flatten images if they are not already flattened, + # meaning that parent info is present + try: + self.driver.parent_info(self.rbd_name, pool=self.pool) + except exception.ImageUnacceptable: + LOG.debug( + "Image %(img)s from pool %(pool)s has no parent info, " + "consider it already flat", { + 'img': self.rbd_name, 'pool': self.pool}) + else: + self.driver.flatten(self.rbd_name, pool=self.pool) def get_model(self, connection): secret = None