Skip to content

Commit 89413ff

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "libvirt: flatten rbd images when unshelving an instance" into stable/stein
2 parents 81cf1b8 + e802ede commit 89413ff

File tree

4 files changed

+89
-2
lines changed

4 files changed

+89
-2
lines changed

nova/tests/unit/virt/libvirt/test_driver.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ def _create_test_instance():
852852
'vcpu_model': None,
853853
'host': 'fake-host',
854854
'task_state': None,
855+
'vm_state': None,
855856
'trusted_certs': None
856857
}
857858

@@ -17096,8 +17097,8 @@ def fake_prepare(instance, name, tag):
1709617097
prepare.side_effect = fake_prepare
1709717098
drvr = libvirt_driver.LibvirtDriver(virtapi, False)
1709817099

17099-
instance = objects.Instance(vm_state=vm_states.BUILDING,
17100-
**self.test_instance)
17100+
instance = objects.Instance(**self.test_instance)
17101+
instance.vm_state = vm_states.BUILDING
1710117102
vifs = [{'id': uuids.vif_1, 'active': False},
1710217103
{'id': uuids.vif_2, 'active': False}]
1710317104

@@ -18866,6 +18867,7 @@ def _create_instance(self, params=None):
1886618867
inst['system_metadata'] = {}
1886718868
inst['metadata'] = {}
1886818869
inst['task_state'] = None
18870+
inst['vm_state'] = None
1886918871

1887018872
inst.update(params)
1887118873

@@ -19849,6 +19851,54 @@ def test_is_booted_from_volume(self):
1984919851
bdm.append({'boot_index': 0})
1985019852
self.assertTrue(func(bdi))
1985119853

19854+
def test_unshelve_noop_flatten_fetch_image_cache(self):
19855+
instance = self._create_instance(
19856+
params={'vm_state': vm_states.SHELVED_OFFLOADED})
19857+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
19858+
mock_imagebackend = mock.Mock(spec=imagebackend.Lvm)
19859+
mock_imagebackend.flatten.side_effect = NotImplementedError()
19860+
19861+
# Assert that this doesn't raise NotImplementedError
19862+
drvr._try_fetch_image_cache(mock_imagebackend, mock.sentinel.fetch,
19863+
self.context, mock.sentinel.filename, uuids.image_id,
19864+
instance, mock.sentinel.size)
19865+
19866+
# Assert that we cache and then flatten the image when an instance is
19867+
# still SHELVED_OFFLOADED during _try_fetch_image_cache.
19868+
mock_imagebackend.cache.assert_called_once_with(
19869+
fetch_func=mock.sentinel.fetch, context=self.context,
19870+
filename=mock.sentinel.filename, image_id=uuids.image_id,
19871+
size=mock.sentinel.size, trusted_certs=instance.trusted_certs)
19872+
mock_imagebackend.flatten.assert_called_once()
19873+
19874+
def test_unshelve_rbd_image_flatten_during_fetch_image_cache(self):
19875+
instance = self._create_instance(
19876+
params={'vm_state': vm_states.SHELVED_OFFLOADED})
19877+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
19878+
mock_rbd_driver = mock.Mock(spec=rbd_utils.RBDDriver)
19879+
mock_rbd_imagebackend = mock.Mock(spec=imagebackend.Rbd)
19880+
mock_rbd_imagebackend.rbd_name = mock.sentinel.rbd_name
19881+
mock_rbd_imagebackend.pool = mock.sentinel.rbd_pool
19882+
# This is logged so we can't use a sentinel
19883+
mock_rbd_imagebackend.path = 'rbd:pool/vol_disk'
19884+
mock_rbd_imagebackend.driver = mock_rbd_driver
19885+
mock_rbd_imagebackend.flatten.side_effect = \
19886+
imagebackend.Rbd.flatten(mock_rbd_imagebackend)
19887+
19888+
drvr._try_fetch_image_cache(mock_rbd_imagebackend, mock.sentinel.fetch,
19889+
self.context, mock.sentinel.filename, uuids.image_id,
19890+
instance, mock.sentinel.size)
19891+
19892+
# Assert that we cache and then flatten the image when an instance is
19893+
# still SHELVED_OFFLOADED during _try_fetch_image_cache.
19894+
mock_rbd_imagebackend.cache.assert_called_once_with(
19895+
fetch_func=mock.sentinel.fetch, context=self.context,
19896+
filename=mock.sentinel.filename, image_id=uuids.image_id,
19897+
size=mock.sentinel.size, trusted_certs=instance.trusted_certs)
19898+
mock_rbd_imagebackend.flatten.assert_called_once()
19899+
mock_rbd_driver.flatten.assert_called_once_with(
19900+
mock.sentinel.rbd_name, pool=mock.sentinel.rbd_pool)
19901+
1985219902
@mock.patch('nova.virt.libvirt.driver.imagebackend')
1985319903
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._inject_data')
1985419904
@mock.patch('nova.virt.libvirt.driver.imagecache')

