Browse Source

Merge "libvirt: flatten rbd images when unshelving an instance" into stable/queens

tags/17.0.12
Zuul 1 month ago
parent
commit
7ad95441df

+ 52
- 2
nova/tests/unit/virt/libvirt/test_driver.py View File

@@ -764,6 +764,7 @@ def _create_test_instance():
764 764
         'vcpu_model': None,
765 765
         'host': 'fake-host',
766 766
         'task_state': None,
767
+        'vm_state': None,
767 768
     }
768 769
 
769 770
 
@@ -16092,8 +16093,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
16092 16093
         prepare.side_effect = fake_prepare
16093 16094
         drvr = libvirt_driver.LibvirtDriver(virtapi, False)
16094 16095
 
16095
-        instance = objects.Instance(vm_state=vm_states.BUILDING,
16096
-                                    **self.test_instance)
16096
+        instance = objects.Instance(**self.test_instance)
16097
+        instance.vm_state = vm_states.BUILDING
16097 16098
         vifs = [{'id': 'vif1', 'active': False},
16098 16099
                 {'id': 'vif2', 'active': False}]
16099 16100
 
@@ -17410,6 +17411,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
17410 17411
         inst['system_metadata'] = {}
17411 17412
         inst['metadata'] = {}
17412 17413
         inst['task_state'] = None
17414
+        inst['vm_state'] = None
17413 17415
 
17414 17416
         inst.update(params)
17415 17417
 
@@ -18460,6 +18462,54 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
18460 18462
         bdm.append({'boot_index': 0})
18461 18463
         self.assertTrue(func(bdi))
18462 18464
 
18465
+    def test_unshelve_noop_flatten_fetch_image_cache(self):
18466
+        instance = self._create_instance(
18467
+            params={'vm_state': vm_states.SHELVED_OFFLOADED})
18468
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
18469
+        mock_imagebackend = mock.Mock(spec=imagebackend.Lvm)
18470
+        mock_imagebackend.flatten.side_effect = NotImplementedError()
18471
+
18472
+        # Assert that this doesn't raise NotImplementedError
18473
+        drvr._try_fetch_image_cache(mock_imagebackend, mock.sentinel.fetch,
18474
+                self.context, mock.sentinel.filename, uuids.image_id,
18475
+                instance, mock.sentinel.size)
18476
+
18477
+        # Assert that we cache and then flatten the image when an instance is
18478
+        # still SHELVED_OFFLOADED during _try_fetch_image_cache.
18479
+        mock_imagebackend.cache.assert_called_once_with(
18480
+            fetch_func=mock.sentinel.fetch, context=self.context,
18481
+            filename=mock.sentinel.filename, image_id=uuids.image_id,
18482
+            size=mock.sentinel.size)
18483
+        mock_imagebackend.flatten.assert_called_once()
18484
+
18485
+    def test_unshelve_rbd_image_flatten_during_fetch_image_cache(self):
18486
+        instance = self._create_instance(
18487
+            params={'vm_state': vm_states.SHELVED_OFFLOADED})
18488
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
18489
+        mock_rbd_driver = mock.Mock(spec=rbd_utils.RBDDriver)
18490
+        mock_rbd_imagebackend = mock.Mock(spec=imagebackend.Rbd)
18491
+        mock_rbd_imagebackend.rbd_name = mock.sentinel.rbd_name
18492
+        mock_rbd_imagebackend.pool = mock.sentinel.rbd_pool
18493
+        # This is logged so we can't use a sentinel
18494
+        mock_rbd_imagebackend.path = 'rbd:pool/vol_disk'
18495
+        mock_rbd_imagebackend.driver = mock_rbd_driver
18496
+        mock_rbd_imagebackend.flatten.side_effect = \
18497
+            imagebackend.Rbd.flatten(mock_rbd_imagebackend)
18498
+
18499
+        drvr._try_fetch_image_cache(mock_rbd_imagebackend, mock.sentinel.fetch,
18500
+                self.context, mock.sentinel.filename, uuids.image_id,
18501
+                instance, mock.sentinel.size)
18502
+
18503
+        # Assert that we cache and then flatten the image when an instance is
18504
+        # still SHELVED_OFFLOADED during _try_fetch_image_cache.
18505
+        mock_rbd_imagebackend.cache.assert_called_once_with(
18506
+            fetch_func=mock.sentinel.fetch, context=self.context,
18507
+            filename=mock.sentinel.filename, image_id=uuids.image_id,
18508
+            size=mock.sentinel.size)
18509
+        mock_rbd_imagebackend.flatten.assert_called_once()
18510
+        mock_rbd_driver.flatten.assert_called_once_with(
18511
+            mock.sentinel.rbd_name, pool=mock.sentinel.rbd_pool)
18512
+
18463 18513
     @mock.patch('nova.virt.libvirt.driver.imagebackend')
