示例#1
0
    def test_scatter_gather_cells_timeout(self, mock_get_inst, mock_get_result,
                                          mock_timeout, mock_log_warning):
        # This is needed because we're mocking get_by_filters.
        self.useFixture(nova_fixtures.SpawnIsSynchronousFixture())
        ctxt = context.get_context()
        mapping0 = objects.CellMapping(database_connection='fake://db0',
                                       transport_url='none:///',
                                       uuid=objects.CellMapping.CELL0_UUID)
        mapping1 = objects.CellMapping(database_connection='fake://db1',
                                       transport_url='fake://mq1',
                                       uuid=uuids.cell1)
        mappings = objects.CellMappingList(objects=[mapping0, mapping1])

        # Simulate cell1 not responding.
        mock_get_result.side_effect = [(mapping0.uuid,
                                        mock.sentinel.instances),
                                       exception.CellTimeout()]

        results = context.scatter_gather_cells(
            ctxt, mappings, 30, objects.InstanceList.get_by_filters)
        self.assertEqual(2, len(results))
        self.assertIn(mock.sentinel.instances, results.values())
        self.assertIn(context.did_not_respond_sentinel, results.values())
        mock_timeout.assert_called_once_with(30, exception.CellTimeout)
        self.assertTrue(mock_log_warning.called)
示例#2
0
    def test_scatter_gather_cells_exception(self, mock_get_inst,
                                            mock_log_exception):
        # This is needed because we're mocking get_by_filters.
        self.useFixture(nova_fixtures.SpawnIsSynchronousFixture())
        ctxt = context.get_context()
        mapping0 = objects.CellMapping(database_connection='fake://db0',
                                       transport_url='none:///',
                                       uuid=objects.CellMapping.CELL0_UUID)
        mapping1 = objects.CellMapping(database_connection='fake://db1',
                                       transport_url='fake://mq1',
                                       uuid=uuids.cell1)
        mappings = objects.CellMappingList(objects=[mapping0, mapping1])

        # Simulate cell1 raising an exception.
        mock_get_inst.side_effect = [
            mock.sentinel.instances,
            test.TestingException()
        ]

        results = context.scatter_gather_cells(
            ctxt, mappings, 30, objects.InstanceList.get_by_filters)
        self.assertEqual(2, len(results))
        self.assertIn(mock.sentinel.instances, results.values())
        self.assertIn(context.raised_exception_sentinel, results.values())
        self.assertTrue(mock_log_exception.called)
示例#3
0
    def test_scatter_gather_cells(self, mock_get_inst, mock_target_cell):
        ctxt = context.get_context()
        mapping = objects.CellMapping(database_connection='fake://db',
                                      transport_url='fake://mq',
                                      uuid=uuids.cell)
        mappings = objects.CellMappingList(objects=[mapping])

        # Use a mock manager to assert call order across mocks.
        manager = mock.Mock()
        manager.attach_mock(mock_get_inst, 'get_inst')
        manager.attach_mock(mock_target_cell, 'target_cell')

        filters = {'deleted': False}
        context.scatter_gather_cells(
            ctxt, mappings, 60, objects.InstanceList.get_by_filters, filters,
            sort_dir='foo')

        # NOTE(melwitt): This only works without the SpawnIsSynchronous fixture
        # because when the spawn is treated as synchronous and the thread
        # function is called immediately, it will occur inside the target_cell
        # context manager scope when it wouldn't with a real spawn.

        # Assert that InstanceList.get_by_filters was called before the
        # target_cell context manager exited.
        get_inst_call = mock.call.get_inst(
            mock_target_cell.return_value.__enter__.return_value, filters,
            sort_dir='foo')
        expected_calls = [get_inst_call,
                          mock.call.target_cell().__exit__(None, None, None)]
        manager.assert_has_calls(expected_calls)
示例#4
0
    def test_scatter_gather_cells_all_timeout(self, mock_get_inst,
                                              mock_get_result, mock_timeout,
                                              mock_log_warning):
        """This is a regression test for bug 1847131.
        test_scatter_gather_cells_timeout did not catch the issue because it
        yields a result which sets the cell_uuid variable in scope before the
        CellTimeout is processed and logged. In this test we only raise the
        CellTimeout so cell_uuid will not be in scope for the log message.
        """
        # This is needed because we're mocking get_by_filters.
        self.useFixture(nova_fixtures.SpawnIsSynchronousFixture())
        ctxt = context.get_context()
        mapping0 = objects.CellMapping(database_connection='fake://db0',
                                       transport_url='none:///',
                                       uuid=objects.CellMapping.CELL0_UUID)
        mappings = objects.CellMappingList(objects=[mapping0])

        # Simulate cell0 not responding.
        mock_get_result.side_effect = exception.CellTimeout()

        results = context.scatter_gather_cells(
            ctxt, mappings, 30, objects.InstanceList.get_by_filters, {})
        self.assertEqual(1, len(results))
        self.assertIn(context.did_not_respond_sentinel, results.values())
        mock_timeout.assert_called_once_with(30, exception.CellTimeout)
        mock_log_warning.assert_called_once_with(
            'Timed out waiting for response from cell', exc_info=True)
    def test_scatter_gather_cells(self, mock_get_inst, mock_target_cell):
        self.useFixture(nova_fixtures.SpawnIsSynchronousFixture())
        ctxt = context.get_context()
        mapping = objects.CellMapping(database_connection='fake://db',
                                      transport_url='fake://mq',
                                      uuid=uuids.cell)
        mappings = objects.CellMappingList(objects=[mapping])

        filters = {'deleted': False}
        context.scatter_gather_cells(
            ctxt, mappings, 60, objects.InstanceList.get_by_filters, filters,
            sort_dir='foo')

        mock_get_inst.assert_called_once_with(
            mock_target_cell.return_value.__enter__.return_value, filters,
            sort_dir='foo')
    def test_scatter_gather_skip_cell0(self, mock_get_all, mock_scatter):
        ctxt = context.get_context()
        mapping0 = objects.CellMapping(database_connection='fake://db0',
                                       transport_url='none:///',
                                       uuid=objects.CellMapping.CELL0_UUID)
        mapping1 = objects.CellMapping(database_connection='fake://db1',
                                       transport_url='fake://mq1',
                                       uuid=uuids.cell1)
        mock_get_all.return_value = objects.CellMappingList(
            objects=[mapping0, mapping1])

        filters = {'deleted': False}
        context.scatter_gather_skip_cell0(
            ctxt, objects.InstanceList.get_by_filters, filters, sort_dir='foo')

        mock_scatter.assert_called_once_with(
            ctxt, [mapping1], 60, objects.InstanceList.get_by_filters, filters,
            sort_dir='foo')
