From 40a56ce05b185ae418d5b548e19577209dfe772f Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 11 Jan 2024 07:28:22 -0800 Subject: [PATCH] Catch ImageNotFound on snapshot failure Right now we're logging this as an unexpected failure, complete with full traceback. However, this is expected if an image is deleted mid-snapshot, as is the case in one of our tempest tests. Change-Id: I6eb76c0500c7940778b7a15ac5202659ef92a82a --- nova/tests/unit/virt/libvirt/test_driver.py | 30 +++++++++++++++++++++ nova/virt/libvirt/driver.py | 9 +++++++ 2 files changed, 39 insertions(+) diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 5afd87e08d07..8b3786f0bfd9 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -29337,6 +29337,36 @@ class LibvirtSnapshotTests(_BaseSnapshotTests): rbd.remove_snap.assert_called_with('c', 'd', ignore_errors=True, pool='b', force=True) + @mock.patch.object(libvirt_driver, 'LOG') + @mock.patch('nova.virt.libvirt.utils.get_disk_type_from_path', + new=mock.Mock(return_value='rbd')) + @mock.patch('nova.virt.libvirt.utils.find_disk', + new=mock.Mock(return_value=('rbd://some/fake/rbd/image', + 'raw'))) + @mock.patch.object(rbd_utils, 'RBDDriver') + @mock.patch.object(rbd_utils, 'rbd') + def test_raw_with_rbd_clone_fails_image_deleted( + self, mock_rbd, mock_driver, mock_log): + self.flags(images_type='rbd', group='libvirt') + rbd = mock_driver.return_value + rbd.parent_info = mock.Mock(return_value=['test-pool', '', '']) + rbd.parse_url = mock.Mock(return_value=['a', 'b', 'c', 'd']) + with mock.patch.object(self.image_service, 'update', + side_effect=exception.ImageNotFound( + image_id=uuids.new_snapshot)): + self.assertRaises(exception.ImageNotFound, self._test_snapshot, + disk_format='raw') + rbd.clone.assert_called_with(mock.ANY, mock.ANY, dest_pool='test-pool') + rbd.flatten.assert_called_with(mock.ANY, pool='test-pool') + # Ensure that the direct_snapshot attempt was cleaned up + rbd.remove_snap.assert_called_with('c', 'd', ignore_errors=True, + pool='b', force=True) + # Make sure we took the NotFound-specific error path, and thus just + # logged a warning, instead of the catch-all which logs exception. + mock_log.exception.assert_not_called() + mock_log.warning.assert_called_once_with( + 'Failed to snapshot image because it was deleted') + @mock.patch('nova.virt.libvirt.utils.get_disk_type_from_path', new=mock.Mock(return_value='rbd')) @mock.patch('nova.virt.libvirt.utils.find_disk', diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index f56bc7d05735..a7db984ba425 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -3180,6 +3180,15 @@ class LibvirtDriver(driver.ComputeDriver): image_id, metadata, image_file) + except exception.ImageNotFound: + with excutils.save_and_reraise_exception(): + LOG.warning("Failed to snapshot image because it was deleted") + failed_snap = metadata.pop('location', None) + if failed_snap: + failed_snap = {'url': str(failed_snap)} + root_disk.cleanup_direct_snapshot( + failed_snap, also_destroy_volume=True, + ignore_errors=True) except Exception: with excutils.save_and_reraise_exception(): LOG.exception("Failed to snapshot image")