API: add support to abort queued live migration in microversion 2.65
This patch bumped API microversion to 2.65 to add support for abort live migrations in ``queued`` and ``preparing`` status. Part of blueprint abort-live-migration-in-queued-status Change-Id: I4636a8d270ce01c1831bc951c4497ad472bc9aa8
This commit is contained in:
parent
e8192177e8
commit
4cae503767
@ -184,6 +184,9 @@ Abort an in-progress live migration.
|
||||
|
||||
.. note:: Microversion 2.24 or greater is required for this API.
|
||||
|
||||
.. note:: With microversion 2.65 or greater, you can abort live migrations
|
||||
also in ``queued`` and ``preparing`` status.
|
||||
|
||||
.. note:: Not all compute back ends support aborting an in-progress live
|
||||
migration.
|
||||
|
||||
@ -198,7 +201,9 @@ The server OS-EXT-STS:task_state value must be ``migrating``.
|
||||
If the server is locked, you must have administrator privileges to force the
|
||||
completion of the server migration.
|
||||
|
||||
The migration status must be ``running``.
|
||||
For microversions from 2.24 to 2.64 the migration status must be ``running``,
|
||||
for microversion 2.65 and greater, the migration status can also be ``queued``
|
||||
and ``preparing``.
|
||||
|
||||
**Asynchronous Postconditions**
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"os-migrateLive": {
|
||||
"host": null,
|
||||
"block_migration": "auto"
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.64",
|
||||
"version": "2.65",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.64",
|
||||
"version": "2.65",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -156,6 +156,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
and ``rules`` (optional) fields are added in response body of
|
||||
GET, POST /os-server-groups APIs and GET
|
||||
/os-server-groups/{group_id} API.
|
||||
* 2.65 - Add support for abort live migrations in ``queued`` and
|
||||
``preparing`` status.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -164,7 +166,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = "2.1"
|
||||
_MAX_API_VERSION = "2.64"
|
||||
_MAX_API_VERSION = "2.65"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
# Almost all proxy APIs which are related to network, images and baremetal
|
||||
|
@ -835,4 +835,10 @@ in server group APIs:
|
||||
``/os-server-groups/{server_group_id}`` API.
|
||||
* The ``policies`` and ``metadata`` fields have been removed from the response
|
||||
body of POST, GET ``/os-server-groups`` API and GET
|
||||
``/os-server-groups/{server_group_id}`` API.
|
||||
``/os-server-groups/{server_group_id}`` API.
|
||||
|
||||
2.65
|
||||
----
|
||||
|
||||
Add support for abort live migrations in ``queued`` and ``preparing`` status
|
||||
for API ``DELETE /servers/{server_id}/migrations/{migration_id}``.
|
||||
|
@ -146,9 +146,13 @@ class ServerMigrationsController(wsgi.Controller):
|
||||
context = req.environ['nova.context']
|
||||
context.can(sm_policies.POLICY_ROOT % 'delete')
|
||||
|
||||
support_abort_in_queue = api_version_request.is_supported(req, '2.65')
|
||||
|
||||
instance = common.get_instance(self.compute_api, context, server_id)
|
||||
try:
|
||||
self.compute_api.live_migrate_abort(context, instance, id)
|
||||
self.compute_api.live_migrate_abort(
|
||||
context, instance, id,
|
||||
support_abort_in_queue=support_abort_in_queue)
|
||||
except exception.InstanceInvalidState as state_error:
|
||||
common.raise_http_conflict_for_instance_invalid_state(
|
||||
state_error, "abort live migration", server_id)
|
||||
@ -156,3 +160,5 @@ class ServerMigrationsController(wsgi.Controller):
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.InvalidMigrationState as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||
except exception.AbortQueuedLiveMigrationNotYetSupported as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
|
@ -106,6 +106,7 @@ BFV_RESERVE_MIN_COMPUTE_VERSION = 17
|
||||
CINDER_V3_ATTACH_MIN_COMPUTE_VERSION = 24
|
||||
MIN_COMPUTE_MULTIATTACH = 27
|
||||
MIN_COMPUTE_TRUSTED_CERTS = 31
|
||||
MIN_COMPUTE_ABORT_QUEUED_LIVE_MIGRATION = 34
|
||||
|
||||
# FIXME(danms): Keep a global cache of the cells we find the
|
||||
# first time we look. This needs to be refreshed on a timer or
|
||||
@ -4411,12 +4412,15 @@ class API(base.Base):
|
||||
@check_instance_lock
|
||||
@check_instance_cell
|
||||
@check_instance_state(task_state=[task_states.MIGRATING])
|
||||
def live_migrate_abort(self, context, instance, migration_id):
|
||||
def live_migrate_abort(self, context, instance, migration_id,
|
||||
support_abort_in_queue=False):
|
||||
"""Abort an in-progress live migration.
|
||||
|
||||
:param context: Security context
|
||||
:param instance: The instance that is being migrated
|
||||
:param migration_id: ID of in-progress live migration
|
||||
:param support_abort_in_queue: Flag indicating whether we can support
|
||||
abort migrations in "queued" or "preparing" status.
|
||||
|
||||
"""
|
||||
migration = objects.Migration.get_by_id_and_instance(context,
|
||||
@ -4424,7 +4428,30 @@ class API(base.Base):
|
||||
LOG.debug("Going to cancel live migration %s",
|
||||
migration.id, instance=instance)
|
||||
|
||||
if migration.status != 'running':
|
||||
# If the microversion does not support abort migration in queue,
|
||||
# we are only be able to abort migrations with `running` status;
|
||||
# if it is supported, we are able to also abort migrations in
|
||||
# `queued` and `preparing` status.
|
||||
allowed_states = ['running']
|
||||
queued_states = ['queued', 'preparing']
|
||||
if support_abort_in_queue:
|
||||
# The user requested a microversion that supports aborting a queued
|
||||
# or preparing live migration. But we need to check that the
|
||||
# compute service hosting the instance is new enough to support
|
||||
# aborting a queued/preparing live migration, so we check the
|
||||
# service version here.
|
||||
# TODO(Kevin_Zheng): This service version check can be removed in
|
||||
# Stein (at the earliest) when the API only supports Rocky or
|
||||
# newer computes.
|
||||
if migration.status in queued_states:
|
||||
service = objects.Service.get_by_compute_host(
|
||||
context, instance.host)
|
||||
if service.version < MIN_COMPUTE_ABORT_QUEUED_LIVE_MIGRATION:
|
||||
raise exception.AbortQueuedLiveMigrationNotYetSupported(
|
||||
migration_id=migration_id, status=migration.status)
|
||||
allowed_states.extend(queued_states)
|
||||
|
||||
if migration.status not in allowed_states:
|
||||
raise exception.InvalidMigrationState(migration_id=migration_id,
|
||||
instance_uuid=instance.uuid,
|
||||
state=migration.status,
|
||||
|
@ -1162,6 +1162,12 @@ class InvalidMigrationState(Invalid):
|
||||
"migration is in this state.")
|
||||
|
||||
|
||||
class AbortQueuedLiveMigrationNotYetSupported(NovaException):
|
||||
msg_fmt = _("Aborting live migration %(migration_id)s with status "
|
||||
"%(status)s is not yet supported for this instance.")
|
||||
code = 409
|
||||
|
||||
|
||||
class ConsoleLogOutputException(NovaException):
|
||||
msg_fmt = _("Console log output could not be retrieved for instance "
|
||||
"%(instance_id)s. Reason: %(reason)s")
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"os-migrateLive": {
|
||||
"host": null,
|
||||
"block_migration": "auto"
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from concurrent import futures
|
||||
import datetime
|
||||
|
||||
import mock
|
||||
@ -226,3 +227,21 @@ class ServerMigrationsSamplesJsonTestV2_59(
|
||||
self.fake_migrations[1][
|
||||
'uuid'] = '22341d4b-346a-40d0-83c6-5f4f6892b650'
|
||||
super(ServerMigrationsSamplesJsonTestV2_59, self).setUp()
|
||||
|
||||
|
||||
class ServerMigrationsSampleJsonTestV2_65(ServerMigrationsSampleJsonTestV2_24):
|
||||
ADMIN_API = True
|
||||
microversion = '2.65'
|
||||
scenarios = [('v2_65', {'api_major_version': 'v2.1'})]
|
||||
|
||||
@mock.patch.object(conductor_manager.ComputeTaskManager, '_live_migrate')
|
||||
def test_live_migrate_abort_migration_queued(self, _live_migrate):
|
||||
self.migration.status = 'queued'
|
||||
self.migration.save()
|
||||
self._do_post('servers/%s/action' % self.uuid, 'live-migrate-server',
|
||||
{'hostname': self.compute.host})
|
||||
self.compute._waiting_live_migrations[self.uuid] = (
|
||||
self.migration, futures.Future())
|
||||
uri = 'servers/%s/migrations/%s' % (self.uuid, self.migration.id)
|
||||
response = self._do_delete(uri)
|
||||
self.assertEqual(202, response.status_code)
|
||||
|
@ -17,6 +17,7 @@ import copy
|
||||
import datetime
|
||||
|
||||
import mock
|
||||
import six
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute import server_migrations
|
||||
@ -273,7 +274,8 @@ class ServerMigrationsTestsV224(ServerMigrationsTestsV21):
|
||||
self.controller.delete(self.req, 'server-id', 'migration-id')
|
||||
mock_abort.assert_called_once_with(self.context,
|
||||
mock_get(),
|
||||
'migration-id')
|
||||
'migration-id',
|
||||
support_abort_in_queue=False)
|
||||
_do_test()
|
||||
|
||||
def _test_cancel_live_migration_failed(self, fake_exc, expected_exc):
|
||||
@ -318,6 +320,39 @@ class ServerMigrationsTestsV224(ServerMigrationsTestsV21):
|
||||
'migration-id')
|
||||
|
||||
|
||||
class ServerMigrationsTestsV265(ServerMigrationsTestsV224):
|
||||
wsgi_api_version = '2.65'
|
||||
|
||||
def test_cancel_live_migration_succeeded(self):
|
||||
@mock.patch.object(self.compute_api, 'live_migrate_abort')
|
||||
@mock.patch.object(self.compute_api, 'get')
|
||||
def _do_test(mock_get, mock_abort):
|
||||
self.controller.delete(self.req, 'server-id', 1)
|
||||
mock_abort.assert_called_once_with(self.context,
|
||||
mock_get.return_value, 1,
|
||||
support_abort_in_queue=True)
|
||||
_do_test()
|
||||
|
||||
def test_cancel_live_migration_in_queue_not_yet_available(self):
|
||||
exc = exception.AbortQueuedLiveMigrationNotYetSupported(
|
||||
migration_id=1, status='queued')
|
||||
|
||||
@mock.patch.object(self.compute_api, 'live_migrate_abort',
|
||||
side_effect=exc)
|
||||
@mock.patch.object(self.compute_api, 'get')
|
||||
def _do_test(mock_get, mock_abort):
|
||||
error = self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.controller.delete,
|
||||
self.req, 'server-id', 1)
|
||||
self.assertIn("Aborting live migration 1 with status queued is "
|
||||
"not yet supported for this instance.",
|
||||
six.text_type(error))
|
||||
mock_abort.assert_called_once_with(self.context,
|
||||
mock_get.return_value, 1,
|
||||
support_abort_in_queue=True)
|
||||
_do_test()
|
||||
|
||||
|
||||
class ServerMigrationsPolicyEnforcementV21(test.NoDBTestCase):
|
||||
wsgi_api_version = '2.22'
|
||||
|
||||
|
@ -5364,6 +5364,65 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||
instance_actions.LIVE_MIGRATION_CANCEL)
|
||||
mock_lm_abort.called_once_with(self.context, instance, migration.id)
|
||||
|
||||
@mock.patch('nova.compute.api.API._record_action_start')
|
||||
@mock.patch.object(compute_rpcapi.ComputeAPI, 'live_migration_abort')
|
||||
@mock.patch.object(objects.Migration, 'get_by_id_and_instance')
|
||||
@mock.patch.object(objects.Service, 'get_by_compute_host')
|
||||
def test_live_migrate_abort_in_queue_succeeded(self,
|
||||
mock_get_service,
|
||||
mock_get_migration,
|
||||
mock_lm_abort,
|
||||
mock_rec_action):
|
||||
service_obj = objects.Service()
|
||||
service_obj.version = (
|
||||
compute_api.MIN_COMPUTE_ABORT_QUEUED_LIVE_MIGRATION)
|
||||
mock_get_service.return_value = service_obj
|
||||
instance = self._create_instance_obj()
|
||||
instance.task_state = task_states.MIGRATING
|
||||
for migration_status in ('queued', 'preparing'):
|
||||
migration = self._get_migration(
|
||||
21, migration_status, 'live-migration')
|
||||
mock_get_migration.return_value = migration
|
||||
self.compute_api.live_migrate_abort(self.context,
|
||||
instance,
|
||||
migration.id,
|
||||
support_abort_in_queue=True)
|
||||
mock_rec_action.assert_called_once_with(
|
||||
self.context, instance, instance_actions.LIVE_MIGRATION_CANCEL)
|
||||
mock_lm_abort.called_once_with(self.context, instance, migration)
|
||||
mock_get_migration.reset_mock()
|
||||
mock_rec_action.reset_mock()
|
||||
mock_lm_abort.reset_mock()
|
||||
|
||||
@mock.patch.object(objects.Migration, 'get_by_id_and_instance')
|
||||
def test_live_migration_abort_in_queue_old_microversion_fails(
|
||||
self, mock_get_migration):
|
||||
instance = self._create_instance_obj()
|
||||
instance.task_state = task_states.MIGRATING
|
||||
migration = self._get_migration(21, 'queued', 'live-migration')
|
||||
mock_get_migration.return_value = migration
|
||||
self.assertRaises(exception.InvalidMigrationState,
|
||||
self.compute_api.live_migrate_abort, self.context,
|
||||
instance, migration.id,
|
||||
support_abort_in_queue=False)
|
||||
|
||||
@mock.patch.object(objects.Migration, 'get_by_id_and_instance')
|
||||
@mock.patch.object(objects.Service, 'get_by_compute_host')
|
||||
def test_live_migration_abort_in_queue_old_compute_conflict(
|
||||
self, mock_get_service, mock_get_migration):
|
||||
service_obj = objects.Service()
|
||||
service_obj.version = (
|
||||
compute_api.MIN_COMPUTE_ABORT_QUEUED_LIVE_MIGRATION - 1)
|
||||
mock_get_service.return_value = service_obj
|
||||
instance = self._create_instance_obj()
|
||||
instance.task_state = task_states.MIGRATING
|
||||
migration = self._get_migration(21, 'queued', 'live-migration')
|
||||
mock_get_migration.return_value = migration
|
||||
self.assertRaises(exception.AbortQueuedLiveMigrationNotYetSupported,
|
||||
self.compute_api.live_migrate_abort, self.context,
|
||||
instance, migration.id,
|
||||
support_abort_in_queue=True)
|
||||
|
||||
@mock.patch.object(objects.Migration, 'get_by_id_and_instance')
|
||||
def test_live_migration_abort_wrong_migration_status(self,
|
||||
mock_get_migration):
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- The support to abort live migrations with ``queued`` and ``preparing``
|
||||
status using ``DELETE /servers/{server_id}/migrations/{migration_id}``
|
||||
API has been added in microversion 2.65.
|
Loading…
Reference in New Issue
Block a user