示例#7
0
    def test_scatter_gather_cells_exception(self, mock_get_inst,
                                            mock_log_exception):
        # This is needed because we're mocking get_by_filters.
        self.useFixture(nova_fixtures.SpawnIsSynchronousFixture())
        ctxt = context.get_context()
        mapping0 = objects.CellMapping(database_connection='fake://db0',
                                       transport_url='none:///',
                                       uuid=objects.CellMapping.CELL0_UUID)
        mapping1 = objects.CellMapping(database_connection='fake://db1',
                                       transport_url='fake://mq1',
                                       uuid=uuids.cell1)
        mappings = objects.CellMappingList(objects=[mapping0, mapping1])

        # Simulate cell1 raising an exception.
        mock_get_inst.side_effect = [
            mock.sentinel.instances,
            test.TestingException()
        ]

        filters = {'deleted': False}
        results = context.scatter_gather_cells(
            ctxt, mappings, 30, objects.InstanceList.get_by_filters, filters)
        self.assertEqual(2, len(results))
        self.assertIn(mock.sentinel.instances, results.values())
        self.assertIsInstance(results[mapping1.uuid], Exception)
        # non-NovaException gets logged
        self.assertTrue(mock_log_exception.called)

        # Now run it again with a NovaException to see it's not logged.
        mock_log_exception.reset_mock()
        mock_get_inst.side_effect = [
            mock.sentinel.instances,
            exception.NotFound()
        ]

        results = context.scatter_gather_cells(
            ctxt, mappings, 30, objects.InstanceList.get_by_filters, filters)
        self.assertEqual(2, len(results))
        self.assertIn(mock.sentinel.instances, results.values())
        self.assertIsInstance(results[mapping1.uuid], exception.NovaException)
        # NovaExceptions are not logged, the caller should handle them.
        mock_log_exception.assert_not_called()
