def test_show_late_host_mapping_gone(self): s_ref = self._create_compute_service() with mock.patch.object(self.controller.api, 'instance_get_all_by_host') as m: m.side_effect = exception.HostMappingNotFound(name='something') self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, self.req, s_ref['host'])
def _get_by_host_from_db(context, host): db_mapping = (context.session.query(api_models.HostMapping).options( joinedload('cell_mapping')).filter( api_models.HostMapping.host == host)).first() if not db_mapping: raise exception.HostMappingNotFound(name=host) return db_mapping
def _test(self, mock_service): req = self._get_request(True) mock_service.side_effect = exception.HostMappingNotFound( name='foo') self.assertRaises(exc.HTTPNotFound, self.controller.show, req, compute_nodes[0].id) self.assertTrue(mock_service.called)
def setUp(self): super(ComputeRpcAPITestCase, self).setUp() self.context = context.get_admin_context() self.fake_flavor_obj = fake_flavor.fake_flavor_obj(self.context) self.fake_flavor = jsonutils.to_primitive(self.fake_flavor_obj) instance_attr = { 'host': 'fake_host', 'instance_type_id': self.fake_flavor_obj['id'], 'instance_type': self.fake_flavor_obj } self.fake_instance_obj = fake_instance.fake_instance_obj( self.context, **instance_attr) self.fake_instance = jsonutils.to_primitive(self.fake_instance_obj) self.fake_volume_bdm = objects_block_dev.BlockDeviceMapping( **fake_block_device.FakeDbBlockDeviceDict( { 'source_type': 'volume', 'destination_type': 'volume', 'instance_uuid': self.fake_instance_obj.uuid, 'volume_id': 'fake-volume-id' })) # FIXME(melwitt): Temporary while things have no mappings self.patcher1 = mock.patch('nova.objects.InstanceMapping.' 'get_by_instance_uuid') self.patcher2 = mock.patch('nova.objects.HostMapping.get_by_host') mock_inst_mapping = self.patcher1.start() mock_host_mapping = self.patcher2.start() mock_inst_mapping.side_effect = exception.InstanceMappingNotFound( uuid=self.fake_instance_obj.uuid) mock_host_mapping.side_effect = exception.HostMappingNotFound( name=self.fake_instance_obj.host)
def test_servers_not_mapped(self): req = self._get_request(True) with mock.patch.object(self.controller.host_api, 'instance_get_all_by_host') as m: m.side_effect = exception.HostMappingNotFound(name='something') self.assertRaises(exc.HTTPNotFound, self.controller.servers, req, 'hyper')
def fake_service_get_by_compute_host(self, context, host): if host == 'bad-host': raise exception.ComputeHostNotFound(host=host) elif host == 'unmapped-host': raise exception.HostMappingNotFound(name=host) else: return {'host_name': host, 'service': 'compute', 'zone': 'nova'}
def _save_in_db(context, obj, updates): db_mapping = context.session.query(api_models.HostMapping).filter_by( id=obj.id).first() if not db_mapping: raise exception.HostMappingNotFound(name=obj.host) db_mapping.update(updates) return db_mapping
def _test(mock_service, mock_search): mock_search.return_value = [mock.MagicMock()] mock_service.side_effect = exception.HostMappingNotFound( name='foo') req = self._get_request(True) self.assertRaises(exc.HTTPNotFound, self.controller.search, req, 'a') self.assertTrue(mock_service.called)
def _destroy_in_db(context, host): session = db_api.get_api_session() with session.begin(): result = session.query(api_models.HostMapping).filter_by( host=host).delete() if not result: raise exception.HostMappingNotFound(name=host)
def test_services_enable_with_unmapped_host(self): body = {'host': 'invalid', 'binary': 'nova-compute'} with mock.patch.object(self.controller.host_api, 'service_update') as m: m.side_effect = exception.HostMappingNotFound(name='something') self.assertRaises(webob.exc.HTTPNotFound, self.controller.update, self.req, "enable", body=body)
def test_uptime_hypervisor_not_mapped(self): with mock.patch.object(self.controller.host_api, 'get_host_uptime', side_effect=exception.HostMappingNotFound( name='dummy')) as mock_get_uptime: req = self._get_request(True) self.assertRaises(exc.HTTPBadRequest, self.controller.uptime, req, self.TEST_HYPERS_OBJ[0].id) mock_get_uptime.assert_called_once_with( mock.ANY, self.TEST_HYPERS_OBJ[0].host)
def _get_by_host_from_db(context, host): db_mapping = (context.session.query( api_models.HostMapping).join(api_models.CellMapping).with_entities( api_models.HostMapping, api_models.CellMapping).filter( api_models.HostMapping.host == host)).first() if not db_mapping: raise exception.HostMappingNotFound(name=host) host_mapping = db_mapping[0] host_mapping["cell_mapping"] = db_mapping[1] return host_mapping
def test_uptime_hypervisor_not_mapped_service_get(self): @mock.patch.object(self.controller.host_api, 'compute_node_get') @mock.patch.object(self.controller.host_api, 'get_host_uptime') @mock.patch.object( self.controller.host_api, 'service_get_by_compute_host', side_effect=exception.HostMappingNotFound(name='dummy')) def _test(mock_get, _, __): req = self._get_request(True) self.assertRaises(exc.HTTPNotFound, self.controller.uptime, req, self.TEST_HYPERS_OBJ[0].id) self.assertTrue(mock_get.called) _test()
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))
def fake_service_get_by_compute_host(context, host): if host == TEST_HYPERS[0]['host']: return TEST_SERVICES[0] raise exception.HostMappingNotFound(name=host)
class LiveMigrationTaskTestCase(test.NoDBTestCase): def setUp(self): super(LiveMigrationTaskTestCase, self).setUp() self.context = "context" self.instance_host = "host" self.instance_uuid = uuids.instance self.instance_image = "image_ref" db_instance = fake_instance.fake_db_instance( host=self.instance_host, uuid=self.instance_uuid, power_state=power_state.RUNNING, vm_state=vm_states.ACTIVE, memory_mb=512, image_ref=self.instance_image) self.instance = objects.Instance._from_db_object( self.context, objects.Instance(), db_instance) self.instance.system_metadata = {'image_hw_disk_bus': 'scsi'} self.destination = "destination" self.block_migration = "bm" self.disk_over_commit = "doc" self.migration = objects.Migration() self.fake_spec = objects.RequestSpec() self._generate_task() def _generate_task(self): self.task = live_migrate.LiveMigrationTask( self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit, self.migration, compute_rpcapi.ComputeAPI(), servicegroup.API(), scheduler_client.SchedulerClient(), self.fake_spec) def test_execute_with_destination(self): dest_node = objects.ComputeNode(hypervisor_hostname='dest_node') with test.nested( mock.patch.object(self.task, '_check_host_is_up'), mock.patch.object(self.task, '_check_requested_destination', return_value=(mock.sentinel.source_node, dest_node)), mock.patch.object(scheduler_utils, 'claim_resources_on_destination'), mock.patch.object(self.migration, 'save'), mock.patch.object(self.task.compute_rpcapi, 'live_migration'), ) as (mock_check_up, mock_check_dest, mock_claim, mock_save, mock_mig): mock_mig.return_value = "bob" self.assertEqual("bob", self.task.execute()) mock_check_up.assert_called_once_with(self.instance_host) mock_check_dest.assert_called_once_with() mock_claim.assert_called_once_with( self.task.scheduler_client.reportclient, self.instance, mock.sentinel.source_node, dest_node) mock_mig.assert_called_once_with( self.context, host=self.instance_host, instance=self.instance, dest=self.destination, block_migration=self.block_migration, migration=self.migration, migrate_data=None) self.assertTrue(mock_save.called) # make sure the source/dest fields were set on the migration object self.assertEqual(self.instance.node, self.migration.source_node) self.assertEqual(dest_node.hypervisor_hostname, self.migration.dest_node) self.assertEqual(self.task.destination, self.migration.dest_compute) def test_execute_without_destination(self): self.destination = None self._generate_task() self.assertIsNone(self.task.destination) with test.nested( mock.patch.object(self.task, '_check_host_is_up'), mock.patch.object(self.task, '_find_destination'), mock.patch.object(self.task.compute_rpcapi, 'live_migration'), mock.patch.object(self.migration, 'save')) as (mock_check, mock_find, mock_mig, mock_save): mock_find.return_value = ("found_host", "found_node") mock_mig.return_value = "bob" self.assertEqual("bob", self.task.execute()) mock_check.assert_called_once_with(self.instance_host) mock_find.assert_called_once_with() mock_mig.assert_called_once_with( self.context, host=self.instance_host, instance=self.instance, dest="found_host", block_migration=self.block_migration, migration=self.migration, migrate_data=None) self.assertTrue(mock_save.called) self.assertEqual('found_host', self.migration.dest_compute) self.assertEqual('found_node', self.migration.dest_node) self.assertEqual(self.instance.node, self.migration.source_node) def test_check_instance_is_active_passes_when_paused(self): self.task.instance['power_state'] = power_state.PAUSED self.task._check_instance_is_active() def test_check_instance_is_active_fails_when_shutdown(self): self.task.instance['power_state'] = power_state.SHUTDOWN self.assertRaises(exception.InstanceInvalidState, self.task._check_instance_is_active) @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(servicegroup.API, 'service_is_up') def test_check_instance_host_is_up(self, mock_is_up, mock_get): mock_get.return_value = "service" mock_is_up.return_value = True self.task._check_host_is_up("host") mock_get.assert_called_once_with(self.context, "host") mock_is_up.assert_called_once_with("service") @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(servicegroup.API, 'service_is_up') def test_check_instance_host_is_up_fails_if_not_up(self, mock_is_up, mock_get): mock_get.return_value = "service" mock_is_up.return_value = False self.assertRaises(exception.ComputeServiceUnavailable, self.task._check_host_is_up, "host") mock_get.assert_called_once_with(self.context, "host") mock_is_up.assert_called_once_with("service") @mock.patch.object(objects.Service, 'get_by_compute_host', side_effect=exception.ComputeHostNotFound(host='host')) def test_check_instance_host_is_up_fails_if_not_found(self, mock): self.assertRaises(exception.ComputeHostNotFound, self.task._check_host_is_up, "host") @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') @mock.patch.object(servicegroup.API, 'service_is_up') @mock.patch.object(compute_rpcapi.ComputeAPI, 'check_can_live_migrate_destination') def test_check_requested_destination(self, mock_check, mock_is_up, mock_get_info, mock_get_host): mock_get_host.return_value = "service" mock_is_up.return_value = True hypervisor_details = objects.ComputeNode(hypervisor_type="a", hypervisor_version=6.1, free_ram_mb=513, memory_mb=512, ram_allocation_ratio=1.0) mock_get_info.return_value = hypervisor_details mock_check.return_value = "migrate_data" self.assertEqual((hypervisor_details, hypervisor_details), self.task._check_requested_destination()) self.assertEqual("migrate_data", self.task.migrate_data) mock_get_host.assert_called_once_with(self.context, self.destination) mock_is_up.assert_called_once_with("service") self.assertEqual([ mock.call(self.destination), mock.call(self.instance_host), mock.call(self.destination) ], mock_get_info.call_args_list) mock_check.assert_called_once_with(self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit) def test_check_requested_destination_fails_with_same_dest(self): self.task.destination = "same" self.task.source = "same" self.assertRaises(exception.UnableToMigrateToSelf, self.task._check_requested_destination) @mock.patch.object(objects.Service, 'get_by_compute_host', side_effect=exception.ComputeHostNotFound(host='host')) def test_check_requested_destination_fails_when_destination_is_up( self, mock): self.assertRaises(exception.ComputeHostNotFound, self.task._check_requested_destination) @mock.patch.object(live_migrate.LiveMigrationTask, '_check_host_is_up') @mock.patch.object(objects.ComputeNode, 'get_first_node_by_host_for_old_compat') def test_check_requested_destination_fails_with_not_enough_memory( self, mock_get_first, mock_is_up): mock_get_first.return_value = (objects.ComputeNode( free_ram_mb=513, memory_mb=1024, ram_allocation_ratio=0.9, )) # free_ram is bigger than instance.ram (512) but the allocation # ratio reduces the total available RAM to 410MB # (1024 * 0.9 - (1024 - 513)) self.assertRaises(exception.MigrationPreCheckError, self.task._check_requested_destination) mock_is_up.assert_called_once_with(self.destination) mock_get_first.assert_called_once_with(self.context, self.destination) @mock.patch.object(live_migrate.LiveMigrationTask, '_check_host_is_up') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_destination_has_enough_memory') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') def test_check_requested_destination_fails_with_hypervisor_diff( self, mock_get_info, mock_check, mock_is_up): mock_get_info.side_effect = [ objects.ComputeNode(hypervisor_type='b'), objects.ComputeNode(hypervisor_type='a') ] self.assertRaises(exception.InvalidHypervisorType, self.task._check_requested_destination) mock_is_up.assert_called_once_with(self.destination) mock_check.assert_called_once_with() self.assertEqual( [mock.call(self.instance_host), mock.call(self.destination)], mock_get_info.call_args_list) @mock.patch.object(live_migrate.LiveMigrationTask, '_check_host_is_up') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_destination_has_enough_memory') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') def test_check_requested_destination_fails_with_hypervisor_too_old( self, mock_get_info, mock_check, mock_is_up): host1 = {'hypervisor_type': 'a', 'hypervisor_version': 7} host2 = {'hypervisor_type': 'a', 'hypervisor_version': 6} mock_get_info.side_effect = [ objects.ComputeNode(**host1), objects.ComputeNode(**host2) ] self.assertRaises(exception.DestinationHypervisorTooOld, self.task._check_requested_destination) mock_is_up.assert_called_once_with(self.destination) mock_check.assert_called_once_with() self.assertEqual( [mock.call(self.instance_host), mock.call(self.destination)], mock_get_info.call_args_list) @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') @mock.patch.object(servicegroup.API, 'service_is_up') @mock.patch.object(compute_rpcapi.ComputeAPI, 'check_can_live_migrate_destination') @mock.patch.object( objects.HostMapping, 'get_by_host', return_value=objects.HostMapping(cell_mapping=objects.CellMapping( uuid=uuids.different))) def test_check_requested_destination_fails_different_cells( self, mock_get_host_mapping, mock_check, mock_is_up, mock_get_info, mock_get_host): mock_get_host.return_value = "service" mock_is_up.return_value = True hypervisor_details = objects.ComputeNode(hypervisor_type="a", hypervisor_version=6.1, free_ram_mb=513, memory_mb=512, ram_allocation_ratio=1.0) mock_get_info.return_value = hypervisor_details mock_check.return_value = "migrate_data" ex = self.assertRaises(exception.MigrationPreCheckError, self.task._check_requested_destination) self.assertIn('across cells', six.text_type(ex)) def test_find_destination_works(self): self.mox.StubOutWithMock(utils, 'get_image_from_system_metadata') self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group') self.mox.StubOutWithMock(objects.RequestSpec, 'reset_forced_destinations') self.mox.StubOutWithMock(self.task.scheduler_client, 'select_destinations') self.mox.StubOutWithMock(self.task, '_check_compatible_with_source_hypervisor') self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') utils.get_image_from_system_metadata( self.instance.system_metadata).AndReturn("image") scheduler_utils.setup_instance_group(self.context, self.fake_spec) self.fake_spec.reset_forced_destinations() self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host1', 'nodename': 'node1' }]) self.task._check_compatible_with_source_hypervisor("host1") self.task._call_livem_checks_on_host("host1") self.mox.ReplayAll() self.assertEqual(("host1", "node1"), self.task._find_destination()) # Make sure the request_spec was updated to include the cell # mapping. self.assertIsNotNone(self.fake_spec.requested_destination.cell) def test_find_destination_works_with_no_request_spec(self): task = live_migrate.LiveMigrationTask( self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit, self.migration, compute_rpcapi.ComputeAPI(), servicegroup.API(), scheduler_client.SchedulerClient(), request_spec=None) another_spec = objects.RequestSpec() self.instance.flavor = objects.Flavor() self.instance.numa_topology = None self.instance.pci_requests = None @mock.patch.object(task, '_call_livem_checks_on_host') @mock.patch.object(task, '_check_compatible_with_source_hypervisor') @mock.patch.object(task.scheduler_client, 'select_destinations') @mock.patch.object(objects.RequestSpec, 'from_components') @mock.patch.object(scheduler_utils, 'setup_instance_group') @mock.patch.object(utils, 'get_image_from_system_metadata') def do_test(get_image, setup_ig, from_components, select_dest, check_compat, call_livem_checks): get_image.return_value = "image" from_components.return_value = another_spec select_dest.return_value = [{'host': 'host1', 'nodename': 'node1'}] self.assertEqual(("host1", "node1"), task._find_destination()) get_image.assert_called_once_with(self.instance.system_metadata) setup_ig.assert_called_once_with(self.context, another_spec) select_dest.assert_called_once_with(self.context, another_spec, [self.instance.uuid]) # Make sure the request_spec was updated to include the cell # mapping. self.assertIsNotNone(another_spec.requested_destination.cell) check_compat.assert_called_once_with("host1") call_livem_checks.assert_called_once_with("host1") do_test() def test_find_destination_no_image_works(self): self.instance['image_ref'] = '' self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group') self.mox.StubOutWithMock(self.task.scheduler_client, 'select_destinations') self.mox.StubOutWithMock(self.task, '_check_compatible_with_source_hypervisor') self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') scheduler_utils.setup_instance_group(self.context, self.fake_spec) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host1', 'nodename': 'node1' }]) self.task._check_compatible_with_source_hypervisor("host1") self.task._call_livem_checks_on_host("host1") self.mox.ReplayAll() self.assertEqual(("host1", "node1"), self.task._find_destination()) def _test_find_destination_retry_hypervisor_raises(self, error): self.mox.StubOutWithMock(utils, 'get_image_from_system_metadata') self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group') self.mox.StubOutWithMock(self.task.scheduler_client, 'select_destinations') self.mox.StubOutWithMock(self.task, '_check_compatible_with_source_hypervisor') self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') utils.get_image_from_system_metadata( self.instance.system_metadata).AndReturn("image") scheduler_utils.setup_instance_group(self.context, self.fake_spec) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host1', 'nodename': 'node1' }]) self.task._check_compatible_with_source_hypervisor("host1")\ .AndRaise(error) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host2', 'nodename': 'node2' }]) self.task._check_compatible_with_source_hypervisor("host2") self.task._call_livem_checks_on_host("host2") self.mox.ReplayAll() with mock.patch.object(self.task, '_remove_host_allocations') as remove_allocs: self.assertEqual(("host2", "node2"), self.task._find_destination()) # Should have removed allocations for the first host. remove_allocs.assert_called_once_with('host1', 'node1') def test_find_destination_retry_with_old_hypervisor(self): self._test_find_destination_retry_hypervisor_raises( exception.DestinationHypervisorTooOld) def test_find_destination_retry_with_invalid_hypervisor_type(self): self._test_find_destination_retry_hypervisor_raises( exception.InvalidHypervisorType) def test_find_destination_retry_with_invalid_livem_checks(self): self.flags(migrate_max_retries=1) self.mox.StubOutWithMock(utils, 'get_image_from_system_metadata') self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group') self.mox.StubOutWithMock(self.task.scheduler_client, 'select_destinations') self.mox.StubOutWithMock(self.task, '_check_compatible_with_source_hypervisor') self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') utils.get_image_from_system_metadata( self.instance.system_metadata).AndReturn("image") scheduler_utils.setup_instance_group(self.context, self.fake_spec) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host1', 'nodename': 'node1' }]) self.task._check_compatible_with_source_hypervisor("host1") self.task._call_livem_checks_on_host("host1")\ .AndRaise(exception.Invalid) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host2', 'nodename': 'node2' }]) self.task._check_compatible_with_source_hypervisor("host2") self.task._call_livem_checks_on_host("host2") self.mox.ReplayAll() with mock.patch.object(self.task, '_remove_host_allocations') as remove_allocs: self.assertEqual(("host2", "node2"), self.task._find_destination()) # Should have removed allocations for the first host. remove_allocs.assert_called_once_with('host1', 'node1') def test_find_destination_retry_with_failed_migration_pre_checks(self): self.flags(migrate_max_retries=1) self.mox.StubOutWithMock(utils, 'get_image_from_system_metadata') self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group') self.mox.StubOutWithMock(self.task.scheduler_client, 'select_destinations') self.mox.StubOutWithMock(self.task, '_check_compatible_with_source_hypervisor') self.mox.StubOutWithMock(self.task, '_call_livem_checks_on_host') utils.get_image_from_system_metadata( self.instance.system_metadata).AndReturn("image") scheduler_utils.setup_instance_group(self.context, self.fake_spec) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host1', 'nodename': 'node1' }]) self.task._check_compatible_with_source_hypervisor("host1") self.task._call_livem_checks_on_host("host1")\ .AndRaise(exception.MigrationPreCheckError("reason")) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host2', 'nodename': 'node2' }]) self.task._check_compatible_with_source_hypervisor("host2") self.task._call_livem_checks_on_host("host2") self.mox.ReplayAll() with mock.patch.object(self.task, '_remove_host_allocations') as remove_allocs: self.assertEqual(("host2", "node2"), self.task._find_destination()) # Should have removed allocations for the first host. remove_allocs.assert_called_once_with('host1', 'node1') def test_find_destination_retry_exceeds_max(self): self.flags(migrate_max_retries=0) self.mox.StubOutWithMock(utils, 'get_image_from_system_metadata') self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group') self.mox.StubOutWithMock(self.task.scheduler_client, 'select_destinations') self.mox.StubOutWithMock(self.task, '_check_compatible_with_source_hypervisor') utils.get_image_from_system_metadata( self.instance.system_metadata).AndReturn("image") scheduler_utils.setup_instance_group(self.context, self.fake_spec) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndReturn([{ 'host': 'host1', 'nodename': 'node1' }]) self.task._check_compatible_with_source_hypervisor("host1")\ .AndRaise(exception.DestinationHypervisorTooOld) self.mox.ReplayAll() with test.nested( mock.patch.object(self.task.migration, 'save'), mock.patch.object( self.task, '_remove_host_allocations')) as (save_mock, remove_allocs): self.assertRaises(exception.MaxRetriesExceeded, self.task._find_destination) self.assertEqual('failed', self.task.migration.status) save_mock.assert_called_once_with() # Should have removed allocations for the first host. remove_allocs.assert_called_once_with('host1', 'node1') def test_find_destination_when_runs_out_of_hosts(self): self.mox.StubOutWithMock(utils, 'get_image_from_system_metadata') self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group') self.mox.StubOutWithMock(self.task.scheduler_client, 'select_destinations') utils.get_image_from_system_metadata( self.instance.system_metadata).AndReturn("image") scheduler_utils.setup_instance_group(self.context, self.fake_spec) self.task.scheduler_client.select_destinations( self.context, self.fake_spec, [self.instance.uuid]).AndRaise(exception.NoValidHost(reason="")) self.mox.ReplayAll() self.assertRaises(exception.NoValidHost, self.task._find_destination) @mock.patch("nova.utils.get_image_from_system_metadata") @mock.patch("nova.scheduler.utils.build_request_spec") @mock.patch("nova.scheduler.utils.setup_instance_group") @mock.patch("nova.objects.RequestSpec.from_primitives") def test_find_destination_with_remoteError( self, m_from_primitives, m_setup_instance_group, m_build_request_spec, m_get_image_from_system_metadata): m_get_image_from_system_metadata.return_value = {'properties': {}} m_build_request_spec.return_value = {} fake_spec = objects.RequestSpec() m_from_primitives.return_value = fake_spec with mock.patch.object(self.task.scheduler_client, 'select_destinations') as m_select_destinations: error = messaging.RemoteError() m_select_destinations.side_effect = error self.assertRaises(exception.MigrationSchedulerRPCError, self.task._find_destination) def test_call_livem_checks_on_host(self): with mock.patch.object(self.task.compute_rpcapi, 'check_can_live_migrate_destination', side_effect=messaging.MessagingTimeout): self.assertRaises(exception.MigrationPreCheckError, self.task._call_livem_checks_on_host, {}) @mock.patch.object( objects.InstanceMapping, 'get_by_instance_uuid', side_effect=exception.InstanceMappingNotFound(uuid=uuids.instance)) def test_get_source_cell_mapping_not_found(self, mock_get): """Negative test where InstanceMappingNotFound is raised and converted to MigrationPreCheckError. """ self.assertRaises(exception.MigrationPreCheckError, self.task._get_source_cell_mapping) mock_get.assert_called_once_with(self.task.context, self.task.instance.uuid) @mock.patch.object( objects.HostMapping, 'get_by_host', side_effect=exception.HostMappingNotFound(name='destination')) def test_get_destination_cell_mapping_not_found(self, mock_get): """Negative test where HostMappingNotFound is raised and converted to MigrationPreCheckError. """ self.assertRaises(exception.MigrationPreCheckError, self.task._get_destination_cell_mapping) mock_get.assert_called_once_with(self.task.context, self.task.destination) @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename', side_effect=exception.ComputeHostNotFound(host='host')) def test_remove_host_allocations_compute_host_not_found(self, get_cn): """Tests that failing to find a ComputeNode will not blow up the _remove_host_allocations method. """ with mock.patch.object( self.task.scheduler_client.reportclient, 'remove_provider_from_instance_allocation') as remove_provider: self.task._remove_host_allocations('host', 'node') remove_provider.assert_not_called()
def _destroy_in_db(context, host): result = context.session.query( api_models.HostMapping).filter_by(host=host).delete() if not result: raise exception.HostMappingNotFound(name=host)
class LiveMigrationTaskTestCase(test.NoDBTestCase): def setUp(self): super(LiveMigrationTaskTestCase, self).setUp() self.context = nova_context.get_admin_context() self.instance_host = "host" self.instance_uuid = uuids.instance self.instance_image = "image_ref" db_instance = fake_instance.fake_db_instance( host=self.instance_host, uuid=self.instance_uuid, power_state=power_state.RUNNING, vm_state = vm_states.ACTIVE, memory_mb=512, image_ref=self.instance_image) self.instance = objects.Instance._from_db_object( self.context, objects.Instance(), db_instance) self.instance.system_metadata = {'image_hw_disk_bus': 'scsi'} self.instance.numa_topology = None self.instance.pci_requests = None self.instance.resources = None self.destination = "destination" self.block_migration = "bm" self.disk_over_commit = "doc" self.migration = objects.Migration() self.fake_spec = objects.RequestSpec() self._generate_task() _p = mock.patch('nova.compute.utils.heal_reqspec_is_bfv') self.heal_reqspec_is_bfv_mock = _p.start() self.addCleanup(_p.stop) _p = mock.patch('nova.objects.RequestSpec.ensure_network_metadata') self.ensure_network_metadata_mock = _p.start() self.addCleanup(_p.stop) _p = mock.patch( 'nova.network.neutron.API.' 'get_requested_resource_for_instance', return_value=[]) self.mock_get_res_req = _p.start() self.addCleanup(_p.stop) def _generate_task(self): self.task = live_migrate.LiveMigrationTask(self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit, self.migration, compute_rpcapi.ComputeAPI(), servicegroup.API(), query.SchedulerQueryClient(), report.SchedulerReportClient(), self.fake_spec) @mock.patch('nova.availability_zones.get_host_availability_zone', return_value='fake-az') def test_execute_with_destination(self, mock_get_az): dest_node = objects.ComputeNode(hypervisor_hostname='dest_node') with test.nested( mock.patch.object(self.task, '_check_host_is_up'), mock.patch.object(self.task, '_check_requested_destination'), mock.patch.object(scheduler_utils, 'claim_resources_on_destination'), mock.patch.object(self.migration, 'save'), mock.patch.object(self.task.compute_rpcapi, 'live_migration'), mock.patch('nova.conductor.tasks.migrate.' 'replace_allocation_with_migration'), mock.patch.object(self.task, '_check_destination_is_not_source'), mock.patch.object(self.task, '_check_destination_has_enough_memory'), mock.patch.object(self.task, '_check_compatible_with_source_hypervisor', return_value=(mock.sentinel.source_node, dest_node)), ) as (mock_check_up, mock_check_dest, mock_claim, mock_save, mock_mig, m_alloc, m_check_diff, m_check_enough_mem, m_check_compatible): mock_mig.return_value = "bob" m_alloc.return_value = (mock.MagicMock(), mock.sentinel.allocs) self.assertEqual("bob", self.task.execute()) mock_check_up.assert_has_calls([ mock.call(self.instance_host), mock.call(self.destination)]) mock_check_dest.assert_called_once_with() m_check_diff.assert_called_once() m_check_enough_mem.assert_called_once() m_check_compatible.assert_called_once() allocs = mock.sentinel.allocs mock_claim.assert_called_once_with( self.context, self.task.report_client, self.instance, mock.sentinel.source_node, dest_node, source_allocations=allocs, consumer_generation=None) mock_mig.assert_called_once_with( self.context, host=self.instance_host, instance=self.instance, dest=self.destination, block_migration=self.block_migration, migration=self.migration, migrate_data=None) self.assertTrue(mock_save.called) mock_get_az.assert_called_once_with(self.context, self.destination) self.assertEqual('fake-az', self.instance.availability_zone) # make sure the source/dest fields were set on the migration object self.assertEqual(self.instance.node, self.migration.source_node) self.assertEqual(dest_node.hypervisor_hostname, self.migration.dest_node) self.assertEqual(self.task.destination, self.migration.dest_compute) m_alloc.assert_called_once_with(self.context, self.instance, self.migration) # When the task is executed with a destination it means the host is # being forced and we don't call the scheduler, so we don't need to # heal the request spec. self.heal_reqspec_is_bfv_mock.assert_not_called() # When the task is executed with a destination it means the host is # being forced and we don't call the scheduler, so we don't need to # modify the request spec self.ensure_network_metadata_mock.assert_not_called() @mock.patch('nova.availability_zones.get_host_availability_zone', return_value='nova') def test_execute_without_destination(self, mock_get_az): self.destination = None self._generate_task() self.assertIsNone(self.task.destination) with test.nested( mock.patch.object(self.task, '_check_host_is_up'), mock.patch.object(self.task, '_find_destination'), mock.patch.object(self.task.compute_rpcapi, 'live_migration'), mock.patch.object(self.migration, 'save'), mock.patch('nova.conductor.tasks.migrate.' 'replace_allocation_with_migration'), ) as (mock_check, mock_find, mock_mig, mock_save, mock_alloc): mock_find.return_value = ("found_host", "found_node", None) mock_mig.return_value = "bob" mock_alloc.return_value = (mock.MagicMock(), mock.MagicMock()) self.assertEqual("bob", self.task.execute()) mock_check.assert_called_once_with(self.instance_host) mock_find.assert_called_once_with() mock_mig.assert_called_once_with(self.context, host=self.instance_host, instance=self.instance, dest="found_host", block_migration=self.block_migration, migration=self.migration, migrate_data=None) self.assertTrue(mock_save.called) mock_get_az.assert_called_once_with(self.context, 'found_host') self.assertEqual('found_host', self.migration.dest_compute) self.assertEqual('found_node', self.migration.dest_node) self.assertEqual(self.instance.node, self.migration.source_node) self.assertTrue(mock_alloc.called) def test_check_instance_is_active_passes_when_paused(self): self.task.instance['power_state'] = power_state.PAUSED self.task._check_instance_is_active() def test_check_instance_is_active_fails_when_shutdown(self): self.task.instance['power_state'] = power_state.SHUTDOWN self.assertRaises(exception.InstanceInvalidState, self.task._check_instance_is_active) @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename') def test_check_instance_has_no_numa_passes_no_numa(self, mock_get): self.flags(enable_numa_live_migration=False, group='workarounds') self.task.instance.numa_topology = None mock_get.return_value = objects.ComputeNode( uuid=uuids.cn1, hypervisor_type='qemu') self.task._check_instance_has_no_numa() @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename') def test_check_instance_has_no_numa_passes_non_kvm(self, mock_get): self.flags(enable_numa_live_migration=False, group='workarounds') self.task.instance.numa_topology = objects.InstanceNUMATopology(cells=[ objects.InstanceNUMACell( id=0, cpuset=set([0]), pcpuset=set(), memory=1024), ]) mock_get.return_value = objects.ComputeNode( uuid=uuids.cn1, hypervisor_type='xen') self.task._check_instance_has_no_numa() @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename') @mock.patch.object(objects.Service, 'get_minimum_version', return_value=39) def test_check_instance_has_no_numa_passes_workaround( self, mock_get_min_ver, mock_get): self.flags(enable_numa_live_migration=True, group='workarounds') self.task.instance.numa_topology = objects.InstanceNUMATopology(cells=[ objects.InstanceNUMACell( id=0, cpuset=set([0]), pcpuset=set(), memory=1024), ]) mock_get.return_value = objects.ComputeNode( uuid=uuids.cn1, hypervisor_type='qemu') self.task._check_instance_has_no_numa() mock_get_min_ver.assert_called_once_with(self.context, 'nova-compute') @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename') @mock.patch.object(objects.Service, 'get_minimum_version', return_value=39) def test_check_instance_has_no_numa_fails(self, mock_get_min_ver, mock_get): self.flags(enable_numa_live_migration=False, group='workarounds') mock_get.return_value = objects.ComputeNode( uuid=uuids.cn1, hypervisor_type='qemu') self.task.instance.numa_topology = objects.InstanceNUMATopology(cells=[ objects.InstanceNUMACell( id=0, cpuset=set([0]), pcpuset=set(), memory=1024), ]) self.assertRaises(exception.MigrationPreCheckError, self.task._check_instance_has_no_numa) mock_get_min_ver.assert_called_once_with(self.context, 'nova-compute') @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename') @mock.patch.object(objects.Service, 'get_minimum_version', return_value=40) def test_check_instance_has_no_numa_new_svc_passes(self, mock_get_min_ver, mock_get): self.flags(enable_numa_live_migration=False, group='workarounds') mock_get.return_value = objects.ComputeNode( uuid=uuids.cn1, hypervisor_type='qemu') self.task.instance.numa_topology = objects.InstanceNUMATopology(cells=[ objects.InstanceNUMACell( id=0, cpuset=set([0]), pcpuset=set(), memory=1024), ]) self.task._check_instance_has_no_numa() mock_get_min_ver.assert_called_once_with(self.context, 'nova-compute') @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(servicegroup.API, 'service_is_up') def test_check_instance_host_is_up(self, mock_is_up, mock_get): mock_get.return_value = "service" mock_is_up.return_value = True self.task._check_host_is_up("host") mock_get.assert_called_once_with(self.context, "host") mock_is_up.assert_called_once_with("service") @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(servicegroup.API, 'service_is_up') def test_check_instance_host_is_up_fails_if_not_up(self, mock_is_up, mock_get): mock_get.return_value = "service" mock_is_up.return_value = False self.assertRaises(exception.ComputeServiceUnavailable, self.task._check_host_is_up, "host") mock_get.assert_called_once_with(self.context, "host") mock_is_up.assert_called_once_with("service") @mock.patch.object(objects.Service, 'get_by_compute_host', side_effect=exception.ComputeHostNotFound(host='host')) def test_check_instance_host_is_up_fails_if_not_found(self, mock): self.assertRaises(exception.ComputeHostNotFound, self.task._check_host_is_up, "host") def test_check_destination_fails_with_same_dest(self): self.task.destination = "same" self.task.source = "same" self.assertRaises(exception.UnableToMigrateToSelf, self.task._check_destination_is_not_source) @mock.patch.object(objects.ComputeNode, 'get_first_node_by_host_for_old_compat') def test_check_destination_fails_with_not_enough_memory( self, mock_get_first): mock_get_first.return_value = ( objects.ComputeNode(free_ram_mb=513, memory_mb=1024, ram_allocation_ratio=0.9,)) # free_ram is bigger than instance.ram (512) but the allocation # ratio reduces the total available RAM to 410MB # (1024 * 0.9 - (1024 - 513)) self.assertRaises(exception.MigrationPreCheckError, self.task._check_destination_has_enough_memory) mock_get_first.assert_called_once_with(self.context, self.destination) @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') def test_check_compatible_fails_with_hypervisor_diff( self, mock_get_info): mock_get_info.side_effect = [ objects.ComputeNode(hypervisor_type='b'), objects.ComputeNode(hypervisor_type='a')] self.assertRaises(exception.InvalidHypervisorType, self.task._check_compatible_with_source_hypervisor, self.destination) self.assertEqual([mock.call(self.instance_host), mock.call(self.destination)], mock_get_info.call_args_list) @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') def test_check_compatible_fails_with_hypervisor_too_old( self, mock_get_info): host1 = {'hypervisor_type': 'a', 'hypervisor_version': 7} host2 = {'hypervisor_type': 'a', 'hypervisor_version': 6} mock_get_info.side_effect = [objects.ComputeNode(**host1), objects.ComputeNode(**host2)] self.assertRaises(exception.DestinationHypervisorTooOld, self.task._check_compatible_with_source_hypervisor, self.destination) self.assertEqual([mock.call(self.instance_host), mock.call(self.destination)], mock_get_info.call_args_list) @mock.patch.object(compute_rpcapi.ComputeAPI, 'check_can_live_migrate_destination') def test_check_requested_destination(self, mock_check): mock_check.return_value = "migrate_data" self.task.limits = fake_limits1 with test.nested( mock.patch.object(self.task.network_api, 'supports_port_binding_extension', return_value=False), mock.patch.object(self.task, '_check_can_migrate_pci')): self.assertIsNone(self.task._check_requested_destination()) self.assertEqual("migrate_data", self.task.migrate_data) mock_check.assert_called_once_with(self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit, self.task.migration, fake_limits1) @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') @mock.patch.object(servicegroup.API, 'service_is_up') @mock.patch.object(compute_rpcapi.ComputeAPI, 'check_can_live_migrate_destination') @mock.patch.object(objects.HostMapping, 'get_by_host', return_value=objects.HostMapping( cell_mapping=objects.CellMapping( uuid=uuids.different))) def test_check_requested_destination_fails_different_cells( self, mock_get_host_mapping, mock_check, mock_is_up, mock_get_info, mock_get_host): mock_get_host.return_value = "service" mock_is_up.return_value = True hypervisor_details = objects.ComputeNode( hypervisor_type="a", hypervisor_version=6.1, free_ram_mb=513, memory_mb=512, ram_allocation_ratio=1.0) mock_get_info.return_value = hypervisor_details mock_check.return_value = "migrate_data" with test.nested( mock.patch.object(self.task.network_api, 'supports_port_binding_extension', return_value=False), mock.patch.object(self.task, '_check_can_migrate_pci')): ex = self.assertRaises(exception.MigrationPreCheckError, self.task._check_requested_destination) self.assertIn('across cells', str(ex)) @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', return_value=[[fake_selection1]]) @mock.patch.object(objects.RequestSpec, 'reset_forced_destinations') @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_works(self, mock_setup, mock_reset, mock_select, mock_check, mock_call): self.assertEqual(("host1", "node1", fake_limits1), self.task._find_destination()) # Make sure the request_spec was updated to include the cell # mapping. self.assertIsNotNone(self.fake_spec.requested_destination.cell) # Make sure the spec was updated to include the project_id. self.assertEqual(self.fake_spec.project_id, self.instance.project_id) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_reset.assert_called_once_with() self.ensure_network_metadata_mock.assert_called_once_with( self.instance) self.heal_reqspec_is_bfv_mock.assert_called_once_with( self.context, self.fake_spec, self.instance) mock_select.assert_called_once_with(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) mock_check.assert_called_once_with('host1') mock_call.assert_called_once_with('host1', {}) @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', return_value=[[fake_selection1]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_no_image_works(self, mock_setup, mock_select, mock_check, mock_call): self.instance['image_ref'] = '' self.assertEqual(("host1", "node1", fake_limits1), self.task._find_destination()) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_called_once_with( self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) mock_check.assert_called_once_with('host1') mock_call.assert_called_once_with('host1', {}) @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', side_effect=[[[fake_selection1]], [[fake_selection2]]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def _test_find_destination_retry_hypervisor_raises( self, error, mock_setup, mock_select, mock_check, mock_call, mock_remove): mock_check.side_effect = [error, None] self.assertEqual(("host2", "node2", fake_limits2), self.task._find_destination()) # Should have removed allocations for the first host. mock_remove.assert_called_once_with(fake_selection1.compute_node_uuid) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_has_calls([ mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False), mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False)]) mock_check.assert_has_calls([mock.call('host1'), mock.call('host2')]) mock_call.assert_called_once_with('host2', {}) def test_find_destination_retry_with_old_hypervisor(self): self._test_find_destination_retry_hypervisor_raises( exception.DestinationHypervisorTooOld) def test_find_destination_retry_with_invalid_hypervisor_type(self): self._test_find_destination_retry_hypervisor_raises( exception.InvalidHypervisorType) @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', side_effect=[[[fake_selection1]], [[fake_selection2]]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_retry_with_invalid_livem_checks( self, mock_setup, mock_select, mock_check, mock_call, mock_remove): self.flags(migrate_max_retries=1) mock_call.side_effect = [exception.Invalid(), None] self.assertEqual(("host2", "node2", fake_limits2), self.task._find_destination()) # Should have removed allocations for the first host. mock_remove.assert_called_once_with(fake_selection1.compute_node_uuid) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_has_calls([ mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False), mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False)]) mock_check.assert_has_calls([mock.call('host1'), mock.call('host2')]) mock_call.assert_has_calls( [mock.call('host1', {}), mock.call('host2', {})]) @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', side_effect=[[[fake_selection1]], [[fake_selection2]]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_retry_with_failed_migration_pre_checks( self, mock_setup, mock_select, mock_check, mock_call, mock_remove): self.flags(migrate_max_retries=1) mock_call.side_effect = [exception.MigrationPreCheckError('reason'), None] self.assertEqual(("host2", "node2", fake_limits2), self.task._find_destination()) # Should have removed allocations for the first host. mock_remove.assert_called_once_with(fake_selection1.compute_node_uuid) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_has_calls([ mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False), mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False)]) mock_check.assert_has_calls([mock.call('host1'), mock.call('host2')]) mock_call.assert_has_calls( [mock.call('host1', {}), mock.call('host2', {})]) @mock.patch.object(objects.Migration, 'save') @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor', side_effect=exception.DestinationHypervisorTooOld()) @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', return_value=[[fake_selection1]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_retry_exceeds_max( self, mock_setup, mock_select, mock_check, mock_remove, mock_save): self.flags(migrate_max_retries=0) self.assertRaises(exception.MaxRetriesExceeded, self.task._find_destination) self.assertEqual('failed', self.task.migration.status) mock_save.assert_called_once_with() # Should have removed allocations for the first host. mock_remove.assert_called_once_with(fake_selection1.compute_node_uuid) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_called_once_with( self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) mock_check.assert_called_once_with('host1') @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', side_effect=exception.NoValidHost(reason="")) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_when_runs_out_of_hosts(self, mock_setup, mock_select): self.assertRaises(exception.NoValidHost, self.task._find_destination) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_called_once_with( self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) @mock.patch("nova.utils.get_image_from_system_metadata") @mock.patch("nova.scheduler.utils.build_request_spec") @mock.patch("nova.scheduler.utils.setup_instance_group") @mock.patch("nova.objects.RequestSpec.from_primitives") def test_find_destination_with_remoteError(self, m_from_primitives, m_setup_instance_group, m_build_request_spec, m_get_image_from_system_metadata): m_get_image_from_system_metadata.return_value = {'properties': {}} m_build_request_spec.return_value = {} fake_spec = objects.RequestSpec() m_from_primitives.return_value = fake_spec with mock.patch.object(self.task.query_client, 'select_destinations') as m_select_destinations: error = messaging.RemoteError() m_select_destinations.side_effect = error self.assertRaises(exception.MigrationSchedulerRPCError, self.task._find_destination) def test_call_livem_checks_on_host(self): with test.nested( mock.patch.object(self.task.compute_rpcapi, 'check_can_live_migrate_destination', side_effect=messaging.MessagingTimeout), mock.patch.object(self.task, '_check_can_migrate_pci')): self.assertRaises(exception.MigrationPreCheckError, self.task._call_livem_checks_on_host, {}, {}) @mock.patch('nova.network.neutron.API.bind_ports_to_host') def test_bind_ports_on_destination_merges_profiles(self, mock_bind_ports): """Assert that if both the migration_data and the provider mapping contains binding profile related information then such information is merged in the resulting profile. """ self.task.migrate_data = objects.LibvirtLiveMigrateData( vifs=[ objects.VIFMigrateData( port_id=uuids.port1, profile_json=jsonutils.dumps( {'some-key': 'value'})) ]) provider_mappings = {uuids.port1: [uuids.dest_bw_rp]} self.task._bind_ports_on_destination('dest-host', provider_mappings) mock_bind_ports.assert_called_once_with( context=self.context, instance=self.instance, host='dest-host', vnic_types=None, port_profiles={uuids.port1: {'allocation': uuids.dest_bw_rp, 'some-key': 'value'}}) @mock.patch('nova.network.neutron.API.bind_ports_to_host') def test_bind_ports_on_destination_migration_data(self, mock_bind_ports): """Assert that if only the migration_data contains binding profile related information then that is sent to neutron. """ self.task.migrate_data = objects.LibvirtLiveMigrateData( vifs=[ objects.VIFMigrateData( port_id=uuids.port1, profile_json=jsonutils.dumps( {'some-key': 'value'})) ]) provider_mappings = {} self.task._bind_ports_on_destination('dest-host', provider_mappings) mock_bind_ports.assert_called_once_with( context=self.context, instance=self.instance, host='dest-host', vnic_types=None, port_profiles={uuids.port1: {'some-key': 'value'}}) @mock.patch('nova.network.neutron.API.bind_ports_to_host') def test_bind_ports_on_destination_provider_mapping(self, mock_bind_ports): """Assert that if only the provider mapping contains binding profile related information then that is sent to neutron. """ self.task.migrate_data = objects.LibvirtLiveMigrateData( vifs=[ objects.VIFMigrateData( port_id=uuids.port1) ]) provider_mappings = {uuids.port1: [uuids.dest_bw_rp]} self.task._bind_ports_on_destination('dest-host', provider_mappings) mock_bind_ports.assert_called_once_with( context=self.context, instance=self.instance, host='dest-host', vnic_types=None, port_profiles={uuids.port1: {'allocation': uuids.dest_bw_rp}}) @mock.patch( 'nova.compute.utils.' 'update_pci_request_spec_with_allocated_interface_name') @mock.patch('nova.scheduler.utils.fill_provider_mapping') @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(query.SchedulerQueryClient, 'select_destinations', return_value=[[fake_selection1]]) @mock.patch.object(objects.RequestSpec, 'reset_forced_destinations') @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_with_resource_request( self, mock_setup, mock_reset, mock_select, mock_check, mock_call, mock_fill_provider_mapping, mock_update_pci_req): resource_req = [objects.RequestGroup(requester_id=uuids.port_id)] self.mock_get_res_req.return_value = resource_req self.instance.pci_requests = objects.InstancePCIRequests(requests=[]) self.assertEqual(("host1", "node1", fake_limits1), self.task._find_destination()) # Make sure the request_spec was updated to include the cell # mapping. self.assertIsNotNone(self.fake_spec.requested_destination.cell) # Make sure the spec was updated to include the project_id. self.assertEqual(self.fake_spec.project_id, self.instance.project_id) # Make sure that requested_resources are added to the request spec self.assertEqual( resource_req, self.task.request_spec.requested_resources) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_reset.assert_called_once_with() self.ensure_network_metadata_mock.assert_called_once_with( self.instance) self.heal_reqspec_is_bfv_mock.assert_called_once_with( self.context, self.fake_spec, self.instance) mock_select.assert_called_once_with(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) mock_check.assert_called_once_with('host1') mock_call.assert_called_once_with('host1', {uuids.port_id: []}) mock_fill_provider_mapping.assert_called_once_with( self.task.request_spec, fake_selection1) mock_update_pci_req.assert_called_once_with( self.context, self.task.report_client, [], {uuids.port_id: []}) @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid', side_effect=exception.InstanceMappingNotFound( uuid=uuids.instance)) def test_get_source_cell_mapping_not_found(self, mock_get): """Negative test where InstanceMappingNotFound is raised and converted to MigrationPreCheckError. """ self.assertRaises(exception.MigrationPreCheckError, self.task._get_source_cell_mapping) mock_get.assert_called_once_with( self.task.context, self.task.instance.uuid) @mock.patch.object(objects.HostMapping, 'get_by_host', side_effect=exception.HostMappingNotFound( name='destination')) def test_get_destination_cell_mapping_not_found(self, mock_get): """Negative test where HostMappingNotFound is raised and converted to MigrationPreCheckError. """ self.assertRaises(exception.MigrationPreCheckError, self.task._get_destination_cell_mapping) mock_get.assert_called_once_with( self.task.context, self.task.destination) @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 'remove_provider_tree_from_instance_allocation') def test_remove_host_allocations(self, remove_provider): self.task._remove_host_allocations(uuids.cn) remove_provider.assert_called_once_with( self.task.context, self.task.instance.uuid, uuids.cn) def test_check_can_migrate_pci(self): """Tests that _check_can_migrate_pci() allows live-migration if instance does not contain non-network related PCI requests and raises MigrationPreCheckError otherwise """ @mock.patch.object(self.task.network_api, 'supports_port_binding_extension') @mock.patch.object(live_migrate, 'supports_vif_related_pci_allocations') def _test(instance_pci_reqs, supp_binding_ext_retval, supp_vif_related_pci_alloc_retval, mock_supp_vif_related_pci_alloc, mock_supp_port_binding_ext): mock_supp_vif_related_pci_alloc.return_value = \ supp_vif_related_pci_alloc_retval mock_supp_port_binding_ext.return_value = \ supp_binding_ext_retval self.task.instance.pci_requests = instance_pci_reqs self.task._check_can_migrate_pci("Src", "Dst") # in case we managed to get away without rasing, check mocks if instance_pci_reqs: mock_supp_port_binding_ext.assert_called_once_with( self.context) self.assertTrue(mock_supp_vif_related_pci_alloc.called) # instance has no PCI requests _test(None, False, False) # No support in Neutron and Computes _test(None, True, False) # No support in Computes _test(None, False, True) # No support in Neutron _test(None, True, True) # Support in both Neutron and Computes # instance contains network related PCI requests (alias_name=None) pci_requests = objects.InstancePCIRequests( requests=[objects.InstancePCIRequest(alias_name=None)]) self.assertRaises(exception.MigrationPreCheckError, _test, pci_requests, False, False) self.assertRaises(exception.MigrationPreCheckError, _test, pci_requests, True, False) self.assertRaises(exception.MigrationPreCheckError, _test, pci_requests, False, True) _test(pci_requests, True, True) # instance contains Non network related PCI requests (alias_name!=None) pci_requests.requests.append( objects.InstancePCIRequest(alias_name="non-network-related-pci")) self.assertRaises(exception.MigrationPreCheckError, _test, pci_requests, False, False) self.assertRaises(exception.MigrationPreCheckError, _test, pci_requests, True, False) self.assertRaises(exception.MigrationPreCheckError, _test, pci_requests, False, True) self.assertRaises(exception.MigrationPreCheckError, _test, pci_requests, True, True) def test_check_can_migrate_specific_resources(self): """Test _check_can_migrate_specific_resources allows live migration with vpmem. """ @mock.patch.object(live_migrate, 'supports_vpmem_live_migration') def _test(resources, supp_lm_vpmem_retval, mock_support_lm_vpmem): self.instance.resources = resources mock_support_lm_vpmem.return_value = supp_lm_vpmem_retval self.task._check_can_migrate_specific_resources() vpmem_0 = objects.LibvirtVPMEMDevice( label='4GB', name='ns_0', devpath='/dev/dax0.0', size=4292870144, align=2097152) resource_0 = objects.Resource( provider_uuid=uuids.rp, resource_class="CUSTOM_PMEM_NAMESPACE_4GB", identifier='ns_0', metadata=vpmem_0) resources = objects.ResourceList( objects=[resource_0]) _test(None, False) _test(None, True) _test(resources, True) self.assertRaises(exception.MigrationPreCheckError, _test, resources, False)
class LiveMigrationTaskTestCase(test.NoDBTestCase): def setUp(self): super(LiveMigrationTaskTestCase, self).setUp() self.context = "context" self.instance_host = "host" self.instance_uuid = uuids.instance self.instance_image = "image_ref" db_instance = fake_instance.fake_db_instance( host=self.instance_host, uuid=self.instance_uuid, power_state=power_state.RUNNING, vm_state=vm_states.ACTIVE, memory_mb=512, image_ref=self.instance_image) self.instance = objects.Instance._from_db_object( self.context, objects.Instance(), db_instance) self.instance.system_metadata = {'image_hw_disk_bus': 'scsi'} self.destination = "destination" self.block_migration = "bm" self.disk_over_commit = "doc" self.migration = objects.Migration() self.fake_spec = objects.RequestSpec() self._generate_task() _p = mock.patch('nova.compute.utils.heal_reqspec_is_bfv') self.heal_reqspec_is_bfv_mock = _p.start() self.addCleanup(_p.stop) _p = mock.patch('nova.objects.RequestSpec.ensure_network_metadata') self.ensure_network_metadata_mock = _p.start() self.addCleanup(_p.stop) def _generate_task(self): self.task = live_migrate.LiveMigrationTask( self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit, self.migration, compute_rpcapi.ComputeAPI(), servicegroup.API(), scheduler_client.SchedulerClient(), self.fake_spec) def test_execute_with_destination(self): dest_node = objects.ComputeNode(hypervisor_hostname='dest_node') with test.nested( mock.patch.object(self.task, '_check_host_is_up'), mock.patch.object(self.task, '_check_requested_destination', return_value=(mock.sentinel.source_node, dest_node)), mock.patch.object(scheduler_utils, 'claim_resources_on_destination'), mock.patch.object(self.migration, 'save'), mock.patch.object(self.task.compute_rpcapi, 'live_migration'), mock.patch('nova.conductor.tasks.migrate.' 'replace_allocation_with_migration'), ) as (mock_check_up, mock_check_dest, mock_claim, mock_save, mock_mig, m_alloc): mock_mig.return_value = "bob" m_alloc.return_value = (mock.MagicMock(), mock.sentinel.allocs) self.assertEqual("bob", self.task.execute()) mock_check_up.assert_called_once_with(self.instance_host) mock_check_dest.assert_called_once_with() allocs = mock.sentinel.allocs mock_claim.assert_called_once_with( self.context, self.task.scheduler_client.reportclient, self.instance, mock.sentinel.source_node, dest_node, source_allocations=allocs, consumer_generation=None) mock_mig.assert_called_once_with( self.context, host=self.instance_host, instance=self.instance, dest=self.destination, block_migration=self.block_migration, migration=self.migration, migrate_data=None) self.assertTrue(mock_save.called) # make sure the source/dest fields were set on the migration object self.assertEqual(self.instance.node, self.migration.source_node) self.assertEqual(dest_node.hypervisor_hostname, self.migration.dest_node) self.assertEqual(self.task.destination, self.migration.dest_compute) m_alloc.assert_called_once_with(self.context, self.instance, self.migration) # When the task is executed with a destination it means the host is # being forced and we don't call the scheduler, so we don't need to # heal the request spec. self.heal_reqspec_is_bfv_mock.assert_not_called() # When the task is executed with a destination it means the host is # being forced and we don't call the scheduler, so we don't need to # modify the request spec self.ensure_network_metadata_mock.assert_not_called() def test_execute_without_destination(self): self.destination = None self._generate_task() self.assertIsNone(self.task.destination) with test.nested( mock.patch.object(self.task, '_check_host_is_up'), mock.patch.object(self.task, '_find_destination'), mock.patch.object(self.task.compute_rpcapi, 'live_migration'), mock.patch.object(self.migration, 'save'), mock.patch('nova.conductor.tasks.migrate.' 'replace_allocation_with_migration'), ) as (mock_check, mock_find, mock_mig, mock_save, mock_alloc): mock_find.return_value = ("found_host", "found_node") mock_mig.return_value = "bob" mock_alloc.return_value = (mock.MagicMock(), mock.MagicMock()) self.assertEqual("bob", self.task.execute()) mock_check.assert_called_once_with(self.instance_host) mock_find.assert_called_once_with() mock_mig.assert_called_once_with( self.context, host=self.instance_host, instance=self.instance, dest="found_host", block_migration=self.block_migration, migration=self.migration, migrate_data=None) self.assertTrue(mock_save.called) self.assertEqual('found_host', self.migration.dest_compute) self.assertEqual('found_node', self.migration.dest_node) self.assertEqual(self.instance.node, self.migration.source_node) self.assertTrue(mock_alloc.called) def test_check_instance_is_active_passes_when_paused(self): self.task.instance['power_state'] = power_state.PAUSED self.task._check_instance_is_active() def test_check_instance_is_active_fails_when_shutdown(self): self.task.instance['power_state'] = power_state.SHUTDOWN self.assertRaises(exception.InstanceInvalidState, self.task._check_instance_is_active) @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(servicegroup.API, 'service_is_up') def test_check_instance_host_is_up(self, mock_is_up, mock_get): mock_get.return_value = "service" mock_is_up.return_value = True self.task._check_host_is_up("host") mock_get.assert_called_once_with(self.context, "host") mock_is_up.assert_called_once_with("service") @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(servicegroup.API, 'service_is_up') def test_check_instance_host_is_up_fails_if_not_up(self, mock_is_up, mock_get): mock_get.return_value = "service" mock_is_up.return_value = False self.assertRaises(exception.ComputeServiceUnavailable, self.task._check_host_is_up, "host") mock_get.assert_called_once_with(self.context, "host") mock_is_up.assert_called_once_with("service") @mock.patch.object(objects.Service, 'get_by_compute_host', side_effect=exception.ComputeHostNotFound(host='host')) def test_check_instance_host_is_up_fails_if_not_found(self, mock): self.assertRaises(exception.ComputeHostNotFound, self.task._check_host_is_up, "host") @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') @mock.patch.object(servicegroup.API, 'service_is_up') @mock.patch.object(compute_rpcapi.ComputeAPI, 'check_can_live_migrate_destination') def test_check_requested_destination(self, mock_check, mock_is_up, mock_get_info, mock_get_host): mock_get_host.return_value = "service" mock_is_up.return_value = True hypervisor_details = objects.ComputeNode(hypervisor_type="a", hypervisor_version=6.1, free_ram_mb=513, memory_mb=512, ram_allocation_ratio=1.0) mock_get_info.return_value = hypervisor_details mock_check.return_value = "migrate_data" with mock.patch.object(self.task.network_api, 'supports_port_binding_extension', return_value=False): self.assertEqual((hypervisor_details, hypervisor_details), self.task._check_requested_destination()) self.assertEqual("migrate_data", self.task.migrate_data) mock_get_host.assert_called_once_with(self.context, self.destination) mock_is_up.assert_called_once_with("service") self.assertEqual([ mock.call(self.destination), mock.call(self.instance_host), mock.call(self.destination) ], mock_get_info.call_args_list) mock_check.assert_called_once_with(self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit) def test_check_requested_destination_fails_with_same_dest(self): self.task.destination = "same" self.task.source = "same" self.assertRaises(exception.UnableToMigrateToSelf, self.task._check_requested_destination) @mock.patch.object(objects.Service, 'get_by_compute_host', side_effect=exception.ComputeHostNotFound(host='host')) def test_check_requested_destination_fails_when_destination_is_up( self, mock): self.assertRaises(exception.ComputeHostNotFound, self.task._check_requested_destination) @mock.patch.object(live_migrate.LiveMigrationTask, '_check_host_is_up') @mock.patch.object(objects.ComputeNode, 'get_first_node_by_host_for_old_compat') def test_check_requested_destination_fails_with_not_enough_memory( self, mock_get_first, mock_is_up): mock_get_first.return_value = (objects.ComputeNode( free_ram_mb=513, memory_mb=1024, ram_allocation_ratio=0.9, )) # free_ram is bigger than instance.ram (512) but the allocation # ratio reduces the total available RAM to 410MB # (1024 * 0.9 - (1024 - 513)) self.assertRaises(exception.MigrationPreCheckError, self.task._check_requested_destination) mock_is_up.assert_called_once_with(self.destination) mock_get_first.assert_called_once_with(self.context, self.destination) @mock.patch.object(live_migrate.LiveMigrationTask, '_check_host_is_up') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_destination_has_enough_memory') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') def test_check_requested_destination_fails_with_hypervisor_diff( self, mock_get_info, mock_check, mock_is_up): mock_get_info.side_effect = [ objects.ComputeNode(hypervisor_type='b'), objects.ComputeNode(hypervisor_type='a') ] self.assertRaises(exception.InvalidHypervisorType, self.task._check_requested_destination) mock_is_up.assert_called_once_with(self.destination) mock_check.assert_called_once_with() self.assertEqual( [mock.call(self.instance_host), mock.call(self.destination)], mock_get_info.call_args_list) @mock.patch.object(live_migrate.LiveMigrationTask, '_check_host_is_up') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_destination_has_enough_memory') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') def test_check_requested_destination_fails_with_hypervisor_too_old( self, mock_get_info, mock_check, mock_is_up): host1 = {'hypervisor_type': 'a', 'hypervisor_version': 7} host2 = {'hypervisor_type': 'a', 'hypervisor_version': 6} mock_get_info.side_effect = [ objects.ComputeNode(**host1), objects.ComputeNode(**host2) ] self.assertRaises(exception.DestinationHypervisorTooOld, self.task._check_requested_destination) mock_is_up.assert_called_once_with(self.destination) mock_check.assert_called_once_with() self.assertEqual( [mock.call(self.instance_host), mock.call(self.destination)], mock_get_info.call_args_list) @mock.patch.object(objects.Service, 'get_by_compute_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_get_compute_info') @mock.patch.object(servicegroup.API, 'service_is_up') @mock.patch.object(compute_rpcapi.ComputeAPI, 'check_can_live_migrate_destination') @mock.patch.object( objects.HostMapping, 'get_by_host', return_value=objects.HostMapping(cell_mapping=objects.CellMapping( uuid=uuids.different))) def test_check_requested_destination_fails_different_cells( self, mock_get_host_mapping, mock_check, mock_is_up, mock_get_info, mock_get_host): mock_get_host.return_value = "service" mock_is_up.return_value = True hypervisor_details = objects.ComputeNode(hypervisor_type="a", hypervisor_version=6.1, free_ram_mb=513, memory_mb=512, ram_allocation_ratio=1.0) mock_get_info.return_value = hypervisor_details mock_check.return_value = "migrate_data" with mock.patch.object(self.task.network_api, 'supports_port_binding_extension', return_value=False): ex = self.assertRaises(exception.MigrationPreCheckError, self.task._check_requested_destination) self.assertIn('across cells', six.text_type(ex)) @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations', return_value=[[fake_selection1]]) @mock.patch.object(objects.RequestSpec, 'reset_forced_destinations') @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_works(self, mock_setup, mock_reset, mock_select, mock_check, mock_call): self.assertEqual(("host1", "node1"), self.task._find_destination()) # Make sure the request_spec was updated to include the cell # mapping. self.assertIsNotNone(self.fake_spec.requested_destination.cell) # Make sure the spec was updated to include the project_id. self.assertEqual(self.fake_spec.project_id, self.instance.project_id) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_reset.assert_called_once_with() self.ensure_network_metadata_mock.assert_called_once_with( self.instance) self.heal_reqspec_is_bfv_mock.assert_called_once_with( self.context, self.fake_spec, self.instance) mock_select.assert_called_once_with(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) mock_check.assert_called_once_with('host1') mock_call.assert_called_once_with('host1') def test_find_destination_works_with_no_request_spec(self): task = live_migrate.LiveMigrationTask( self.context, self.instance, self.destination, self.block_migration, self.disk_over_commit, self.migration, compute_rpcapi.ComputeAPI(), servicegroup.API(), scheduler_client.SchedulerClient(), request_spec=None) another_spec = objects.RequestSpec() self.instance.flavor = objects.Flavor() self.instance.numa_topology = None self.instance.pci_requests = None @mock.patch.object(task, '_call_livem_checks_on_host') @mock.patch.object(task, '_check_compatible_with_source_hypervisor') @mock.patch.object(task.scheduler_client, 'select_destinations') @mock.patch.object(objects.RequestSpec, 'from_components') @mock.patch.object(scheduler_utils, 'setup_instance_group') @mock.patch.object(utils, 'get_image_from_system_metadata') def do_test(get_image, setup_ig, from_components, select_dest, check_compat, call_livem_checks): get_image.return_value = "image" from_components.return_value = another_spec select_dest.return_value = [[fake_selection1]] self.assertEqual(("host1", "node1"), task._find_destination()) get_image.assert_called_once_with(self.instance.system_metadata) setup_ig.assert_called_once_with(self.context, another_spec) self.ensure_network_metadata_mock.assert_called_once_with( self.instance) self.heal_reqspec_is_bfv_mock.assert_called_once_with( self.context, another_spec, self.instance) select_dest.assert_called_once_with(self.context, another_spec, [self.instance.uuid], return_objects=True, return_alternates=False) # Make sure the request_spec was updated to include the cell # mapping. self.assertIsNotNone(another_spec.requested_destination.cell) check_compat.assert_called_once_with("host1") call_livem_checks.assert_called_once_with("host1") do_test() @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations', return_value=[[fake_selection1]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_no_image_works(self, mock_setup, mock_select, mock_check, mock_call): self.instance['image_ref'] = '' self.assertEqual(("host1", "node1"), self.task._find_destination()) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_called_once_with(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) mock_check.assert_called_once_with('host1') mock_call.assert_called_once_with('host1') @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations', side_effect=[[[fake_selection1]], [[fake_selection2]]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def _test_find_destination_retry_hypervisor_raises(self, error, mock_setup, mock_select, mock_check, mock_call, mock_remove): mock_check.side_effect = [error, None] self.assertEqual(("host2", "node2"), self.task._find_destination()) # Should have removed allocations for the first host. mock_remove.assert_called_once_with('host1', 'node1') mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_has_calls([ mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False), mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) ]) mock_check.assert_has_calls([mock.call('host1'), mock.call('host2')]) mock_call.assert_called_once_with('host2') def test_find_destination_retry_with_old_hypervisor(self): self._test_find_destination_retry_hypervisor_raises( exception.DestinationHypervisorTooOld) def test_find_destination_retry_with_invalid_hypervisor_type(self): self._test_find_destination_retry_hypervisor_raises( exception.InvalidHypervisorType) @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations', side_effect=[[[fake_selection1]], [[fake_selection2]]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_retry_with_invalid_livem_checks( self, mock_setup, mock_select, mock_check, mock_call, mock_remove): self.flags(migrate_max_retries=1) mock_call.side_effect = [exception.Invalid(), None] self.assertEqual(("host2", "node2"), self.task._find_destination()) # Should have removed allocations for the first host. mock_remove.assert_called_once_with('host1', 'node1') mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_has_calls([ mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False), mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) ]) mock_check.assert_has_calls([mock.call('host1'), mock.call('host2')]) mock_call.assert_has_calls([mock.call('host1'), mock.call('host2')]) @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_call_livem_checks_on_host') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor') @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations', side_effect=[[[fake_selection1]], [[fake_selection2]]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_retry_with_failed_migration_pre_checks( self, mock_setup, mock_select, mock_check, mock_call, mock_remove): self.flags(migrate_max_retries=1) mock_call.side_effect = [ exception.MigrationPreCheckError('reason'), None ] self.assertEqual(("host2", "node2"), self.task._find_destination()) # Should have removed allocations for the first host. mock_remove.assert_called_once_with('host1', 'node1') mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_has_calls([ mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False), mock.call(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) ]) mock_check.assert_has_calls([mock.call('host1'), mock.call('host2')]) mock_call.assert_has_calls([mock.call('host1'), mock.call('host2')]) @mock.patch.object(objects.Migration, 'save') @mock.patch.object(live_migrate.LiveMigrationTask, '_remove_host_allocations') @mock.patch.object(live_migrate.LiveMigrationTask, '_check_compatible_with_source_hypervisor', side_effect=exception.DestinationHypervisorTooOld()) @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations', return_value=[[fake_selection1]]) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_retry_exceeds_max(self, mock_setup, mock_select, mock_check, mock_remove, mock_save): self.flags(migrate_max_retries=0) self.assertRaises(exception.MaxRetriesExceeded, self.task._find_destination) self.assertEqual('failed', self.task.migration.status) mock_save.assert_called_once_with() # Should have removed allocations for the first host. mock_remove.assert_called_once_with('host1', 'node1') mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_called_once_with(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) mock_check.assert_called_once_with('host1') @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations', side_effect=exception.NoValidHost(reason="")) @mock.patch.object(scheduler_utils, 'setup_instance_group') def test_find_destination_when_runs_out_of_hosts(self, mock_setup, mock_select): self.assertRaises(exception.NoValidHost, self.task._find_destination) mock_setup.assert_called_once_with(self.context, self.fake_spec) mock_select.assert_called_once_with(self.context, self.fake_spec, [self.instance.uuid], return_objects=True, return_alternates=False) @mock.patch("nova.utils.get_image_from_system_metadata") @mock.patch("nova.scheduler.utils.build_request_spec") @mock.patch("nova.scheduler.utils.setup_instance_group") @mock.patch("nova.objects.RequestSpec.from_primitives") def test_find_destination_with_remoteError( self, m_from_primitives, m_setup_instance_group, m_build_request_spec, m_get_image_from_system_metadata): m_get_image_from_system_metadata.return_value = {'properties': {}} m_build_request_spec.return_value = {} fake_spec = objects.RequestSpec() m_from_primitives.return_value = fake_spec with mock.patch.object(self.task.scheduler_client, 'select_destinations') as m_select_destinations: error = messaging.RemoteError() m_select_destinations.side_effect = error self.assertRaises(exception.MigrationSchedulerRPCError, self.task._find_destination) def test_call_livem_checks_on_host(self): with mock.patch.object(self.task.compute_rpcapi, 'check_can_live_migrate_destination', side_effect=messaging.MessagingTimeout): self.assertRaises(exception.MigrationPreCheckError, self.task._call_livem_checks_on_host, {}) @mock.patch('nova.conductor.tasks.live_migrate.' 'supports_extended_port_binding', return_value=True) def test_call_livem_checks_on_host_bind_ports(self, mock_supports_ext): data = objects.LibvirtLiveMigrateData() bindings = { uuids.port1: { 'host': 'dest-host' }, uuids.port2: { 'host': 'dest-host' } } @mock.patch.object(self.task.compute_rpcapi, 'check_can_live_migrate_destination', return_value=data) @mock.patch.object(self.task.network_api, 'supports_port_binding_extension', return_value=True) @mock.patch.object(self.task.network_api, 'bind_ports_to_host', return_value=bindings) def _test(mock_bind_ports_to_host, mock_supports_port_binding, mock_check_can_live_migrate_dest): nwinfo = network_model.NetworkInfo([ network_model.VIF(uuids.port1), network_model.VIF(uuids.port2) ]) self.instance.info_cache = objects.InstanceInfoCache( network_info=nwinfo) self.task._call_livem_checks_on_host('dest-host') # Assert the migrate_data set on the task based on the port # bindings created. self.assertIn('vifs', data) self.assertEqual(2, len(data.vifs)) for vif in data.vifs: self.assertIn('source_vif', vif) self.assertEqual('dest-host', vif.host) self.assertEqual(vif.port_id, vif.source_vif['id']) _test() @mock.patch.object( objects.InstanceMapping, 'get_by_instance_uuid', side_effect=exception.InstanceMappingNotFound(uuid=uuids.instance)) def test_get_source_cell_mapping_not_found(self, mock_get): """Negative test where InstanceMappingNotFound is raised and converted to MigrationPreCheckError. """ self.assertRaises(exception.MigrationPreCheckError, self.task._get_source_cell_mapping) mock_get.assert_called_once_with(self.task.context, self.task.instance.uuid) @mock.patch.object( objects.HostMapping, 'get_by_host', side_effect=exception.HostMappingNotFound(name='destination')) def test_get_destination_cell_mapping_not_found(self, mock_get): """Negative test where HostMappingNotFound is raised and converted to MigrationPreCheckError. """ self.assertRaises(exception.MigrationPreCheckError, self.task._get_destination_cell_mapping) mock_get.assert_called_once_with(self.task.context, self.task.destination) @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename', side_effect=exception.ComputeHostNotFound(host='host')) def test_remove_host_allocations_compute_host_not_found(self, get_cn): """Tests that failing to find a ComputeNode will not blow up the _remove_host_allocations method. """ with mock.patch.object( self.task.scheduler_client.reportclient, 'remove_provider_from_instance_allocation') as remove_provider: self.task._remove_host_allocations('host', 'node') remove_provider.assert_not_called()
def fake_get_host_mapping(ctxt, host): if host == 'host2': return else: raise exception.HostMappingNotFound(name=host)
def _hm_get(context, host): if host in ['a', 'b', 'c']: return objects.HostMapping() raise exception.HostMappingNotFound(name=host)
class TestClientRouter(test.NoDBTestCase): @mock.patch('nova.objects.InstanceMapping.get_by_instance_uuid') @mock.patch('nova.rpc.create_transport') @mock.patch('oslo_messaging.RPCClient') def test_by_instance(self, mock_rpcclient, mock_create, mock_get): default_client = mock.Mock() cell_client = mock.Mock() mock_rpcclient.return_value = cell_client ctxt = mock.Mock() cm = objects.CellMapping(uuid=uuids.cell_mapping, transport_url='fake:///') mock_get.return_value = objects.InstanceMapping(cell_mapping=cm) instance = objects.Instance(uuid=uuids.instance) router = rpc.ClientRouter(default_client) client = router.by_instance(ctxt, instance) mock_get.assert_called_once_with(ctxt, instance.uuid) # verify a client was created by ClientRouter mock_rpcclient.assert_called_once_with( mock_create.return_value, default_client.target, version_cap=default_client.version_cap, serializer=default_client.serializer) # verify cell client was returned self.assertEqual(cell_client, client) # reset and check that cached client is returned the second time mock_rpcclient.reset_mock() mock_create.reset_mock() mock_get.reset_mock() client = router.by_instance(ctxt, instance) mock_get.assert_called_once_with(ctxt, instance.uuid) mock_rpcclient.assert_not_called() mock_create.assert_not_called() self.assertEqual(cell_client, client) @mock.patch('nova.objects.HostMapping.get_by_host') @mock.patch('nova.rpc.create_transport') @mock.patch('oslo_messaging.RPCClient') def test_by_host(self, mock_rpcclient, mock_create, mock_get): default_client = mock.Mock() cell_client = mock.Mock() mock_rpcclient.return_value = cell_client ctxt = mock.Mock() cm = objects.CellMapping(uuid=uuids.cell_mapping, transport_url='fake:///') mock_get.return_value = objects.HostMapping(cell_mapping=cm) host = 'fake-host' router = rpc.ClientRouter(default_client) client = router.by_host(ctxt, host) mock_get.assert_called_once_with(ctxt, host) # verify a client was created by ClientRouter mock_rpcclient.assert_called_once_with( mock_create.return_value, default_client.target, version_cap=default_client.version_cap, serializer=default_client.serializer) # verify cell client was returned self.assertEqual(cell_client, client) # reset and check that cached client is returned the second time mock_rpcclient.reset_mock() mock_create.reset_mock() mock_get.reset_mock() client = router.by_host(ctxt, host) mock_get.assert_called_once_with(ctxt, host) mock_rpcclient.assert_not_called() mock_create.assert_not_called() self.assertEqual(cell_client, client) @mock.patch( 'nova.objects.InstanceMapping.get_by_instance_uuid', side_effect=exception.InstanceMappingNotFound(uuid=uuids.instance)) @mock.patch('nova.rpc.create_transport') @mock.patch('oslo_messaging.RPCClient') def test_by_instance_not_found(self, mock_rpcclient, mock_create, mock_get): default_client = mock.Mock() cell_client = mock.Mock() mock_rpcclient.return_value = cell_client ctxt = mock.Mock() instance = objects.Instance(uuid=uuids.instance) router = rpc.ClientRouter(default_client) client = router.by_instance(ctxt, instance) mock_get.assert_called_once_with(ctxt, instance.uuid) mock_rpcclient.assert_not_called() mock_create.assert_not_called() # verify default client was returned self.assertEqual(default_client, client) @mock.patch('nova.objects.HostMapping.get_by_host', side_effect=exception.HostMappingNotFound(name='fake-host')) @mock.patch('nova.rpc.create_transport') @mock.patch('oslo_messaging.RPCClient') def test_by_host_not_found(self, mock_rpcclient, mock_create, mock_get): default_client = mock.Mock() cell_client = mock.Mock() mock_rpcclient.return_value = cell_client ctxt = mock.Mock() host = 'fake-host' router = rpc.ClientRouter(default_client) client = router.by_host(ctxt, host) mock_get.assert_called_once_with(ctxt, host) mock_rpcclient.assert_not_called() mock_create.assert_not_called() # verify default client was returned self.assertEqual(default_client, client) @mock.patch('nova.objects.InstanceMapping.get_by_instance_uuid') @mock.patch('nova.rpc.create_transport') @mock.patch('oslo_messaging.RPCClient') def test_remove_stale_clients(self, mock_rpcclient, mock_create, mock_get): t0 = datetime.datetime(2016, 8, 9, 0, 0, 0) time_fixture = self.useFixture(utils_fixture.TimeFixture(t0)) default_client = mock.Mock() ctxt = mock.Mock() cm1 = objects.CellMapping(uuid=uuids.cell_mapping1, transport_url='fake:///') cm2 = objects.CellMapping(uuid=uuids.cell_mapping2, transport_url='fake:///') cm3 = objects.CellMapping(uuid=uuids.cell_mapping3, transport_url='fake:///') mock_get.side_effect = [ objects.InstanceMapping(cell_mapping=cm1), objects.InstanceMapping(cell_mapping=cm2), objects.InstanceMapping(cell_mapping=cm3), objects.InstanceMapping(cell_mapping=cm3) ] instance1 = objects.Instance(uuid=uuids.instance1) instance2 = objects.Instance(uuid=uuids.instance2) instance3 = objects.Instance(uuid=uuids.instance3) router = rpc.ClientRouter(default_client) cell1_client = router.by_instance(ctxt, instance1) cell2_client = router.by_instance(ctxt, instance2) # default client, cell1 client, cell2 client self.assertEqual(3, len(router.clients)) expected = { 'default': default_client, uuids.cell_mapping1: cell1_client, uuids.cell_mapping2: cell2_client } for client_id, client in expected.items(): self.assertEqual(client, router.clients[client_id].client) # expire cell1 client and cell2 client time_fixture.advance_time_seconds(80) # add cell3 client cell3_client = router.by_instance(ctxt, instance3) router._remove_stale_clients(ctxt) # default client, cell3 client expected = { 'default': default_client, uuids.cell_mapping3: cell3_client } self.assertEqual(2, len(router.clients)) for client_id, client in expected.items(): self.assertEqual(client, router.clients[client_id].client) # expire cell3 client time_fixture.advance_time_seconds(80) # access cell3 client to refresh it cell3_client = router.by_instance(ctxt, instance3) router._remove_stale_clients(ctxt) # default client and cell3 client should be there self.assertEqual(2, len(router.clients)) for client_id, client in expected.items(): self.assertEqual(client, router.clients[client_id].client)