API: Remove evacuate/live-migrate 'force' parameter

Add a new microversion that removes support for the aforementioned
argument, which cannot be adequately guaranteed in the new placement
world.

Change-Id: I2a395aa6eccad75a97fa49e993b0300bdcfc7258
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
Implements: blueprint remove-force-flag-from-live-migrate-and-evacuate
APIImpact
This commit is contained in:
Stephen Finucane 2019-02-03 10:41:03 +00:00 committed by Matt Riedemann
parent c1fb445b8d
commit 36a91936a8
22 changed files with 297 additions and 30 deletions

View File

@ -3143,6 +3143,7 @@ force_evacuate:
required: false required: false
type: boolean type: boolean
min_version: 2.29 min_version: 2.29
max_version: 2.67
force_live_migrate: force_live_migrate:
description: | description: |
Force a live-migration by not verifying the provided destination host by Force a live-migration by not verifying the provided destination host by
@ -3156,6 +3157,7 @@ force_live_migrate:
required: false required: false
type: boolean type: boolean
min_version: 2.30 min_version: 2.30
max_version: 2.67
force_migration_complete: force_migration_complete:
description: | description: |
The action to force an in-progress live migration to complete. The action to force an in-progress live migration to complete.

View File

@ -11,6 +11,10 @@ Evacuates a server from a failed host to a new host.
- In the request body, if ``onSharedStorage`` is set, then do not set ``adminPass``. - In the request body, if ``onSharedStorage`` is set, then do not set ``adminPass``.
- The target host should not be the same as the instance host. - The target host should not be the same as the instance host.
Starting from API version 2.68, the ``force`` parameter is no longer accepted
as this could not be meaningfully supported by servers with complex resource
allocations.
Normal response codes: 200 Normal response codes: 200
Error response codes: badRequest(400), unauthorized(401), forbidden(403), Error response codes: badRequest(400), unauthorized(401), forbidden(403),

View File

@ -137,6 +137,10 @@ Nova responds immediately, and no pre-live-migration checks are returned.
The instance will not immediately change state to ``ERROR``, if a failure of The instance will not immediately change state to ``ERROR``, if a failure of
the live-migration checks occurs. the live-migration checks occurs.
Starting from API version 2.68, the ``force`` parameter is no longer accepted
as this could not be meaningfully supported by servers with complex resource
allocations.
Normal response codes: 202 Normal response codes: 202
Error response codes: badRequest(400), unauthorized(401), forbidden(403) Error response codes: badRequest(400), unauthorized(401), forbidden(403)

View File

@ -0,0 +1,6 @@
{
"os-migrateLive": {
"host": "01c0cadef72d47e28a672a76060d492c",
"block_migration": "auto"
}
}

View File

@ -19,7 +19,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.67", "version": "2.68",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -22,7 +22,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.67", "version": "2.68",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -164,6 +164,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
os-migrations API. os-migrations API.
* 2.67 - Adds the optional ``volume_type`` field to the * 2.67 - Adds the optional ``volume_type`` field to the
``block_device_mapping_v2`` parameter when creating a server. ``block_device_mapping_v2`` parameter when creating a server.
* 2.68 - Remove support for forced live migration and evacuate server
actions.
""" """
# The minimum and maximum versions of the API supported # The minimum and maximum versions of the API supported
@ -172,7 +174,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
# Note(cyeoh): This only applies for the v2.1 API once microversions # Note(cyeoh): This only applies for the v2.1 API once microversions
# support is fully merged. It does not affect the V2 API. # support is fully merged. It does not affect the V2 API.
_MIN_API_VERSION = "2.1" _MIN_API_VERSION = "2.1"
_MAX_API_VERSION = "2.67" _MAX_API_VERSION = "2.68"
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION
# Almost all proxy APIs which are related to network, images and baremetal # Almost all proxy APIs which are related to network, images and baremetal

View File

@ -75,7 +75,8 @@ class EvacuateController(wsgi.Controller):
@wsgi.action('evacuate') @wsgi.action('evacuate')
@validation.schema(evacuate.evacuate, "2.0", "2.13") @validation.schema(evacuate.evacuate, "2.0", "2.13")
@validation.schema(evacuate.evacuate_v214, "2.14", "2.28") @validation.schema(evacuate.evacuate_v214, "2.14", "2.28")
@validation.schema(evacuate.evacuate_v2_29, "2.29") @validation.schema(evacuate.evacuate_v2_29, "2.29", "2.67")
@validation.schema(evacuate.evacuate_v2_68, "2.68")
def _evacuate(self, req, id, body): def _evacuate(self, req, id, body):
"""Permit admins to evacuate a server from a failed host """Permit admins to evacuate a server from a failed host
to a new one. to a new one.

