diff options
Diffstat (limited to 'nova/tests/functional/regressions/test_bug_1815153.py')
-rw-r--r-- | nova/tests/functional/regressions/test_bug_1815153.py | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/nova/tests/functional/regressions/test_bug_1815153.py b/nova/tests/functional/regressions/test_bug_1815153.py new file mode 100644 index 0000000000..15012b0971 --- /dev/null +++ b/nova/tests/functional/regressions/test_bug_1815153.py @@ -0,0 +1,174 @@ +# Copyright 2019 NTT Corporation +# +# 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. + +import six + +from nova import context +from nova import objects +from nova import test +from nova.tests import fixtures as nova_fixtures +from nova.tests.functional.api import client +from nova.tests.functional import fixtures as func_fixtures +from nova.tests.functional import integrated_helpers +from nova.tests.unit.image import fake as image_fake +from nova.tests.unit import policy_fixture +from nova.virt import fake + + +class NonPersistentFieldNotResetTest( + test.TestCase, integrated_helpers.InstanceHelperMixin): + """Test for regression bug 1815153 + + The bug is that the 'requested_destination' field in the RequestSpec + object is reset when saving the object in the 'heal_reqspec_is_bfv' + method in the case that a server created before Rocky which does not + have is_bfv field. + + Tests the following two cases here. + + * Cold migration with a target host without a force flag + * Evacuate with a target host without a force flag + + The following two cases are not tested here because + 'requested_destination' is not set when the 'heal_reqspec_is_bfv' method + is called. + + * Live migration without a destination host. + * Unshelve a server + """ + + def setUp(self): + super(NonPersistentFieldNotResetTest, self).setUp() + self.useFixture(policy_fixture.RealPolicyFixture()) + self.useFixture(nova_fixtures.NeutronFixture(self)) + self.useFixture(func_fixtures.PlacementFixture()) + + api_fixture = self.useFixture(nova_fixtures.OSAPIFixture( + api_version='v2.1')) + self.api = api_fixture.admin_api + # Use the latest microversion available to make sure something does + # not regress in new microversions; cap as necessary. + self.api.microversion = 'latest' + + image_fake.stub_out_image_service(self) + self.addCleanup(image_fake.FakeImageService_reset) + + self.start_service('conductor') + self.start_service('scheduler') + + self.compute = {} + + self.addCleanup(fake.restore_nodes) + for host in ('host1', 'host2', 'host3'): + fake.set_nodes([host]) + compute_service = self.start_service('compute', host=host) + self.compute.update({host: compute_service}) + + self.ctxt = context.get_admin_context() + + @staticmethod + def _get_target_host(host): + target_host = {'host1': 'host2', + 'host2': 'host3', + 'host3': 'host1'} + return target_host[host] + + def _create_server(self): + # Create a server, it doesn't matter on which host it builds. + server = self._build_minimal_create_server_request( + self.api, 'sample-server', + image_uuid='155d900f-4e14-4e4c-a73d-069cbf4541e6', + networks='none') + server = self.api.post_server({'server': server}) + server = self._wait_for_state_change(self.api, server, 'ACTIVE') + + return server + + def _remove_is_bfv_in_request_spec(self, server_id): + # Now let's hack the RequestSpec.is_bfv field to mimic migrating an + # old instance created before RequestSpec.is_bfv was set in the API, + reqspec = objects.RequestSpec.get_by_instance_uuid(self.ctxt, + server_id) + del reqspec.is_bfv + reqspec.save() + reqspec = objects.RequestSpec.get_by_instance_uuid(self.ctxt, + server_id) + # Make sure 'is_bfv' is not set. + self.assertNotIn('is_bfv', reqspec) + + def test_cold_migrate(self): + server = self._create_server() + original_host = server['OS-EXT-SRV-ATTR:host'] + target_host = self._get_target_host(original_host) + self._remove_is_bfv_in_request_spec(server['id']) + + # Force a target host down + source_compute_id = self.api.get_services( + host=target_host, binary='nova-compute')[0]['id'] + self.compute[target_host].stop() + self.api.put_service( + source_compute_id, {'forced_down': 'true'}) + + # Cold migrate a server with a target host. + # If requested_destination is reset, the server is moved to a host + # that is not a requested target host. + # In that case, the response code is 202. + # If requested_destination is not reset, no valid host error (400) is + # returned because the target host is down. + ex = self.assertRaises(client.OpenStackApiException, + self.api.post_server_action, + server['id'], + {'migrate': {'host': target_host}}) + self.assertEqual(400, ex.response.status_code) + self.assertIn('No valid host was found. No valid host found ' + 'for cold migrate', six.text_type(ex)) + + # Make sure 'is_bfv' is set. + reqspec = objects.RequestSpec.get_by_instance_uuid(self.ctxt, + server['id']) + self.assertIn('is_bfv', reqspec) + self.assertIs(reqspec.is_bfv, False) + + def test_evacuate(self): + server = self._create_server() + original_host = server['OS-EXT-SRV-ATTR:host'] + target_host = self._get_target_host(original_host) + self._remove_is_bfv_in_request_spec(server['id']) + + # Force source and target hosts down + for host in (original_host, target_host): + source_compute_id = self.api.get_services( + host=host, binary='nova-compute')[0]['id'] + self.compute[host].stop() + self.api.put_service( + source_compute_id, {'forced_down': 'true'}) + + # Evacuate a server with a target host. + # If requested_destination is reset, the server is moved to a host + # that is not the target host. + # Its status becomes 'ACTIVE'. + # If requested_destination is not reset, a status of the server + # becomes 'ERROR' because the target host is down. + self.api.post_server_action( + server['id'], {'evacuate': {'host': target_host}}) + expected_params = {'OS-EXT-SRV-ATTR:host': original_host, + 'status': 'ERROR'} + server = self._wait_for_server_parameter(self.api, server, + expected_params) + + # Make sure 'is_bfv' is set. + reqspec = objects.RequestSpec.get_by_instance_uuid(self.ctxt, + server['id']) + self.assertIn('is_bfv', reqspec) + self.assertIs(reqspec.is_bfv, False) |