diff --git a/nova/tests/unit/virt/libvirt/test_imagebackend.py b/nova/tests/unit/virt/libvirt/test_imagebackend.py index 764c544eae61..d825583e9619 100644 --- a/nova/tests/unit/virt/libvirt/test_imagebackend.py +++ b/nova/tests/unit/virt/libvirt/test_imagebackend.py @@ -1582,11 +1582,28 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase): ["server1:1899", "server2:1920", "[::1]:1930"]), 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 a6cbd0ea114e..cfb0040d3fd1 100644 --- a/nova/virt/libvirt/imagebackend.py +++ b/nova/virt/libvirt/imagebackend.py @@ -980,7 +980,17 @@ class Rbd(Image): reason=reason) def flatten(self): - self.driver.flatten(self.rbd_name, pool=self.driver.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.driver.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.driver.pool}) + else: + self.driver.flatten(self.rbd_name, pool=self.driver.pool) def get_model(self, connection): secret = None