# Copyright 2021, Red Hat Inc.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
"""Tests for User Facing Messages in Backup Operations."""

from unittest import mock

from cinder.backup import manager as backup_manager
from cinder import exception
from cinder.message import message_field
from cinder.scheduler import manager as sch_manager
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import test


class BackupUserMessagesTest(test.TestCase):

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager._start_backup')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    def test_backup_create_invalid_status(
            self, mock_notify, mock_working, mock_run,
            mock_msg_create, mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='available', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = {'status': 'backing-up'}.__getitem__
        mock_get_vol.return_value = mock_vol

        self.assertRaises(
            exception.InvalidBackup, manager.create_backup, fake_context,
            fake_backup)
        self.assertEqual(message_field.Action.BACKUP_CREATE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_INVALID_STATE)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager._start_backup')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    def test_backup_create_service_down(
            self, mock_notify, mock_working, mock_run, mock_msg_create,
            mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = {'status': 'backing-up'}.__getitem__
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = False

        mock_run.side_effect = exception.InvalidBackup(reason='test reason')
        self.assertRaises(
            exception.InvalidBackup, manager.create_backup, fake_context,
            fake_backup)
        self.assertEqual(message_field.Action.BACKUP_CREATE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_SERVICE_DOWN)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    @mock.patch(
        'cinder.backup.manager.volume_utils.brick_get_connector_properties')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.get_backup_device')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_cleanup_temp_volumes_snapshots_when_backup_created')
    @mock.patch('cinder.backup.manager.BackupManager._attach_device')
    def test_backup_create_attach_error(
            self, mock_attach, mock_cleanup, mock_get_bak_dev, mock_get_conn,
            mock_notify, mock_working, mock_msg_create, mock_get_vol,
            mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = {'status': 'backing-up'}.__getitem__
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = True
        backup_device = mock.MagicMock()
        mock_attach.side_effect = exception.InvalidVolume(reason="test reason")

        self.assertRaises(exception.InvalidVolume, manager.continue_backup,
                          fake_context, fake_backup, backup_device)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.ATTACH_ERROR)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    @mock.patch(
        'cinder.backup.manager.volume_utils.brick_get_connector_properties')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.get_backup_device')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_cleanup_temp_volumes_snapshots_when_backup_created')
    @mock.patch('cinder.backup.manager.BackupManager._attach_device')
    @mock.patch(
        'cinder.tests.unit.backup.fake_service.FakeBackupService.backup')
    @mock.patch('cinder.backup.manager.open')
    @mock.patch('cinder.backup.manager.BackupManager._detach_device')
    def test_backup_create_driver_error(
            self, mock_detach, mock_open, mock_backup, mock_attach,
            mock_cleanup, mock_get_bak_dev, mock_get_conn, mock_notify,
            mock_working, mock_msg_create, mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = {'status': 'backing-up'}.__getitem__
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = True
        backup_device = mock.MagicMock()
        mock_attach.return_value = {'device': {'path': '/dev/sdb'}}
        mock_backup.side_effect = exception.InvalidBackup(reason="test reason")

        self.assertRaises(exception.InvalidBackup, manager.continue_backup,
                          fake_context, fake_backup, backup_device)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_CREATE_DRIVER_ERROR)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    @mock.patch(
        'cinder.backup.manager.volume_utils.brick_get_connector_properties')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.get_backup_device')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_cleanup_temp_volumes_snapshots_when_backup_created')
    @mock.patch('cinder.backup.manager.BackupManager._attach_device')
    @mock.patch(
        'cinder.tests.unit.backup.fake_service.FakeBackupService.backup')
    @mock.patch('cinder.backup.manager.open')
    @mock.patch('cinder.backup.manager.BackupManager._detach_device')
    def test_backup_create_detach_error(
            self, mock_detach, mock_open, mock_backup, mock_attach,
            mock_cleanup, mock_get_bak_dev, mock_get_conn, mock_notify,
            mock_working, mock_msg_create, mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = {'status': 'backing-up'}.__getitem__
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = True
        backup_device = mock.MagicMock()
        mock_attach.return_value = {'device': {'path': '/dev/sdb'}}
        mock_detach.side_effect = exception.InvalidVolume(reason="test reason")

        self.assertRaises(exception.InvalidVolume, manager.continue_backup,
                          fake_context, fake_backup, backup_device)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.DETACH_ERROR)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    @mock.patch(
        'cinder.backup.manager.volume_utils.brick_get_connector_properties')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.get_backup_device')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_cleanup_temp_volumes_snapshots_when_backup_created')
    @mock.patch('cinder.backup.manager.BackupManager._attach_device')
    @mock.patch(
        'cinder.tests.unit.backup.fake_service.FakeBackupService.backup')
    @mock.patch('cinder.backup.manager.open')
    @mock.patch('cinder.backup.manager.BackupManager._detach_device')
    def test_backup_create_cleanup_error(
            self, mock_detach, mock_open, mock_backup, mock_attach,
            mock_cleanup, mock_get_bak_dev, mock_get_conn, mock_notify,
            mock_working, mock_msg_create, mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = {'status': 'backing-up'}.__getitem__
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = True
        backup_device = mock.MagicMock()
        mock_attach.return_value = {'device': {'path': '/dev/sdb'}}
        mock_cleanup.side_effect = exception.InvalidVolume(
            reason="test reason")

        self.assertRaises(exception.InvalidVolume, manager.continue_backup,
                          fake_context, fake_backup, backup_device)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_CREATE_CLEANUP_ERROR)

    @mock.patch('cinder.scheduler.host_manager.HostManager.'
                '_choose_backend_filters')
    @mock.patch('cinder.scheduler.host_manager.HostManager.'
                '_get_available_backup_service_host')
    @mock.patch('cinder.volume.volume_utils.update_backup_error')
    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.db.volume_get')
    @mock.patch('cinder.message.api.API.create')
    def test_backup_create_scheduling_error(
            self, mock_msg_create, mock_get_vol, mock_vol_update,
            mock_update_error, mock_get_backup_host, mock_choose_filters):
        mock_choose_filters.return_value = ['AvailabilityZoneFilter']
        manager = sch_manager.SchedulerManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(id=fake.BACKUP_ID,
                                     volume_id=fake.VOLUME_ID,
                                     host=None)
        mock_get_vol.return_value = mock.MagicMock()
        exception.ServiceNotFound(service_id='cinder-backup')
        mock_get_backup_host.side_effect = exception.ServiceNotFound(
            service_id='cinder-backup')

        manager.create_backup(fake_context, fake_backup)
        mock_msg_create.assert_called_once_with(
            fake_context,
            action=message_field.Action.BACKUP_CREATE,
            resource_type=message_field.Resource.VOLUME_BACKUP,
            resource_uuid=fake_backup.id,
            detail=message_field.Detail.BACKUP_SCHEDULE_ERROR)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch(
        'cinder.backup.manager.BackupManager._notify_about_backup_usage')
    def test_backup_delete_invalid_state(
            self, mock_notify, mock_msg_create, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='available', volume_id=fake.VOLUME_ID,
            snapshot_id=None)

        self.assertRaises(
            exception.InvalidBackup, manager.delete_backup, fake_context,
            fake_backup)
        self.assertEqual(message_field.Action.BACKUP_DELETE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_INVALID_STATE)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch(
        'cinder.backup.manager.BackupManager._notify_about_backup_usage')
    def test_backup_delete_service_down(
            self, mock_notify, mock_working, mock_msg_create,
            mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='deleting', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        mock_working.return_value = False

        self.assertRaises(
            exception.InvalidBackup, manager.delete_backup, fake_context,
            fake_backup)
        self.assertEqual(message_field.Action.BACKUP_DELETE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_SERVICE_DOWN)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager._is_our_backup')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch(
        'cinder.backup.manager.BackupManager._notify_about_backup_usage')
    def test_backup_delete_driver_error(
            self, mock_notify, mock_working, mock_our_back,
            mock_msg_create, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='deleting', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        fake_backup.__getitem__.side_effect = (
            {'display_name': 'fail_on_delete'}.__getitem__)
        mock_working.return_value = True
        mock_our_back.return_value = True

        self.assertRaises(
            IOError, manager.delete_backup, fake_context,
            fake_backup)
        self.assertEqual(message_field.Action.BACKUP_DELETE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_DELETE_DRIVER_ERROR)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    def test_backup_restore_volume_invalid_state(
            self, mock_notify, mock_msg_create, mock_get_vol,
            mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        fake_backup.__getitem__.side_effect = (
            {'status': 'restoring', 'size': 1}.__getitem__)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = (
            {'id': fake.VOLUME_ID, 'status': 'available',
             'size': 1}.__getitem__)
        mock_get_vol.return_value = mock_vol

        self.assertRaises(
            exception.InvalidVolume, manager.restore_backup,
            fake_context, fake_backup, fake.VOLUME_ID, False)
        mock_msg_create.assert_called_once_with(
            fake_context,
            action=message_field.Action.BACKUP_RESTORE,
            resource_type=message_field.Resource.VOLUME_BACKUP,
            resource_uuid=mock_vol.id,
            detail=message_field.Detail.VOLUME_INVALID_STATE)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    def test_backup_restore_backup_invalid_state(
            self, mock_notify, mock_msg_create, mock_get_vol,
            mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        fake_backup.__getitem__.side_effect = (
            {'status': 'available', 'size': 1}.__getitem__)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = (
            {'status': 'restoring-backup', 'size': 1}.__getitem__)
        mock_get_vol.return_value = mock_vol

        self.assertRaises(
            exception.InvalidBackup, manager.restore_backup,
            fake_context, fake_backup, fake.VOLUME_ID, False)
        self.assertEqual(message_field.Action.BACKUP_RESTORE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_INVALID_STATE)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager._is_our_backup')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    @mock.patch(
        'cinder.backup.manager.volume_utils.brick_get_connector_properties')
    @mock.patch(
        'cinder.volume.rpcapi.VolumeAPI.secure_file_operations_enabled')
    @mock.patch('cinder.backup.manager.BackupManager._attach_device')
    @mock.patch('cinder.backup.manager.BackupManager._detach_device')
    def test_backup_restore_attach_error(
            self, mock_detach, mock_attach, mock_sec_opts, mock_get_conn,
            mock_notify, mock_working, mock_our_back, mock_msg_create,
            mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        fake_backup.__getitem__.side_effect = (
            {'status': 'restoring', 'size': 1}.__getitem__)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = (
            {'status': 'restoring-backup', 'size': 1}.__getitem__)
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = True
        mock_our_back.return_value = True
        mock_attach.side_effect = exception.InvalidBackup(
            reason="test reason")

        self.assertRaises(
            exception.InvalidBackup, manager.restore_backup,
            fake_context, fake_backup, fake.VOLUME_ID, False)
        self.assertEqual(message_field.Action.BACKUP_RESTORE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.ATTACH_ERROR)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager._is_our_backup')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    @mock.patch(
        'cinder.backup.manager.volume_utils.brick_get_connector_properties')
    @mock.patch(
        'cinder.volume.rpcapi.VolumeAPI.secure_file_operations_enabled')
    @mock.patch('cinder.backup.manager.BackupManager._attach_device')
    @mock.patch('cinder.backup.manager.open')
    @mock.patch(
        'cinder.tests.unit.backup.fake_service.FakeBackupService.restore')
    @mock.patch('cinder.backup.manager.BackupManager._detach_device')
    def test_backup_restore_driver_error(
            self, mock_detach, mock_restore, mock_open, mock_attach,
            mock_sec_opts, mock_get_conn, mock_notify, mock_working,
            mock_our_back, mock_msg_create, mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        fake_backup.__getitem__.side_effect = (
            {'status': 'restoring', 'size': 1}.__getitem__)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = (
            {'status': 'restoring-backup', 'size': 1}.__getitem__)
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = True
        mock_our_back.return_value = True
        mock_attach.return_value = {'device': {'path': '/dev/sdb'}}
        mock_restore.side_effect = exception.InvalidBackup(
            reason="test reason")

        self.assertRaises(
            exception.InvalidBackup, manager.restore_backup,
            fake_context, fake_backup, fake.VOLUME_ID, False)
        self.assertEqual(message_field.Action.BACKUP_RESTORE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.BACKUP_RESTORE_ERROR)

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.objects.volume.Volume.get_by_id')
    @mock.patch('cinder.message.api.API.create_from_request_context')
    @mock.patch('cinder.backup.manager.BackupManager._is_our_backup')
    @mock.patch('cinder.backup.manager.BackupManager.is_working')
    @mock.patch('cinder.backup.manager.BackupManager.'
                '_notify_about_backup_usage')
    @mock.patch(
        'cinder.backup.manager.volume_utils.brick_get_connector_properties')
    @mock.patch(
        'cinder.volume.rpcapi.VolumeAPI.secure_file_operations_enabled')
    @mock.patch('cinder.backup.manager.BackupManager._attach_device')
    @mock.patch('cinder.backup.manager.open')
    @mock.patch(
        'cinder.tests.unit.backup.fake_service.FakeBackupService.restore')
    @mock.patch('cinder.backup.manager.BackupManager._detach_device')
    def test_backup_restore_detach_error(
            self, mock_detach, mock_restore, mock_open, mock_attach,
            mock_sec_opts, mock_get_conn, mock_notify, mock_working,
            mock_our_back, mock_msg_create, mock_get_vol, mock_vol_update):
        manager = backup_manager.BackupManager()
        fake_context = mock.MagicMock()
        fake_backup = mock.MagicMock(
            id=fake.BACKUP_ID, status='creating', volume_id=fake.VOLUME_ID,
            snapshot_id=None)
        fake_backup.__getitem__.side_effect = (
            {'status': 'restoring', 'size': 1}.__getitem__)
        mock_vol = mock.MagicMock()
        mock_vol.__getitem__.side_effect = (
            {'status': 'restoring-backup', 'size': 1}.__getitem__)
        mock_get_vol.return_value = mock_vol
        mock_working.return_value = True
        mock_our_back.return_value = True
        mock_attach.return_value = {'device': {'path': '/dev/sdb'}}
        mock_detach.side_effect = exception.InvalidBackup(
            reason="test reason")

        self.assertRaises(
            exception.InvalidBackup, manager.restore_backup,
            fake_context, fake_backup, fake.VOLUME_ID, False)
        self.assertEqual(message_field.Action.BACKUP_RESTORE,
                         fake_context.message_action)
        self.assertEqual(message_field.Resource.VOLUME_BACKUP,
                         fake_context.message_resource_type)
        self.assertEqual(fake_backup.id,
                         fake_context.message_resource_id)
        mock_msg_create.assert_called_with(
            fake_context,
            detail=message_field.Detail.DETACH_ERROR)