View File

@ -84,7 +84,8 @@ class MigrateServerController(wsgi.Controller):
@wsgi.action('os-migrateLive') @wsgi.action('os-migrateLive')
@validation.schema(migrate_server.migrate_live, "2.0", "2.24") @validation.schema(migrate_server.migrate_live, "2.0", "2.24")
@validation.schema(migrate_server.migrate_live_v2_25, "2.25", "2.29") @validation.schema(migrate_server.migrate_live_v2_25, "2.25", "2.29")
@validation.schema(migrate_server.migrate_live_v2_30, "2.30") @validation.schema(migrate_server.migrate_live_v2_30, "2.30", "2.67")
@validation.schema(migrate_server.migrate_live_v2_68, "2.68")
def _migrate_live(self, req, id, body): def _migrate_live(self, req, id, body):
"""Permit admins to (live) migrate a server to a new host.""" """Permit admins to (live) migrate a server to a new host."""
context = req.environ["nova.context"] context = req.environ["nova.context"]

View File

@ -860,3 +860,8 @@ following APIs to filter by changes before or equal to the resource
Adds the ``volume_type`` parameter to ``block_device_mapping_v2``, which can Adds the ``volume_type`` parameter to ``block_device_mapping_v2``, which can
be used to specify cinder ``volume_type`` when creating a server. be used to specify cinder ``volume_type`` when creating a server.
2.68
----
Remove support for forced live migration and evacuate server actions.

View File

@ -42,3 +42,7 @@ del evacuate_v214['properties']['evacuate']['required']
evacuate_v2_29 = copy.deepcopy(evacuate_v214) evacuate_v2_29 = copy.deepcopy(evacuate_v214)
evacuate_v2_29['properties']['evacuate']['properties'][ evacuate_v2_29['properties']['evacuate']['properties'][
'force'] = parameter_types.boolean 'force'] = parameter_types.boolean
# v2.68 removes the 'force' parameter added in v2.29, meaning it is identical
# to v2.14
evacuate_v2_68 = copy.deepcopy(evacuate_v214)

View File

@ -68,3 +68,7 @@ migrate_live_v2_25['properties']['os-migrateLive']['required'] = (
migrate_live_v2_30 = copy.deepcopy(migrate_live_v2_25) migrate_live_v2_30 = copy.deepcopy(migrate_live_v2_25)
migrate_live_v2_30['properties']['os-migrateLive']['properties'][ migrate_live_v2_30['properties']['os-migrateLive']['properties'][
'force'] = parameter_types.boolean 'force'] = parameter_types.boolean
# v2.68 removes the 'force' parameter added in v2.30, meaning it is identical
# to v2.25
migrate_live_v2_68 = copy.deepcopy(migrate_live_v2_25)

View File

@ -4659,6 +4659,8 @@ class API(base.Base):
source=fields_obj.NotificationSource.API) source=fields_obj.NotificationSource.API)
# NOTE(sbauza): Force is a boolean by the new related API version # NOTE(sbauza): Force is a boolean by the new related API version
# TODO(stephenfin): Any reason we can't use 'not force' here to handle
# the pre-v2.29 API microversion, which wouldn't set force
if force is False and host: if force is False and host:
nodes = objects.ComputeNodeList.get_all_by_host(context, host) nodes = objects.ComputeNodeList.get_all_by_host(context, host)
# NOTE(sbauza): Unset the host to make sure we call the scheduler # NOTE(sbauza): Unset the host to make sure we call the scheduler

View File

@ -0,0 +1,5 @@
{
"evacuate": {
"adminPass": "%(adminPass)s"
}
}

View File

@ -0,0 +1,6 @@
{
"evacuate": {
"host": "%(host)s",
"adminPass": "%(adminPass)s"
}
}

View File

@ -0,0 +1,6 @@
{
"os-migrateLive": {
"host": "%(hostname)s",
"block_migration": "auto"
}
}

View File