示例#8
0
class ComputeHostAPITestCase(test.TestCase):
    def setUp(self):
        super(ComputeHostAPITestCase, self).setUp()
        self.host_api = compute.HostAPI()
        self.aggregate_api = compute.AggregateAPI()
        self.ctxt = context.get_admin_context()
        fake_notifier.stub_notifier(self)
        self.addCleanup(fake_notifier.reset)
        self.req = fakes.HTTPRequest.blank('')
        self.controller = services.ServiceController()
        self.useFixture(nova_fixtures.SingleCellSimple())

    def _compare_obj(self, obj, db_obj):
        test_objects.compare_obj(self,
                                 obj,
                                 db_obj,
                                 allow_missing=test_service.OPTIONAL)

    def _compare_objs(self, obj_list, db_obj_list):
        self.assertEqual(len(obj_list), len(db_obj_list),
                         "The length of two object lists are different.")
        for index, obj in enumerate(obj_list):
            self._compare_obj(obj, db_obj_list[index])

    def test_set_host_enabled(self):
        fake_notifier.NOTIFICATIONS = []

        @mock.patch.object(self.host_api.rpcapi,
                           'set_host_enabled',
                           return_value='fake-result')
        @mock.patch.object(self.host_api,
                           '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_set_host_enabled):
            result = self.host_api.set_host_enabled(self.ctxt, 'fake_host',
                                                    'fake_enabled')
            self.assertEqual('fake-result', result)
            self.assertEqual(2, len(fake_notifier.NOTIFICATIONS))
            msg = fake_notifier.NOTIFICATIONS[0]
            self.assertEqual('HostAPI.set_enabled.start', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_enabled', msg.payload['enabled'])
            self.assertEqual('fake_host', msg.payload['host_name'])
            msg = fake_notifier.NOTIFICATIONS[1]
            self.assertEqual('HostAPI.set_enabled.end', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_enabled', msg.payload['enabled'])
            self.assertEqual('fake_host', msg.payload['host_name'])

        _do_test()

    def test_host_name_from_assert_hosts_exists(self):
        @mock.patch.object(self.host_api.rpcapi,
                           'set_host_enabled',
                           return_value='fake-result')
        @mock.patch.object(self.host_api,
                           '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_set_host_enabled):
            result = self.host_api.set_host_enabled(self.ctxt, 'fake_host',
                                                    'fake_enabled')
            self.assertEqual('fake-result', result)

        _do_test()

    def test_get_host_uptime(self):
        @mock.patch.object(self.host_api.rpcapi,
                           'get_host_uptime',
                           return_value='fake-result')
        @mock.patch.object(self.host_api,
                           '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_get_host_uptime):
            result = self.host_api.get_host_uptime(self.ctxt, 'fake_host')
            self.assertEqual('fake-result', result)

        _do_test()

    def test_get_host_uptime_service_down(self):
        @mock.patch.object(self.host_api.db,
                           'service_get_by_compute_host',
                           return_value=dict(test_service.fake_service, id=1))
        @mock.patch.object(self.host_api.servicegroup_api,
                           'service_is_up',
                           return_value=False)
        def _do_test(mock_service_is_up, mock_service_get_by_compute_host):
            self.assertRaises(exception.ComputeServiceUnavailable,
                              self.host_api.get_host_uptime, self.ctxt,
                              'fake_host')

        _do_test()

    def test_host_power_action(self):
        fake_notifier.NOTIFICATIONS = []

        @mock.patch.object(self.host_api.rpcapi,
                           'host_power_action',
                           return_value='fake-result')
        @mock.patch.object(self.host_api,
                           '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_host_power_action):
            result = self.host_api.host_power_action(self.ctxt, 'fake_host',
                                                     'fake_action')
            self.assertEqual('fake-result', result)
            self.assertEqual(2, len(fake_notifier.NOTIFICATIONS))
            msg = fake_notifier.NOTIFICATIONS[0]
            self.assertEqual('HostAPI.power_action.start', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_action', msg.payload['action'])
            self.assertEqual('fake_host', msg.payload['host_name'])
            msg = fake_notifier.NOTIFICATIONS[1]
            self.assertEqual('HostAPI.power_action.end', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_action', msg.payload['action'])
            self.assertEqual('fake_host', msg.payload['host_name'])

        _do_test()

    def test_set_host_maintenance(self):
        fake_notifier.NOTIFICATIONS = []

        @mock.patch.object(self.host_api.rpcapi,
                           'host_maintenance_mode',
                           return_value='fake-result')
        @mock.patch.object(self.host_api,
                           '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_host_maintenance_mode):
            result = self.host_api.set_host_maintenance(
                self.ctxt, 'fake_host', 'fake_mode')
            self.assertEqual('fake-result', result)
            self.assertEqual(2, len(fake_notifier.NOTIFICATIONS))
            msg = fake_notifier.NOTIFICATIONS[0]
            self.assertEqual('HostAPI.set_maintenance.start', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_host', msg.payload['host_name'])
            self.assertEqual('fake_mode', msg.payload['mode'])
            msg = fake_notifier.NOTIFICATIONS[1]
            self.assertEqual('HostAPI.set_maintenance.end', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_host', msg.payload['host_name'])
            self.assertEqual('fake_mode', msg.payload['mode'])

        _do_test()

    def test_service_get_all_cells(self):
        cells = objects.CellMappingList.get_all(self.ctxt)
        for cell in cells:
            with context.target_cell(self.ctxt, cell) as cctxt:
                objects.Service(context=cctxt,
                                binary='nova-compute',
                                host='host-%s' % cell.uuid).create()
        services = self.host_api.service_get_all(self.ctxt, all_cells=True)
        self.assertEqual(sorted(['host-%s' % cell.uuid for cell in cells]),
                         sorted([svc.host for svc in services]))

    @mock.patch('nova.context.scatter_gather_cells')
    def test_service_get_all_cells_with_failures(self, mock_sg):
        service = objects.Service(binary='nova-compute',
                                  host='host-%s' % uuids.cell1)
        mock_sg.return_value = {
            uuids.cell1: [service],
            uuids.cell2: context.did_not_respond_sentinel
        }
        services = self.host_api.service_get_all(self.ctxt, all_cells=True)
        # returns the results from cell1 and ignores cell2.
        self.assertEqual(['host-%s' % uuids.cell1],
                         [svc.host for svc in services])

    @mock.patch('nova.objects.CellMappingList.get_all')
    @mock.patch.object(objects.HostMappingList, 'get_by_cell_id')
    @mock.patch('nova.context.scatter_gather_all_cells')
    def test_service_get_all_cells_with_minimal_constructs(
            self, mock_sg, mock_get_hm, mock_cm_list):
        service = objects.Service(binary='nova-compute',
                                  host='host-%s' % uuids.cell0)
        cells = [
            objects.CellMapping(uuid=uuids.cell1, id=1),
            objects.CellMapping(uuid=uuids.cell2, id=2),
        ]
        mock_cm_list.return_value = cells
        context.load_cells()
        # create two hms in cell1, which is the down cell in this test.
        hm1 = objects.HostMapping(self.ctxt,
                                  host='host1-unavailable',
                                  cell_mapping=cells[0])
        hm1.create()
        hm2 = objects.HostMapping(self.ctxt,
                                  host='host2-unavailable',
                                  cell_mapping=cells[0])
        hm2.create()
        mock_sg.return_value = {
            cells[0].uuid: [service],
            cells[1].uuid: context.did_not_respond_sentinel,
        }
        mock_get_hm.return_value = [hm1, hm2]
        services = self.host_api.service_get_all(self.ctxt,
                                                 all_cells=True,
                                                 cell_down_support=True)
        # returns the results from cell0 and minimal construct from cell1.
        self.assertEqual(
            sorted([
                'host-%s' % uuids.cell0, 'host1-unavailable',
                'host2-unavailable'
            ]), sorted([svc.host for svc in services]))
        mock_sg.assert_called_once_with(self.ctxt,
                                        objects.ServiceList.get_all,
                                        None,
                                        set_zones=False)
        mock_get_hm.assert_called_once_with(self.ctxt, cells[1].id)

    def test_service_get_all_no_zones(self):
        services = [
            dict(test_service.fake_service,
                 id=1,
                 topic='compute',
                 host='host1'),
            dict(test_service.fake_service, topic='compute', host='host2')
        ]

        @mock.patch.object(self.host_api.db, 'service_get_all')
        def _do_test(mock_service_get_all):
            mock_service_get_all.return_value = services
            # Test no filters
            result = self.host_api.service_get_all(self.ctxt)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, services)

            # Test no filters #2
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt, filters={})
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, services)

            # Test w/ filter
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt,
                                                   filters=dict(host='host2'))
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, [services[1]])

        _do_test()

    def test_service_get_all(self):
        services = [
            dict(test_service.fake_service, topic='compute', host='host1'),
            dict(test_service.fake_service, topic='compute', host='host2')
        ]
        exp_services = []
        for service in services:
            exp_service = {}
            exp_service.update(availability_zone='nova', **service)
            exp_services.append(exp_service)

        @mock.patch.object(self.host_api.db, 'service_get_all')
        def _do_test(mock_service_get_all):
            mock_service_get_all.return_value = services

            # Test no filters
            result = self.host_api.service_get_all(self.ctxt, set_zones=True)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, exp_services)

            # Test no filters #2
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt,
                                                   filters={},
                                                   set_zones=True)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, exp_services)

            # Test w/ filter
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt,
                                                   filters=dict(host='host2'),
                                                   set_zones=True)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, [exp_services[1]])

            # Test w/ zone filter but no set_zones arg.
            mock_service_get_all.reset_mock()
            filters = {'availability_zone': 'nova'}
            result = self.host_api.service_get_all(self.ctxt, filters=filters)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, exp_services)

        _do_test()

    def test_service_get_by_compute_host(self):
        @mock.patch.object(self.host_api.db,
                           'service_get_by_compute_host',
                           return_value=test_service.fake_service)
        def _do_test(mock_service_get_by_compute_host):
            result = self.host_api.service_get_by_compute_host(
                self.ctxt, 'fake-host')
            self.assertEqual(test_service.fake_service['id'], result.id)

        _do_test()

    def test_service_update_by_host_and_binary(self):
        host_name = 'fake-host'
        binary = 'nova-compute'
        params_to_update = dict(disabled=True)
        service_id = 42
        expected_result = dict(test_service.fake_service, id=service_id)

        @mock.patch.object(self.host_api, '_update_compute_provider_status')
        @mock.patch.object(self.host_api.db, 'service_get_by_host_and_binary')
        @mock.patch.object(self.host_api.db, 'service_update')
        def _do_test(mock_service_update, mock_service_get_by_host_and_binary,
                     mock_update_compute_provider_status):
            mock_service_get_by_host_and_binary.return_value = expected_result
            mock_service_update.return_value = expected_result

            result = self.host_api.service_update_by_host_and_binary(
                self.ctxt, host_name, binary, params_to_update)
            self._compare_obj(result, expected_result)
            mock_update_compute_provider_status.assert_called_once_with(
                self.ctxt, test.MatchType(objects.Service))

        _do_test()

    @mock.patch('nova.compute.api.HostAPI._update_compute_provider_status',
                new_callable=mock.NonCallableMock)
    def test_service_update_no_update_provider_status(self, mock_ucps):
        """Tests the scenario that the service is updated but the disabled
        field is not changed, for example the forced_down field is only
        updated. In this case _update_compute_provider_status should not be
        called.
        """
        service = objects.Service(forced_down=True)
        self.assertIn('forced_down', service.obj_what_changed())
        with mock.patch.object(service, 'save') as mock_save:
            retval = self.host_api.service_update(self.ctxt, service)
            self.assertIs(retval, service)
            mock_save.assert_called_once_with()

    @mock.patch('nova.compute.rpcapi.ComputeAPI.set_host_enabled',
                new_callable=mock.NonCallableMock)
    def test_update_compute_provider_status_service_too_old(self, mock_she):
        """Tests the scenario that the service is up but is too old to sync the
        COMPUTE_STATUS_DISABLED trait.
        """
        service = objects.Service(host='fake-host')
        service.version = compute.MIN_COMPUTE_SYNC_COMPUTE_STATUS_DISABLED - 1
        with mock.patch.object(self.host_api.servicegroup_api,
                               'service_is_up',
                               return_value=True) as service_is_up:
            self.host_api._update_compute_provider_status(self.ctxt, service)
            service_is_up.assert_called_once_with(service)
        self.assertIn(
            'Compute service on host fake-host is too old to sync '
            'the COMPUTE_STATUS_DISABLED trait in Placement.',
            self.stdlog.logger.output)

    @mock.patch('nova.compute.rpcapi.ComputeAPI.set_host_enabled',
                side_effect=messaging.MessagingTimeout)
    def test_update_compute_provider_status_service_rpc_error(self, mock_she):
        """Tests the scenario that the RPC call to the compute service raised
        some exception.
        """
        service = objects.Service(host='fake-host', disabled=True)
        with mock.patch.object(self.host_api.servicegroup_api,
                               'service_is_up',
                               return_value=True) as service_is_up:
            self.host_api._update_compute_provider_status(self.ctxt, service)
            service_is_up.assert_called_once_with(service)
        mock_she.assert_called_once_with(self.ctxt, 'fake-host', False)
        log_output = self.stdlog.logger.output
        self.assertIn(
            'An error occurred while updating the '
            'COMPUTE_STATUS_DISABLED trait on compute node '
            'resource providers managed by host fake-host.', log_output)
        self.assertIn('MessagingTimeout', log_output)

    @mock.patch.object(objects.InstanceList,
                       'get_by_host',
                       return_value=['fake-responses'])
    def test_instance_get_all_by_host(self, mock_get):
        result = self.host_api.instance_get_all_by_host(self.ctxt, 'fake-host')
        self.assertEqual(['fake-responses'], result)

    def test_task_log_get_all(self):
        @mock.patch.object(self.host_api.db,
                           'task_log_get_all',
                           return_value='fake-response')
        def _do_test(mock_task_log_get_all):
            result = self.host_api.task_log_get_all(self.ctxt,
                                                    'fake-name',
                                                    'fake-begin',
                                                    'fake-end',
                                                    host='fake-host',
                                                    state='fake-state')
            self.assertEqual('fake-response', result)

        _do_test()

    @mock.patch.object(
        objects.CellMappingList,
        'get_all',
        return_value=objects.CellMappingList(objects=[
            objects.CellMapping(uuid=uuids.cell1_uuid,
                                transport_url='mq://fake1',
                                database_connection='db://fake1'),
            objects.CellMapping(uuid=uuids.cell2_uuid,
                                transport_url='mq://fake2',
                                database_connection='db://fake2'),
            objects.CellMapping(uuid=uuids.cell3_uuid,
                                transport_url='mq://fake3',
                                database_connection='db://fake3')
        ]))
    @mock.patch.object(
        objects.Service,
        'get_by_uuid',
        side_effect=[
            exception.ServiceNotFound(service_id=uuids.service_uuid),
            objects.Service(uuid=uuids.service_uuid)
        ])
    def test_service_get_by_id_using_uuid(self, service_get_by_uuid,
                                          cell_mappings_get_all):
        """Tests that we can lookup a service in the HostAPI using a uuid.
        There are two calls to objects.Service.get_by_uuid and the first
        raises ServiceNotFound so that we ensure we keep looping over the
        cells. We'll find the service in the second cell and break the loop
        so that we don't needlessly check in the third cell.
        """
        def _fake_set_target_cell(ctxt, cell_mapping):
            if cell_mapping:
                # These aren't really what would be set for values but let's
                # keep this simple so we can assert something is set when a
                # mapping is provided.
                ctxt.db_connection = cell_mapping.database_connection
                ctxt.mq_connection = cell_mapping.transport_url

        # We have to override the SingleCellSimple fixture.
        self.useFixture(
            fixtures.MonkeyPatch('nova.context.set_target_cell',
                                 _fake_set_target_cell))
        ctxt = context.get_admin_context()
        self.assertIsNone(ctxt.db_connection)
        self.host_api.service_get_by_id(ctxt, uuids.service_uuid)
        # We should have broken the loop over the cells and set the target cell
        # on the context.
        service_get_by_uuid.assert_has_calls(
            [mock.call(ctxt, uuids.service_uuid)] * 2)
        self.assertEqual('db://fake2', ctxt.db_connection)

    @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
                'aggregate_remove_host')
    @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
                'aggregate_add_host')
    @mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
    @mock.patch.object(objects.HostMapping, 'get_by_host')
    def test_service_delete_compute_in_aggregate(self, mock_hm, mock_get_cn,
                                                 mock_add_host,
                                                 mock_remove_host):
        compute = objects.Service(
            self.ctxt, **{
                'host': 'fake-compute-host',
                'binary': 'nova-compute',
                'topic': 'compute',
                'report_count': 0
            })
        compute.create()
        # This is needed because of lazy-loading service.compute_node
        cn = objects.ComputeNode(uuid=uuids.cn,
                                 host="fake-compute-host",
                                 hypervisor_hostname="fake-compute-host")
        mock_get_cn.return_value = [cn]
        aggregate = self.aggregate_api.create_aggregate(
            self.ctxt, 'aggregate', None)
        self.aggregate_api.add_host_to_aggregate(self.ctxt, aggregate.id,
                                                 'fake-compute-host')
        mock_add_host.assert_called_once_with(mock.ANY,
                                              aggregate.uuid,
                                              host_name='fake-compute-host')
        self.controller.delete(self.req, compute.id)
        result = self.aggregate_api.get_aggregate(self.ctxt,
                                                  aggregate.id).hosts
        self.assertEqual([], result)
        mock_hm.return_value.destroy.assert_called_once_with()
        mock_remove_host.assert_called_once_with(mock.ANY, aggregate.uuid,
                                                 'fake-compute-host')

    @mock.patch('nova.db.api.compute_node_statistics')
    def test_compute_node_statistics(self, mock_cns):
        # Note this should only be called twice
        mock_cns.side_effect = [
            {
                'stat1': 1,
                'stat2': 4.0
            },
            {
                'stat1': 5,
                'stat2': 1.2
            },
        ]
        compute.CELLS = [
            objects.CellMapping(uuid=uuids.cell1),
            objects.CellMapping(uuid=objects.CellMapping.CELL0_UUID),
            objects.CellMapping(uuid=uuids.cell2)
        ]
        stats = self.host_api.compute_node_statistics(self.ctxt)
        self.assertEqual({'stat1': 6, 'stat2': 5.2}, stats)

    @mock.patch.object(
        objects.CellMappingList,
        'get_all',
        return_value=objects.CellMappingList(objects=[
            objects.CellMapping(uuid=objects.CellMapping.CELL0_UUID,
                                transport_url='mq://cell0',
                                database_connection='db://cell0'),
            objects.CellMapping(uuid=uuids.cell1_uuid,
                                transport_url='mq://fake1',
                                database_connection='db://fake1'),
            objects.CellMapping(uuid=uuids.cell2_uuid,
                                transport_url='mq://fake2',
                                database_connection='db://fake2')
        ]))
    @mock.patch.object(objects.ComputeNode,
                       'get_by_uuid',
                       side_effect=[
                           exception.ComputeHostNotFound(host=uuids.cn_uuid),
                           objects.ComputeNode(uuid=uuids.cn_uuid)
                       ])
    def test_compute_node_get_using_uuid(self, compute_get_by_uuid,
                                         cell_mappings_get_all):
        """Tests that we can lookup a compute node in the HostAPI using a uuid.
        """
        self.host_api.compute_node_get(self.ctxt, uuids.cn_uuid)
        # cell0 should have been skipped, and the compute node wasn't found
        # in cell1 so we checked cell2 and found it
        self.assertEqual(2, compute_get_by_uuid.call_count)
        compute_get_by_uuid.assert_has_calls(
            [mock.call(self.ctxt, uuids.cn_uuid)] * 2)

    @mock.patch.object(
        objects.CellMappingList,
        'get_all',
        return_value=objects.CellMappingList(objects=[
            objects.CellMapping(uuid=objects.CellMapping.CELL0_UUID,
                                transport_url='mq://cell0',
                                database_connection='db://cell0'),
            objects.CellMapping(uuid=uuids.cell1_uuid,
                                transport_url='mq://fake1',
                                database_connection='db://fake1'),
            objects.CellMapping(uuid=uuids.cell2_uuid,
                                transport_url='mq://fake2',
                                database_connection='db://fake2')
        ]))
    @mock.patch.object(
        objects.ComputeNode,
        'get_by_uuid',
        side_effect=exception.ComputeHostNotFound(host=uuids.cn_uuid))
    def test_compute_node_get_not_found(self, compute_get_by_uuid,
                                        cell_mappings_get_all):
        """Tests that we can lookup a compute node in the HostAPI using a uuid
        and will fail with ComputeHostNotFound if we didn't find it in any
        cell.
        """
        self.assertRaises(exception.ComputeHostNotFound,
                          self.host_api.compute_node_get, self.ctxt,
                          uuids.cn_uuid)
        # cell0 should have been skipped, and the compute node wasn't found
        # in cell1 or cell2.
        self.assertEqual(2, compute_get_by_uuid.call_count)
        compute_get_by_uuid.assert_has_calls(
            [mock.call(self.ctxt, uuids.cn_uuid)] * 2)
