Keep volume's status in 'creating' when creating from backup

When volume is created via restoring backup action, the volume
status would change into 'restoring-backup' from 'creating', this
state change would confusing end user. This patch updates this
and keep volume status in 'creating' when creating.

Change-Id: I82d741ea8278de75ed817379b0a04f1f919a27c7
This commit is contained in:
TommyLike 2018-06-29 15:37:14 +08:00
parent 6d426c0876
commit a61677ffd5
4 changed files with 73 additions and 15 deletions

View File

@ -43,6 +43,7 @@ from oslo_service import loopingcall
from oslo_service import periodic_task
from oslo_utils import excutils
from oslo_utils import importutils
from oslo_utils import timeutils
import six
from cinder.backup import driver
@ -544,17 +545,22 @@ class BackupManager(manager.ThreadPoolManager):
backup.host = self.host
backup.save()
expected_status = 'restoring-backup'
actual_status = volume['status']
if actual_status != expected_status:
expected_status = [fields.VolumeStatus.RESTORING_BACKUP,
fields.VolumeStatus.CREATING]
volume_previous_status = volume['status']
if volume_previous_status not in expected_status:
err = (_('Restore backup aborted, expected volume status '
'%(expected_status)s but got %(actual_status)s.') %
{'expected_status': expected_status,
'actual_status': actual_status})
{'expected_status': ','.join(expected_status),
'actual_status': volume_previous_status})
backup.status = fields.BackupStatus.AVAILABLE
backup.save()
self.db.volume_update(context, volume_id,
{'status': 'error_restoring'})
self.db.volume_update(
context, volume_id,
{'status':
(fields.VolumeStatus.ERROR if
volume_previous_status == fields.VolumeStatus.CREATING else
fields.VolumeStatus.ERROR_RESTORING)})
raise exception.InvalidVolume(reason=err)
expected_status = fields.BackupStatus.RESTORING
@ -565,7 +571,8 @@ class BackupManager(manager.ThreadPoolManager):
{'expected_status': expected_status,
'actual_status': actual_status})
self._update_backup_error(backup, err)
self.db.volume_update(context, volume_id, {'status': 'error'})
self.db.volume_update(context, volume_id,
{'status': fields.VolumeStatus.ERROR})
raise exception.InvalidBackup(reason=err)
if volume['size'] > backup['size']:
@ -587,22 +594,34 @@ class BackupManager(manager.ThreadPoolManager):
}
backup.status = fields.BackupStatus.AVAILABLE
backup.save()
self.db.volume_update(context, volume_id, {'status': 'error'})
self.db.volume_update(context, volume_id,
{'status': fields.VolumeStatus.ERROR})
raise exception.InvalidBackup(reason=err)
canceled = False
try:
canceled = False
self._run_restore(context, backup, volume)
except exception.BackupRestoreCancel:
canceled = True
except Exception:
with excutils.save_and_reraise_exception():
self.db.volume_update(context, volume_id,
{'status': 'error_restoring'})
self.db.volume_update(
context, volume_id,
{'status': (fields.VolumeStatus.ERROR if
actual_status == fields.VolumeStatus.CREATING
else fields.VolumeStatus.ERROR_RESTORING)})
backup.status = fields.BackupStatus.AVAILABLE
backup.save()
volume.status = 'error' if canceled else 'available'
if canceled:
volume.status = fields.VolumeStatus.ERROR
else:
volume.status = fields.VolumeStatus.AVAILABLE
# NOTE(tommylikehu): If previous status is 'creating', this is
# just a new created volume and we need update the 'launched_at'
# attribute as well.
if volume_previous_status == fields.VolumeStatus.CREATING:
volume['launched_at'] = timeutils.utcnow()
volume.save()
backup.status = fields.BackupStatus.AVAILABLE
backup.save()

View File

@ -177,10 +177,12 @@ class VolumeStatus(BaseCinderEnum):
IN_USE = 'in-use'
DETACHING = 'detaching'
MAINTENANCE = 'maintenance'
RESTORING_BACKUP = 'restoring-backup'
ERROR_RESTORING = 'error_restoring'
ALL = (CREATING, AVAILABLE, DELETING, ERROR, ERROR_DELETING,
ERROR_MANAGING, MANAGING, ATTACHING, IN_USE, DETACHING,
MAINTENANCE)
MAINTENANCE, RESTORING_BACKUP, ERROR_RESTORING)
class VolumeStatusField(BaseEnumField):

View File

@ -1134,6 +1134,44 @@ class BackupTestCase(BaseBackupTest):
self.assertEqual(fields.BackupStatus.AVAILABLE, backup.status)
self.assertTrue(mock_run_restore.called)
def test_restore_backup_with_creating_volume(self):
"""Test restore backup with a creating volume."""
vol_id = self._create_volume_db_entry(
status=fields.VolumeStatus.CREATING,
size=1)
backup = self._create_backup_db_entry(
status=fields.BackupStatus.RESTORING, volume_id=vol_id)
mock_run_restore = self.mock_object(
self.backup_mgr,
'_run_restore')
self.backup_mgr.restore_backup(self.ctxt, backup, vol_id)
vol = objects.Volume.get_by_id(self.ctxt, vol_id)
self.assertEqual(fields.VolumeStatus.AVAILABLE, vol.status)
self.assertIsNotNone(vol.launched_at)
backup.refresh()
self.assertEqual(fields.BackupStatus.AVAILABLE, backup.status)
self.assertTrue(mock_run_restore.called)
def test_restore_backup_canceled_with_creating_volume(self):
"""Test restore backup with a creating volume."""
vol_id = self._create_volume_db_entry(
status=fields.VolumeStatus.CREATING,
size=1)
backup = self._create_backup_db_entry(
status=fields.BackupStatus.RESTORING, volume_id=vol_id)
mock_run_restore = self.mock_object(
self.backup_mgr,
'_run_restore')
mock_run_restore.side_effect = exception.BackupRestoreCancel(
vol_id=vol_id, back_id=backup.id)
# We shouldn't raise an exception on the call, it's OK to cancel
self.backup_mgr.restore_backup(self.ctxt, backup, vol_id)
vol = objects.Volume.get_by_id(self.ctxt, vol_id)
self.assertEqual(fields.VolumeStatus.ERROR, vol.status)
backup.refresh()
self.assertEqual(fields.BackupStatus.AVAILABLE, backup.status)
self.assertTrue(mock_run_restore.called)
def test_restore_backup_with_bad_service(self):
"""Test error handling.

View File

@ -973,7 +973,6 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
"backup service to restore the volume with backup.",
{'id': backup_id})
model_update = self._create_raw_volume(volume, **kwargs) or {}
model_update.update({'status': 'restoring-backup'})
volume.update(model_update)
volume.save()