コード例 #1
0
 def execute(self, context, **kwargs):
     scheduler_hints = kwargs.pop('scheduler_hints', None)
     db_vt = kwargs.pop('volume_type')
     kwargs['volume_type'] = None
     if db_vt:
         kwargs['volume_type'] = objects.VolumeType()
         objects.VolumeType()._from_db_object(context,
                                              kwargs['volume_type'], db_vt)
     request_spec = objects.RequestSpec(**kwargs)
     filter_properties = {}
     if scheduler_hints:
         filter_properties['scheduler_hints'] = scheduler_hints
     self._cast_create_volume(context, request_spec, filter_properties)
コード例 #2
0
class SchedulerManagerTestCase(test.TestCase):
    """Test case for scheduler manager."""

    manager_cls = manager.SchedulerManager
    driver_cls = driver.Scheduler
    driver_cls_name = 'cinder.scheduler.driver.Scheduler'

    class AnException(Exception):
        pass

    def setUp(self):
        super(SchedulerManagerTestCase, self).setUp()
        self.flags(scheduler_driver=self.driver_cls_name)
        self.manager = self.manager_cls()
        self.manager._startup_delay = False
        self.context = context.get_admin_context()
        self.topic = 'fake_topic'
        self.fake_args = (1, 2, 3)
        self.fake_kwargs = {'cat': 'meow', 'dog': 'woof'}

    def test_1_correct_init(self):
        # Correct scheduler driver
        manager = self.manager
        self.assertIsInstance(manager.driver, self.driver_cls)

    @mock.patch('cinder.scheduler.driver.Scheduler.is_first_receive')
    @mock.patch('eventlet.sleep')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.publish_service_capabilities')
    def test_init_host_with_rpc_delay_after_3_tries(self,
                                                    publish_capabilities_mock,
                                                    sleep_mock,
                                                    is_first_receive_mock):
        self.manager._startup_delay = True
        is_first_receive_mock.side_effect = [False, False, True]
        self.manager.init_host_with_rpc()
        publish_capabilities_mock.assert_called_once_with(mock.ANY)
        calls = [mock.call(1)] * 2
        sleep_mock.assert_has_calls(calls)
        self.assertEqual(2, sleep_mock.call_count)
        self.assertFalse(self.manager._startup_delay)

    @mock.patch('cinder.scheduler.driver.Scheduler.is_first_receive')
    @mock.patch('eventlet.sleep')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.publish_service_capabilities')
    @ddt.data(71, 17)
    def test_init_host_with_rpc_delay_uses_new_config(
            self, new_cfg_value, publish_capabilities_mock, sleep_mock,
            is_first_receive_mock):
        # previously used CONF.periodic_interval; see Bug #1828748
        new_cfg_name = 'scheduler_driver_init_wait_time'

        self.addCleanup(CONF.clear_override, new_cfg_name)
        CONF.set_override(new_cfg_name, new_cfg_value)
        is_first_receive_mock.return_value = False
        self.manager.init_host_with_rpc()
        self.assertEqual(new_cfg_value, sleep_mock.call_count)

    @mock.patch('cinder.scheduler.driver.Scheduler.backend_passes_filters')
    @mock.patch(
        'cinder.scheduler.host_manager.BackendState.consume_from_volume')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.manage_existing_snapshot')
    def test_manage_existing_snapshot(self, mock_manage_existing_snapshot,
                                      mock_consume, mock_backend_passes):
        volume = fake_volume.fake_volume_obj(self.context, **{'size': 1})
        fake_backend = fake_scheduler.FakeBackendState('host1', {})
        mock_backend_passes.return_value = fake_backend

        self.manager.manage_existing_snapshot(self.context, volume,
                                              'fake_snapshot', 'fake_ref',
                                              None)

        mock_consume.assert_called_once_with({'size': 1})
        mock_manage_existing_snapshot.assert_called_once_with(
            self.context, 'fake_snapshot', 'fake_ref',
            volume.service_topic_queue)

    @mock.patch('cinder.objects.service.Service.get_minimum_rpc_version')
    @mock.patch('cinder.objects.service.Service.get_minimum_obj_version')
    @mock.patch('cinder.rpc.LAST_RPC_VERSIONS', {'cinder-volume': '1.3'})
    @mock.patch('cinder.rpc.LAST_OBJ_VERSIONS', {
        'cinder-volume': '1.4',
        'cinder-scheduler': '1.4',
        'cinder-backup': '1.5'
    })
    def test_reset(self, get_min_obj, get_min_rpc):
        mgr = self.manager_cls()

        volume_rpcapi = mgr.driver.volume_rpcapi
        self.assertEqual('1.3', volume_rpcapi.client.version_cap)
        self.assertEqual('1.4',
                         volume_rpcapi.client.serializer._base.version_cap)
        get_min_obj.return_value = objects.base.OBJ_VERSIONS.get_current()
        mgr.reset()

        volume_rpcapi = mgr.driver.volume_rpcapi
        self.assertEqual(get_min_rpc.return_value,
                         volume_rpcapi.client.version_cap)
        self.assertEqual(get_min_obj.return_value,
                         volume_rpcapi.client.serializer._base.version_cap)
        self.assertIsNone(volume_rpcapi.client.serializer._base.manifest)

    @mock.patch('cinder.message.api.API.cleanup_expired_messages')
    def test_clean_expired_messages(self, mock_clean):

        self.manager._clean_expired_messages(self.context)

        mock_clean.assert_called_once_with(self.context)

    @mock.patch('cinder.scheduler.driver.Scheduler.backend_passes_filters')
    @mock.patch(
        'cinder.scheduler.host_manager.BackendState.consume_from_volume')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.extend_volume')
    def test_extend_volume(self, mock_extend, mock_consume,
                           mock_backend_passes):
        volume = fake_volume.fake_volume_obj(self.context, **{'size': 1})
        fake_backend = fake_scheduler.FakeBackendState('host1', {})
        mock_backend_passes.return_value = fake_backend

        self.manager.extend_volume(self.context, volume, 2, 'fake_reservation')

        mock_consume.assert_called_once_with({'size': 1})
        mock_extend.assert_called_once_with(self.context, volume, 2,
                                            'fake_reservation')

    @ddt.data({'key': 'value'}, objects.RequestSpec(volume_id=fake.VOLUME2_ID))
    def test_append_operation_decorator(self, rs):
        @manager.append_operation_type()
        def _fake_schedule_method1(request_spec=None):
            return request_spec

        @manager.append_operation_type(name='_fake_schedule_method22')
        def _fake_schedule_method2(request_spec=None):
            return request_spec

        @manager.append_operation_type()
        def _fake_schedule_method3(request_spec2=None):
            return request_spec2

        result1 = _fake_schedule_method1(request_spec=copy.deepcopy(rs))
        result2 = _fake_schedule_method2(request_spec=copy.deepcopy(rs))
        result3 = _fake_schedule_method3(request_spec2=copy.deepcopy(rs))
        self.assertEqual('_fake_schedule_method1', result1['operation'])
        self.assertEqual('_fake_schedule_method22', result2['operation'])
        self.assertEqual(rs, result3)

    @ddt.data([{
        'key1': 'value1'
    }, {
        'key1': 'value2'
    }], [
        objects.RequestSpec(volume_id=fake.VOLUME_ID),
        objects.RequestSpec(volume_id=fake.VOLUME2_ID)
    ])
    def test_append_operation_decorator_with_list(self, rs_list):
        @manager.append_operation_type()
        def _fake_schedule_method(request_spec_list=None):
            return request_spec_list

        result1 = _fake_schedule_method(request_spec_list=rs_list)
        for rs in result1:
            self.assertEqual('_fake_schedule_method', rs['operation'])

    @ddt.data('available', 'in-use')
    @mock.patch('cinder.scheduler.driver.Scheduler.backend_passes_filters')
    @mock.patch(
        'cinder.scheduler.host_manager.BackendState.consume_from_volume')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.extend_volume')
    @mock.patch('cinder.quota.QUOTAS.rollback')
    @mock.patch('cinder.message.api.API.create')
    def test_extend_volume_no_valid_host(self, status, mock_create,
                                         mock_rollback, mock_extend,
                                         mock_consume, mock_backend_passes):
        volume = fake_volume.fake_volume_obj(
            self.context, **{
                'size': 1,
                'previous_status': status
            })
        no_valid_backend = exception.NoValidBackend(reason='')
        mock_backend_passes.side_effect = [no_valid_backend]

        with mock.patch.object(self.manager,
                               '_set_volume_state_and_notify') as mock_notify:
            self.manager.extend_volume(self.context, volume, 2,
                                       'fake_reservation')
            mock_notify.assert_called_once_with(
                'extend_volume',
                {'volume_state': {
                    'status': status,
                    'previous_status': None
                }}, self.context, no_valid_backend, None)
            mock_rollback.assert_called_once_with(self.context,
                                                  'fake_reservation',
                                                  project_id=volume.project_id)
            mock_consume.assert_not_called()
            mock_extend.assert_not_called()
            mock_create.assert_called_once_with(
                self.context,
                message_field.Action.EXTEND_VOLUME,
                resource_uuid=volume.id,
                exception=no_valid_backend)

    @mock.patch('cinder.quota.QuotaEngine.expire')
    def test_clean_expired_reservation(self, mock_clean):

        self.manager._clean_expired_reservation(self.context)

        mock_clean.assert_called_once_with(self.context)

    @mock.patch('cinder.scheduler.driver.Scheduler.'
                'update_service_capabilities')
    def test_update_service_capabilities_empty_dict(self, _mock_update_cap):
        # Test no capabilities passes empty dictionary
        service = 'fake_service'
        host = 'fake_host'

        self.manager.update_service_capabilities(self.context,
                                                 service_name=service,
                                                 host=host)
        _mock_update_cap.assert_called_once_with(service, host, {}, None, None)

    @mock.patch('cinder.scheduler.driver.Scheduler.'
                'update_service_capabilities')
    def test_update_service_capabilities_correct(self, _mock_update_cap):
        # Test capabilities passes correctly
        service = 'fake_service'
        host = 'fake_host'
        capabilities = {'fake_capability': 'fake_value'}

        self.manager.update_service_capabilities(self.context,
                                                 service_name=service,
                                                 host=host,
                                                 capabilities=capabilities)
        _mock_update_cap.assert_called_once_with(service, host, capabilities,
                                                 None, None)

    @mock.patch('cinder.scheduler.driver.Scheduler.'
                'notify_service_capabilities')
    def test_notify_service_capabilities_no_timestamp(self, _mock_notify_cap):
        """Test old interface that receives host."""
        service = 'volume'
        host = 'fake_host'
        capabilities = {'fake_capability': 'fake_value'}

        self.manager.notify_service_capabilities(self.context,
                                                 service_name=service,
                                                 host=host,
                                                 capabilities=capabilities)
        _mock_notify_cap.assert_called_once_with(service, host, capabilities,
                                                 None)

    @mock.patch('cinder.scheduler.driver.Scheduler.'
                'notify_service_capabilities')
    def test_notify_service_capabilities_timestamp(self, _mock_notify_cap):
        """Test new interface that receives backend and timestamp."""
        service = 'volume'
        backend = 'fake_cluster'
        capabilities = {'fake_capability': 'fake_value'}

        timestamp = '1970-01-01T00:00:00.000000'

        self.manager.notify_service_capabilities(self.context,
                                                 service_name=service,
                                                 backend=backend,
                                                 capabilities=capabilities,
                                                 timestamp=timestamp)
        _mock_notify_cap.assert_called_once_with(service, backend,
                                                 capabilities,
                                                 datetime(1970, 1, 1))

    @mock.patch('cinder.scheduler.driver.Scheduler.schedule_create_volume')
    @mock.patch('cinder.message.api.API.create')
    @mock.patch('cinder.db.volume_update')
    def test_create_volume_exception_puts_volume_in_error_state(
            self, _mock_volume_update, _mock_message_create,
            _mock_sched_create):
        # Test NoValidBackend exception behavior for create_volume.
        # Puts the volume in 'error' state and eats the exception.
        _mock_sched_create.side_effect = exception.NoValidBackend(reason="")
        volume = fake_volume.fake_volume_obj(self.context)
        request_spec = {
            'volume_id': volume.id,
            'volume': {
                'id': volume.id,
                '_name_id': None,
                'metadata': {},
                'admin_metadata': {},
                'glance_metadata': {}
            }
        }
        request_spec_obj = objects.RequestSpec.from_primitives(request_spec)

        self.manager.create_volume(self.context,
                                   volume,
                                   request_spec=request_spec_obj,
                                   filter_properties={})
        _mock_volume_update.assert_called_once_with(self.context, volume.id,
                                                    {'status': 'error'})
        _mock_sched_create.assert_called_once_with(self.context,
                                                   request_spec_obj, {})

        _mock_message_create.assert_called_once_with(
            self.context,
            message_field.Action.SCHEDULE_ALLOCATE_VOLUME,
            resource_uuid=volume.id,
            exception=mock.ANY)

    @mock.patch('cinder.scheduler.driver.Scheduler.schedule_create_volume')
    @mock.patch('eventlet.sleep')
    def test_create_volume_no_delay(self, _mock_sleep, _mock_sched_create):
        volume = fake_volume.fake_volume_obj(self.context)

        request_spec = {'volume_id': volume.id}
        request_spec_obj = objects.RequestSpec.from_primitives(request_spec)

        self.manager.create_volume(self.context,
                                   volume,
                                   request_spec=request_spec_obj,
                                   filter_properties={})
        _mock_sched_create.assert_called_once_with(self.context,
                                                   request_spec_obj, {})
        self.assertFalse(_mock_sleep.called)

    @mock.patch('cinder.scheduler.driver.Scheduler.schedule_create_volume')
    @mock.patch('eventlet.sleep')
    def test_create_volume_set_worker(self, _mock_sleep, _mock_sched_create):
        """Make sure that the worker is created when creating a volume."""
        volume = tests_utils.create_volume(self.context, status='creating')

        request_spec = {'volume_id': volume.id}

        self.manager.create_volume(self.context,
                                   volume,
                                   request_spec=request_spec,
                                   filter_properties={})
        volume.set_worker.assert_called_once_with()

    @mock.patch('cinder.scheduler.driver.Scheduler.schedule_create_volume')
    @mock.patch('cinder.scheduler.driver.Scheduler.is_ready')
    @mock.patch('eventlet.sleep')
    def test_create_volume_delay_scheduled_after_3_tries(
            self, _mock_sleep, _mock_is_ready, _mock_sched_create):
        self.manager._startup_delay = True
        volume = fake_volume.fake_volume_obj(self.context)

        request_spec = {'volume_id': volume.id}
        request_spec_obj = objects.RequestSpec.from_primitives(request_spec)

        _mock_is_ready.side_effect = [False, False, True]

        self.manager.create_volume(self.context,
                                   volume,
                                   request_spec=request_spec_obj,
                                   filter_properties={})
        _mock_sched_create.assert_called_once_with(self.context,
                                                   request_spec_obj, {})
        calls = [mock.call(1)] * 2
        _mock_sleep.assert_has_calls(calls)
        self.assertEqual(2, _mock_sleep.call_count)

    @mock.patch('cinder.scheduler.driver.Scheduler.schedule_create_volume')
    @mock.patch('cinder.scheduler.driver.Scheduler.is_ready')
    @mock.patch('eventlet.sleep')
    def test_create_volume_delay_scheduled_in_1_try(self, _mock_sleep,
                                                    _mock_is_ready,
                                                    _mock_sched_create):
        self.manager._startup_delay = True
        volume = fake_volume.fake_volume_obj(self.context)

        request_spec = {'volume_id': volume.id}
        request_spec_obj = objects.RequestSpec.from_primitives(request_spec)

        _mock_is_ready.return_value = True

        self.manager.create_volume(self.context,
                                   volume,
                                   request_spec=request_spec_obj,
                                   filter_properties={})
        _mock_sched_create.assert_called_once_with(self.context,
                                                   request_spec_obj, {})
        self.assertFalse(_mock_sleep.called)

    @mock.patch('cinder.db.volume_get')
    @mock.patch('cinder.scheduler.driver.Scheduler.backend_passes_filters')
    @mock.patch('cinder.db.volume_update')
    def test_migrate_volume_exception_returns_volume_state(
            self, _mock_volume_update, _mock_backend_passes, _mock_volume_get):
        # Test NoValidBackend exception behavior for migrate_volume_to_host.
        # Puts the volume in 'error_migrating' state and eats the exception.
        fake_updates = {'migration_status': 'error'}
        self._test_migrate_volume_exception_returns_volume_state(
            _mock_volume_update, _mock_backend_passes, _mock_volume_get,
            'available', fake_updates)

    @mock.patch('cinder.db.volume_get')
    @mock.patch('cinder.scheduler.driver.Scheduler.backend_passes_filters')
    @mock.patch('cinder.db.volume_update')
    def test_migrate_volume_exception_returns_volume_state_maintenance(
            self, _mock_volume_update, _mock_backend_passes, _mock_volume_get):
        fake_updates = {'status': 'available', 'migration_status': 'error'}
        self._test_migrate_volume_exception_returns_volume_state(
            _mock_volume_update, _mock_backend_passes, _mock_volume_get,
            'maintenance', fake_updates)

    def _test_migrate_volume_exception_returns_volume_state(
            self, _mock_volume_update, _mock_backend_passes, _mock_volume_get,
            status, fake_updates):
        volume = tests_utils.create_volume(self.context,
                                           status=status,
                                           previous_status='available')
        fake_volume_id = volume.id
        request_spec = {'volume_id': fake_volume_id}
        _mock_backend_passes.side_effect = exception.NoValidBackend(reason="")
        _mock_volume_get.return_value = volume

        self.manager.migrate_volume_to_host(self.context,
                                            volume,
                                            'host',
                                            True,
                                            request_spec=request_spec,
                                            filter_properties={})
        _mock_volume_update.assert_called_once_with(self.context,
                                                    fake_volume_id,
                                                    fake_updates)
        _mock_backend_passes.assert_called_once_with(self.context, 'host',
                                                     request_spec, {})

    @mock.patch('cinder.db.volume_update')
    @mock.patch('cinder.db.volume_attachment_get_all_by_volume_id')
    @mock.patch('cinder.quota.QUOTAS.rollback')
    def test_retype_volume_exception_returns_volume_state(
            self, quota_rollback, _mock_vol_attachment_get, _mock_vol_update):
        # Test NoValidBackend exception behavior for retype.
        # Puts the volume in original state and eats the exception.
        volume = tests_utils.create_volume(self.context,
                                           status='retyping',
                                           previous_status='in-use')
        instance_uuid = '12345678-1234-5678-1234-567812345678'
        volume_attach = tests_utils.attach_volume(self.context, volume.id,
                                                  instance_uuid, None,
                                                  '/dev/fake')
        _mock_vol_attachment_get.return_value = [volume_attach]
        reservations = mock.sentinel.reservations
        request_spec = {
            'volume_id': volume.id,
            'volume_type': {
                'id': 3
            },
            'migration_policy': 'on-demand',
            'quota_reservations': reservations
        }
        _mock_vol_update.return_value = {'status': 'in-use'}
        _mock_find_retype_backend = mock.Mock(
            side_effect=exception.NoValidBackend(reason=""))
        orig_retype = self.manager.driver.find_retype_backend
        self.manager.driver.find_retype_backend = _mock_find_retype_backend

        self.manager.retype(self.context,
                            volume,
                            request_spec=request_spec,
                            filter_properties={})

        _mock_find_retype_backend.assert_called_once_with(
            self.context, request_spec, {}, 'on-demand')
        quota_rollback.assert_called_once_with(self.context, reservations)
        _mock_vol_update.assert_called_once_with(self.context, volume.id,
                                                 {'status': 'in-use'})
        self.manager.driver.find_retype_host = orig_retype

    def test_do_cleanup(self):
        vol = tests_utils.create_volume(self.context, status='creating')
        self.manager._do_cleanup(self.context, vol)

        vol.refresh()
        self.assertEqual('error', vol.status)

    @mock.patch('cinder.scheduler.rpcapi.SchedulerAPI'
                '.determine_rpc_version_cap', mock.Mock(return_value='2.0'))
    def test_upgrading_cloud(self):
        self.assertTrue(self.manager.upgrading_cloud)

    @mock.patch('cinder.scheduler.rpcapi.SchedulerAPI'
                '.determine_rpc_version_cap')
    def test_upgrading_cloud_not(self, cap_mock):
        cap_mock.return_value = self.manager.RPC_API_VERSION
        self.assertFalse(self.manager.upgrading_cloud)

    def test_cleanup_destination_scheduler(self):
        service = objects.Service(id=1,
                                  host='hostname',
                                  binary='cinder-scheduler')
        result = self.manager._cleanup_destination(None, service)
        expected = self.manager.sch_api.do_cleanup, None, service.host
        self.assertEqual(expected, result)

    def test_cleanup_destination_volume(self):
        service = objects.Service(id=1,
                                  host='hostname',
                                  cluster_name=None,
                                  binary=constants.VOLUME_BINARY)
        result = self.manager._cleanup_destination(None, service)
        expected = self.manager.volume_api.do_cleanup, service, service.host
        self.assertEqual(expected, result)

    def test_cleanup_destination_volume_cluster_cache_hit(self):
        cluster = objects.Cluster(id=1,
                                  name='mycluster',
                                  binary=constants.VOLUME_BINARY)
        service = objects.Service(id=2,
                                  host='hostname',
                                  cluster_name=cluster.name,
                                  binary=constants.VOLUME_BINARY)
        cluster_cache = {'cinder-volume': {'mycluster': cluster}}
        result = self.manager._cleanup_destination(cluster_cache, service)
        expected = self.manager.volume_api.do_cleanup, cluster, cluster.name
        self.assertEqual(expected, result)

    @mock.patch('cinder.objects.Cluster.get_by_id')
    def test_cleanup_destination_volume_cluster_cache_miss(self, get_mock):
        cluster = objects.Cluster(id=1,
                                  name='mycluster',
                                  binary=constants.VOLUME_BINARY)
        service = objects.Service(self.context,
                                  id=2,
                                  host='hostname',
                                  cluster_name=cluster.name,
                                  binary=constants.VOLUME_BINARY)
        get_mock.return_value = cluster
        cluster_cache = collections.defaultdict(dict)
        result = self.manager._cleanup_destination(cluster_cache, service)
        expected = self.manager.volume_api.do_cleanup, cluster, cluster.name
        self.assertEqual(expected, result)

    @mock.patch('cinder.scheduler.manager.SchedulerManager.upgrading_cloud')
    def test_work_cleanup_upgrading(self, upgrading_mock):
        cleanup_request = objects.CleanupRequest(host='myhost')
        upgrading_mock.return_value = True
        self.assertRaises(exception.UnavailableDuringUpgrade,
                          self.manager.work_cleanup, self.context,
                          cleanup_request)

    @mock.patch('cinder.objects.Cluster.is_up', True)
    @mock.patch('cinder.objects.Service.is_up', False)
    @mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.do_cleanup')
    @mock.patch('cinder.volume.rpcapi.VolumeAPI.do_cleanup')
    @mock.patch('cinder.objects.ServiceList.get_all')
    def test_work_cleanup(self, get_mock, vol_clean_mock, sch_clean_mock):
        args = dict(service_id=1,
                    cluster_name='cluster_name',
                    host='host',
                    binary=constants.VOLUME_BINARY,
                    is_up=False,
                    disabled=True,
                    resource_id=fake.VOLUME_ID,
                    resource_type='Volume')

        cluster = objects.Cluster(id=1,
                                  name=args['cluster_name'],
                                  binary=constants.VOLUME_BINARY)
        services = [
            objects.Service(self.context,
                            id=2,
                            host='hostname',
                            cluster_name=cluster.name,
                            binary=constants.VOLUME_BINARY,
                            cluster=cluster),
            objects.Service(self.context,
                            id=3,
                            host='hostname',
                            cluster_name=None,
                            binary=constants.SCHEDULER_BINARY),
            objects.Service(self.context,
                            id=4,
                            host='hostname',
                            cluster_name=None,
                            binary=constants.VOLUME_BINARY)
        ]
        get_mock.return_value = services

        cleanup_request = objects.CleanupRequest(self.context, **args)
        res = self.manager.work_cleanup(self.context, cleanup_request)
        self.assertEqual((services[:2], services[2:]), res)
        self.assertEqual(1, vol_clean_mock.call_count)
        self.assertEqual(1, sch_clean_mock.call_count)

    @mock.patch('cinder.backup.rpcapi.BackupAPI.create_backup')
    @mock.patch('cinder.objects.backup.Backup.save')
    @mock.patch('cinder.scheduler.driver.Scheduler.get_backup_host')
    @mock.patch('cinder.db.volume_get')
    def test_create_backup(self, mock_volume_get, mock_host, mock_save,
                           mock_create):
        volume = fake_volume.fake_db_volume()
        mock_volume_get.return_value = volume
        mock_host.return_value = 'cinder-backup'
        backup = fake_backup.fake_backup_obj(self.context)

        self.manager.create_backup(self.context, backup=backup)

        mock_save.assert_called_once()
        mock_host.assert_called_once_with(volume)
        mock_volume_get.assert_called_once_with(self.context, backup.volume_id)
        mock_create.assert_called_once_with(self.context, backup)

    @mock.patch('cinder.volume.volume_utils.update_backup_error')
    @mock.patch('cinder.scheduler.driver.Scheduler.get_backup_host')
    @mock.patch('cinder.db.volume_get')
    @mock.patch('cinder.db.volume_update')
    def test_create_backup_no_service(self, mock_volume_update,
                                      mock_volume_get, mock_host, mock_error):
        volume = fake_volume.fake_db_volume()
        volume['status'] = 'backing-up'
        volume['previous_status'] = 'available'
        mock_volume_get.return_value = volume
        mock_host.side_effect = exception.ServiceNotFound(
            service_id='cinder-volume')
        backup = fake_backup.fake_backup_obj(self.context)

        self.manager.create_backup(self.context, backup=backup)

        mock_host.assert_called_once_with(volume)
        mock_volume_get.assert_called_once_with(self.context, backup.volume_id)
        mock_volume_update.assert_called_once_with(
            self.context, backup.volume_id, {
                'status': 'available',
                'previous_status': 'backing-up'
            })
        mock_error.assert_called_once_with(
            backup, 'Service not found for creating backup.')