Merge "Remove useless volume when boot from volume failed"

This commit is contained in:
Jenkins 2015-06-08 16:21:52 +00:00 committed by Gerrit Code Review
commit 4db0c9224d
5 changed files with 173 additions and 10 deletions

View File

@ -1386,15 +1386,18 @@ class ComputeManager(manager.Manager):
volume = self.volume_api.get(context, vol_id)
volume_status = volume['status']
if volume_status not in ['creating', 'downloading']:
if volume_status != 'available':
LOG.warning(_LW("Volume id: %s finished being created but "
"was not set as 'available'"), vol_id)
return attempt
if volume_status == 'available':
return attempt
LOG.warning(_LW("Volume id: %(vol_id)s finished being "
"created but its status is %(vol_status)s."),
{'vol_id': vol_id,
'vol_status': volume_status})
break
greenthread.sleep(CONF.block_device_allocate_retries_interval)
# NOTE(harlowja): Should only happen if we ran out of attempts
raise exception.VolumeNotCreated(volume_id=vol_id,
seconds=int(time.time() - start),
attempts=attempts)
attempts=attempt,
volume_status=volume_status)
def _decode_files(self, injected_files):
"""Base64 decode the list of files to inject."""

View File

@ -275,7 +275,7 @@ class VolumeUnattached(Invalid):
class VolumeNotCreated(NovaException):
msg_fmt = _("Volume %(volume_id)s did not finish being created"
" even after we waited %(seconds)s seconds or %(attempts)s"
" attempts.")
" attempts. And its status is %(volume_status)s.")
class InvalidKeypair(Invalid):

View File

@ -452,6 +452,17 @@ class ComputeVolumeTestCase(BaseTestCase):
self.compute._await_block_device_map_created,
self.context, '1')
def test_await_block_device_created_failed(self):
c = self.compute
fake_result = {'status': 'error', 'id': 'blah'}
with mock.patch.object(c.volume_api, 'get',
return_value=fake_result) as fake_get:
self.assertRaises(exception.VolumeNotCreated,
c._await_block_device_map_created,
self.context, '1')
fake_get.assert_called_once_with(self.context, '1')
def test_await_block_device_created_slow(self):
c = self.compute
self.flags(block_device_allocate_retries=4)

View File

