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:
parent
c1fb445b8d
commit
36a91936a8
@ -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.
|
||||||
|
@ -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),
|
||||||
|
@ -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)
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"os-migrateLive": {
|
||||||
|
"host": "01c0cadef72d47e28a672a76060d492c",
|
||||||
|
"block_migration": "auto"
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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"]
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"evacuate": {
|
||||||
|
"adminPass": "%(adminPass)s"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"evacuate": {
|
||||||
|
"host": "%(host)s",
|
||||||
|
"adminPass": "%(adminPass)s"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"os-migrateLive": {
|
||||||
|
"host": "%(hostname)s",
|
||||||
|
"block_migration": "auto"
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user