# Copyright (c) 2011 OpenStack Foundation # 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 HostManager """ import collections import contextlib import datetime from unittest import mock from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import versionutils import nova from nova.compute import task_states from nova.compute import vm_states from nova import context as nova_context from nova import exception from nova import objects from nova.objects import base as obj_base from nova.pci import stats as pci_stats from nova.scheduler import filters from nova.scheduler import host_manager from nova import test from nova.tests import fixtures from nova.tests.unit import fake_instance from nova.tests.unit.scheduler import fakes class FakeFilterClass1(filters.BaseHostFilter): def host_passes(self, host_state, filter_properties): pass class FakeFilterClass2(filters.BaseHostFilter): def host_passes(self, host_state, filter_properties): pass class HostManagerTestCase(test.NoDBTestCase): """Test case for HostManager class.""" @mock.patch.object(host_manager.HostManager, '_init_instance_info') @mock.patch.object(host_manager.HostManager, '_init_aggregates') def setUp(self, mock_init_agg, mock_init_inst): super(HostManagerTestCase, self).setUp() self.flags(available_filters=[ __name__ + '.FakeFilterClass1', __name__ + '.FakeFilterClass2'], group='filter_scheduler') self.flags(enabled_filters=['FakeFilterClass1'], group='filter_scheduler') self.host_manager = host_manager.HostManager() cell = uuids.cell self.fake_hosts = [host_manager.HostState('fake_host%s' % x, 'fake-node', cell) for x in range(1, 5)] self.fake_hosts += [host_manager.HostState('fake_multihost', 'fake-node%s' % x, cell) for x in range(1, 5)] self.useFixture(fixtures.SpawnIsSynchronousFixture()) def test_load_filters(self): filters = self.host_manager._load_filters() self.assertEqual(filters, ['FakeFilterClass1']) def test_refresh_cells_caches(self): ctxt = nova_context.RequestContext('fake', 'fake') # Loading the non-cell0 mapping from the base test class. self.assertEqual(1, len(self.host_manager.enabled_cells)) self.assertEqual(1, len(self.host_manager.cells)) # Creating cell mappings for mocking the list of cell_mappings obtained # so that the refreshing mechanism can be properly tested. This will in # turn ignore the loaded cell mapping from the base test case setup. cell_uuid1 = uuids.cell1 cell_mapping1 = objects.CellMapping(context=ctxt, uuid=cell_uuid1, database_connection='fake:///db1', transport_url='fake:///mq1', disabled=False) cell_uuid2 = uuids.cell2 cell_mapping2 = objects.CellMapping(context=ctxt, uuid=cell_uuid2, database_connection='fake:///db2', transport_url='fake:///mq2', disabled=True) cell_uuid3 = uuids.cell3 cell_mapping3 = objects.CellMapping(context=ctxt, uuid=cell_uuid3, database_connection='fake:///db3', transport_url='fake:///mq3', disabled=False) cells = [cell_mapping1, cell_mapping2, cell_mapping3] # Throw a random host-to-cell in that cache to make sure it gets reset. self.host_manager.host_to_cell_uuid['fake-host'] = cell_uuid1 with mock.patch('nova.objects.CellMappingList.get_all', return_value=cells) as mock_cm: self.host_manager.refresh_cells_caches() mock_cm.assert_called_once() # Cell2 is not in the enabled list. self.assertEqual(2, len(self.host_manager.enabled_cells)) self.assertEqual(cell_uuid3, self.host_manager.enabled_cells[1].uuid) # But it is still in the full list. self.assertEqual(3, len(self.host_manager.cells)) self.assertIn(cell_uuid2, self.host_manager.cells) # The host_to_cell_uuid cache should have been reset. self.assertEqual({}, self.host_manager.host_to_cell_uuid) def test_refresh_cells_caches_except_cell0(self): ctxt = nova_context.RequestContext('fake-user', 'fake_project') cell_uuid0 = objects.CellMapping.CELL0_UUID cell_mapping0 = objects.CellMapping(context=ctxt, uuid=cell_uuid0, database_connection='fake:///db1', transport_url='fake:///mq1') cells = objects.CellMappingList(cell_mapping0) # Mocking the return value of get_all cell_mappings to return only # the cell0 mapping to check if its filtered or not. with mock.patch('nova.objects.CellMappingList.get_all', return_value=cells) as mock_cm: self.host_manager.refresh_cells_caches() mock_cm.assert_called_once() self.assertEqual(0, len(self.host_manager.cells)) @mock.patch('nova.objects.HostMapping.get_by_host', return_value=objects.HostMapping( cell_mapping=objects.CellMapping(uuid=uuids.cell1))) def test_get_cell_mapping_for_host(self, mock_get_by_host): # Starting with an empty cache, assert that the HostMapping is looked # up and the result is cached. ctxt = nova_context.get_admin_context() host = 'fake-host' self.assertEqual({}, self.host_manager.host_to_cell_uuid) cm = self.host_manager._get_cell_mapping_for_host(ctxt, host) self.assertIs(cm, mock_get_by_host.return_value.cell_mapping) self.assertIn(host, self.host_manager.host_to_cell_uuid) self.assertEqual( uuids.cell1, self.host_manager.host_to_cell_uuid[host]) mock_get_by_host.assert_called_once_with(ctxt, host) # Reset the mock and do it again, assert we do not query the DB. mock_get_by_host.reset_mock() self.host_manager._get_cell_mapping_for_host(ctxt, host) mock_get_by_host.assert_not_called() # Mix up the cache such that the host is mapped to a cell that # is not in the cache which will make us query the DB. Also make the # HostMapping query raise HostMappingNotFound to make sure that comes # up to the caller. mock_get_by_host.reset_mock() self.host_manager.host_to_cell_uuid[host] = uuids.random_cell mock_get_by_host.side_effect = exception.HostMappingNotFound(name=host) with mock.patch('nova.scheduler.host_manager.LOG.warning') as warning: self.assertRaises(exception.HostMappingNotFound, self.host_manager._get_cell_mapping_for_host, ctxt, host) # We should have logged a warning because the host is cached to # a cell uuid but that cell uuid is not in the cells cache. warning.assert_called_once() self.assertIn('Host %s is expected to be in cell %s', warning.call_args[0][0]) # And we should have also tried to lookup the HostMapping in the DB mock_get_by_host.assert_called_once_with(ctxt, host) @mock.patch.object(nova.objects.InstanceList, 'get_by_filters') @mock.patch.object(nova.objects.ComputeNodeList, 'get_all') def test_init_instance_info_batches(self, mock_get_all, mock_get_by_filters): cn_list = objects.ComputeNodeList() for num in range(22): host_name = 'host_%s' % num cn_list.objects.append(objects.ComputeNode(host=host_name)) mock_get_all.return_value = cn_list self.host_manager._init_instance_info() self.assertEqual(mock_get_by_filters.call_count, 3) @mock.patch.object(nova.objects.InstanceList, 'get_by_filters') @mock.patch.object(nova.objects.ComputeNodeList, 'get_all') def test_init_instance_info(self, mock_get_all, mock_get_by_filters): cn1 = objects.ComputeNode(host='host1') cn2 = objects.ComputeNode(host='host2') inst1 = objects.Instance(host='host1', uuid=uuids.instance_1) inst2 = objects.Instance(host='host1', uuid=uuids.instance_2) inst3 = objects.Instance(host='host2', uuid=uuids.instance_3) mock_get_all.return_value = objects.ComputeNodeList(objects=[cn1, cn2]) mock_get_by_filters.return_value = objects.InstanceList( objects=[inst1, inst2, inst3]) hm = self.host_manager hm._instance_info = {} hm._init_instance_info() self.assertEqual(len(hm._instance_info), 2) fake_info = hm._instance_info['host1'] self.assertIn(uuids.instance_1, fake_info['instances']) self.assertIn(uuids.instance_2, fake_info['instances']) self.assertNotIn(uuids.instance_3, fake_info['instances']) exp_filters = {'deleted': False, 'host': [u'host1', u'host2']} mock_get_by_filters.assert_called_once_with(mock.ANY, exp_filters) @mock.patch.object(nova.objects.InstanceList, 'get_by_filters') @mock.patch.object(nova.objects.ComputeNodeList, 'get_all') def test_init_instance_info_compute_nodes(self, mock_get_all, mock_get_by_filters): cn1 = objects.ComputeNode(host='host1') cn2 = objects.ComputeNode(host='host2') inst1 = objects.Instance(host='host1', uuid=uuids.instance_1) inst2 = objects.Instance(host='host1', uuid=uuids.instance_2) inst3 = objects.Instance(host='host2', uuid=uuids.instance_3) cell = objects.CellMapping(database_connection='', target_url='', uuid=uuids.cell_uuid) mock_get_by_filters.return_value = objects.InstanceList( objects=[inst1, inst2, inst3]) hm = self.host_manager hm._instance_info = {} hm._init_instance_info({cell: [cn1, cn2]}) self.assertEqual(len(hm._instance_info), 2) fake_info = hm._instance_info['host1'] self.assertIn(uuids.instance_1, fake_info['instances']) self.assertIn(uuids.instance_2, fake_info['instances']) self.assertNotIn(uuids.instance_3, fake_info['instances']) exp_filters = {'deleted': False, 'host': [u'host1', u'host2']} mock_get_by_filters.assert_called_once_with(mock.ANY, exp_filters) # should not be called if the list of nodes was passed explicitly self.assertFalse(mock_get_all.called) def test_enabled_filters(self): enabled_filters = self.host_manager.enabled_filters self.assertEqual(1, len(enabled_filters)) self.assertIsInstance(enabled_filters[0], FakeFilterClass1) @mock.patch.object(host_manager.HostManager, '_init_instance_info') @mock.patch.object(objects.AggregateList, 'get_all') def test_init_aggregates_no_aggs(self, agg_get_all, mock_init_info): agg_get_all.return_value = [] self.host_manager = host_manager.HostManager() self.assertEqual({}, self.host_manager.aggs_by_id) self.assertEqual({}, self.host_manager.host_aggregates_map) @mock.patch.object(host_manager.HostManager, '_init_instance_info') @mock.patch.object(objects.AggregateList, 'get_all') def test_init_aggregates_one_agg_no_hosts(self, agg_get_all, mock_init_info): fake_agg = objects.Aggregate(id=1, hosts=[]) agg_get_all.return_value = [fake_agg] self.host_manager = host_manager.HostManager() self.assertEqual({1: fake_agg}, self.host_manager.aggs_by_id) self.assertEqual({}, self.host_manager.host_aggregates_map) @mock.patch.object(host_manager.HostManager, '_init_instance_info') @mock.patch.object(objects.AggregateList, 'get_all') def test_init_aggregates_one_agg_with_hosts(self, agg_get_all, mock_init_info): fake_agg = objects.Aggregate(id=1, hosts=['fake-host']) agg_get_all.return_value = [fake_agg] self.host_manager = host_manager.HostManager() self.assertEqual({1: fake_agg}, self.host_manager.aggs_by_id) self.assertEqual({'fake-host': set([1])}, self.host_manager.host_aggregates_map) def test_update_aggregates(self): fake_agg = objects.Aggregate(id=1, hosts=['fake-host']) self.host_manager.update_aggregates([fake_agg]) self.assertEqual({1: fake_agg}, self.host_manager.aggs_by_id) self.assertEqual({'fake-host': set([1])}, self.host_manager.host_aggregates_map) def test_update_aggregates_remove_hosts(self): fake_agg = objects.Aggregate(id=1, hosts=['fake-host']) self.host_manager.update_aggregates([fake_agg]) self.assertEqual({1: fake_agg}, self.host_manager.aggs_by_id) self.assertEqual({'fake-host': set([1])}, self.host_manager.host_aggregates_map) # Let's remove the host from the aggregate and update again fake_agg.hosts = [] self.host_manager.update_aggregates([fake_agg]) self.assertEqual({1: fake_agg}, self.host_manager.aggs_by_id) self.assertEqual({'fake-host': set([])}, self.host_manager.host_aggregates_map) def test_delete_aggregate(self): fake_agg = objects.Aggregate(id=1, hosts=['fake-host']) self.host_manager.host_aggregates_map = collections.defaultdict( set, {'fake-host': set([1])}) self.host_manager.aggs_by_id = {1: fake_agg} self.host_manager.delete_aggregate(fake_agg) self.assertEqual({}, self.host_manager.aggs_by_id) self.assertEqual({'fake-host': set([])}, self.host_manager.host_aggregates_map) def test_choose_host_filters_not_found(self): self.assertRaises(exception.SchedulerHostFilterNotFound, self.host_manager._choose_host_filters, 'FakeFilterClass3') def test_choose_host_filters(self): # Test we return 1 correct filter object host_filters = self.host_manager._choose_host_filters( ['FakeFilterClass2']) self.assertEqual(1, len(host_filters)) self.assertIsInstance(host_filters[0], FakeFilterClass2) def _mock_get_filtered_hosts(self, info): info['got_objs'] = [] info['got_fprops'] = [] def fake_filter_one(_self, obj, filter_props): info['got_objs'].append(obj) info['got_fprops'].append(filter_props) return True self.stub_out(__name__ + '.FakeFilterClass1._filter_one', fake_filter_one) def _verify_result(self, info, result, filters=True): for x in info['got_fprops']: self.assertEqual(x, info['expected_fprops']) if filters: self.assertEqual(set(info['expected_objs']), set(info['got_objs'])) self.assertEqual(set(info['expected_objs']), set(result)) def test_get_filtered_hosts(self): fake_properties = objects.RequestSpec(ignore_hosts=[], instance_uuid=uuids.instance, force_hosts=[], force_nodes=[]) info = {'expected_objs': self.fake_hosts, 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result) def test_get_filtered_hosts_with_requested_destination(self): dest = objects.Destination(host='fake_host1', node='fake-node') fake_properties = objects.RequestSpec(requested_destination=dest, ignore_hosts=[], instance_uuid=uuids.fake_uuid1, force_hosts=[], force_nodes=[]) info = {'expected_objs': [self.fake_hosts[0]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result) def test_get_filtered_hosts_with_wrong_requested_destination(self): dest = objects.Destination(host='dummy', node='fake-node') fake_properties = objects.RequestSpec(requested_destination=dest, ignore_hosts=[], instance_uuid=uuids.fake_uuid1, force_hosts=[], force_nodes=[]) info = {'expected_objs': [], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result) def test_get_filtered_hosts_with_ignore(self): fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=['fake_host1', 'fake_host3', 'fake_host5', 'fake_multihost'], force_hosts=[], force_nodes=[]) # [1] and [3] are host2 and host4 info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result) def test_get_filtered_hosts_with_ignore_case_insensitive(self): fake_properties = objects.RequestSpec( instance_uuid=uuids.fakehost, ignore_hosts=['FAKE_HOST1', 'FaKe_HoSt3', 'Fake_Multihost'], force_hosts=[], force_nodes=[]) # [1] and [3] are host2 and host4 info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result) def test_get_filtered_hosts_with_force_hosts(self): fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=[], force_hosts=['fake_host1', 'fake_host3', 'fake_host5'], force_nodes=[]) # [0] and [2] are host1 and host3 info = {'expected_objs': [self.fake_hosts[0], self.fake_hosts[2]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_force_case_insensitive(self): fake_properties = objects.RequestSpec( instance_uuid=uuids.fakehost, ignore_hosts=[], force_hosts=['FAKE_HOST1', 'FaKe_HoSt3', 'fake_host4', 'faKe_host5'], force_nodes=[]) # [1] and [3] are host2 and host4 info = {'expected_objs': [self.fake_hosts[0], self.fake_hosts[2], self.fake_hosts[3]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_no_matching_force_hosts(self): fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=[], force_hosts=['fake_host5', 'fake_host6'], force_nodes=[]) info = {'expected_objs': [], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) with mock.patch.object(self.host_manager.filter_handler, 'get_filtered_objects') as fake_filter: result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self.assertFalse(fake_filter.called) self._verify_result(info, result, False) def test_get_filtered_hosts_with_ignore_and_force_hosts(self): # Ensure ignore_hosts processed before force_hosts in host filters. fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=['fake_host1'], force_hosts=['fake_host3', 'fake_host1'], force_nodes=[]) # only fake_host3 should be left. info = {'expected_objs': [self.fake_hosts[2]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_force_host_and_many_nodes(self): # Ensure all nodes returned for a host with many nodes fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=[], force_hosts=['fake_multihost'], force_nodes=[]) info = {'expected_objs': [self.fake_hosts[4], self.fake_hosts[5], self.fake_hosts[6], self.fake_hosts[7]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_force_nodes(self): fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=[], force_hosts=[], force_nodes=['fake-node2', 'fake-node4', 'fake-node9']) # [5] is fake-node2, [7] is fake-node4 info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_force_hosts_and_nodes(self): # Ensure only overlapping results if both force host and node fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=[], force_hosts=['fake-host1', 'fake_multihost'], force_nodes=['fake-node2', 'fake-node9']) # [5] is fake-node2 info = {'expected_objs': [self.fake_hosts[5]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_force_hosts_and_wrong_nodes(self): # Ensure non-overlapping force_node and force_host yield no result fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=[], force_hosts=['fake_multihost'], force_nodes=['fake-node']) info = {'expected_objs': [], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_ignore_hosts_and_force_nodes(self): # Ensure ignore_hosts can coexist with force_nodes fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=['fake_host1', 'fake_host2'], force_hosts=[], force_nodes=['fake-node4', 'fake-node2']) info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) def test_get_filtered_hosts_with_ignore_hosts_and_force_same_nodes(self): # Ensure ignore_hosts is processed before force_nodes fake_properties = objects.RequestSpec( instance_uuid=uuids.instance, ignore_hosts=['fake_multihost'], force_hosts=[], force_nodes=['fake_node4', 'fake_node2']) info = {'expected_objs': [], 'expected_fprops': fake_properties} self._mock_get_filtered_hosts(info) result = self.host_manager.get_filtered_hosts(self.fake_hosts, fake_properties) self._verify_result(info, result, False) @mock.patch('nova.scheduler.host_manager.LOG') @mock.patch('nova.objects.ServiceList.get_by_binary') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.InstanceList.get_uuids_by_host') def test_get_host_states(self, mock_get_by_host, mock_get_all, mock_get_by_binary, mock_log): mock_get_by_host.return_value = [] mock_get_all.return_value = fakes.COMPUTE_NODES mock_get_by_binary.return_value = fakes.SERVICES context = nova_context.get_admin_context() compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fakes.COMPUTE_NODES]) # _get_host_states returns a generator, so make a map from it host_states_map = {(state.host, state.nodename): state for state in self.host_manager._get_host_states( context, compute_nodes, services)} self.assertEqual(4, len(host_states_map)) calls = [ mock.call( "Host %(hostname)s has more disk space than database " "expected (%(physical)s GB > %(database)s GB)", {'physical': 3333, 'database': 3072, 'hostname': 'node3'} ), mock.call( "No compute service record found for host %(host)s", {'host': 'fake'} ) ] self.assertEqual(calls, mock_log.warning.call_args_list) # Check that .service is set properly for i in range(4): compute_node = fakes.COMPUTE_NODES[i] host = compute_node.host node = compute_node.hypervisor_hostname state_key = (host, node) self.assertEqual(host_states_map[state_key].service, obj_base.obj_to_primitive(fakes.get_service_by_host(host))) self.assertEqual(host_states_map[('host1', 'node1')].free_ram_mb, 512) # 511GB self.assertEqual(host_states_map[('host1', 'node1')].free_disk_mb, 524288) self.assertEqual(host_states_map[('host2', 'node2')].free_ram_mb, 1024) # 1023GB self.assertEqual(host_states_map[('host2', 'node2')].free_disk_mb, 1048576) self.assertEqual(host_states_map[('host3', 'node3')].free_ram_mb, 3072) # 3071GB self.assertEqual(host_states_map[('host3', 'node3')].free_disk_mb, 3145728) self.assertEqual(host_states_map[('host4', 'node4')].free_ram_mb, 8192) # 8191GB self.assertEqual(host_states_map[('host4', 'node4')].free_disk_mb, 8388608) @mock.patch.object(nova.objects.InstanceList, 'get_uuids_by_host') @mock.patch.object(host_manager.HostState, '_update_from_compute_node') @mock.patch.object(objects.ComputeNodeList, 'get_all_by_uuids') @mock.patch.object(objects.ServiceList, 'get_by_binary') def test_get_host_states_with_no_aggs(self, svc_get_by_binary, cn_get_all, update_from_cn, mock_get_by_host): fake_compute_nodes = [ objects.ComputeNode( uuid=uuids.cn, host='fake', hypervisor_hostname='fake', ), ] svc_get_by_binary.return_value = [objects.Service(host='fake')] cn_get_all.return_value = fake_compute_nodes mock_get_by_host.return_value = [] self.host_manager.host_aggregates_map = collections.defaultdict(set) context = nova_context.get_admin_context() compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fake_compute_nodes]) hosts = self.host_manager._get_host_states( context, compute_nodes, services) # _get_host_states returns a generator, so make a map from it host_states_map = {(state.host, state.nodename): state for state in hosts} host_state = host_states_map[('fake', 'fake')] self.assertEqual([], host_state.aggregates) @mock.patch.object(nova.objects.InstanceList, 'get_uuids_by_host') @mock.patch.object(host_manager.HostState, '_update_from_compute_node') @mock.patch.object(objects.ComputeNodeList, 'get_all_by_uuids') @mock.patch.object(objects.ServiceList, 'get_by_binary') def test_get_host_states_with_matching_aggs(self, svc_get_by_binary, cn_get_all, update_from_cn, mock_get_by_host): fake_compute_nodes = [ objects.ComputeNode( uuid=uuids.cn, host='fake', hypervisor_hostname='fake', ), ] svc_get_by_binary.return_value = [objects.Service(host='fake')] cn_get_all.return_value = fake_compute_nodes mock_get_by_host.return_value = [] fake_agg = objects.Aggregate(id=1) self.host_manager.host_aggregates_map = collections.defaultdict( set, {'fake': set([1])}) self.host_manager.aggs_by_id = {1: fake_agg} context = nova_context.get_admin_context() compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fake_compute_nodes]) hosts = self.host_manager._get_host_states( context, compute_nodes, services) # _get_host_states returns a generator, so make a map from it host_states_map = {(state.host, state.nodename): state for state in hosts} host_state = host_states_map[('fake', 'fake')] self.assertEqual([fake_agg], host_state.aggregates) @mock.patch.object(nova.objects.InstanceList, 'get_uuids_by_host') @mock.patch.object(host_manager.HostState, '_update_from_compute_node') @mock.patch.object(objects.ComputeNodeList, 'get_all_by_uuids') @mock.patch.object(objects.ServiceList, 'get_by_binary') def test_get_host_states_with_not_matching_aggs(self, svc_get_by_binary, cn_get_all, update_from_cn, mock_get_by_host): fake_compute_nodes = [ objects.ComputeNode( uuid=uuids.cn1, host='fake', hypervisor_hostname='fake'), objects.ComputeNode( uuid=uuids.cn2, host='other', hypervisor_hostname='other'), ] svc_get_by_binary.return_value = [objects.Service(host='fake'), objects.Service(host='other')] cn_get_all.return_value = fake_compute_nodes mock_get_by_host.return_value = [] fake_agg = objects.Aggregate(id=1) self.host_manager.host_aggregates_map = collections.defaultdict( set, {'other': set([1])}) self.host_manager.aggs_by_id = {1: fake_agg} context = nova_context.get_admin_context() compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fake_compute_nodes]) hosts = self.host_manager._get_host_states( context, compute_nodes, services) # _get_host_states returns a generator, so make a map from it host_states_map = {(state.host, state.nodename): state for state in hosts} host_state = host_states_map[('fake', 'fake')] self.assertEqual([], host_state.aggregates) @mock.patch.object(nova.objects.InstanceList, 'get_uuids_by_host', return_value=[]) @mock.patch.object(host_manager.HostState, '_update_from_compute_node') @mock.patch.object(objects.ComputeNodeList, 'get_all_by_uuids') @mock.patch.object(objects.ServiceList, 'get_by_binary') def test_get_host_states_corrupt_aggregates_info(self, svc_get_by_binary, cn_get_all, update_from_cn, mock_get_by_host): """Regression test for bug 1605804 A host can be in multiple host-aggregates at the same time. When a host gets removed from an aggregate in thread A and this aggregate gets deleted in thread B, there can be a race-condition where the mapping data in the host_manager can get out of sync for a moment. This test simulates this condition for the bug-fix. """ host_a = 'host_a' host_b = 'host_b' fake_compute_nodes = [ objects.ComputeNode( uuid=uuids.cn_a, host=host_a, hypervisor_hostname=host_a, ), objects.ComputeNode( uuid=uuids.cn_b, host=host_b, hypervisor_hostname=host_b, ), ] svc_get_by_binary.return_value = [objects.Service(host=host_a), objects.Service(host=host_b)] cn_get_all.return_value = fake_compute_nodes aggregate = objects.Aggregate(id=1) aggregate.hosts = [host_a, host_b] aggr_list = objects.AggregateList() aggr_list.objects = [aggregate] self.host_manager.update_aggregates(aggr_list) aggregate.hosts = [host_a] self.host_manager.delete_aggregate(aggregate) context = nova_context.get_admin_context() compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fake_compute_nodes]) self.host_manager._get_host_states(context, compute_nodes, services) @mock.patch('nova.objects.InstanceList.get_by_host') def test_host_state_update(self, mock_get_by_host): context = 'fake_context' hm = self.host_manager inst1 = objects.Instance(uuid=uuids.instance) cn1 = objects.ComputeNode(host='host1') hm._instance_info = {'host1': {'instances': {uuids.instance: inst1}, 'updated': True}} host_state = host_manager.HostState('host1', cn1, uuids.cell) self.assertFalse(host_state.instances) mock_get_by_host.return_value = None host_state.update( inst_dict=hm._get_instance_info(context, cn1)) self.assertFalse(mock_get_by_host.called) self.assertTrue(host_state.instances) self.assertEqual(host_state.instances[uuids.instance], inst1) @mock.patch('nova.objects.InstanceList.get_uuids_by_host') def test_host_state_not_updated(self, mock_get_by_host): context = nova_context.get_admin_context() hm = self.host_manager inst1 = objects.Instance(uuid=uuids.instance) cn1 = objects.ComputeNode(host='host1') hm._instance_info = {'host1': {'instances': {uuids.instance: inst1}, 'updated': False}} host_state = host_manager.HostState('host1', cn1, uuids.cell) self.assertFalse(host_state.instances) mock_get_by_host.return_value = [uuids.instance] host_state.update( inst_dict=hm._get_instance_info(context, cn1)) mock_get_by_host.assert_called_once_with(context, cn1.host) self.assertTrue(host_state.instances) self.assertIn(uuids.instance, host_state.instances) inst = host_state.instances[uuids.instance] self.assertEqual(uuids.instance, inst.uuid) self.assertIsNotNone(inst._context, 'Instance is orphaned.') @mock.patch('nova.objects.InstanceList.get_uuids_by_host') def test_recreate_instance_info(self, mock_get_by_host): context = nova_context.get_admin_context() host_name = 'fake_host' inst1 = fake_instance.fake_instance_obj(context, uuid=uuids.instance_1) inst2 = fake_instance.fake_instance_obj(context, uuid=uuids.instance_2) orig_inst_dict = {inst1.uuid: inst1, inst2.uuid: inst2} mock_get_by_host.return_value = [uuids.instance_1, uuids.instance_2] self.host_manager._instance_info = { host_name: { 'instances': orig_inst_dict, 'updated': True, }} self.host_manager._recreate_instance_info(context, host_name) new_info = self.host_manager._instance_info[host_name] self.assertEqual(len(new_info['instances']), len(mock_get_by_host.return_value)) self.assertFalse(new_info['updated']) def test_update_instance_info(self): host_name = 'fake_host' inst1 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_1, host=host_name) inst2 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_2, host=host_name) orig_inst_dict = {inst1.uuid: inst1, inst2.uuid: inst2} self.host_manager._instance_info = { host_name: { 'instances': orig_inst_dict, 'updated': False, }} inst3 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_3, host=host_name) inst4 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_4, host=host_name) update = objects.InstanceList(objects=[inst3, inst4]) self.host_manager.update_instance_info('fake_context', host_name, update) new_info = self.host_manager._instance_info[host_name] self.assertEqual(len(new_info['instances']), 4) self.assertTrue(new_info['updated']) def test_update_instance_info_unknown_host(self): self.host_manager._recreate_instance_info = mock.MagicMock() host_name = 'fake_host' inst1 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_1, host=host_name) inst2 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_2, host=host_name) orig_inst_dict = {inst1.uuid: inst1, inst2.uuid: inst2} self.host_manager._instance_info = { host_name: { 'instances': orig_inst_dict, 'updated': False, }} bad_host = 'bad_host' inst3 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_3, host=bad_host) inst_list3 = objects.InstanceList(objects=[inst3]) self.host_manager.update_instance_info('fake_context', bad_host, inst_list3) new_info = self.host_manager._instance_info[host_name] self.host_manager._recreate_instance_info.assert_called_once_with( 'fake_context', bad_host) self.assertEqual(len(new_info['instances']), len(orig_inst_dict)) self.assertFalse(new_info['updated']) @mock.patch('nova.objects.HostMapping.get_by_host', side_effect=exception.HostMappingNotFound(name='host1')) def test_update_instance_info_unknown_host_mapping_not_found(self, get_by_host): """Tests that case that update_instance_info is called with an unregistered host so the host manager attempts to recreate the instance list, but there is no host mapping found for the given host (it might have just started not be discovered for cells v2 yet). """ ctxt = nova_context.RequestContext() instance_info = objects.InstanceList() self.host_manager.update_instance_info(ctxt, 'host1', instance_info) self.assertDictEqual( {}, self.host_manager._instance_info['host1']['instances']) get_by_host.assert_called_once_with(ctxt, 'host1') def test_delete_instance_info(self): host_name = 'fake_host' inst1 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_1, host=host_name) inst2 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_2, host=host_name) orig_inst_dict = {inst1.uuid: inst1, inst2.uuid: inst2} self.host_manager._instance_info = { host_name: { 'instances': orig_inst_dict, 'updated': False, }} self.host_manager.delete_instance_info('fake_context', host_name, inst1.uuid) new_info = self.host_manager._instance_info[host_name] self.assertEqual(len(new_info['instances']), 1) self.assertTrue(new_info['updated']) def test_delete_instance_info_unknown_host(self): self.host_manager._recreate_instance_info = mock.MagicMock() host_name = 'fake_host' inst1 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_1, host=host_name) inst2 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_2, host=host_name) orig_inst_dict = {inst1.uuid: inst1, inst2.uuid: inst2} self.host_manager._instance_info = { host_name: { 'instances': orig_inst_dict, 'updated': False, }} bad_host = 'bad_host' self.host_manager.delete_instance_info('fake_context', bad_host, uuids.instance_1) new_info = self.host_manager._instance_info[host_name] self.host_manager._recreate_instance_info.assert_called_once_with( 'fake_context', bad_host) self.assertEqual(len(new_info['instances']), len(orig_inst_dict)) self.assertFalse(new_info['updated']) def test_sync_instance_info(self): self.host_manager._recreate_instance_info = mock.MagicMock() host_name = 'fake_host' inst1 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_1, host=host_name) inst2 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_2, host=host_name) orig_inst_dict = {inst1.uuid: inst1, inst2.uuid: inst2} self.host_manager._instance_info = { host_name: { 'instances': orig_inst_dict, 'updated': False, }} self.host_manager.sync_instance_info('fake_context', host_name, [uuids.instance_2, uuids.instance_1]) new_info = self.host_manager._instance_info[host_name] self.assertFalse(self.host_manager._recreate_instance_info.called) self.assertTrue(new_info['updated']) def test_sync_instance_info_fail(self): self.host_manager._recreate_instance_info = mock.MagicMock() host_name = 'fake_host' inst1 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_1, host=host_name) inst2 = fake_instance.fake_instance_obj('fake_context', uuid=uuids.instance_2, host=host_name) orig_inst_dict = {inst1.uuid: inst1, inst2.uuid: inst2} self.host_manager._instance_info = { host_name: { 'instances': orig_inst_dict, 'updated': False, }} self.host_manager.sync_instance_info('fake_context', host_name, [uuids.instance_2, uuids.instance_1, 'new']) new_info = self.host_manager._instance_info[host_name] self.host_manager._recreate_instance_info.assert_called_once_with( 'fake_context', host_name) self.assertFalse(new_info['updated']) @mock.patch('nova.objects.CellMappingList.get_all') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.ServiceList.get_by_binary') def test_get_computes_for_cells(self, mock_sl, mock_cn, mock_cm): cells = [ objects.CellMapping(uuid=uuids.cell1, db_connection='none://1', transport_url='none://'), objects.CellMapping(uuid=uuids.cell2, db_connection='none://2', transport_url='none://'), ] mock_cm.return_value = cells mock_sl.side_effect = [ [objects.ServiceList(host='foo')], [objects.ServiceList(host='bar')], ] mock_cn.side_effect = [ [objects.ComputeNode(uuid=uuids.cn_a, host='foo')], [objects.ComputeNode(uuid=uuids.cn_b, host='bar')], ] context = nova_context.RequestContext('fake', 'fake') cns, srv = self.host_manager._get_computes_for_cells( context, cells, compute_uuids=[uuids.cn_a, uuids.cn_b]) self.assertEqual({uuids.cell1: ['foo'], uuids.cell2: ['bar']}, {cell: [cn.host for cn in computes] for cell, computes in cns.items()}) self.assertEqual(['bar', 'foo'], sorted(list(srv.keys()))) @mock.patch('nova.objects.CellMappingList.get_all') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.ServiceList.get_by_binary') def test_get_computes_for_cells_uuid(self, mock_sl, mock_cn, mock_cm): cells = [ objects.CellMapping(uuid=uuids.cell1, db_connection='none://1', transport_url='none://'), objects.CellMapping(uuid=uuids.cell2, db_connection='none://2', transport_url='none://'), ] mock_cm.return_value = cells mock_sl.side_effect = [ [objects.ServiceList(host='foo')], [objects.ServiceList(host='bar')], ] mock_cn.side_effect = [ [objects.ComputeNode(host='foo')], [objects.ComputeNode(host='bar')], ] context = nova_context.RequestContext('fake', 'fake') cns, srv = self.host_manager._get_computes_for_cells( context, cells, []) self.assertEqual({uuids.cell1: ['foo'], uuids.cell2: ['bar']}, {cell: [cn.host for cn in computes] for cell, computes in cns.items()}) self.assertEqual(['bar', 'foo'], sorted(list(srv.keys()))) @mock.patch('nova.context.target_cell') @mock.patch('nova.objects.CellMappingList.get_all') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.ServiceList.get_by_binary') def test_get_computes_for_cells_limit_to_cell(self, mock_sl, mock_cn, mock_cm, mock_target): host_manager.LOG.debug = host_manager.LOG.error cells = [ objects.CellMapping(uuid=uuids.cell1, database_connection='none://1', transport_url='none://'), objects.CellMapping(uuid=uuids.cell2, database_connection='none://2', transport_url='none://'), ] compute_nodes = [ objects.ComputeNode(uuid=uuids.cn, host='foo'), ] mock_sl.return_value = [objects.ServiceList(host='foo')] mock_cn.return_value = compute_nodes mock_cm.return_value = cells @contextlib.contextmanager def fake_set_target(context, cell): yield mock.sentinel.cctxt mock_target.side_effect = fake_set_target context = nova_context.RequestContext('fake', 'fake') cns, srv = self.host_manager._get_computes_for_cells( context, cells=cells[1:], compute_uuids=[cn.uuid for cn in compute_nodes]) self.assertEqual({uuids.cell2: ['foo']}, {cell: [cn.host for cn in computes] for cell, computes in cns.items()}) self.assertEqual(['foo'], list(srv.keys())) # NOTE(danms): We have two cells, but we should only have # targeted one if we honored the only-cell destination requirement, # and only looked up services and compute nodes in one mock_target.assert_called_once_with(context, cells[1]) mock_cn.assert_called_once_with( mock.sentinel.cctxt, [cn.uuid for cn in compute_nodes]) mock_sl.assert_called_once_with(mock.sentinel.cctxt, 'nova-compute', include_disabled=True) @mock.patch('nova.context.scatter_gather_cells') def test_get_computes_for_cells_failures(self, mock_sg): mock_sg.return_value = { uuids.cell1: ([mock.MagicMock(host='a'), mock.MagicMock(host='b')], [mock.sentinel.c1n1, mock.sentinel.c1n2]), uuids.cell2: nova_context.did_not_respond_sentinel, uuids.cell3: exception.ComputeHostNotFound(host='c'), } context = nova_context.RequestContext('fake', 'fake') cns, srv = self.host_manager._get_computes_for_cells( context, [], compute_uuids=[uuids.c1n1, uuids.c1n2]) self.assertEqual({uuids.cell1: [mock.sentinel.c1n1, mock.sentinel.c1n2]}, cns) self.assertEqual(['a', 'b'], sorted(srv.keys())) @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.ComputeNode.get_by_nodename') @mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename') @mock.patch('nova.objects.ComputeNodeList.get_all_by_host') def test_get_compute_nodes_by_host_or_node(self, mock_get_all, mock_get_host_node, mock_get_node, mock_get_hm): def _varify_result(expected, result): self.assertEqual(len(expected), len(result)) for expected_cn, result_cn in zip(expected, result): self.assertEqual(expected_cn.host, result_cn.host) self.assertEqual(expected_cn.node, result_cn.node) context = nova_context.RequestContext('fake', 'fake') cn1 = objects.ComputeNode(host='fake_multihost', node='fake_node1') cn2 = objects.ComputeNode(host='fake_multihost', node='fake_node2') cn3 = objects.ComputeNode(host='fake_host1', node='fake_node') mock_get_all.return_value = objects.ComputeNodeList(objects=[cn1, cn2]) mock_get_host_node.return_value = cn1 mock_get_node.return_value = cn3 mock_get_hm.return_value = objects.HostMapping( context=context, host='fake_multihost', cell_mapping=objects.CellMapping(uuid=uuids.cell1, db_connection='none://1', transport_url='none://')) # Case1: call it with host host = 'fake_multihost' node = None result = self.host_manager.get_compute_nodes_by_host_or_node( context, host, node) expected = objects.ComputeNodeList(objects=[cn1, cn2]) _varify_result(expected, result) mock_get_all.assert_called_once_with(context, 'fake_multihost') mock_get_host_node.assert_not_called() mock_get_node.assert_not_called() mock_get_hm.assert_called_once_with(context, 'fake_multihost') mock_get_all.reset_mock() mock_get_hm.reset_mock() # Case2: call it with host and node host = 'fake_multihost' node = 'fake_node1' result = self.host_manager.get_compute_nodes_by_host_or_node( context, host, node) expected = objects.ComputeNodeList(objects=[cn1]) _varify_result(expected, result) mock_get_all.assert_not_called() mock_get_host_node.assert_called_once_with( context, 'fake_multihost', 'fake_node1') mock_get_node.assert_not_called() mock_get_hm.assert_called_once_with(context, 'fake_multihost') mock_get_host_node.reset_mock() mock_get_hm.reset_mock() # Case3: call it with node host = None node = 'fake_node' result = self.host_manager.get_compute_nodes_by_host_or_node( context, host, node) expected = objects.ComputeNodeList(objects=[cn3]) _varify_result(expected, result) mock_get_all.assert_not_called() mock_get_host_node.assert_not_called() mock_get_node.assert_called_once_with(context, 'fake_node') mock_get_hm.assert_not_called() @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.ComputeNodeList.get_all_by_host') def test_get_compute_nodes_by_host_or_node_empty_list( self, mock_get_all, mock_get_hm): mock_get_all.side_effect = exception.ComputeHostNotFound(host='fake') mock_get_hm.side_effect = exception.HostMappingNotFound(name='fake') context = nova_context.RequestContext('fake', 'fake') host = 'fake' node = None result = self.host_manager.get_compute_nodes_by_host_or_node( context, host, node) self.assertEqual(0, len(result)) @mock.patch('nova.context.scatter_gather_cells', side_effect=( # called twice, different return values {uuids.cell1: test.TestingException('conn fail')}, {uuids.cell1: nova_context.did_not_respond_sentinel})) def test_get_compute_nodes_by_host_or_node_filter_errors(self, mock_sgc): """Make sure get_compute_nodes_by_host_or_node filters out exception and cell timeout results from scatter_gather_cells. """ ctxt = nova_context.get_context() cell1 = objects.CellMapping(uuid=uuids.cell1) for x in range(2): # run twice because we have two side effects nodes = self.host_manager.get_compute_nodes_by_host_or_node( ctxt, 'host1', None, cell=cell1) self.assertEqual(0, len(nodes), nodes) self.assertEqual(2, mock_sgc.call_count, mock_sgc.mock_calls) mock_sgc.assert_has_calls([mock.call( ctxt, [cell1], nova_context.CELL_TIMEOUT, mock.ANY)] * 2) class HostManagerChangedNodesTestCase(test.NoDBTestCase): """Test case for HostManager class.""" @mock.patch.object(host_manager.HostManager, '_init_instance_info') @mock.patch.object(host_manager.HostManager, '_init_aggregates') def setUp(self, mock_init_agg, mock_init_inst): super(HostManagerChangedNodesTestCase, self).setUp() self.host_manager = host_manager.HostManager() @mock.patch('nova.objects.ServiceList.get_by_binary') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.InstanceList.get_uuids_by_host') def test_get_host_states(self, mock_get_by_host, mock_get_all, mock_get_by_binary): mock_get_by_host.return_value = [] mock_get_all.return_value = fakes.COMPUTE_NODES mock_get_by_binary.return_value = fakes.SERVICES context = nova_context.get_admin_context() compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fakes.COMPUTE_NODES]) # _get_host_states returns a generator, so make a map from it host_states_map = {(state.host, state.nodename): state for state in self.host_manager._get_host_states( context, compute_nodes, services)} self.assertEqual(len(host_states_map), 4) @mock.patch('nova.objects.ServiceList.get_by_binary') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.InstanceList.get_uuids_by_host') def test_get_host_states_after_delete_one(self, mock_get_by_host, mock_get_all, mock_get_by_binary): getter = (lambda n: n.hypervisor_hostname if 'hypervisor_hostname' in n else None) running_nodes = [n for n in fakes.COMPUTE_NODES if getter(n) != 'node4'] mock_get_by_host.return_value = [] mock_get_all.side_effect = [fakes.COMPUTE_NODES, running_nodes] mock_get_by_binary.side_effect = [fakes.SERVICES, fakes.SERVICES] context = nova_context.get_admin_context() # first call: all nodes compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fakes.COMPUTE_NODES]) hosts = self.host_manager._get_host_states( context, compute_nodes, services) # _get_host_states returns a generator, so make a map from it host_states_map = {(state.host, state.nodename): state for state in hosts} self.assertEqual(len(host_states_map), 4) # second call: just running nodes compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fakes.COMPUTE_NODES]) hosts = self.host_manager._get_host_states( context, compute_nodes, services) host_states_map = {(state.host, state.nodename): state for state in hosts} self.assertEqual(len(host_states_map), 3) @mock.patch('nova.objects.ServiceList.get_by_binary') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.InstanceList.get_uuids_by_host') def test_get_host_states_after_delete_all(self, mock_get_by_host, mock_get_all, mock_get_by_binary): mock_get_by_host.return_value = [] mock_get_all.side_effect = [fakes.COMPUTE_NODES, []] mock_get_by_binary.side_effect = [fakes.SERVICES, fakes.SERVICES] context = nova_context.get_admin_context() # first call: all nodes compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fakes.COMPUTE_NODES]) hosts = self.host_manager._get_host_states( context, compute_nodes, services) # _get_host_states returns a generator, so make a map from it host_states_map = {(state.host, state.nodename): state for state in hosts} self.assertEqual(len(host_states_map), 4) # second call: no nodes compute_nodes, services = self.host_manager._get_computes_for_cells( context, self.host_manager.enabled_cells, compute_uuids=[cn.uuid for cn in fakes.COMPUTE_NODES]) hosts = self.host_manager._get_host_states( context, compute_nodes, services) host_states_map = {(state.host, state.nodename): state for state in hosts} self.assertEqual(len(host_states_map), 0) @mock.patch('nova.objects.ServiceList.get_by_binary') @mock.patch('nova.objects.ComputeNodeList.get_all_by_uuids') @mock.patch('nova.objects.InstanceList.get_uuids_by_host') def test_get_host_states_by_uuids(self, mock_get_by_host, mock_get_all, mock_get_by_binary): mock_get_by_host.return_value = [] mock_get_all.side_effect = [fakes.COMPUTE_NODES, []] mock_get_by_binary.side_effect = [fakes.SERVICES, fakes.SERVICES] # Request 1: all nodes can satisfy the request hosts1 = self.host_manager.get_host_states_by_uuids( mock.sentinel.ctxt1, mock.sentinel.uuids1, objects.RequestSpec()) # get_host_states_by_uuids returns a generator so convert the values # into an iterator host_states1 = iter(hosts1) # Request 2: no nodes can satisfy the request hosts2 = self.host_manager.get_host_states_by_uuids( mock.sentinel.ctxt2, mock.sentinel.uuids2, objects.RequestSpec()) host_states2 = iter(hosts2) # Fake a concurrent request that is still processing the first result # to make sure all nodes are still available candidates to Request 1. num_hosts1 = len(list(host_states1)) self.assertEqual(4, num_hosts1) # Verify that no nodes are available to Request 2. num_hosts2 = len(list(host_states2)) self.assertEqual(0, num_hosts2) @mock.patch('nova.scheduler.host_manager.HostManager.' '_get_computes_for_cells', return_value=(mock.sentinel.compute_nodes, mock.sentinel.services)) @mock.patch('nova.scheduler.host_manager.HostManager._get_host_states') def test_get_host_states_by_uuids_allow_cross_cell_move( self, mock_get_host_states, mock_get_computes): """Tests that get_host_states_by_uuids will not restrict to a given cell if allow_cross_cell_move=True in the request spec. """ ctxt = nova_context.get_admin_context() compute_uuids = [uuids.compute_node_uuid] spec_obj = objects.RequestSpec( requested_destination=objects.Destination( cell=objects.CellMapping(uuid=uuids.cell1), allow_cross_cell_move=True)) self.host_manager.get_host_states_by_uuids( ctxt, compute_uuids, spec_obj) mock_get_computes.assert_called_once_with( ctxt, self.host_manager.enabled_cells, compute_uuids=compute_uuids) mock_get_host_states.assert_called_once_with( ctxt, mock.sentinel.compute_nodes, mock.sentinel.services) class HostStateTestCase(test.NoDBTestCase): """Test case for HostState class.""" # update_from_compute_node() and consume_from_request() are tested # in HostManagerTestCase.test_get_host_states() @mock.patch('nova.utils.synchronized', side_effect=lambda a: lambda f: lambda *args: f(*args)) def test_stat_consumption_from_compute_node(self, sync_mock): stats = { 'num_instances': '5', 'num_proj_12345': '3', 'num_proj_23456': '1', 'num_vm_%s' % vm_states.BUILDING: '2', 'num_vm_%s' % vm_states.SUSPENDED: '1', 'num_task_%s' % task_states.RESIZE_MIGRATING: '1', 'num_task_%s' % task_states.MIGRATING: '2', 'num_os_type_linux': '4', 'num_os_type_windoze': '1', 'io_workload': '42', } hyper_ver_int = versionutils.convert_version_to_int('6.0.0') compute = objects.ComputeNode( uuid=uuids.cn1, stats=stats, memory_mb=1, free_disk_gb=0, local_gb=0, local_gb_used=0, free_ram_mb=0, vcpus=0, vcpus_used=0, disk_available_least=None, updated_at=datetime.datetime(2015, 11, 11, 11, 0, 0), host_ip='127.0.0.1', hypervisor_type='htype', hypervisor_hostname='hostname', cpu_info='cpu_info', supported_hv_specs=[], hypervisor_version=hyper_ver_int, numa_topology=None, pci_device_pools=None, metrics=None, cpu_allocation_ratio=16.0, ram_allocation_ratio=1.5, disk_allocation_ratio=1.0) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) host.update(compute=compute) sync_mock.assert_called_once_with(("fakehost", "fakenode")) self.assertEqual(5, host.num_instances) self.assertEqual(42, host.num_io_ops) self.assertEqual(10, len(host.stats)) self.assertEqual('127.0.0.1', str(host.host_ip)) self.assertEqual('htype', host.hypervisor_type) self.assertEqual('hostname', host.hypervisor_hostname) self.assertEqual('cpu_info', host.cpu_info) self.assertEqual([], host.supported_instances) self.assertEqual(hyper_ver_int, host.hypervisor_version) def test_stat_consumption_from_compute_node_non_pci(self): stats = { 'num_instances': '5', 'num_proj_12345': '3', 'num_proj_23456': '1', 'num_vm_%s' % vm_states.BUILDING: '2', 'num_vm_%s' % vm_states.SUSPENDED: '1', 'num_task_%s' % task_states.RESIZE_MIGRATING: '1', 'num_task_%s' % task_states.MIGRATING: '2', 'num_os_type_linux': '4', 'num_os_type_windoze': '1', 'io_workload': '42', } hyper_ver_int = versionutils.convert_version_to_int('6.0.0') compute = objects.ComputeNode( uuid=uuids.cn1, stats=stats, memory_mb=0, free_disk_gb=0, local_gb=0, local_gb_used=0, free_ram_mb=0, vcpus=0, vcpus_used=0, disk_available_least=None, updated_at=datetime.datetime(2015, 11, 11, 11, 0, 0), host_ip='127.0.0.1', hypervisor_type='htype', hypervisor_hostname='hostname', cpu_info='cpu_info', supported_hv_specs=[], hypervisor_version=hyper_ver_int, numa_topology=None, pci_device_pools=None, metrics=None, cpu_allocation_ratio=16.0, ram_allocation_ratio=1.5, disk_allocation_ratio=1.0) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) host.update(compute=compute) self.assertEqual([], host.pci_stats.pools) self.assertEqual(hyper_ver_int, host.hypervisor_version) def test_stat_consumption_from_compute_node_rescue_unshelving(self): stats = { 'num_instances': '5', 'num_proj_12345': '3', 'num_proj_23456': '1', 'num_vm_%s' % vm_states.BUILDING: '2', 'num_vm_%s' % vm_states.SUSPENDED: '1', 'num_task_%s' % task_states.UNSHELVING: '1', 'num_task_%s' % task_states.RESCUING: '2', 'num_os_type_linux': '4', 'num_os_type_windoze': '1', 'io_workload': '42', } hyper_ver_int = versionutils.convert_version_to_int('6.0.0') compute = objects.ComputeNode( uuid=uuids.cn1, stats=stats, memory_mb=0, free_disk_gb=0, local_gb=0, local_gb_used=0, free_ram_mb=0, vcpus=0, vcpus_used=0, disk_available_least=None, updated_at=datetime.datetime(2015, 11, 11, 11, 0, 0), host_ip='127.0.0.1', hypervisor_type='htype', hypervisor_hostname='hostname', cpu_info='cpu_info', supported_hv_specs=[], hypervisor_version=hyper_ver_int, numa_topology=None, pci_device_pools=None, metrics=None, cpu_allocation_ratio=16.0, ram_allocation_ratio=1.5, disk_allocation_ratio=1.0) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) host.update(compute=compute) self.assertEqual(5, host.num_instances) self.assertEqual(42, host.num_io_ops) self.assertEqual(10, len(host.stats)) self.assertEqual([], host.pci_stats.pools) self.assertEqual(hyper_ver_int, host.hypervisor_version) @mock.patch('nova.utils.synchronized', side_effect=lambda a: lambda f: lambda *args: f(*args)) @mock.patch('nova.virt.hardware.numa_usage_from_instance_numa') @mock.patch('nova.objects.Instance') @mock.patch('nova.virt.hardware.numa_fit_instance_to_host') def test_stat_consumption_from_instance(self, numa_fit_mock, instance_init_mock, numa_usage_mock, sync_mock): fake_numa_topology = objects.InstanceNUMATopology( cells=[objects.InstanceNUMACell()]) fake_host_numa_topology = mock.Mock() fake_instance = objects.Instance(numa_topology=fake_numa_topology) numa_usage_mock.return_value = fake_host_numa_topology numa_fit_mock.return_value = fake_numa_topology instance_init_mock.return_value = fake_instance spec_obj = objects.RequestSpec( instance_uuid=uuids.instance, flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0, vcpus=0), numa_topology=fake_numa_topology, pci_requests=objects.InstancePCIRequests(requests=[])) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) host.numa_topology = fake_host_numa_topology self.assertIsNone(host.updated) host.consume_from_request(spec_obj) numa_fit_mock.assert_called_once_with( fake_host_numa_topology, fake_numa_topology, limits=None, pci_requests=None, pci_stats=None, provider_mapping=None, ) numa_usage_mock.assert_called_once_with(fake_host_numa_topology, fake_numa_topology) sync_mock.assert_called_once_with(("fakehost", "fakenode")) self.assertEqual(fake_host_numa_topology, host.numa_topology) self.assertIsNotNone(host.updated) numa_fit_mock.reset_mock() numa_usage_mock.reset_mock() spec_obj = objects.RequestSpec( instance_uuid=uuids.instance, flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0, vcpus=0), numa_topology=None, pci_requests=objects.InstancePCIRequests(requests=[])) host.consume_from_request(spec_obj) numa_fit_mock.assert_not_called() numa_usage_mock.assert_not_called() self.assertEqual(2, host.num_instances) self.assertEqual(2, host.num_io_ops) self.assertIsNotNone(host.updated) def test_stat_consumption_from_instance_pci(self): inst_topology = objects.InstanceNUMATopology( cells = [objects.InstanceNUMACell( cpuset=set([0]), memory=512, id=0)]) fake_requests = [{'request_id': uuids.request_id, 'count': 1, 'spec': [{'vendor_id': '8086'}]}] fake_requests_obj = objects.InstancePCIRequests( requests=[objects.InstancePCIRequest(**r) for r in fake_requests], instance_uuid=uuids.instance) req_spec = objects.RequestSpec( instance_uuid=uuids.instance, project_id='12345', numa_topology=inst_topology, pci_requests=fake_requests_obj, flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=512, vcpus=1)) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) self.assertIsNone(host.updated) host.pci_stats = pci_stats.PciDeviceStats( objects.NUMATopology(), [objects.PciDevicePool(vendor_id='8086', product_id='15ed', numa_node=1, count=1)]) host.numa_topology = fakes.NUMA_TOPOLOGY host.consume_from_request(req_spec) self.assertIsInstance(req_spec.numa_topology, objects.InstanceNUMATopology) self.assertEqual(512, host.numa_topology.cells[1].memory_usage) self.assertEqual(1, host.numa_topology.cells[1].cpu_usage) self.assertEqual(0, len(host.pci_stats.pools)) self.assertIsNotNone(host.updated) def test_stat_consumption_from_instance_with_pci_exception(self): fake_requests = [{'request_id': uuids.request_id, 'count': 3, 'spec': [{'vendor_id': '8086'}]}] fake_requests_obj = objects.InstancePCIRequests( requests=[objects.InstancePCIRequest(**r) for r in fake_requests], instance_uuid=uuids.instance) req_spec = objects.RequestSpec( instance_uuid=uuids.instance, project_id='12345', numa_topology=None, pci_requests=fake_requests_obj, flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=1024, vcpus=1)) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) self.assertIsNone(host.updated) fake_updated = mock.sentinel.fake_updated host.updated = fake_updated host.pci_stats = pci_stats.PciDeviceStats(objects.NUMATopology()) with mock.patch.object(host.pci_stats, 'apply_requests', side_effect=exception.PciDeviceRequestFailed): host.consume_from_request(req_spec) self.assertEqual(fake_updated, host.updated) def test_resources_consumption_from_compute_node(self): _ts_now = datetime.datetime(2015, 11, 11, 11, 0, 0) metrics = [ dict(name='cpu.frequency', value=1.0, source='source1', timestamp=_ts_now), dict(name='numa.membw.current', numa_membw_values={"0": 10, "1": 43}, source='source2', timestamp=_ts_now), ] hyper_ver_int = versionutils.convert_version_to_int('6.0.0') compute = objects.ComputeNode( uuid=uuids.cn1, metrics=jsonutils.dumps(metrics), memory_mb=0, free_disk_gb=0, local_gb=0, local_gb_used=0, free_ram_mb=0, vcpus=0, vcpus_used=0, disk_available_least=None, updated_at=datetime.datetime(2015, 11, 11, 11, 0, 0), host_ip='127.0.0.1', hypervisor_type='htype', hypervisor_hostname='hostname', cpu_info='cpu_info', supported_hv_specs=[], hypervisor_version=hyper_ver_int, numa_topology=fakes.NUMA_TOPOLOGY._to_json(), stats=None, pci_device_pools=None, cpu_allocation_ratio=16.0, ram_allocation_ratio=1.5, disk_allocation_ratio=1.0) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) host.update(compute=compute) self.assertEqual(len(host.metrics), 2) self.assertEqual(1.0, host.metrics.to_list()[0]['value']) self.assertEqual('source1', host.metrics[0].source) self.assertEqual('cpu.frequency', host.metrics[0].name) self.assertEqual('numa.membw.current', host.metrics[1].name) self.assertEqual('source2', host.metrics.to_list()[1]['source']) self.assertEqual({'0': 10, '1': 43}, host.metrics[1].numa_membw_values) self.assertIsInstance(host.numa_topology, objects.NUMATopology) def test_stat_consumption_from_compute_node_not_ready(self): compute = objects.ComputeNode(free_ram_mb=100, uuid=uuids.compute_node_uuid) host = host_manager.HostState("fakehost", "fakenode", uuids.cell) host._update_from_compute_node(compute) # Because compute record not ready, the update of free ram # will not happen and the value will still be 0 self.assertEqual(0, host.free_ram_mb) # same with failed_builds self.assertEqual(0, host.failed_builds)