Skip to content

Commit 7f48351

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "libvirt: flatten rbd images when unshelving an instance" into stable/rocky
2 parents 1e27fa9 + e93bc57 commit 7f48351

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
@@ -856,6 +856,7 @@ def _create_test_instance():
856856
'vcpu_model': None,
857857
'host': 'fake-host',
858858
'task_state': None,
859+
'vm_state': None,
859860
'trusted_certs': None
860861
}
861862

@@ -16910,8 +16911,8 @@ def fake_prepare(instance, name, tag):
1691016911
prepare.side_effect = fake_prepare
1691116912
drvr = libvirt_driver.LibvirtDriver(virtapi, False)
1691216913

16913-
instance = objects.Instance(vm_state=vm_states.BUILDING,
16914-
**self.test_instance)
16914+
instance = objects.Instance(**self.test_instance)
16915+
instance.vm_state = vm_states.BUILDING
1691516916
vifs = [{'id': uuids.vif_1, 'active': False},
1691616917
{'id': uuids.vif_2, 'active': False}]
1691716918

@@ -18306,6 +18307,7 @@ def _create_instance(self, params=None):
1830618307
inst['system_metadata'] = {}
1830718308
inst['metadata'] = {}
1830818309
inst['task_state'] = None
18310+
inst['vm_state'] = None
1830918311

1831018312
inst.update(params)
1831118313

@@ -19289,6 +19291,54 @@ def test_is_booted_from_volume(self):
1928919291
bdm.append({'boot_index': 0})
1929019292
self.assertTrue(func(bdi))
1929119293

19294+
def test_unshelve_noop_flatten_fetch_image_cache(self):
19295+
instance = self._create_instance(
19296+
params={'vm_state': vm_states.SHELVED_OFFLOADED})
19297+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
19298+
mock_imagebackend = mock.Mock(spec=imagebackend.Lvm)
19299+
mock_imagebackend.flatten.side_effect = NotImplementedError()
19300+
19301+
# Assert that this doesn't raise NotImplementedError
19302+
drvr._try_fetch_image_cache(mock_imagebackend, mock.sentinel.fetch,
19303+
self.context, mock.sentinel.filename, uuids.image_id,
19304+
instance, mock.sentinel.size)
19305+
19306+
# Assert that we cache and then flatten the image when an instance is
19307+
# still SHELVED_OFFLOADED during _try_fetch_image_cache.
19308+
mock_imagebackend.cache.assert_called_once_with(
19309+
fetch_func=mock.sentinel.fetch, context=self.context,
19310+
filename=mock.sentinel.filename, image_id=uuids.image_id,
19311+
size=mock.sentinel.size, trusted_certs=instance.trusted_certs)
19312+
mock_imagebackend.flatten.assert_called_once()
19313+
19314+
def test_unshelve_rbd_image_flatten_during_fetch_image_cache(self):
19315+
instance = self._create_instance(
19316+
params={'vm_state': vm_states.SHELVED_OFFLOADED})
19317+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
19318+
mock_rbd_driver = mock.Mock(spec=rbd_utils.RBDDriver)
19319+
mock_rbd_imagebackend = mock.Mock(spec=imagebackend.Rbd)
19320+
mock_rbd_imagebackend.rbd_name = mock.sentinel.rbd_name
19321+
mock_rbd_imagebackend.pool = mock.sentinel.rbd_pool
19322+
# This is logged so we can't use a sentinel
19323+
mock_rbd_imagebackend.path = 'rbd:pool/vol_disk'
19324+
mock_rbd_imagebackend.driver = mock_rbd_driver
19325+
mock_rbd_imagebackend.flatten.side_effect = \
19326+
imagebackend.Rbd.flatten(mock_rbd_imagebackend)
19327+
19328+
drvr._try_fetch_image_cache(mock_rbd_imagebackend, mock.sentinel.fetch,
19329+
self.context, mock.sentinel.filename, uuids.image_id,
19330+
instance, mock.sentinel.size)
19331+
19332+
# Assert that we cache and then flatten the image when an instance is
19333+
# still SHELVED_OFFLOADED during _try_fetch_image_cache.
19334+
mock_rbd_imagebackend.cache.assert_called_once_with(
19335+
fetch_func=mock.sentinel.fetch, context=self.context,
19336+
filename=mock.sentinel.filename, image_id=uuids.image_id,
19337+
size=mock.sentinel.size, trusted_certs=instance.trusted_certs)
19338+
mock_rbd_imagebackend.flatten.assert_called_once()
19339+
mock_rbd_driver.flatten.assert_called_once_with(
19340+
mock.sentinel.rbd_name, pool=mock.sentinel.rbd_pool)
19341+
1929219342
@mock.patch('nova.virt.libvirt.driver.imagebackend')
1929319343
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._inject_data')
1929419344
@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
@@ -1550,6 +1550,12 @@ def get_mon_addrs():
15501550
["server1:1899", "server2:1920"]),
15511551
model)
15521552

1553+
@mock.patch.object(rbd_utils.RBDDriver, 'flatten')
1554+
def test_flatten(self, mock_flatten):
1555+
image = self.image_class(self.INSTANCE, self.NAME)
1556+
image.flatten()
1557+
mock_flatten.assert_called_once_with(image.rbd_name, pool=self.POOL)
1558+
15531559
def test_import_file(self):
15541560
image = self.image_class(self.INSTANCE, self.NAME)
15551561

nova/virt/libvirt/driver.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7820,6 +7820,26 @@ def copy_from_host(target):
78207820
image.cache(fetch_func=copy_from_host, size=size,
78217821
filename=filename)
78227822

7823+
# NOTE(lyarwood): If the instance vm_state is shelved offloaded then we
7824+
# must be unshelving for _try_fetch_image_cache to be called.
7825+
if instance.vm_state == vm_states.SHELVED_OFFLOADED:
7826+
# NOTE(lyarwood): When using the rbd imagebackend the call to cache
7827+
# above will attempt to clone from the shelved snapshot in Glance
7828+
# if available from this compute. We then need to flatten the
7829+
# resulting image to avoid it still referencing and ultimately
7830+
# blocking the removal of the shelved snapshot at the end of the
7831+
# unshelve. This is a no-op for all but the rbd imagebackend.
7832+
try:
7833+
image.flatten()
7834+
LOG.debug('Image %s flattened successfully while unshelving '
7835+
'instance.', image.path, instance=instance)
7836+
except NotImplementedError:
7837+
# NOTE(lyarwood): There's an argument to be made for logging
7838+
# our inability to call flatten here, however given this isn't
7839+
# implemented for most of the backends it may do more harm than
7840+
# good, concerning operators etc so for now just pass.
7841+
pass
7842+
78237843
def _create_images_and_backing(self, context, instance, instance_dir,
78247844
disk_info, fallback_from_host=None):
78257845
""":param context: security context

nova/virt/libvirt/imagebackend.py

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

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

972+
def flatten(self):
973+
self.driver.flatten(self.rbd_name, pool=self.pool)
974+
964975
def get_model(self, connection):
965976
secret = None
966977
if CONF.libvirt.rbd_secret_uuid:

0 commit comments

Comments
 (0)