@ -180,3 +180,33 @@ class EvacuateJsonTestV229(EvacuateJsonTestV214):
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY, orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
on_shared_storage=None, preserve_ephemeral=mock.ANY, on_shared_storage=None, preserve_ephemeral=mock.ANY,
host='testHost', request_spec=mock.ANY) host='testHost', request_spec=mock.ANY)
class EvacuateJsonTestV268(EvacuateJsonTestV229):
microversion = '2.68'
scenarios = [('v2_68', {'api_major_version': 'v2.1'})]
@mock.patch('nova.conductor.manager.ComputeTaskManager.rebuild_instance')
@mock.patch('nova.objects.ComputeNodeList.get_all_by_host')
def test_server_evacuate(self, compute_node_get_all_by_host, rebuild_mock):
# Note (wingwj): The host can't be the same one
req_subs = {
'host': 'testHost',
"adminPass": "MySecretPass",
}
fake_computes = objects.ComputeNodeList(
objects=[objects.ComputeNode(host='testHost',
hypervisor_hostname='host')])
compute_node_get_all_by_host.return_value = fake_computes
self._test_evacuate(req_subs, 'server-evacuate-req',
server_resp=None, expected_resp_code=200)
rebuild_mock.assert_called_once_with(mock.ANY, instance=mock.ANY,
orig_image_ref=mock.ANY, image_ref=mock.ANY,
injected_files=mock.ANY, new_pass="MySecretPass",
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
on_shared_storage=None, preserve_ephemeral=mock.ANY,
host=None, request_spec=mock.ANY)
def test_server_evacuate_with_force(self):
# doesn't apply to v2.68+, which removed the ability to force migrate
pass

View File

