Merge "libvirt: flatten rbd image during cross-cell move spawn at dest"

This commit is contained in:
Zuul 2019-12-23 14:31:15 +00:00 committed by Gerrit Code Review
commit 7e15762c0a
5 changed files with 85 additions and 17 deletions

View File

@ -5925,13 +5925,6 @@ class ComputeManager(manager.Manager):
# If we spawned from a temporary snapshot image we can delete that now,
# similar to how unshelve works.
if snapshot_id:
# FIXME(mriedem): Need to deal with bug 1653953 for libvirt with
# the rbd image backend. I think the cleanest thing we can do is
# from the driver check to see if instance.migration_context is not
# None and if so, get the Migration record for that context
# (instance.migration_context.migration_id) and from that check the
# Migration.cross_cell_move flag and if True, then flatten the
# image.
compute_utils.delete_image(
ctxt, instance, self.image_api, snapshot_id)

View File

@ -18,6 +18,7 @@ from oslo_utils import versionutils
from nova.db import api as db
from nova import exception
from nova import objects
from nova.objects import base
from nova.objects import fields
@ -120,3 +121,19 @@ class MigrationContext(base.NovaPersistentObject, base.NovaObject):
for upd_dev in updated_pci_devs
if curr_dev.request_id == upd_dev.request_id}
return {}
def is_cross_cell_move(self):
"""Helper to determine if this is a context for a cross-cell move.
Based on the ``migration_id`` in this context, gets the Migration
object and returns its ``cross_cell_move`` value.
The result is cached for subsequent lookups.
:return: True if this is a cross cell move migration, False otherwise.
"""
if not hasattr(self, '_cached_cross_cell_move'):
migration = objects.Migration.get_by_id(
self._context, self.migration_id)
setattr(self, '_cached_cross_cell_move', migration.cross_cell_move)
return self._cached_cross_cell_move

View File

@ -14,6 +14,7 @@ import mock
from oslo_serialization import jsonutils
from oslo_utils.fixture import uuidsentinel as uuids
from nova import context
from nova import exception
from nova import objects
from nova.tests.unit.objects import test_instance_numa
@ -125,6 +126,17 @@ class _TestMigrationContext(object):
objects.MigrationContext.get_by_instance_uuid,
self.context, 'fake_uuid')
@mock.patch('nova.objects.Migration.get_by_id',
return_value=objects.Migration(cross_cell_move=True))
def test_is_cross_cell_move(self, mock_get_by_id):
ctxt = context.get_admin_context()
mig_ctx = get_fake_migration_context_obj(ctxt)
self.assertTrue(mig_ctx.is_cross_cell_move())
mock_get_by_id.assert_called_once_with(ctxt, mig_ctx.migration_id)
# Call it again to make sure the result was cached.
self.assertTrue(mig_ctx.is_cross_cell_move())
mock_get_by_id.assert_called_once_with(ctxt, mig_ctx.migration_id)
class TestMigrationContext(test_objects._LocalTest, _TestMigrationContext):

View File

