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 65825ebfbd)
(cherry picked from commit 03d59e2893)
(cherry picked from commit dd3c17216c)
This commit is contained in:
Vladyslav Drok 2020-01-27 15:31:53 +01:00 committed by Lee Yarwood
parent fd50cfacd2
commit 5d44052fed
2 changed files with 29 additions and 2 deletions

View File

@ -1550,11 +1550,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)

View File

@ -970,7 +970,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