nova/tests/unit/virt/libvirt/test_imagebackend.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,12 @@ def get_mon_addrs():
15651565
["server1:1899", "server2:1920"]),
15661566
model)
15671567

1568+
@mock.patch.object(rbd_utils.RBDDriver, 'flatten')
1569+
def test_flatten(self, mock_flatten):
1570+
image = self.image_class(self.INSTANCE, self.NAME)
1571+
image.flatten()
1572+
mock_flatten.assert_called_once_with(image.rbd_name, pool=self.POOL)
1573+
15681574
def test_import_file(self):
15691575
image = self.image_class(self.INSTANCE, self.NAME)
15701576

nova/virt/libvirt/driver.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8338,6 +8338,26 @@ def copy_from_host(target):
83388338
image.cache(fetch_func=copy_from_host, size=size,
83398339
filename=filename)
83408340

8341+
# NOTE(lyarwood): If the instance vm_state is shelved offloaded then we
8342+
# must be unshelving for _try_fetch_image_cache to be called.
8343+
if instance.vm_state == vm_states.SHELVED_OFFLOADED:
8344+
# NOTE(lyarwood): When using the rbd imagebackend the call to cache
8345+
# above will attempt to clone from the shelved snapshot in Glance
8346+
# if available from this compute. We then need to flatten the
8347+
# resulting image to avoid it still referencing and ultimately
8348+
# blocking the removal of the shelved snapshot at the end of the
8349+
# unshelve. This is a no-op for all but the rbd imagebackend.
8350+
try:
8351+
image.flatten()
8352+
LOG.debug('Image %s flattened successfully while unshelving '
8353+
'instance.', image.path, instance=instance)
8354+
except NotImplementedError:
8355+
# NOTE(lyarwood): There's an argument to be made for logging
8356+
# our inability to call flatten here, however given this isn't
8357+
# implemented for most of the backends it may do more harm than
8358+
# good, concerning operators etc so for now just pass.
8359+
pass
8360+
83418361
def _create_images_and_backing(self, context, instance, instance_dir,
83428362
disk_info, fallback_from_host=None):
83438363
""":param context: security context

nova/virt/libvirt/imagebackend.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,14 @@ def clone(self, context, image_id_or_uri):
430430
raise exception.ImageUnacceptable(image_id=image_id_or_uri,
431431
reason=reason)
432432

433+
def flatten(self):
434+
"""Flatten an image.
435+
436+
The implementation of this method is optional and therefore is
437+
not an abstractmethod.
438+
"""
439+
raise NotImplementedError('flatten() is not implemented')
440+
433441
def direct_snapshot(self, context, snapshot_name, image_format, image_id,
434442
base_image_id):
435443
"""Prepare a snapshot for direct reference from glance.
@@ -959,6 +967,9 @@ def clone(self, context, image_id_or_uri):
959967
raise exception.ImageUnacceptable(image_id=image_id_or_uri,
960968
reason=reason)
961969

970+
def flatten(self):
971+
self.driver.flatten(self.rbd_name, pool=self.pool)
972+
962973
def get_model(self, connection):
963974
secret = None
964975
if CONF.libvirt.rbd_secret_uuid:

0 commit comments

Comments
 (0)