@ -919,6 +919,7 @@ def _create_test_instance():
'vm_state': None,
'trusted_certs': None,
'resources': None,
'migration_context': None,
}
@ -20394,9 +20395,12 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
# Attributes which we need to be set so they don't touch the db,
# but it's not worth the effort to fake properly
for field in ['numa_topology', 'vcpu_model', 'trusted_certs',
'resources', 'migration_context']:
'resources']:
setattr(instance, field, None)
# fake_instance_obj nulls migration_context so set it here.
instance.migration_context = params.get('migration_context')
return instance
@mock.patch(('nova.virt.libvirt.driver.LibvirtDriver.'
@ -21421,9 +21425,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
bdm.append({'boot_index': 0})
self.assertTrue(func(bdi))
def test_unshelve_noop_flatten_fetch_image_cache(self):
instance = self._create_instance(
params={'vm_state': vm_states.SHELVED_OFFLOADED})
def _test_noop_flatten_fetch_image_cache(self, instance):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
mock_imagebackend = mock.Mock(spec=imagebackend.Lvm)
mock_imagebackend.flatten.side_effect = NotImplementedError()
@ -21434,16 +21436,28 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
instance, mock.sentinel.size)
# Assert that we cache and then flatten the image when an instance is
# still SHELVED_OFFLOADED during _try_fetch_image_cache.
# still SHELVED_OFFLOADED or doing a cross-cell move during
# _try_fetch_image_cache.
mock_imagebackend.cache.assert_called_once_with(
fetch_func=mock.sentinel.fetch, context=self.context,
filename=mock.sentinel.filename, image_id=uuids.image_id,
size=mock.sentinel.size, trusted_certs=instance.trusted_certs)
mock_imagebackend.flatten.assert_called_once()
def test_unshelve_rbd_image_flatten_during_fetch_image_cache(self):
def test_unshelve_noop_flatten_fetch_image_cache(self):
instance = self._create_instance(
params={'vm_state': vm_states.SHELVED_OFFLOADED})
self._test_noop_flatten_fetch_image_cache(instance)
@mock.patch('nova.objects.MigrationContext.is_cross_cell_move',
return_value=True)
def test_cross_cell_move_noop_flatten_fetch_image_cache(self, mock_is_ccm):
instance = self._create_instance(
params={'migration_context': objects.MigrationContext()})
self._test_noop_flatten_fetch_image_cache(instance)
mock_is_ccm.assert_called_once_with()
def _test_rbd_image_flatten_during_fetch_image_cache(self, instance):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
mock_rbd_driver = mock.Mock(spec=rbd_utils.RBDDriver)
mock_rbd_driver.pool = mock.sentinel.rbd_pool
@ -21460,7 +21474,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
instance, mock.sentinel.size)
# Assert that we cache and then flatten the image when an instance is
# still SHELVED_OFFLOADED during _try_fetch_image_cache.
# still SHELVED_OFFLOADED or doing a cross-cell move during
# _try_fetch_image_cache.
mock_rbd_imagebackend.cache.assert_called_once_with(
fetch_func=mock.sentinel.fetch, context=self.context,
filename=mock.sentinel.filename, image_id=uuids.image_id,
@ -21469,6 +21484,27 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
mock_rbd_driver.flatten.assert_called_once_with(
mock.sentinel.rbd_name, pool=mock.sentinel.rbd_pool)
@mock.patch('nova.virt.libvirt.driver.LOG.debug')
def test_rbd_image_flatten_during_fetch_image_cache(self, mock_debug):
instance = self._create_instance(
params={'vm_state': vm_states.SHELVED_OFFLOADED})
self._test_rbd_image_flatten_during_fetch_image_cache(instance)
mock_debug.assert_called_once()
self.assertEqual('unshelving instance', mock_debug.call_args[0][2])
@mock.patch('nova.virt.libvirt.driver.LOG.debug')
@mock.patch('nova.objects.MigrationContext.is_cross_cell_move',
return_value=True)
def test_cross_cell_move_rbd_flatten_fetch_image_cache(self, mock_is_ccm,
mock_debug):
instance = self._create_instance(
params={'migration_context': objects.MigrationContext()})
self._test_rbd_image_flatten_during_fetch_image_cache(instance)
mock_is_ccm.assert_called_once_with()
mock_debug.assert_called_once()
self.assertEqual('migrating instance across cells',
mock_debug.call_args[0][2])
@mock.patch('nova.virt.libvirt.driver.imagebackend')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._inject_data')
@mock.patch('nova.virt.libvirt.driver.imagecache')

View File

@ -9243,17 +9243,27 @@ class LibvirtDriver(driver.ComputeDriver):
# NOTE(lyarwood): If the instance vm_state is shelved offloaded then we
# must be unshelving for _try_fetch_image_cache to be called.
if instance.vm_state == vm_states.SHELVED_OFFLOADED:
# NOTE(mriedem): Alternatively if we are doing a cross-cell move of a
# non-volume-backed server and finishing (spawning) on the dest host,
# we have to flatten the rbd image so we can delete the temporary
# snapshot in the compute manager.
mig_context = instance.migration_context
cross_cell_move = (
mig_context and mig_context.is_cross_cell_move() or False)
if instance.vm_state == vm_states.SHELVED_OFFLOADED or cross_cell_move:
# NOTE(lyarwood): When using the rbd imagebackend the call to cache
# above will attempt to clone from the shelved snapshot in Glance
# if available from this compute. We then need to flatten the
# resulting image to avoid it still referencing and ultimately
# blocking the removal of the shelved snapshot at the end of the
# unshelve. This is a no-op for all but the rbd imagebackend.
action = (
'migrating instance across cells' if cross_cell_move
else 'unshelving instance')
try:
image.flatten()
LOG.debug('Image %s flattened successfully while unshelving '
'instance.', image.path, instance=instance)
LOG.debug('Image %s flattened successfully while %s.',
image.path, action, instance=instance)
except NotImplementedError:
# NOTE(lyarwood): There's an argument to be made for logging
# our inability to call flatten here, however given this isn't