18464 18514
     @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._inject_data')
18465 18515
     @mock.patch('nova.virt.libvirt.driver.imagecache')

+ 6
- 0
nova/tests/unit/virt/libvirt/test_imagebackend.py View File

@@ -1558,6 +1558,12 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
1558 1558
             ["server1:1899", "server2:1920"]),
1559 1559
                          model)
1560 1560
 
1561
+    @mock.patch.object(rbd_utils.RBDDriver, 'flatten')
1562
+    def test_flatten(self, mock_flatten):
1563
+        image = self.image_class(self.INSTANCE, self.NAME)
1564
+        image.flatten()
1565
+        mock_flatten.assert_called_once_with(image.rbd_name, pool=self.POOL)
1566
+
1561 1567
     def test_import_file(self):
1562 1568
         image = self.image_class(self.INSTANCE, self.NAME)
1563 1569
 

+ 20
- 0
nova/virt/libvirt/driver.py View File

@@ -7680,6 +7680,26 @@ class LibvirtDriver(driver.ComputeDriver):
7680 7680
             image.cache(fetch_func=copy_from_host, size=size,
7681 7681
                         filename=filename)
7682 7682
 
7683
+        # NOTE(lyarwood): If the instance vm_state is shelved offloaded then we
7684
+        # must be unshelving for _try_fetch_image_cache to be called.
7685
+        if instance.vm_state == vm_states.SHELVED_OFFLOADED:
7686
+            # NOTE(lyarwood): When using the rbd imagebackend the call to cache
7687
+            # above will attempt to clone from the shelved snapshot in Glance
7688
+            # if available from this compute. We then need to flatten the
7689
+            # resulting image to avoid it still referencing and ultimately
7690
+            # blocking the removal of the shelved snapshot at the end of the
7691
+            # unshelve. This is a no-op for all but the rbd imagebackend.
7692
+            try:
7693
+                image.flatten()
7694
+                LOG.debug('Image %s flattened successfully while unshelving '
7695
+                          'instance.', image.path, instance=instance)
7696
+            except NotImplementedError:
7697
+                # NOTE(lyarwood): There's an argument to be made for logging
7698
+                # our inability to call flatten here, however given this isn't
7699
+                # implemented for most of the backends it may do more harm than
7700
+                # good, concerning operators etc so for now just pass.
7701
+                pass
7702
+
7683 7703
     def _create_images_and_backing(self, context, instance, instance_dir,
7684 7704
                                    disk_info, fallback_from_host=None):
7685 7705
         """:param context: security context

+ 11
- 0
nova/virt/libvirt/imagebackend.py View File

@@ -430,6 +430,14 @@ class Image(object):
430 430
         raise exception.ImageUnacceptable(image_id=image_id_or_uri,
431 431
                                           reason=reason)
432 432
 
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
+
433 441
     def direct_snapshot(self, context, snapshot_name, image_format, image_id,
434 442
                         base_image_id):
435 443
         """Prepare a snapshot for direct reference from glance.
@@ -960,6 +968,9 @@ class Rbd(Image):
960 968
         raise exception.ImageUnacceptable(image_id=image_id_or_uri,
961 969
                                           reason=reason)
962 970
 
971
+    def flatten(self):
972
+        self.driver.flatten(self.rbd_name, pool=self.pool)
973
+
963 974
     def get_model(self, connection):
964 975
         secret = None
965 976
         if CONF.libvirt.rbd_secret_uuid:

Loading…
Cancel
Save