174 lines
7.0 KiB
Python
174 lines
7.0 KiB
Python
# 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 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(nova_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)
|