Merge "Handle glance exception during rotating instance backup" into stable/pike

This commit is contained in:
Zuul 2018-03-08 15:43:26 +00:00 committed by Gerrit Code Review
commit 44c7f4162e
5 changed files with 91 additions and 0 deletions

View File

@ -3317,6 +3317,12 @@ class ComputeManager(manager.Manager):
LOG.info("Failed to find image %(image_id)s to "
"delete", {'image_id': image_id},
instance=instance)
except (exception.ImageDeleteConflict, Exception) as exc:
LOG.info("Failed to delete image %(image_id)s during "
"deleting excess backups. "
"Continuing for next image.. %(exc)s",
{'image_id': image_id, 'exc': exc},
instance=instance)
@wrap_exception()
@reverts_task_state

View File

@ -652,6 +652,10 @@ class ImageNotFound(NotFound):
msg_fmt = _("Image %(image_id)s could not be found.")
class ImageDeleteConflict(NovaException):
msg_fmt = _("Conflict deleting image. Reason: %(reason)s.")
class PreserveEphemeralNotSupported(Invalid):
msg_fmt = _("The current driver does not support "
"preserving ephemeral partitions.")

View File

@ -533,6 +533,7 @@ class GlanceImageServiceV2(object):
:raises: ImageNotFound if the image does not exist.
:raises: NotAuthorized if the user is not an owner.
:raises: ImageNotAuthorized if the user is not authorized.
:raises: ImageDeleteConflict if the image is conflicted to delete.
"""
try:
@ -541,6 +542,8 @@ class GlanceImageServiceV2(object):
raise exception.ImageNotFound(image_id=image_id)
except glanceclient.exc.HTTPForbidden:
raise exception.ImageNotAuthorized(image_id=image_id)
except glanceclient.exc.HTTPConflict as exc:
raise exception.ImageDeleteConflict(reason=six.text_type(exc))
return True

View File

@ -3526,6 +3526,74 @@ class ComputeTestCase(BaseTestCase,
rotation=1)
self.assertEqual(2, mock_delete.call_count)
@mock.patch('nova.image.api.API.get_all')
def test_rotate_backups_with_image_delete_failed(self,
mock_get_all_images):
instance = self._create_fake_instance_obj()
instance_uuid = instance['uuid']
fake_images = [{
'id': uuids.image_id_1,
'created_at': timeutils.parse_strtime('2017-01-04T00:00:00.00'),
'name': 'fake_name_1',
'status': 'active',
'properties': {'kernel_id': uuids.kernel_id_1,
'ramdisk_id': uuids.ramdisk_id_1,
'image_type': 'backup',
'backup_type': 'daily',
'instance_uuid': instance_uuid},
},
{
'id': uuids.image_id_2,
'created_at': timeutils.parse_strtime('2017-01-03T00:00:00.00'),
'name': 'fake_name_2',
'status': 'active',
'properties': {'kernel_id': uuids.kernel_id_2,
'ramdisk_id': uuids.ramdisk_id_2,
'image_type': 'backup',
'backup_type': 'daily',
'instance_uuid': instance_uuid},
},
{
'id': uuids.image_id_3,
'created_at': timeutils.parse_strtime('2017-01-02T00:00:00.00'),
'name': 'fake_name_3',
'status': 'active',
'properties': {'kernel_id': uuids.kernel_id_3,
'ramdisk_id': uuids.ramdisk_id_3,
'image_type': 'backup',
'backup_type': 'daily',
'instance_uuid': instance_uuid},
},
{
'id': uuids.image_id_4,
'created_at': timeutils.parse_strtime('2017-01-01T00:00:00.00'),
'name': 'fake_name_4',
'status': 'active',
'properties': {'kernel_id': uuids.kernel_id_4,
'ramdisk_id': uuids.ramdisk_id_4,
'image_type': 'backup',
'backup_type': 'daily',
'instance_uuid': instance_uuid},
}]
mock_get_all_images.return_value = fake_images
def _check_image_id(context, image_id):
self.assertIn(image_id, [uuids.image_id_2, uuids.image_id_3,
uuids.image_id_4])
if image_id == uuids.image_id_3:
raise Exception('fake %s delete exception' % image_id)
if image_id == uuids.image_id_4:
raise exception.ImageDeleteConflict(reason='image is in use')
with mock.patch.object(nova.image.api.API, 'delete',
side_effect=_check_image_id) as mock_delete:
# Fake images 4,3,2 should be rotated in sequence
self.compute._rotate_backups(self.context, instance=instance,
backup_type='daily',
rotation=1)
self.assertEqual(3, mock_delete.call_count)
def test_console_output(self):
# Make sure we can get console output from instance.
instance = self._create_fake_instance_obj()

View File

@ -1595,6 +1595,16 @@ class TestDelete(test.NoDBTestCase):
self.assertRaises(exception.ImageNotFound, service.delete, ctx,
mock.sentinel.image_id)
def test_delete_client_conflict_failure_v2(self):
client = mock.MagicMock()
fake_details = 'Image %s is in use' % mock.sentinel.image_id
client.call.side_effect = glanceclient.exc.HTTPConflict(
details=fake_details)
ctx = mock.sentinel.ctx
service = glance.GlanceImageServiceV2(client)
self.assertRaises(exception.ImageDeleteConflict, service.delete, ctx,
mock.sentinel.image_id)
class TestGlanceApiServers(test.NoDBTestCase):