rest api version bumped for async pre live migration checks
pre live-migration checks now are done in async way. This patch updates rest api version to keep this tracked. bp: async-live-migration-rest-check Change-Id: I9fed3079d0f1b7de3ad1b3ecd309c93785fd11fe
This commit is contained in:
parent
20dc4942bb
commit
452be384cd
api-ref/source
doc/api_samples/versions
nova
api/openstack
tests/unit/api/openstack/compute
releasenotes/notes
@ -155,6 +155,12 @@ Policy defaults enable only users with the administrative role to
|
|||||||
perform this operation. Cloud providers can change these permissions
|
perform this operation. Cloud providers can change these permissions
|
||||||
through the ``policy.json`` file.
|
through the ``policy.json`` file.
|
||||||
|
|
||||||
|
Starting from REST API version 2.34 pre-live-migration checks are done
|
||||||
|
asynchronously, results of these checks are available in ``instance-actions``.
|
||||||
|
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 live-migration checks occurs.
|
||||||
|
|
||||||
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)
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"status": "CURRENT",
|
"status": "CURRENT",
|
||||||
"version": "2.33",
|
"version": "2.34",
|
||||||
"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.33",
|
"version": "2.34",
|
||||||
"min_version": "2.1",
|
"min_version": "2.1",
|
||||||
"updated": "2013-07-23T11:33:21Z"
|
"updated": "2013-07-23T11:33:21Z"
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,10 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
|||||||
* 2.32 - Add tag to networks and block_device_mapping_v2 in server boot
|
* 2.32 - Add tag to networks and block_device_mapping_v2 in server boot
|
||||||
request body.
|
request body.
|
||||||
* 2.33 - Add pagination support for hypervisors.
|
* 2.33 - Add pagination support for hypervisors.
|
||||||
|
* 2.34 - Checks before live-migration are made in asynchronous way.
|
||||||
|
os-Migratelive Action does not throw badRequest in case of
|
||||||
|
pre-checks failure. Verification result is available over
|
||||||
|
instance-actions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The minimum and maximum versions of the API supported
|
# The minimum and maximum versions of the API supported
|
||||||
@ -91,7 +95,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.33"
|
_MAX_API_VERSION = "2.34"
|
||||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import excutils
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
@ -25,8 +27,10 @@ from nova.api import validation
|
|||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
from nova.i18n import _LE
|
||||||
from nova.policies import migrate_server as ms_policies
|
from nova.policies import migrate_server as ms_policies
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
ALIAS = "os-migrate-server"
|
ALIAS = "os-migrate-server"
|
||||||
|
|
||||||
|
|
||||||
@ -72,13 +76,9 @@ class MigrateServerController(wsgi.Controller):
|
|||||||
host = body["os-migrateLive"]["host"]
|
host = body["os-migrateLive"]["host"]
|
||||||
block_migration = body["os-migrateLive"]["block_migration"]
|
block_migration = body["os-migrateLive"]["block_migration"]
|
||||||
force = None
|
force = None
|
||||||
|
async = api_version_request.is_supported(req, min_version='2.34')
|
||||||
if api_version_request.is_supported(req, min_version='2.30'):
|
if api_version_request.is_supported(req, min_version='2.30'):
|
||||||
force = body["os-migrateLive"].get("force", False)
|
force = self._get_force_param_for_live_migration(body, host)
|
||||||
force = strutils.bool_from_string(force, strict=True)
|
|
||||||
if force is True and not host:
|
|
||||||
message = _("Can't force to a non-provided destination")
|
|
||||||
raise exc.HTTPBadRequest(explanation=message)
|
|
||||||
if api_version_request.is_supported(req, min_version='2.25'):
|
if api_version_request.is_supported(req, min_version='2.25'):
|
||||||
if block_migration == 'auto':
|
if block_migration == 'auto':
|
||||||
block_migration = None
|
block_migration = None
|
||||||
@ -97,7 +97,7 @@ class MigrateServerController(wsgi.Controller):
|
|||||||
try:
|
try:
|
||||||
instance = common.get_instance(self.compute_api, context, id)
|
instance = common.get_instance(self.compute_api, context, id)
|
||||||
self.compute_api.live_migrate(context, instance, block_migration,
|
self.compute_api.live_migrate(context, instance, block_migration,
|
||||||
disk_over_commit, host, force)
|
disk_over_commit, host, force, async)
|
||||||
except exception.InstanceUnknownCell as e:
|
except exception.InstanceUnknownCell as e:
|
||||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||||
except (exception.NoValidHost,
|
except (exception.NoValidHost,
|
||||||
@ -111,13 +111,27 @@ class MigrateServerController(wsgi.Controller):
|
|||||||
exception.HypervisorUnavailable,
|
exception.HypervisorUnavailable,
|
||||||
exception.MigrationPreCheckError,
|
exception.MigrationPreCheckError,
|
||||||
exception.LiveMigrationWithOldNovaNotSupported) as ex:
|
exception.LiveMigrationWithOldNovaNotSupported) as ex:
|
||||||
raise exc.HTTPBadRequest(explanation=ex.format_message())
|
if async:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error(_LE("Unexpected exception received from "
|
||||||
|
"conductor during pre-live-migration checks "
|
||||||
|
"'%(ex)s'"), {'ex': ex})
|
||||||
|
else:
|
||||||
|
raise exc.HTTPBadRequest(explanation=ex.format_message())
|
||||||
except exception.InstanceIsLocked as e:
|
except exception.InstanceIsLocked as e:
|
||||||
raise exc.HTTPConflict(explanation=e.format_message())
|
raise exc.HTTPConflict(explanation=e.format_message())
|
||||||
except exception.InstanceInvalidState as state_error:
|
except exception.InstanceInvalidState as state_error:
|
||||||
common.raise_http_conflict_for_instance_invalid_state(state_error,
|
common.raise_http_conflict_for_instance_invalid_state(state_error,
|
||||||
'os-migrateLive', id)
|
'os-migrateLive', id)
|
||||||
|
|
||||||
|
def _get_force_param_for_live_migration(self, body, host):
|
||||||
|
force = body["os-migrateLive"].get("force", False)
|
||||||
|
force = strutils.bool_from_string(force, strict=True)
|
||||||
|
if force is True and not host:
|
||||||
|
message = _("Can't force to a non-provided destination")
|
||||||
|
raise exc.HTTPBadRequest(explanation=message)
|
||||||
|
return force
|
||||||
|
|
||||||
|
|
||||||
class MigrateServer(extensions.V21APIExtensionBase):
|
class MigrateServer(extensions.V21APIExtensionBase):
|
||||||
"""Enable migrate and live-migrate server actions."""
|
"""Enable migrate and live-migrate server actions."""
|
||||||
|
@ -343,3 +343,10 @@ user documentation.
|
|||||||
API request::
|
API request::
|
||||||
|
|
||||||
GET /v2.1/{tenant_id}/os-hypervisors?marker={hypervisor_id}&limit={limit}
|
GET /v2.1/{tenant_id}/os-hypervisors?marker={hypervisor_id}&limit={limit}
|
||||||
|
|
||||||
|
2.34
|
||||||
|
----
|
||||||
|
|
||||||
|
Checks in ``os-migrateLive`` before live-migration actually starts are now
|
||||||
|
made in background. ``os-migrateLive`` is not throwing `400 Bad Request` if
|
||||||
|
pre-live-migration checks fail.
|
||||||
|
@ -33,6 +33,7 @@ class MigrateServerTestsV21(admin_only_action_common.CommonTests):
|
|||||||
_api_version = '2.1'
|
_api_version = '2.1'
|
||||||
disk_over_commit = False
|
disk_over_commit = False
|
||||||
force = None
|
force = None
|
||||||
|
async = False
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(MigrateServerTestsV21, self).setUp()
|
super(MigrateServerTestsV21, self).setUp()
|
||||||
@ -59,7 +60,8 @@ class MigrateServerTestsV21(admin_only_action_common.CommonTests):
|
|||||||
'_migrate_live': 'live_migrate'}
|
'_migrate_live': 'live_migrate'}
|
||||||
body_map = {'_migrate_live': self._get_migration_body(host='hostname')}
|
body_map = {'_migrate_live': self._get_migration_body(host='hostname')}
|
||||||
args_map = {'_migrate_live': ((False, self.disk_over_commit,
|
args_map = {'_migrate_live': ((False, self.disk_over_commit,
|
||||||
'hostname', self.force), {})}
|
'hostname', self.force, self.async),
|
||||||
|
{})}
|
||||||
self._test_actions(['_migrate', '_migrate_live'], body_map=body_map,
|
self._test_actions(['_migrate', '_migrate_live'], body_map=body_map,
|
||||||
method_translations=method_translations,
|
method_translations=method_translations,
|
||||||
args_map=args_map)
|
args_map=args_map)
|
||||||
@ -69,7 +71,7 @@ class MigrateServerTestsV21(admin_only_action_common.CommonTests):
|
|||||||
'_migrate_live': 'live_migrate'}
|
'_migrate_live': 'live_migrate'}
|
||||||
body_map = {'_migrate_live': self._get_migration_body(host=None)}
|
body_map = {'_migrate_live': self._get_migration_body(host=None)}
|
||||||
args_map = {'_migrate_live': ((False, self.disk_over_commit, None,
|
args_map = {'_migrate_live': ((False, self.disk_over_commit, None,
|
||||||
self.force),
|
self.force, self.async),
|
||||||
{})}
|
{})}
|
||||||
self._test_actions(['_migrate', '_migrate_live'], body_map=body_map,
|
self._test_actions(['_migrate', '_migrate_live'], body_map=body_map,
|
||||||
method_translations=method_translations,
|
method_translations=method_translations,
|
||||||
@ -85,7 +87,8 @@ class MigrateServerTestsV21(admin_only_action_common.CommonTests):
|
|||||||
'_migrate_live': 'live_migrate'}
|
'_migrate_live': 'live_migrate'}
|
||||||
body_map = self._get_migration_body(host='hostname')
|
body_map = self._get_migration_body(host='hostname')
|
||||||
args_map = {'_migrate_live': ((False, self.disk_over_commit,
|
args_map = {'_migrate_live': ((False, self.disk_over_commit,
|
||||||
'hostname', self.force), {})}
|
'hostname', self.force, self.async),
|
||||||
|
{})}
|
||||||
exception_arg = {'_migrate': 'migrate',
|
exception_arg = {'_migrate': 'migrate',
|
||||||
'_migrate_live': 'os-migrateLive'}
|
'_migrate_live': 'os-migrateLive'}
|
||||||
self._test_actions_raise_conflict_on_invalid_state(
|
self._test_actions_raise_conflict_on_invalid_state(
|
||||||
@ -100,7 +103,8 @@ class MigrateServerTestsV21(admin_only_action_common.CommonTests):
|
|||||||
body_map = {'_migrate_live':
|
body_map = {'_migrate_live':
|
||||||
self._get_migration_body(host='hostname')}
|
self._get_migration_body(host='hostname')}
|
||||||
args_map = {'_migrate_live': ((False, self.disk_over_commit,
|
args_map = {'_migrate_live': ((False, self.disk_over_commit,
|
||||||
'hostname', self.force), {})}
|
'hostname', self.force, self.async),
|
||||||
|
{})}
|
||||||
self._test_actions_with_locked_instance(
|
self._test_actions_with_locked_instance(
|
||||||
['_migrate', '_migrate_live'], body_map=body_map,
|
['_migrate', '_migrate_live'], body_map=body_map,
|
||||||
args_map=args_map, method_translations=method_translations)
|
args_map=args_map, method_translations=method_translations)
|
||||||
@ -125,19 +129,13 @@ class MigrateServerTestsV21(admin_only_action_common.CommonTests):
|
|||||||
instance = self._stub_instance_get()
|
instance = self._stub_instance_get()
|
||||||
self.compute_api.live_migrate(self.context, instance, False,
|
self.compute_api.live_migrate(self.context, instance, False,
|
||||||
self.disk_over_commit, 'hostname',
|
self.disk_over_commit, 'hostname',
|
||||||
self.force)
|
self.force, self.async)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
live_migrate_method = self.controller._migrate_live
|
||||||
res = self.controller._migrate_live(self.req, instance.uuid,
|
live_migrate_method(self.req, instance.uuid,
|
||||||
body={'os-migrateLive': param})
|
body={'os-migrateLive': param})
|
||||||
# NOTE: on v2.1, http status code is set as wsgi_code of API
|
self.assertEqual(202, live_migrate_method.wsgi_code)
|
||||||
# method instead of status_int in a response object.
|
|
||||||
if self._api_version == '2.1':
|
|
||||||
status_int = self.controller._migrate_live.wsgi_code
|
|
||||||
else:
|
|
||||||
status_int = res.status_int
|
|
||||||
self.assertEqual(202, status_int)
|
|
||||||
|
|
||||||
def test_migrate_live_enabled(self):
|
def test_migrate_live_enabled(self):
|
||||||
param = self._get_params(host='hostname')
|
param = self._get_params(host='hostname')
|
||||||
@ -204,9 +202,8 @@ class MigrateServerTestsV21(admin_only_action_common.CommonTests):
|
|||||||
instance = self._stub_instance_get(uuid=uuid)
|
instance = self._stub_instance_get(uuid=uuid)
|
||||||
self.compute_api.live_migrate(self.context, instance, False,
|
self.compute_api.live_migrate(self.context, instance, False,
|
||||||
self.disk_over_commit,
|
self.disk_over_commit,
|
||||||
'hostname', self.force
|
'hostname', self.force, self.async
|
||||||
).AndRaise(fake_exc)
|
).AndRaise(fake_exc)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
body = self._get_migration_body(host='hostname')
|
body = self._get_migration_body(host='hostname')
|
||||||
@ -308,8 +305,8 @@ class MigrateServerTestsV225(MigrateServerTestsV21):
|
|||||||
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'}}}
|
||||||
args_map = {'_migrate_live': ((None, None, 'hostname', self.force),
|
args_map = {'_migrate_live': ((None, None, 'hostname', self.force,
|
||||||
{})}
|
self.async), {})}
|
||||||
self._test_actions(['_migrate_live'], body_map=body_map,
|
self._test_actions(['_migrate_live'], body_map=body_map,
|
||||||
method_translations=method_translations,
|
method_translations=method_translations,
|
||||||
args_map=args_map)
|
args_map=args_map)
|
||||||
@ -329,7 +326,6 @@ class MigrateServerTestsV225(MigrateServerTestsV21):
|
|||||||
|
|
||||||
|
|
||||||
class MigrateServerTestsV230(MigrateServerTestsV225):
|
class MigrateServerTestsV230(MigrateServerTestsV225):
|
||||||
|
|
||||||
force = False
|
force = False
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -346,8 +342,8 @@ class MigrateServerTestsV230(MigrateServerTestsV225):
|
|||||||
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': litteral_force}}}
|
||||||
args_map = {'_migrate_live': ((None, None, 'hostname', force),
|
args_map = {'_migrate_live': ((None, None, 'hostname', force,
|
||||||
{})}
|
self.async), {})}
|
||||||
self._test_actions(['_migrate_live'], body_map=body_map,
|
self._test_actions(['_migrate_live'], body_map=body_map,
|
||||||
method_translations=method_translations,
|
method_translations=method_translations,
|
||||||
args_map=args_map)
|
args_map=args_map)
|
||||||
@ -366,6 +362,75 @@ class MigrateServerTestsV230(MigrateServerTestsV225):
|
|||||||
self.req, fakes.FAKE_UUID, body=body)
|
self.req, fakes.FAKE_UUID, body=body)
|
||||||
|
|
||||||
|
|
||||||
|
class MigrateServerTestsV234(MigrateServerTestsV230):
|
||||||
|
async = True
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(MigrateServerTestsV230, self).setUp()
|
||||||
|
self.req.api_version_request = api_version_request.APIVersionRequest(
|
||||||
|
'2.34')
|
||||||
|
|
||||||
|
# NOTE(tdurakov): for REST API version 2.34 and above, tests below are not
|
||||||
|
# valid, as they are made in background.
|
||||||
|
def test_migrate_live_compute_service_unavailable(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_invalid_hypervisor_type(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_invalid_cpu_info(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_unable_to_migrate_to_self(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_destination_hypervisor_too_old(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_no_valid_host(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_invalid_local_storage(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_invalid_shared_storage(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_hypervisor_unavailable(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_instance_not_active(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_pre_check_error(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_migration_precheck_client_exception(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_migration_with_unexpected_error(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_migration_with_old_nova_not_supported(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_migrate_live_unexpected_error(self):
|
||||||
|
exc = exception.NoValidHost(reason="No valid host found")
|
||||||
|
self.mox.StubOutWithMock(self.compute_api, 'live_migrate')
|
||||||
|
instance = self._stub_instance_get()
|
||||||
|
self.compute_api.live_migrate(self.context, instance, None,
|
||||||
|
self.disk_over_commit, 'hostname',
|
||||||
|
self.force, self.async).AndRaise(exc)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
body = {'os-migrateLive':
|
||||||
|
{'host': 'hostname', 'block_migration': 'auto'}}
|
||||||
|
|
||||||
|
self.assertRaises(webob.exc.HTTPInternalServerError,
|
||||||
|
self.controller._migrate_live,
|
||||||
|
self.req, instance.uuid, body=body)
|
||||||
|
|
||||||
|
|
||||||
class MigrateServerPolicyEnforcementV21(test.NoDBTestCase):
|
class MigrateServerPolicyEnforcementV21(test.NoDBTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Starting from REST API microversion 2.34 pre-live-migration checks are
|
||||||
|
performed asynchronously. ``instance-actions`` should be used for getting
|
||||||
|
information about the checks results. New approach allows to reduce rpc
|
||||||
|
timeouts amount, as previous workflow was fully blocking and checks before
|
||||||
|
live-migration make blocking rpc request to both source and destionation
|
||||||
|
compute node.
|
Loading…
x
Reference in New Issue
Block a user