@ -21,6 +21,21 @@ from nova import objects
from nova.tests.functional.api_sample_tests import test_servers from nova.tests.functional.api_sample_tests import test_servers
def fake_get_compute(context, host):
# TODO(stephenfin): It's gross that we even need this in a functional test
# where we can control the running compute services. Stop doing it.
service = dict(host=host,
binary='nova-compute',
topic='compute',
report_count=1,
updated_at='foo',
hypervisor_type='bar',
hypervisor_version=(
versionutils.convert_version_to_int('1.0')),
disabled=False)
return {'compute_node': [service]}
class MigrateServerSamplesJsonTest(test_servers.ServersSampleBase): class MigrateServerSamplesJsonTest(test_servers.ServersSampleBase):
sample_dir = "os-migrate-server" sample_dir = "os-migrate-server"
USE_NEUTRON = True USE_NEUTRON = True
@ -47,27 +62,16 @@ class MigrateServerSamplesJsonTest(test_servers.ServersSampleBase):
def fake_live_migrate(_self, context, instance, scheduler_hint, def fake_live_migrate(_self, context, instance, scheduler_hint,
block_migration, disk_over_commit, request_spec): block_migration, disk_over_commit, request_spec):
self.assertEqual(self.uuid, instance["uuid"]) self.assertEqual(self.uuid, instance.uuid)
host = scheduler_hint["host"] host = scheduler_hint["host"]
self.assertEqual(self.host_attended, host) self.assertEqual(self.host_attended, host)
self.stub_out( self.stub_out(
'nova.conductor.manager.ComputeTaskManager._live_migrate', 'nova.conductor.manager.ComputeTaskManager._live_migrate',
fake_live_migrate) fake_live_migrate)
self.stub_out(
def fake_get_compute(context, host): 'nova.db.api.service_get_by_compute_host',
service = dict(host=host, fake_get_compute)
binary='nova-compute',
topic='compute',
report_count=1,
updated_at='foo',
hypervisor_type='bar',
hypervisor_version=(
versionutils.convert_version_to_int('1.0')),
disabled=False)
return {'compute_node': [service]}
self.stub_out("nova.db.api.service_get_by_compute_host",
fake_get_compute)
response = self._do_post('servers/%s/action' % self.uuid, response = self._do_post('servers/%s/action' % self.uuid,
'live-migrate-server', 'live-migrate-server',
@ -183,3 +187,38 @@ class MigrateServerSamplesJsonTestV256(test_servers.ServersSampleBase):
response = self._do_post('servers/%s/action' % self.uuid, response = self._do_post('servers/%s/action' % self.uuid,
'migrate-server-null', {}) 'migrate-server-null', {})
self.assertEqual(202, response.status_code) self.assertEqual(202, response.status_code)
class MigrateServerSamplesJsonTestV268(test_servers.ServersSampleBase):
sample_dir = "os-migrate-server"
microversion = '2.68'
scenarios = [('v2_68', {'api_major_version': 'v2.1'})]
USE_NEUTRON = True
def setUp(self):
"""setUp Method for MigrateServer api samples extension
This method creates the server that will be used in each tests
"""
super(MigrateServerSamplesJsonTestV268, self).setUp()
self.uuid = self._post_server()
def test_post_live_migrate_server(self):
# Get api samples to server live migrate request.
req_subs = {'hostname': self.compute.host}
def fake_live_migrate(_self, context, instance, scheduler_hint,
block_migration, disk_over_commit, request_spec):
self.assertEqual(self.uuid, instance.uuid)
self.stub_out(
'nova.conductor.manager.ComputeTaskManager._live_migrate',
fake_live_migrate)
self.stub_out(
'nova.db.api.service_get_by_compute_host',
fake_get_compute)
response = self._do_post('servers/%s/action' % self.uuid,
'live-migrate-server',
req_subs)
self.assertEqual(202, response.status_code)

View File

@ -2450,6 +2450,9 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
source_hostname = self.compute1.host source_hostname = self.compute1.host
dest_hostname = self.compute2.host dest_hostname = self.compute2.host
# the ability to force evacuate a server is removed entirely in 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor1, source_hostname) self.flavor1, source_hostname)
@ -2513,6 +2516,29 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
self._delete_and_check_allocations(server) self._delete_and_check_allocations(server)
def test_evacuate_forced_host_v268(self):
"""Evacuating a server with a forced host was removed in API
microversion 2.68. This test ensures that the request is rejected.
"""
source_hostname = self.compute1.host
dest_hostname = self.compute2.host
server = self._boot_and_check_allocations(
self.flavor1, source_hostname)
# evacuate the server and force the destination host which bypasses
# the scheduler
post = {
'evacuate': {
'host': dest_hostname,
'force': True
}
}
ex = self.assertRaises(client.OpenStackApiException,
self.api.post_server_action,
server['id'], post)
self.assertIn("'force' was unexpected", six.text_type(ex))
# NOTE(gibi): there is a similar test in SchedulerOnlyChecksTargetTest but # NOTE(gibi): there is a similar test in SchedulerOnlyChecksTargetTest but
# we want this test here as well because ServerMovingTest is a parent class # we want this test here as well because ServerMovingTest is a parent class
# of multiple test classes that run this test case with different compute # of multiple test classes that run this test case with different compute
@ -2542,7 +2568,6 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
post = { post = {
'evacuate': { 'evacuate': {
'host': dest_hostname, 'host': dest_hostname,
'force': False
} }
} }
self.api.post_server_action(server['id'], post) self.api.post_server_action(server['id'], post)
@ -2863,8 +2888,15 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
source_rp_uuid = self._get_provider_uuid_by_host(source_hostname) source_rp_uuid = self._get_provider_uuid_by_host(source_hostname)
dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname) dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname)
# the ability to force live migrate a server is removed entirely in
# 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor1, source_hostname) self.flavor1, source_hostname)
# live migrate the server and force the destination host which bypasses
# the scheduler
post = { post = {
'os-migrateLive': { 'os-migrateLive': {
'host': dest_hostname, 'host': dest_hostname,
@ -2894,6 +2926,31 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
self._delete_and_check_allocations(server) self._delete_and_check_allocations(server)
def test_live_migrate_forced_v268(self):
"""Live migrating a server with a forced host was removed in API
microversion 2.68. This test ensures that the request is rejected.
"""
source_hostname = self.compute1.host
dest_hostname = self.compute2.host
server = self._boot_and_check_allocations(
self.flavor1, source_hostname)
# live migrate the server and force the destination host which bypasses
# the scheduler
post = {
'os-migrateLive': {
'host': dest_hostname,
'block_migration': True,
'force': True,
}
}
ex = self.assertRaises(client.OpenStackApiException,
self.api.post_server_action,
server['id'], post)
self.assertIn("'force' was unexpected", six.text_type(ex))
def test_live_migrate(self): def test_live_migrate(self):
source_hostname = self.compute1.host source_hostname = self.compute1.host
dest_hostname = self.compute2.host dest_hostname = self.compute2.host
@ -3011,6 +3068,10 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
source_rp_uuid = self._get_provider_uuid_by_host(source_hostname) source_rp_uuid = self._get_provider_uuid_by_host(source_hostname)
dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname) dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname)
# the ability to force live migrate a server is removed entirely in
# 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor1, source_hostname) self.flavor1, source_hostname)
@ -4769,6 +4830,10 @@ class ConsumerGenerationConflictTest(
source_hostname = self.compute1.host source_hostname = self.compute1.host
dest_hostname = self.compute2.host dest_hostname = self.compute2.host
# the ability to force live migrate a server is removed entirely in
# 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor, source_hostname) self.flavor, source_hostname)
@ -4810,6 +4875,10 @@ class ConsumerGenerationConflictTest(
source_rp_uuid = self._get_provider_uuid_by_host(source_hostname) source_rp_uuid = self._get_provider_uuid_by_host(source_hostname)
dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname) dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname)
# the ability to force live migrate a server is removed entirely in
# 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor, source_hostname) self.flavor, source_hostname)
@ -4894,6 +4963,9 @@ class ConsumerGenerationConflictTest(
source_hostname = self.compute1.host source_hostname = self.compute1.host
dest_hostname = self.compute2.host dest_hostname = self.compute2.host
# the ability to force evacuate a server is removed entirely in 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor, source_hostname) self.flavor, source_hostname)
@ -5072,6 +5144,10 @@ class ServerMovingTestsWithNestedResourceRequests(
source_rp_uuid = self._get_provider_uuid_by_host(source_hostname) source_rp_uuid = self._get_provider_uuid_by_host(source_hostname)
dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname) dest_rp_uuid = self._get_provider_uuid_by_host(dest_hostname)
# the ability to force live migrate a server is removed entirely in
# 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor1, source_hostname) self.flavor1, source_hostname)
post = { post = {
@ -5116,6 +5192,9 @@ class ServerMovingTestsWithNestedResourceRequests(
source_hostname = self.compute1.host source_hostname = self.compute1.host
dest_hostname = self.compute2.host dest_hostname = self.compute2.host
# the ability to force evacuate a server is removed entirely in 2.68
self.api.microversion = '2.67'
server = self._boot_and_check_allocations( server = self._boot_and_check_allocations(
self.flavor1, source_hostname) self.flavor1, source_hostname)
@ -5213,6 +5292,10 @@ class ServerMovingTestsFromFlatToNested(
# CUSTOM_MAGIC inventory to the root compute RP # CUSTOM_MAGIC inventory to the root compute RP
orig_update_provider_tree = fake.MediumFakeDriver.update_provider_tree orig_update_provider_tree = fake.MediumFakeDriver.update_provider_tree
# the ability to force live migrate a server is removed entirely in
# 2.68
self.api.microversion = '2.67'
def stub_update_provider_tree(self, provider_tree, nodename, def stub_update_provider_tree(self, provider_tree, nodename,
allocations=None): allocations=None):
# do the regular inventory update # do the regular inventory update
@ -5293,6 +5376,9 @@ class ServerMovingTestsFromFlatToNested(
# CUSTOM_MAGIC inventory to the root compute RP # CUSTOM_MAGIC inventory to the root compute RP
orig_update_provider_tree = fake.MediumFakeDriver.update_provider_tree orig_update_provider_tree = fake.MediumFakeDriver.update_provider_tree
# the ability to force evacuate a server is removed entirely in 2.68
self.api.microversion = '2.67'
def stub_update_provider_tree(self, provider_tree, nodename, def stub_update_provider_tree(self, provider_tree, nodename,
allocations=None): allocations=None):
# do the regular inventory update # do the regular inventory update

View File

@ -95,9 +95,8 @@ class EvacuateTestV21(test.NoDBTestCase):
controller=None): controller=None):
controller = controller or self.controller controller = controller or self.controller
body = {'evacuate': body} body = {'evacuate': body}
self.assertRaises(exception, return self.assertRaises(exception, controller._evacuate,
controller._evacuate, self.admin_req, uuid or self.UUID, body=body)
self.admin_req, uuid or self.UUID, body=body)
def test_evacuate_with_valid_instance(self): def test_evacuate_with_valid_instance(self):
admin_pass = 'MyNewPass' admin_pass = 'MyNewPass'
@ -367,9 +366,8 @@ class EvacuateTestV214(EvacuateTestV21):
controller = controller or self.controller controller = controller or self.controller
body.pop('onSharedStorage', None) body.pop('onSharedStorage', None)
body = {'evacuate': body} body = {'evacuate': body}
self.assertRaises(exception, return self.assertRaises(exception, controller._evacuate,
controller._evacuate, self.admin_req, uuid or self.UUID, body=body)
self.admin_req, uuid or self.UUID, body=body)
@mock.patch.object(compute_api.API, 'evacuate') @mock.patch.object(compute_api.API, 'evacuate')
def test_evacuate_instance(self, mock_evacuate): def test_evacuate_instance(self, mock_evacuate):
@ -479,3 +477,29 @@ class EvacuateTestV229(EvacuateTestV214):
def test_forced_evacuate_with_no_host_provided(self): def test_forced_evacuate_with_no_host_provided(self):
self._check_evacuate_failure(webob.exc.HTTPBadRequest, self._check_evacuate_failure(webob.exc.HTTPBadRequest,
{'force': 'true'}) {'force': 'true'})
class EvacuateTestV268(EvacuateTestV229):
def setUp(self):
super(EvacuateTestV268, self).setUp()
self.admin_req = fakes.HTTPRequest.blank('', use_admin_context=True,
version='2.68')
self.req = fakes.HTTPRequest.blank('', version='2.68')
def test_evacuate_with_valid_instance(self):
admin_pass = 'MyNewPass'
res = self._get_evacuate_response({'host': 'my-host',
'adminPass': admin_pass})
self.assertIsNone(res)
@mock.patch.object(compute_api.API, 'evacuate')
def test_evacuate_instance_with_forced_host(self, mock_evacuate):
ex = self._check_evacuate_failure(self.validation_error,
{'host': 'my-host',
'force': 'true'})
self.assertIn('force', six.text_type(ex))
def test_forced_evacuate_with_no_host_provided(self):
# not applicable for v2.68, which removed the 'force' parameter
pass

View File

@ -372,13 +372,13 @@ class MigrateServerTestsV230(MigrateServerTestsV225):
def _test_live_migrate(self, force=False): def _test_live_migrate(self, force=False):
if force is True: if force is True:
litteral_force = 'true' literal_force = 'true'
else: else:
litteral_force = 'false' literal_force = 'false'
method_translations = {'_migrate_live': 'live_migrate'} method_translations = {'_migrate_live': 'live_migrate'}
body_map = {'_migrate_live': {'os-migrateLive': {'host': 'hostname', body_map = {'_migrate_live': {'os-migrateLive': {'host': 'hostname',
'block_migration': 'auto', 'block_migration': 'auto',
'force': litteral_force}}} 'force': literal_force}}}
args_map = {'_migrate_live': ((None, None, 'hostname', force, args_map = {'_migrate_live': ((None, None, 'hostname', force,
self.async_), {})} self.async_), {})}
self._test_actions(['_migrate_live'], body_map=body_map, self._test_actions(['_migrate_live'], body_map=body_map,
@ -602,6 +602,34 @@ class MigrateServerTestsV256(MigrateServerTestsV234):
self._test_migrate_exception(exc_info, webob.exc.HTTPBadRequest) self._test_migrate_exception(exc_info, webob.exc.HTTPBadRequest)
class MigrateServerTestsV268(MigrateServerTestsV256):
force = False
def setUp(self):
super(MigrateServerTestsV268, self).setUp()
self.req.api_version_request = api_version_request.APIVersionRequest(
'2.68')
def test_live_migrate(self):
method_translations = {'_migrate_live': 'live_migrate'}
body_map = {'_migrate_live': {'os-migrateLive': {'host': 'hostname',
'block_migration': 'auto'}}}
args_map = {'_migrate_live': ((None, None, 'hostname', False,
self.async_), {})}
self._test_actions(['_migrate_live'], body_map=body_map,
method_translations=method_translations,
args_map=args_map)
def test_live_migrate_with_forced_host(self):
body = {'os-migrateLive': {'host': 'hostname',
'block_migration': 'auto',
'force': 'true'}}
ex = self.assertRaises(self.validation_error,
self.controller._migrate_live,
self.req, fakes.FAKE_UUID, body=body)
self.assertIn('force', six.text_type(ex))
class MigrateServerPolicyEnforcementV21(test.NoDBTestCase): class MigrateServerPolicyEnforcementV21(test.NoDBTestCase):
def setUp(self): def setUp(self):

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
It is no longer possible to force server live migrations or evacuations
to a specific destination host starting with API microversion 2.68.
This is because it is not possible to support these requests for servers
with complex resource allocations. It is still possible to request a
destination host but it will be validated by the scheduler.