@ -20,6 +20,7 @@ import six
from nova import block_device
from nova import context
from nova import exception
from nova import test
from nova.tests.unit import fake_instance
from nova.tests.unit import matchers
@ -328,6 +329,37 @@ class TestDriverBlockDevice(test.NoDBTestCase):
self.assertEqual('fake-volume-id-2', test_bdm.volume_id)
self.assertEqual(3, test_bdm.volume_size)
def _test_call_wait_func(self, delete_on_termination, delete_fail=False):
test_bdm = self.driver_classes['volume'](self.volume_bdm)
test_bdm['delete_on_termination'] = delete_on_termination
with mock.patch.object(self.volume_api, 'delete') as vol_delete:
wait_func = mock.MagicMock()
mock_exception = exception.VolumeNotCreated(volume_id='fake-id',
seconds=1,
attempts=1,
volume_status='error')
wait_func.side_effect = mock_exception
if delete_on_termination and delete_fail:
vol_delete.side_effect = Exception()
self.assertRaises(exception.VolumeNotCreated,
test_bdm._call_wait_func,
context=self.context,
wait_func=wait_func,
volume_api=self.volume_api,
volume_id='fake-id')
self.assertEqual(delete_on_termination, vol_delete.called)
def test_call_wait_delete_volume(self):
self._test_call_wait_func(True)
def test_call_wait_delete_volume_fail(self):
self._test_call_wait_func(True, True)
def test_call_wait_no_delete_volume(self):
self._test_call_wait_func(False)
def _test_volume_attach(self, driver_bdm, bdm_dict,
fake_volume, check_attach=True,
fail_check_attach=False, driver_attach=False,
@ -560,6 +592,43 @@ class TestDriverBlockDevice(test.NoDBTestCase):
self.virt_driver, wait_func)
self.assertEqual(test_bdm.volume_id, 'fake-volume-id-2')
def test_snapshot_attach_fail_volume(self):
fail_volume_snapshot = self.snapshot_bdm.copy()
fail_volume_snapshot['volume_id'] = None
test_bdm = self.driver_classes['snapshot'](fail_volume_snapshot)
snapshot = {'id': 'fake-volume-id-1',
'attach_status': 'detached'}
volume = {'id': 'fake-volume-id-2',
'attach_status': 'detached'}
instance = fake_instance.fake_instance_obj(mock.sentinel.ctx,
**{'uuid': 'fake-uuid'})
with contextlib.nested(
mock.patch.object(self.volume_api, 'get_snapshot',
return_value=snapshot),
mock.patch.object(self.volume_api, 'create', return_value=volume),
mock.patch.object(self.volume_api, 'delete'),
) as (vol_get_snap, vol_create, vol_delete):
wait_func = mock.MagicMock()
mock_exception = exception.VolumeNotCreated(volume_id=volume['id'],
seconds=1,
attempts=1,
volume_status='error')
wait_func.side_effect = mock_exception
self.assertRaises(exception.VolumeNotCreated,
test_bdm.attach, context=self.context,
instance=instance,
volume_api=self.volume_api,
virt_driver=self.virt_driver,
wait_func=wait_func)
vol_get_snap.assert_called_once_with(
self.context, 'fake-snapshot-id-1')
vol_create.assert_called_once_with(
self.context, 3, '', '', snapshot, availability_zone=None)
vol_delete.assert_called_once_with(self.context, volume['id'])
def test_snapshot_attach_volume(self):
test_bdm = self.driver_classes['snapshot'](
self.snapshot_bdm)
@ -604,6 +673,39 @@ class TestDriverBlockDevice(test.NoDBTestCase):
self.virt_driver, wait_func)
self.assertEqual(test_bdm.volume_id, 'fake-volume-id-2')
def test_image_attach_fail_volume(self):
fail_volume_image = self.image_bdm.copy()
fail_volume_image['volume_id'] = None
test_bdm = self.driver_classes['image'](fail_volume_image)
image = {'id': 'fake-image-id-1'}
volume = {'id': 'fake-volume-id-2',
'attach_status': 'detached'}
instance = fake_instance.fake_instance_obj(mock.sentinel.ctx,
**{'uuid': 'fake-uuid'})
with contextlib.nested(
mock.patch.object(self.volume_api, 'create', return_value=volume),
mock.patch.object(self.volume_api, 'delete'),
) as (vol_create, vol_delete):
wait_func = mock.MagicMock()
mock_exception = exception.VolumeNotCreated(volume_id=volume['id'],
seconds=1,
attempts=1,
volume_status='error')
wait_func.side_effect = mock_exception
self.assertRaises(exception.VolumeNotCreated,
test_bdm.attach, context=self.context,
instance=instance,
volume_api=self.volume_api,
virt_driver=self.virt_driver,
wait_func=wait_func)
vol_create.assert_called_once_with(
self.context, 1, '', '', image_id=image['id'],
availability_zone=None)
vol_delete.assert_called_once_with(self.context, volume['id'])
def test_image_attach_volume(self):
test_bdm = self.driver_classes['image'](
self.image_bdm)
@ -626,6 +728,38 @@ class TestDriverBlockDevice(test.NoDBTestCase):
self.virt_driver)
self.assertEqual(test_bdm.volume_id, 'fake-volume-id-2')
def test_blank_attach_fail_volume(self):
no_blank_volume = self.blank_bdm.copy()
no_blank_volume['volume_id'] = None
test_bdm = self.driver_classes['blank'](no_blank_volume)
instance = fake_instance.fake_instance_obj(mock.sentinel.ctx,
**{'uuid': 'fake-uuid'})
volume = {'id': 'fake-volume-id-2',
'display_name': 'fake-uuid-blank-vol'}
with contextlib.nested(
mock.patch.object(self.volume_api, 'create', return_value=volume),
mock.patch.object(self.volume_api, 'delete'),
) as (vol_create, vol_delete):
wait_func = mock.MagicMock()
mock_exception = exception.VolumeNotCreated(volume_id=volume['id'],
seconds=1,
attempts=1,
volume_status='error')
wait_func.side_effect = mock_exception
self.assertRaises(exception.VolumeNotCreated,
test_bdm.attach, context=self.context,
instance=instance,
volume_api=self.volume_api,
virt_driver=self.virt_driver,
wait_func=wait_func)
vol_create.assert_called_once_with(
self.context, test_bdm.volume_size, 'fake-uuid-blank-vol',
'', availability_zone=instance.availability_zone)
vol_delete.assert_called_once_with(
self.context, volume['id'])
def test_blank_attach_volume(self):
no_blank_volume = self.blank_bdm.copy()
no_blank_volume['volume_id'] = None

View File

@ -22,8 +22,10 @@ from oslo_utils import excutils
import six
from nova import block_device
from nova import exception
from nova.i18n import _LE
from nova.i18n import _LI
from nova.i18n import _LW
from nova import objects
from nova.objects import base as obj_base
from nova.volume import encryptors
@ -303,6 +305,19 @@ class DriverVolumeBlockDevice(DriverBlockDevice):
pass
super(DriverVolumeBlockDevice, self).save()
def _call_wait_func(self, context, wait_func, volume_api, volume_id):
try:
wait_func(context, volume_id)
except exception.VolumeNotCreated:
with excutils.save_and_reraise_exception():
if self['delete_on_termination']:
try:
volume_api.delete(context, volume_id)
except Exception as exc:
LOG.warn(_LW('Failed to delete volume: %(volume_id)s '
'due to %(exc)s'),
{'volume_id': volume_id, 'exc': exc})
class DriverSnapshotBlockDevice(DriverVolumeBlockDevice):
@ -319,7 +334,7 @@ class DriverSnapshotBlockDevice(DriverVolumeBlockDevice):
vol = volume_api.create(context, self.volume_size, '', '',
snapshot, availability_zone=av_zone)
if wait_func:
wait_func(context, vol['id'])
self._call_wait_func(context, wait_func, volume_api, vol['id'])
self.volume_id = vol['id']
@ -342,7 +357,7 @@ class DriverImageBlockDevice(DriverVolumeBlockDevice):
'', '', image_id=self.image_id,
availability_zone=av_zone)
if wait_func:
wait_func(context, vol['id'])
self._call_wait_func(context, wait_func, volume_api, vol['id'])
self.volume_id = vol['id']
@ -364,7 +379,7 @@ class DriverBlankBlockDevice(DriverVolumeBlockDevice):
vol = volume_api.create(context, self.volume_size, vol_name, '',
availability_zone=av_zone)
if wait_func:
wait_func(context, vol['id'])
self._call_wait_func(context, wait_func, volume_api, vol['id'])
self.volume_id = vol['id']