示例#9
0
class ComputeHostAPITestCase(test.TestCase):
    def setUp(self):
        super(ComputeHostAPITestCase, self).setUp()
        self.host_api = compute.HostAPI()
        self.aggregate_api = compute_api.AggregateAPI()
        self.ctxt = context.get_admin_context()
        fake_notifier.stub_notifier(self)
        self.addCleanup(fake_notifier.reset)
        self.req = fakes.HTTPRequest.blank('')
        self.controller = services.ServiceController()
        self.useFixture(nova_fixtures.SingleCellSimple())

    def _compare_obj(self, obj, db_obj):
        test_objects.compare_obj(self, obj, db_obj,
                                 allow_missing=test_service.OPTIONAL)

    def _compare_objs(self, obj_list, db_obj_list):
        self.assertEqual(len(obj_list), len(db_obj_list),
                         "The length of two object lists are different.")
        for index, obj in enumerate(obj_list):
            self._compare_obj(obj, db_obj_list[index])

    def test_set_host_enabled(self):
        fake_notifier.NOTIFICATIONS = []

        @mock.patch.object(self.host_api.rpcapi, 'set_host_enabled',
                           return_value='fake-result')
        @mock.patch.object(self.host_api, '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_set_host_enabled):
            result = self.host_api.set_host_enabled(self.ctxt, 'fake_host',
                                                    'fake_enabled')
            self.assertEqual('fake-result', result)
            self.assertEqual(2, len(fake_notifier.NOTIFICATIONS))
            msg = fake_notifier.NOTIFICATIONS[0]
            self.assertEqual('HostAPI.set_enabled.start', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_enabled', msg.payload['enabled'])
            self.assertEqual('fake_host', msg.payload['host_name'])
            msg = fake_notifier.NOTIFICATIONS[1]
            self.assertEqual('HostAPI.set_enabled.end', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_enabled', msg.payload['enabled'])
            self.assertEqual('fake_host', msg.payload['host_name'])

        _do_test()

    def test_host_name_from_assert_hosts_exists(self):
        @mock.patch.object(self.host_api.rpcapi, 'set_host_enabled',
                           return_value='fake-result')
        @mock.patch.object(self.host_api, '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_set_host_enabled):
            result = self.host_api.set_host_enabled(self.ctxt, 'fake_host',
                                                    'fake_enabled')
            self.assertEqual('fake-result', result)

        _do_test()

    def test_get_host_uptime(self):
        @mock.patch.object(self.host_api.rpcapi, 'get_host_uptime',
                           return_value='fake-result')
        @mock.patch.object(self.host_api, '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_get_host_uptime):
            result = self.host_api.get_host_uptime(self.ctxt, 'fake_host')
            self.assertEqual('fake-result', result)

        _do_test()

    def test_get_host_uptime_service_down(self):
        @mock.patch.object(self.host_api.db, 'service_get_by_compute_host',
                           return_value=dict(test_service.fake_service, id=1))
        @mock.patch.object(self.host_api.servicegroup_api, 'service_is_up',
                           return_value=False)
        def _do_test(mock_service_is_up, mock_service_get_by_compute_host):
            self.assertRaises(exception.ComputeServiceUnavailable,
                              self.host_api.get_host_uptime, self.ctxt,
                              'fake_host')

        _do_test()

    def test_host_power_action(self):
        fake_notifier.NOTIFICATIONS = []

        @mock.patch.object(self.host_api.rpcapi, 'host_power_action',
                           return_value='fake-result')
        @mock.patch.object(self.host_api, '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_host_power_action):
            result = self.host_api.host_power_action(self.ctxt, 'fake_host',
                                                     'fake_action')
            self.assertEqual('fake-result', result)
            self.assertEqual(2, len(fake_notifier.NOTIFICATIONS))
            msg = fake_notifier.NOTIFICATIONS[0]
            self.assertEqual('HostAPI.power_action.start', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_action', msg.payload['action'])
            self.assertEqual('fake_host', msg.payload['host_name'])
            msg = fake_notifier.NOTIFICATIONS[1]
            self.assertEqual('HostAPI.power_action.end', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_action', msg.payload['action'])
            self.assertEqual('fake_host', msg.payload['host_name'])

        _do_test()

    def test_set_host_maintenance(self):
        fake_notifier.NOTIFICATIONS = []

        @mock.patch.object(self.host_api.rpcapi, 'host_maintenance_mode',
                           return_value='fake-result')
        @mock.patch.object(self.host_api, '_assert_host_exists',
                           return_value='fake_host')
        def _do_test(mock_assert_host_exists, mock_host_maintenance_mode):
            result = self.host_api.set_host_maintenance(self.ctxt, 'fake_host',
                                                        'fake_mode')
            self.assertEqual('fake-result', result)
            self.assertEqual(2, len(fake_notifier.NOTIFICATIONS))
            msg = fake_notifier.NOTIFICATIONS[0]
            self.assertEqual('HostAPI.set_maintenance.start', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_host', msg.payload['host_name'])
            self.assertEqual('fake_mode', msg.payload['mode'])
            msg = fake_notifier.NOTIFICATIONS[1]
            self.assertEqual('HostAPI.set_maintenance.end', msg.event_type)
            self.assertEqual('api.fake_host', msg.publisher_id)
            self.assertEqual('INFO', msg.priority)
            self.assertEqual('fake_host', msg.payload['host_name'])
            self.assertEqual('fake_mode', msg.payload['mode'])

        _do_test()

    def test_service_get_all_cells(self):
        cells = objects.CellMappingList.get_all(self.ctxt)
        for cell in cells:
            with context.target_cell(self.ctxt, cell) as cctxt:
                objects.Service(context=cctxt,
                                binary='nova-compute',
                                host='host-%s' % cell.uuid).create()
        services = self.host_api.service_get_all(self.ctxt, all_cells=True)
        self.assertEqual(sorted(['host-%s' % cell.uuid for cell in cells]),
                         sorted([svc.host for svc in services]))

    @mock.patch('nova.context.scatter_gather_cells')
    def test_service_get_all_cells_with_failures(self, mock_sg):
        service = objects.Service(binary='nova-compute',
                                  host='host-%s' % uuids.cell1)
        mock_sg.return_value = {
            uuids.cell1: [service],
            uuids.cell2: context.raised_exception_sentinel
        }
        services = self.host_api.service_get_all(self.ctxt, all_cells=True)
        # returns the results from cell1 and ignores cell2.
        self.assertEqual(['host-%s' % uuids.cell1],
                         [svc.host for svc in services])

    def test_service_get_all_no_zones(self):
        services = [dict(test_service.fake_service,
                         id=1, topic='compute', host='host1'),
                    dict(test_service.fake_service,
                         topic='compute', host='host2')]

        @mock.patch.object(self.host_api.db, 'service_get_all')
        def _do_test(mock_service_get_all):
            mock_service_get_all.return_value = services
            # Test no filters
            result = self.host_api.service_get_all(self.ctxt)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, services)

            # Test no filters #2
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt, filters={})
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, services)

            # Test w/ filter
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt,
                                                   filters=dict(host='host2'))
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, [services[1]])

        _do_test()

    def test_service_get_all(self):
        services = [dict(test_service.fake_service,
                         topic='compute', host='host1'),
                    dict(test_service.fake_service,
                         topic='compute', host='host2')]
        exp_services = []
        for service in services:
            exp_service = {}
            exp_service.update(availability_zone='nova', **service)
            exp_services.append(exp_service)

        @mock.patch.object(self.host_api.db, 'service_get_all')
        def _do_test(mock_service_get_all):
            mock_service_get_all.return_value = services

            # Test no filters
            result = self.host_api.service_get_all(self.ctxt, set_zones=True)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, exp_services)

            # Test no filters #2
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt, filters={},
                                                   set_zones=True)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, exp_services)

            # Test w/ filter
            mock_service_get_all.reset_mock()
            result = self.host_api.service_get_all(self.ctxt,
                                                   filters=dict(host='host2'),
                                                   set_zones=True)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, [exp_services[1]])

            # Test w/ zone filter but no set_zones arg.
            mock_service_get_all.reset_mock()
            filters = {'availability_zone': 'nova'}
            result = self.host_api.service_get_all(self.ctxt,
                                                   filters=filters)
            mock_service_get_all.assert_called_once_with(self.ctxt,
                                                         disabled=None)
            self._compare_objs(result, exp_services)

        _do_test()

    def test_service_get_by_compute_host(self):
        @mock.patch.object(self.host_api.db, 'service_get_by_compute_host',
                           return_value=test_service.fake_service)
        def _do_test(mock_service_get_by_compute_host):
            result = self.host_api.service_get_by_compute_host(self.ctxt,
                                                               'fake-host')
            self.assertEqual(test_service.fake_service['id'], result.id)

        _do_test()

    def test_service_update(self):
        host_name = 'fake-host'
        binary = 'nova-compute'
        params_to_update = dict(disabled=True)
        service_id = 42
        expected_result = dict(test_service.fake_service, id=service_id)

        @mock.patch.object(self.host_api.db, 'service_get_by_host_and_binary')
        @mock.patch.object(self.host_api.db, 'service_update')
        def _do_test(mock_service_update, mock_service_get_by_host_and_binary):
            mock_service_get_by_host_and_binary.return_value = expected_result
            mock_service_update.return_value = expected_result

            result = self.host_api.service_update(
                self.ctxt, host_name, binary, params_to_update)
            self._compare_obj(result, expected_result)

        _do_test()

    @mock.patch.object(objects.InstanceList, 'get_by_host',
                       return_value = ['fake-responses'])
    def test_instance_get_all_by_host(self, mock_get):
        result = self.host_api.instance_get_all_by_host(self.ctxt,
                                                        'fake-host')
        self.assertEqual(['fake-responses'], result)

    def test_task_log_get_all(self):
        @mock.patch.object(self.host_api.db, 'task_log_get_all',
                           return_value='fake-response')
        def _do_test(mock_task_log_get_all):
            result = self.host_api.task_log_get_all(self.ctxt, 'fake-name',
                                                    'fake-begin', 'fake-end',
                                                    host='fake-host',
                                                    state='fake-state')
            self.assertEqual('fake-response', result)

        _do_test()

    @mock.patch.object(objects.CellMappingList, 'get_all',
                       return_value=objects.CellMappingList(objects=[
                           objects.CellMapping(
                               uuid=uuids.cell1_uuid,
                               transport_url='mq://fake1',
                               database_connection='db://fake1'),
                           objects.CellMapping(
                               uuid=uuids.cell2_uuid,
                               transport_url='mq://fake2',
                               database_connection='db://fake2'),
                           objects.CellMapping(
                               uuid=uuids.cell3_uuid,
                               transport_url='mq://fake3',
                               database_connection='db://fake3')]))
    @mock.patch.object(objects.Service, 'get_by_uuid',
                       side_effect=[
                           exception.ServiceNotFound(
                               service_id=uuids.service_uuid),
                           objects.Service(uuid=uuids.service_uuid)])
    def test_service_get_by_id_using_uuid(self, service_get_by_uuid,
                                          cell_mappings_get_all):
        """Tests that we can lookup a service in the HostAPI using a uuid.
        There are two calls to objects.Service.get_by_uuid and the first
        raises ServiceNotFound so that we ensure we keep looping over the
        cells. We'll find the service in the second cell and break the loop
        so that we don't needlessly check in the third cell.
        """

        def _fake_set_target_cell(ctxt, cell_mapping):
            if cell_mapping:
                # These aren't really what would be set for values but let's
                # keep this simple so we can assert something is set when a
                # mapping is provided.
                ctxt.db_connection = cell_mapping.database_connection
                ctxt.mq_connection = cell_mapping.transport_url

        # We have to override the SingleCellSimple fixture.
        self.useFixture(fixtures.MonkeyPatch(
            'nova.context.set_target_cell', _fake_set_target_cell))
        ctxt = context.get_admin_context()
        self.assertIsNone(ctxt.db_connection)
        self.host_api.service_get_by_id(ctxt, uuids.service_uuid)
        # We should have broken the loop over the cells and set the target cell
        # on the context.
        service_get_by_uuid.assert_has_calls(
            [mock.call(ctxt, uuids.service_uuid)] * 2)
        self.assertEqual('db://fake2', ctxt.db_connection)

    @mock.patch('nova.context.set_target_cell')
    @mock.patch('nova.compute.api.load_cells')
    @mock.patch('nova.objects.Service.get_by_id')
    def test_service_delete(self, get_by_id, load_cells, set_target):
        compute_api.CELLS = [
            objects.CellMapping(),
            objects.CellMapping(),
            objects.CellMapping(),
        ]

        service = mock.MagicMock()
        get_by_id.side_effect = [exception.ServiceNotFound(service_id=1),
                                 service,
                                 exception.ServiceNotFound(service_id=1)]
        self.host_api.service_delete(self.ctxt, 1)
        get_by_id.assert_has_calls([mock.call(self.ctxt, 1),
                                    mock.call(self.ctxt, 1),
                                    mock.call(self.ctxt, 1)])
        service.destroy.assert_called_once_with()
        set_target.assert_called_once_with(self.ctxt, compute_api.CELLS[1])

    @mock.patch('nova.context.set_target_cell')
    @mock.patch('nova.compute.api.load_cells')
    @mock.patch('nova.objects.Service.get_by_id')
    def test_service_delete_ambiguous(self, get_by_id, load_cells, set_target):
        compute_api.CELLS = [
            objects.CellMapping(),
            objects.CellMapping(),
            objects.CellMapping(),
        ]

        service1 = mock.MagicMock()
        service2 = mock.MagicMock()
        get_by_id.side_effect = [exception.ServiceNotFound(service_id=1),
                                 service1,
                                 service2]
        self.assertRaises(exception.ServiceNotUnique,
                          self.host_api.service_delete, self.ctxt, 1)
        self.assertFalse(service1.destroy.called)
        self.assertFalse(service2.destroy.called)
        self.assertFalse(set_target.called)

    @mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
    @mock.patch.object(objects.HostMapping, 'get_by_host')
    def test_service_delete_compute_in_aggregate(self, mock_hm, mock_get_cn):
        compute = self.host_api.db.service_create(self.ctxt,
            {'host': 'fake-compute-host',
             'binary': 'nova-compute',
             'topic': 'compute',
             'report_count': 0})
        # This is needed because of lazy-loading service.compute_node
        cn = objects.ComputeNode(uuid=uuids.cn, host="fake-compute-host",
                                 hypervisor_hostname="fake-compute-host")
        mock_get_cn.return_value = [cn]
        aggregate = self.aggregate_api.create_aggregate(self.ctxt,
                                                   'aggregate',
                                                   None)
        self.aggregate_api.add_host_to_aggregate(self.ctxt,
                                                 aggregate.id,
                                                 'fake-compute-host')
        self.controller.delete(self.req, compute.id)
        result = self.aggregate_api.get_aggregate(self.ctxt,
                                                  aggregate.id).hosts
        self.assertEqual([], result)
        mock_hm.return_value.destroy.assert_called_once_with()

    @mock.patch('nova.db.compute_node_statistics')
    def test_compute_node_statistics(self, mock_cns):
        # Note this should only be called twice
        mock_cns.side_effect = [
            {'stat1': 1, 'stat2': 4.0},
            {'stat1': 5, 'stat2': 1.2},
        ]
        compute_api.CELLS = [objects.CellMapping(uuid=uuids.cell1),
                             objects.CellMapping(
                                 uuid=objects.CellMapping.CELL0_UUID),
                             objects.CellMapping(uuid=uuids.cell2)]
        stats = self.host_api.compute_node_statistics(self.ctxt)
        self.assertEqual({'stat1': 6, 'stat2': 5.2}, stats)

    @mock.patch.object(objects.CellMappingList, 'get_all',
                       return_value=objects.CellMappingList(objects=[
                           objects.CellMapping(
                               uuid=objects.CellMapping.CELL0_UUID,
                               transport_url='mq://cell0',
                               database_connection='db://cell0'),
                           objects.CellMapping(
                               uuid=uuids.cell1_uuid,
                               transport_url='mq://fake1',
                               database_connection='db://fake1'),
                           objects.CellMapping(
                               uuid=uuids.cell2_uuid,
                               transport_url='mq://fake2',
                               database_connection='db://fake2')]))
    @mock.patch.object(objects.ComputeNode, 'get_by_uuid',
                       side_effect=[exception.ComputeHostNotFound(
                                        host=uuids.cn_uuid),
                                    objects.ComputeNode(uuid=uuids.cn_uuid)])
    def test_compute_node_get_using_uuid(self, compute_get_by_uuid,
                                         cell_mappings_get_all):
        """Tests that we can lookup a compute node in the HostAPI using a uuid.
        """
        self.host_api.compute_node_get(self.ctxt, uuids.cn_uuid)
        # cell0 should have been skipped, and the compute node wasn't found
        # in cell1 so we checked cell2 and found it
        self.assertEqual(2, compute_get_by_uuid.call_count)
        compute_get_by_uuid.assert_has_calls(
            [mock.call(self.ctxt, uuids.cn_uuid)] * 2)

    @mock.patch.object(objects.CellMappingList, 'get_all',
                       return_value=objects.CellMappingList(objects=[
                           objects.CellMapping(
                               uuid=objects.CellMapping.CELL0_UUID,
                               transport_url='mq://cell0',
                               database_connection='db://cell0'),
                           objects.CellMapping(
                               uuid=uuids.cell1_uuid,
                               transport_url='mq://fake1',
                               database_connection='db://fake1'),
                           objects.CellMapping(
                               uuid=uuids.cell2_uuid,
                               transport_url='mq://fake2',
                               database_connection='db://fake2')]))
    @mock.patch.object(objects.ComputeNode, 'get_by_uuid',
                       side_effect=exception.ComputeHostNotFound(
                           host=uuids.cn_uuid))
    def test_compute_node_get_not_found(self, compute_get_by_uuid,
                                        cell_mappings_get_all):
        """Tests that we can lookup a compute node in the HostAPI using a uuid
        and will fail with ComputeHostNotFound if we didn't find it in any
        cell.
        """
        self.assertRaises(exception.ComputeHostNotFound,
                          self.host_api.compute_node_get,
                          self.ctxt, uuids.cn_uuid)
        # cell0 should have been skipped, and the compute node wasn't found
        # in cell1 or cell2.
        self.assertEqual(2, compute_get_by_uuid.call_count)
        compute_get_by_uuid.assert_has_calls(
            [mock.call(self.ctxt, uuids.cn_uuid)] * 2)