Merge "Fix failure to boot instances with qcow2 format images"

This commit is contained in:
Zuul 2019-05-21 08:28:39 +00:00 committed by Gerrit Code Review
commit 2f26a22100
8 changed files with 110 additions and 3 deletions

View File

@ -232,6 +232,9 @@ Possible values:
Related options:
* ``compute_driver``: Only the libvirt driver uses this option.
* ``[libvirt]/images_type``: If images_type is rbd, setting this option
to False is not allowed. See the bug
https://bugs.launchpad.net/nova/+bug/1816686 for more details.
"""),
# NOTE(yamahata): ListOpt won't work because the command may include a comma.
# For example:

View File

@ -856,6 +856,7 @@ Related options:
* compute.use_cow_images
* images_volume_group
* [workarounds]/ensure_libvirt_rbd_instance_dir_cleanup
* compute.force_raw_images
"""),
cfg.StrOpt('images_volume_group',
help="""

View File

@ -187,6 +187,10 @@ class Invalid(NovaException):
code = 400
class InvalidConfiguration(Invalid):
msg_fmt = _("Configuration is Invalid.")
class InvalidBDM(Invalid):
msg_fmt = _("Block Device Mapping is Invalid.")

View File

@ -1411,12 +1411,44 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
mock_exists.assert_has_calls([mock.call(), mock.call()])
fn.assert_called_once_with(target=self.TEMPLATE_PATH)
@mock.patch.object(images, 'qemu_img_info')
@mock.patch.object(os.path, 'exists', return_value=False)
def test__remove_non_raw_cache_image_not_exists(
self, mock_exists, mock_qemu):
image = self.image_class(self.INSTANCE, self.NAME)
image._remove_non_raw_cache_image(self.TEMPLATE_PATH)
mock_qemu.assert_not_called()
@mock.patch.object(os, 'remove')
@mock.patch.object(images, 'qemu_img_info',
return_value=imageutils.QemuImgInfo())
@mock.patch.object(os.path, 'exists', return_value=True)
def test__remove_non_raw_cache_image_with_raw_cache(
self, mock_exists, mock_qemu, mock_remove):
mock_qemu.return_value.file_format = 'raw'
image = self.image_class(self.INSTANCE, self.NAME)
image._remove_non_raw_cache_image(self.TEMPLATE_PATH)
mock_remove.assert_not_called()
@mock.patch.object(os, 'remove')
@mock.patch.object(images, 'qemu_img_info',
return_value=imageutils.QemuImgInfo())
@mock.patch.object(os.path, 'exists', return_value=True)
def test__remove_non_raw_cache_image_with_qcow2_cache(
self, mock_exists, mock_qemu, mock_remove):
mock_qemu.return_value.file_format = 'qcow2'
image = self.image_class(self.INSTANCE, self.NAME)
image._remove_non_raw_cache_image(self.TEMPLATE_PATH)
mock_remove.assert_called_once_with(self.TEMPLATE_PATH)
@mock.patch.object(images, 'qemu_img_info',
return_value=imageutils.QemuImgInfo())
@mock.patch.object(rbd_utils.RBDDriver, 'resize')
@mock.patch.object(imagebackend.Rbd, 'verify_base_size')
@mock.patch.object(imagebackend.Rbd, 'get_disk_size')
@mock.patch.object(imagebackend.Rbd, 'exists')
def test_create_image_resize(self, mock_exists, mock_get,
mock_verify, mock_resize):
mock_verify, mock_resize, mock_qemu):
fn = mock.MagicMock()
full_size = self.SIZE * 2
@ -1427,6 +1459,7 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
image = self.image_class(self.INSTANCE, self.NAME)
mock_exists.return_value = False
mock_qemu.return_value.file_format = 'raw'
mock_get.return_value = self.SIZE
rbd_name = "%s_%s" % (self.INSTANCE['uuid'], self.NAME)
cmd = ('rbd', 'import', '--pool', self.POOL, self.TEMPLATE_PATH,
@ -1443,13 +1476,17 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
mock_verify.assert_called_once_with(self.TEMPLATE_PATH, full_size)
fn.assert_called_once_with(target=self.TEMPLATE_PATH)
@mock.patch.object(images, 'qemu_img_info',
return_value=imageutils.QemuImgInfo())
@mock.patch.object(imagebackend.Rbd, 'get_disk_size')
@mock.patch.object(imagebackend.Rbd, 'exists')
def test_create_image_already_exists(self, mock_exists, mock_get):
def test_create_image_already_exists(self, mock_exists, mock_get,
mock_qemu):
rbd_utils.rbd.RBD_FEATURE_LAYERING = 1
image = self.image_class(self.INSTANCE, self.NAME)
mock_exists.return_value = True
mock_qemu.return_value.file_format = 'raw'
mock_get.return_value = self.SIZE
rbd_name = "%s_%s" % (self.INSTANCE['uuid'], self.NAME)
fn = mock.MagicMock()
@ -1507,7 +1544,10 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
self.assertEqual(2361393152, image.get_disk_size(image.path))
size_mock.assert_called_once_with(image.rbd_name)
def test_create_image_too_small(self):
@mock.patch.object(images, 'qemu_img_info',
return_value=imageutils.QemuImgInfo())
def test_create_image_too_small(self, mock_qemu):
mock_qemu.return_value.file_format = 'raw'
image = self.image_class(self.INSTANCE, self.NAME)
with mock.patch.object(image, 'driver') as driver_mock:
driver_mock.exists.return_value = True

View File

@ -901,6 +901,21 @@ class LibvirtConnTestCase(_VirtDriverTestCase, test.TestCase):
# stub out the unplug call to os-vif since we don't care about it.
self.stub_out('os_vif.unplug', lambda a, kw: None)
def test_init_host_image_type_rbd_force_raw_images_true(self):
CONF.set_override('images_type', 'rbd', group='libvirt')
CONF.set_override('force_raw_images', True)
self.connection.init_host('myhostname')
def test_init_host_image_type_non_rbd(self):
CONF.set_override('images_type', 'default', group='libvirt')
self.connection.init_host('myhostname')
def test_init_host_raise_invalid_configuration(self):
CONF.set_override('images_type', 'rbd', group='libvirt')
CONF.set_override('force_raw_images', False)
self.assertRaises(exception.InvalidConfiguration,
self.connection.init_host, 'myhostname')
def test_force_hard_reboot(self):
self.flags(wait_soft_reboot_seconds=0, group='libvirt')
self.test_reboot()

View File

@ -612,6 +612,18 @@ class LibvirtDriver(driver.ComputeDriver):
"'live_migration_with_native_tls'.")
raise exception.Invalid(msg)
# Some imagebackends are only able to import raw disk images,
# and will fail if given any other format. See the bug
# https://bugs.launchpad.net/nova/+bug/1816686 for more details.
if CONF.libvirt.images_type in ('rbd',):
if not CONF.force_raw_images:
msg = _("'[DEFAULT]/force_raw_images = False' is not "
"allowed with '[libvirt]/images_type = rbd'. "
"Please check the two configs and if you really "
"do want to use rbd as images_type, set "
"force_raw_images to True.")
raise exception.InvalidConfiguration(msg)
# TODO(sbauza): Remove this code once mediated devices are persisted
# across reboots.
if self._host.has_min_version(MIN_LIBVIRT_MDEV_SUPPORT):

View File

@ -914,9 +914,28 @@ class Rbd(Image):
"""
return self.driver.size(self.rbd_name)
@staticmethod
def _remove_non_raw_cache_image(base):
# NOTE(boxiang): If the cache image file exists, we will check
# the format of it. Only raw format image is compatible for
# RBD image backend. If format is not raw, we will remove it
# at first. We limit force_raw_images to True this time. So
# the format of new cache image must be raw.
# We can remove this in 'U' version later.
if not os.path.exists(base):
return True
image_format = images.qemu_img_info(base)
if image_format.file_format != 'raw':
try:
os.remove(base)
except OSError as e:
LOG.warning("Ignoring failure to remove %(path)s: "
"%(error)s", {'path': base, 'error': e})
def create_image(self, prepare_template, base, size, *args, **kwargs):
if not self.exists():
self._remove_non_raw_cache_image(base)
prepare_template(target=base, *args, **kwargs)
# prepare_template() may have cloned the image into a new rbd

View File

@ -0,0 +1,13 @@
---
upgrade:
- |
The libvirt driver's RBD imagebackend no longer supports setting
force_raw_images to False. Setting force_raw_images = False and
images_type = rbd in nova.conf will cause the nova compute service
to refuse to start. To fix this, set force_raw_images = True. This
change was required to fix the `bug 1816686`_.
Note that non-raw cache image files will be removed if you set
force_raw_images = True and images_type = rbd now.
.. _bug 1816686: https://bugs.launchpad.net/nova/+bug/1816686