# 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. from unittest import mock from oslo_db import exception as db_exc from oslo_utils.fixture import uuidsentinel as uuids from nova import context from nova import exception from nova import objects from nova.objects import host_mapping from nova import test from nova.tests.unit.objects import test_cell_mapping from nova.tests.unit.objects import test_objects def get_db_mapping(mapped_cell=None, **updates): db_mapping = { 'id': 1, 'cell_id': None, 'host': 'fake-host', 'created_at': None, 'updated_at': None, } if mapped_cell: db_mapping["cell_mapping"] = mapped_cell else: db_mapping["cell_mapping"] = test_cell_mapping.get_db_mapping(id=42) db_mapping['cell_id'] = db_mapping["cell_mapping"]["id"] db_mapping.update(updates) return db_mapping class _TestHostMappingObject(object): def _check_cell_map_value(self, db_val, cell_obj): self.assertEqual(db_val, cell_obj.id) @mock.patch.object(host_mapping.HostMapping, '_get_by_host_from_db') def test_get_by_host(self, host_from_db): fake_cell = test_cell_mapping.get_db_mapping(id=1) db_mapping = get_db_mapping(mapped_cell=fake_cell) host_from_db.return_value = db_mapping mapping_obj = objects.HostMapping().get_by_host( self.context, db_mapping['host']) host_from_db.assert_called_once_with(self.context, db_mapping['host']) with mock.patch.object( host_mapping.HostMapping, '_get_cell_mapping') as mock_load: self.compare_obj(mapping_obj, db_mapping, subs={'cell_mapping': 'cell_id'}, comparators={ 'cell_mapping': self._check_cell_map_value}) # Check that lazy loading isn't happening self.assertFalse(mock_load.called) def test_from_db_object_no_cell_map(self): """Test when db object does not have cell_mapping""" fake_cell = test_cell_mapping.get_db_mapping(id=1) db_mapping = get_db_mapping(mapped_cell=fake_cell) # If db object has no cell_mapping, lazy loading should occur db_mapping.pop("cell_mapping") fake_cell_obj = objects.CellMapping(self.context, **fake_cell) mapping_obj = objects.HostMapping()._from_db_object( self.context, objects.HostMapping(), db_mapping) with mock.patch.object( host_mapping.HostMapping, '_get_cell_mapping') as mock_load: mock_load.return_value = fake_cell_obj self.compare_obj(mapping_obj, db_mapping, subs={'cell_mapping': 'cell_id'}, comparators={ 'cell_mapping': self._check_cell_map_value}) # Check that cell_mapping is lazy loaded mock_load.assert_called_once_with() @mock.patch.object(host_mapping.HostMapping, '_create_in_db') def test_create(self, create_in_db): fake_cell = test_cell_mapping.get_db_mapping(id=1) db_mapping = get_db_mapping(mapped_cell=fake_cell) db_mapping.pop("cell_mapping") host = db_mapping['host'] create_in_db.return_value = db_mapping fake_cell_obj = objects.CellMapping(self.context, **fake_cell) mapping_obj = objects.HostMapping(self.context) mapping_obj.host = host mapping_obj.cell_mapping = fake_cell_obj mapping_obj.create() create_in_db.assert_called_once_with(self.context, {'host': host, 'cell_id': fake_cell["id"]}) self.compare_obj(mapping_obj, db_mapping, subs={'cell_mapping': 'cell_id'}, comparators={ 'cell_mapping': self._check_cell_map_value}) @mock.patch.object(host_mapping.HostMapping, '_save_in_db') def test_save(self, save_in_db): db_mapping = get_db_mapping() # This isn't needed here db_mapping.pop("cell_mapping") host = db_mapping['host'] mapping_obj = objects.HostMapping(self.context) mapping_obj.host = host mapping_obj.id = db_mapping['id'] new_fake_cell = test_cell_mapping.get_db_mapping(id=10) fake_cell_obj = objects.CellMapping(self.context, **new_fake_cell) mapping_obj.cell_mapping = fake_cell_obj db_mapping.update({"cell_id": new_fake_cell["id"]}) save_in_db.return_value = db_mapping mapping_obj.save() save_in_db.assert_called_once_with(self.context, test.MatchType(host_mapping.HostMapping), {'cell_id': new_fake_cell["id"], 'host': host, 'id': db_mapping['id']}) self.compare_obj(mapping_obj, db_mapping, subs={'cell_mapping': 'cell_id'}, comparators={ 'cell_mapping': self._check_cell_map_value}) @mock.patch.object(host_mapping.HostMapping, '_destroy_in_db') def test_destroy(self, destroy_in_db): mapping_obj = objects.HostMapping(self.context) mapping_obj.host = "fake-host2" mapping_obj.destroy() destroy_in_db.assert_called_once_with(self.context, "fake-host2") class TestHostMappingObject(test_objects._LocalTest, _TestHostMappingObject): pass class TestRemoteHostMappingObject(test_objects._RemoteTest, _TestHostMappingObject): pass class _TestHostMappingListObject(object): def _check_cell_map_value(self, db_val, cell_obj): self.assertEqual(db_val, cell_obj.id) @mock.patch.object(host_mapping.HostMappingList, '_get_from_db') def test_get_all(self, get_from_db): fake_cell = test_cell_mapping.get_db_mapping(id=1) db_mapping = get_db_mapping(mapped_cell=fake_cell) get_from_db.return_value = [db_mapping] mapping_obj = objects.HostMappingList.get_all(self.context) get_from_db.assert_called_once_with(self.context) self.compare_obj(mapping_obj.objects[0], db_mapping, subs={'cell_mapping': 'cell_id'}, comparators={ 'cell_mapping': self._check_cell_map_value}) class TestCellMappingListObject(test_objects._LocalTest, _TestHostMappingListObject): pass class TestRemoteCellMappingListObject(test_objects._RemoteTest, _TestHostMappingListObject): pass class TestHostMappingDiscovery(test.NoDBTestCase): @mock.patch('nova.objects.CellMappingList.get_all') @mock.patch('nova.objects.HostMapping.create') @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.ComputeNodeList.get_all_by_not_mapped') def test_discover_hosts_all(self, mock_cn_get, mock_hm_get, mock_hm_create, mock_cm): def _hm_get(context, host): if host in ['a', 'b', 'c']: return objects.HostMapping() raise exception.HostMappingNotFound(name=host) mock_hm_get.side_effect = _hm_get mock_cn_get.side_effect = [[objects.ComputeNode(host='d', uuid=uuids.cn1)], [objects.ComputeNode(host='e', uuid=uuids.cn2)]] cell_mappings = [objects.CellMapping(name='foo', uuid=uuids.cm1), objects.CellMapping(name='bar', uuid=uuids.cm2)] mock_cm.return_value = cell_mappings ctxt = context.get_admin_context() with mock.patch('nova.objects.ComputeNode.save') as mock_save: hms = host_mapping.discover_hosts(ctxt) mock_save.assert_has_calls([mock.call(), mock.call()]) self.assertEqual(2, len(hms)) self.assertTrue(mock_hm_create.called) self.assertEqual(['d', 'e'], [hm.host for hm in hms]) @mock.patch('nova.objects.CellMapping.get_by_uuid') @mock.patch('nova.objects.HostMapping.create') @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.ComputeNodeList.get_all_by_not_mapped') def test_discover_hosts_one(self, mock_cn_get, mock_hm_get, mock_hm_create, mock_cm): def _hm_get(context, host): if host in ['a', 'b', 'c']: return objects.HostMapping() raise exception.HostMappingNotFound(name=host) mock_hm_get.side_effect = _hm_get # NOTE(danms): Provide both side effects, but expect it to only # be called once if we provide a cell mock_cn_get.side_effect = [[objects.ComputeNode(host='d', uuid=uuids.cn1)], [objects.ComputeNode(host='e', uuid=uuids.cn2)]] mock_cm.return_value = objects.CellMapping(name='foo', uuid=uuids.cm1) ctxt = context.get_admin_context() with mock.patch('nova.objects.ComputeNode.save') as mock_save: hms = host_mapping.discover_hosts(ctxt, uuids.cm1) mock_save.assert_called_once_with() self.assertEqual(1, len(hms)) self.assertTrue(mock_hm_create.called) self.assertEqual(['d'], [hm.host for hm in hms]) @mock.patch('nova.objects.CellMappingList.get_all') @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.HostMapping.create') @mock.patch('nova.objects.ServiceList.get_by_binary') def test_discover_services(self, mock_srv, mock_hm_create, mock_hm_get, mock_cm): mock_cm.return_value = [ objects.CellMapping(uuid=uuids.cell1), objects.CellMapping(uuid=uuids.cell2), ] mock_srv.side_effect = [ [objects.Service(host='host1'), objects.Service(host='host2')], [objects.Service(host='host3')], ] def fake_get_host_mapping(ctxt, host): if host == 'host2': return else: raise exception.HostMappingNotFound(name=host) mock_hm_get.side_effect = fake_get_host_mapping ctxt = context.get_admin_context() mappings = host_mapping.discover_hosts(ctxt, by_service=True) self.assertEqual(2, len(mappings)) self.assertEqual(['host1', 'host3'], sorted([m.host for m in mappings])) @mock.patch('nova.objects.CellMapping.get_by_uuid') @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.HostMapping.create') @mock.patch('nova.objects.ServiceList.get_by_binary') def test_discover_services_one_cell(self, mock_srv, mock_hm_create, mock_hm_get, mock_cm): mock_cm.return_value = objects.CellMapping(uuid=uuids.cell1) mock_srv.return_value = [ objects.Service(host='host1'), objects.Service(host='host2'), ] def fake_get_host_mapping(ctxt, host): if host == 'host2': return else: raise exception.HostMappingNotFound(name=host) mock_hm_get.side_effect = fake_get_host_mapping lines = [] def fake_status(msg): lines.append(msg) ctxt = context.get_admin_context() mappings = host_mapping.discover_hosts(ctxt, cell_uuid=uuids.cell1, status_fn=fake_status, by_service=True) self.assertEqual(1, len(mappings)) self.assertEqual(['host1'], sorted([m.host for m in mappings])) expected = """\ Getting computes from cell: %(cell)s Creating host mapping for service host1 Found 1 unmapped computes in cell: %(cell)s""" % {'cell': uuids.cell1} self.assertEqual(expected, '\n'.join(lines)) @mock.patch('nova.objects.CellMappingList.get_all') @mock.patch('nova.objects.HostMapping.create') @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.ComputeNodeList.get_all_by_not_mapped') def test_discover_hosts_duplicate(self, mock_cn_get, mock_hm_get, mock_hm_create, mock_cm): mock_cm.return_value = [objects.CellMapping(name='foo', uuid=uuids.cm)] mock_cn_get.return_value = [objects.ComputeNode(host='bar', uuid=uuids.cn)] mock_hm_get.side_effect = exception.HostMappingNotFound(name='bar') mock_hm_create.side_effect = db_exc.DBDuplicateEntry() ctxt = context.get_admin_context() exp = self.assertRaises(exception.HostMappingExists, host_mapping.discover_hosts, ctxt) expected = "Host 'bar' mapping already exists" self.assertIn(expected, str(exp)) @mock.patch('nova.objects.CellMappingList.get_all') @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.objects.HostMapping.create') @mock.patch('nova.objects.ServiceList.get_by_binary') def test_discover_services_duplicate(self, mock_srv, mock_hm_create, mock_hm_get, mock_cm): mock_cm.return_value = [objects.CellMapping(name='foo', uuid=uuids.cm)] mock_srv.return_value = [objects.Service(host='bar')] mock_hm_get.side_effect = exception.HostMappingNotFound(name='bar') mock_hm_create.side_effect = db_exc.DBDuplicateEntry() ctxt = context.get_admin_context() exp = self.assertRaises(exception.HostMappingExists, host_mapping.discover_hosts, ctxt, by_service=True) expected = "Host 'bar' mapping already exists" self.assertIn(expected, str(exp))