Microversion 2.68: Remove 'forced' live migrations, evacuations

Update the commands and Python API bindings to reflect the new
microversion. The various evacuate microversion functions are DRY'd up
along the way.

Change-Id: Ibfc905292258ffde05800387e5d6bbad4823085c
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
Depends-On: https://review.openstack.org/#/c/634600/
Implements: blueprint remove-force-flag-from-live-migrate-and-evacuate
This commit is contained in:
Stephen Finucane 2019-02-06 11:05:09 +00:00 committed by Matt Riedemann
parent 8eb7d1c5cc
commit 162f4769b8
7 changed files with 233 additions and 82 deletions

View File

@ -1249,7 +1249,7 @@ Evacuate server from failed host.
``--force`` ``--force``
Force an evacuation by not verifying the provided destination host by the Force an evacuation by not verifying the provided destination host by the
scheduler. (Supported by API versions '2.29' -'2.latest') scheduler. (Supported by API versions '2.29' - '2.67')
.. warning:: This could result in failures to actually evacuate the .. warning:: This could result in failures to actually evacuate the
server to the specified host. It is recommended to either not specify server to the specified host. It is recommended to either not specify
@ -1650,7 +1650,7 @@ Evacuate all instances from failed host.
``--force`` ``--force``
Force an evacuation by not verifying the provided destination host by the Force an evacuation by not verifying the provided destination host by the
scheduler. (Supported by API versions '2.29' -'2.latest') scheduler. (Supported by API versions '2.29' - '2.67')
.. warning:: This could result in failures to actually evacuate the .. warning:: This could result in failures to actually evacuate the
server to the specified host. It is recommended to either not specify server to the specified host. It is recommended to either not specify
@ -1701,7 +1701,7 @@ Live migrate all instances off the specified host to other available hosts.
``--force`` ``--force``
Force a live-migration by not verifying the provided destination host by Force a live-migration by not verifying the provided destination host by
the scheduler. (Supported by API versions '2.30' -'2.latest') the scheduler. (Supported by API versions '2.30' - '2.67')
.. warning:: This could result in failures to actually live migrate the .. warning:: This could result in failures to actually live migrate the
servers to the specified host. It is recommended to either not specify servers to the specified host. It is recommended to either not specify
@ -2369,7 +2369,7 @@ Migrate running server to a new machine.
``--force`` ``--force``
Force a live-migration by not verifying the provided destination host by Force a live-migration by not verifying the provided destination host by
the scheduler. (Supported by API versions '2.30' -'2.latest') the scheduler. (Supported by API versions '2.30' - '2.67')
.. warning:: This could result in failures to actually live migrate the .. warning:: This could result in failures to actually live migrate the
server to the specified host. It is recommended to either not specify server to the specified host. It is recommended to either not specify

View File

@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
# when client supported the max version, and bumped sequentially, otherwise # when client supported the max version, and bumped sequentially, otherwise
# the client may break due to server side new version may include some # the client may break due to server side new version may include some
# backward incompatible change. # backward incompatible change.
API_MAX_VERSION = api_versions.APIVersion("2.67") API_MAX_VERSION = api_versions.APIVersion("2.68")

View File

@ -1655,3 +1655,43 @@ class ServersV267Test(ServersV263Test):
nics='none', block_device_mapping_v2=bdm) nics='none', block_device_mapping_v2=bdm)
self.assertIn("Block device volume_type is not supported before " self.assertIn("Block device volume_type is not supported before "
"microversion 2.67", six.text_type(ex)) "microversion 2.67", six.text_type(ex))
class ServersV268Test(ServersV267Test):
api_version = "2.68"
def test_evacuate(self):
s = self.cs.servers.get(1234)
ret = s.evacuate('fake_target_host')
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'fake_target_host'}})
ret = self.cs.servers.evacuate(s, 'fake_target_host')
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'fake_target_host'}})
ex = self.assertRaises(TypeError, self.cs.servers.evacuate,
'fake_target_host', force=True)
self.assertIn('force', six.text_type(ex))
def test_live_migrate_server(self):
s = self.cs.servers.get(1234)
ret = s.live_migrate(host='hostname', block_migration='auto')
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('POST', '/servers/1234/action',
{'os-migrateLive': {'host': 'hostname',
'block_migration': 'auto'}})
ret = self.cs.servers.live_migrate(s, host='hostname',
block_migration='auto')
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('POST', '/servers/1234/action',
{'os-migrateLive': {'host': 'hostname',
'block_migration': 'auto'}})
ex = self.assertRaises(TypeError, self.cs.servers.live_migrate,
host='hostname', force=True)
self.assertIn('force', six.text_type(ex))

View File

@ -2690,6 +2690,18 @@ class ShellTest(utils.TestCase):
'block_migration': 'auto', 'block_migration': 'auto',
'force': True}}) 'force': True}})
def test_live_migration_v2_68(self):
self.run_command('live-migration sample-server hostname',
api_version='2.68')
self.assert_called('POST', '/servers/1234/action',
{'os-migrateLive': {'host': 'hostname',
'block_migration': 'auto'}})
self.assertRaises(
SystemExit, self.run_command,
'live-migration --force sample-server hostname',
api_version='2.68')
def test_live_migration_force_complete(self): def test_live_migration_force_complete(self):
self.run_command('live-migration-force-complete sample-server 1', self.run_command('live-migration-force-complete sample-server 1',
api_version='2.22') api_version='2.22')
@ -3437,6 +3449,17 @@ class ShellTest(utils.TestCase):
{'evacuate': {'host': 'new_host', {'evacuate': {'host': 'new_host',
'force': True}}) 'force': True}})
def test_evacuate_v2_68(self):
self.run_command('evacuate sample-server new_host',
api_version='2.68')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host'}})
self.assertRaises(
SystemExit, self.run_command,
'evacuate --force sample-server new_host',
api_version='2.68')
def test_evacuate_with_no_target_host(self): def test_evacuate_with_no_target_host(self):
self.run_command('evacuate sample-server') self.run_command('evacuate sample-server')
self.assert_called('POST', '/servers/1234/action', self.assert_called('POST', '/servers/1234/action',

View File

@ -444,7 +444,7 @@ class Server(base.Resource):
block_migration = "auto" block_migration = "auto"
return self.manager.live_migrate(self, host, block_migration) return self.manager.live_migrate(self, host, block_migration)
@api_versions.wraps("2.30") @api_versions.wraps("2.30", "2.67")
def live_migrate(self, host=None, block_migration=None, force=None): def live_migrate(self, host=None, block_migration=None, force=None):
""" """
Migrates a running instance to a new machine. Migrates a running instance to a new machine.
@ -459,6 +459,20 @@ class Server(base.Resource):
block_migration = "auto" block_migration = "auto"
return self.manager.live_migrate(self, host, block_migration, force) return self.manager.live_migrate(self, host, block_migration, force)
@api_versions.wraps("2.68")
def live_migrate(self, host=None, block_migration=None):
"""
Migrates a running instance to a new machine.
:param host: destination host name.
:param block_migration: if True, do block_migration, the default
value is None which is mapped to 'auto'.
:returns: An instance of novaclient.base.TupleWithMeta
"""
if block_migration is None:
block_migration = "auto"
return self.manager.live_migrate(self, host, block_migration)
def reset_state(self, state='error'): def reset_state(self, state='error'):
""" """
Reset the state of an instance to active or error. Reset the state of an instance to active or error.
@ -524,7 +538,7 @@ class Server(base.Resource):
""" """
return self.manager.evacuate(self, host, password) return self.manager.evacuate(self, host, password)
@api_versions.wraps("2.29") @api_versions.wraps("2.29", "2.67")
def evacuate(self, host=None, password=None, force=None): def evacuate(self, host=None, password=None, force=None):
""" """
Evacuate an instance from failed host to specified host. Evacuate an instance from failed host to specified host.
@ -537,6 +551,18 @@ class Server(base.Resource):
""" """
return self.manager.evacuate(self, host, password, force) return self.manager.evacuate(self, host, password, force)
@api_versions.wraps("2.68")
def evacuate(self, host=None, password=None):
"""
Evacuate an instance from failed host to specified host.
:param host: Name of the target host
:param password: string to set as admin password on the evacuated
server.
:returns: An instance of novaclient.base.TupleWithMeta
"""
return self.manager.evacuate(self, host, password)
def interface_list(self): def interface_list(self):
""" """
List interfaces attached to an instance. List interfaces attached to an instance.
@ -1670,6 +1696,24 @@ class ServerManager(base.BootingManagerWithFind):
return result return result
def _live_migrate(self, server, host, block_migration, disk_over_commit,
force):
"""Inner function to abstract changes in live migration API."""
body = {
'host': host,
'block_migration': block_migration,
}
if disk_over_commit is not None:
body['disk_over_commit'] = disk_over_commit
# NOTE(stephenfin): For some silly reason, we don't set this if it's
# False, hence why we're not explicitly checking against None
if force:
body['force'] = force
return self._action('os-migrateLive', server, body)
@api_versions.wraps('2.0', '2.24') @api_versions.wraps('2.0', '2.24')
def live_migrate(self, server, host, block_migration, disk_over_commit): def live_migrate(self, server, host, block_migration, disk_over_commit):
""" """
@ -1681,10 +1725,10 @@ class ServerManager(base.BootingManagerWithFind):
:param disk_over_commit: if True, allow disk overcommit. :param disk_over_commit: if True, allow disk overcommit.
:returns: An instance of novaclient.base.TupleWithMeta :returns: An instance of novaclient.base.TupleWithMeta
""" """
return self._action('os-migrateLive', server, return self._live_migrate(server, host,
{'host': host, block_migration=block_migration,
'block_migration': block_migration, disk_over_commit=disk_over_commit,
'disk_over_commit': disk_over_commit}) force=None)
@api_versions.wraps('2.25', '2.29') @api_versions.wraps('2.25', '2.29')
def live_migrate(self, server, host, block_migration): def live_migrate(self, server, host, block_migration):
@ -1697,11 +1741,12 @@ class ServerManager(base.BootingManagerWithFind):
'auto' 'auto'
:returns: An instance of novaclient.base.TupleWithMeta :returns: An instance of novaclient.base.TupleWithMeta
""" """
return self._action('os-migrateLive', server, return self._live_migrate(server, host,
{'host': host, block_migration=block_migration,
'block_migration': block_migration}) disk_over_commit=None,
force=None)
@api_versions.wraps('2.30') @api_versions.wraps('2.30', '2.67')
def live_migrate(self, server, host, block_migration, force=None): def live_migrate(self, server, host, block_migration, force=None):
""" """
Migrates a running instance to a new machine. Migrates a running instance to a new machine.
@ -1713,10 +1758,26 @@ class ServerManager(base.BootingManagerWithFind):
:param force: forces to bypass the scheduler if host is provided. :param force: forces to bypass the scheduler if host is provided.
:returns: An instance of novaclient.base.TupleWithMeta :returns: An instance of novaclient.base.TupleWithMeta
""" """
body = {'host': host, 'block_migration': block_migration} return self._live_migrate(server, host,
if force: block_migration=block_migration,
body['force'] = force disk_over_commit=None,
return self._action('os-migrateLive', server, body) force=force)
@api_versions.wraps('2.68')
def live_migrate(self, server, host, block_migration):
"""
Migrates a running instance to a new machine.
:param server: instance id which comes from nova list.
:param host: destination host name.
:param block_migration: if True, do block_migration, can be set as
'auto'
:returns: An instance of novaclient.base.TupleWithMeta
"""
return self._live_migrate(server, host,
block_migration=block_migration,
disk_over_commit=None,
force=None)
def reset_state(self, server, state='error'): def reset_state(self, server, state='error'):
""" """
@ -1771,66 +1832,13 @@ class ServerManager(base.BootingManagerWithFind):
base.getid(server), 'security_groups', base.getid(server), 'security_groups',
SecurityGroup) SecurityGroup)
@api_versions.wraps("2.0", "2.13") def _evacuate(self, server, host, on_shared_storage, password, force):
def evacuate(self, server, host=None, on_shared_storage=True, """Inner function to abstract changes in evacuate API."""
password=None):
"""
Evacuate a server instance.
:param server: The :class:`Server` (or its ID) to share onto.
:param host: Name of the target host.
:param on_shared_storage: Specifies whether instance files located
on shared storage
:param password: string to set as password on the evacuated server.
:returns: An instance of novaclient.base.TupleWithMeta
"""
body = {'onSharedStorage': on_shared_storage}
if host is not None:
body['host'] = host
if password is not None:
body['adminPass'] = password
resp, body = self._action_return_resp_and_body('evacuate', server,
body)
return base.TupleWithMeta((resp, body), resp)
@api_versions.wraps("2.14", "2.28")
def evacuate(self, server, host=None, password=None):
"""
Evacuate a server instance.
:param server: The :class:`Server` (or its ID) to share onto.
:param host: Name of the target host.
:param password: string to set as password on the evacuated server.
:returns: An instance of novaclient.base.TupleWithMeta
"""
body = {} body = {}
if host is not None:
body['host'] = host
if password is not None: if on_shared_storage is not None:
body['adminPass'] = password body['onSharedStorage'] = on_shared_storage
resp, body = self._action_return_resp_and_body('evacuate', server,
body)
return base.TupleWithMeta((resp, body), resp)
@api_versions.wraps("2.29")
def evacuate(self, server, host=None, password=None, force=None):
"""
Evacuate a server instance.
:param server: The :class:`Server` (or its ID) to share onto.
:param host: Name of the target host.
:param password: string to set as password on the evacuated server.
:param force: forces to bypass the scheduler if host is provided.
:returns: An instance of novaclient.base.TupleWithMeta
"""
body = {}
if host is not None: if host is not None:
body['host'] = host body['host'] = host
@ -1844,6 +1852,70 @@ class ServerManager(base.BootingManagerWithFind):
body) body)
return base.TupleWithMeta((resp, body), resp) return base.TupleWithMeta((resp, body), resp)
@api_versions.wraps("2.0", "2.13")
def evacuate(self, server, host=None, on_shared_storage=True,
password=None):
"""
Evacuate a server instance.
:param server: The :class:`Server` (or its ID) to evacuate to.
:param host: Name of the target host.
:param on_shared_storage: Specifies whether instance files located
on shared storage
:param password: string to set as password on the evacuated server.
:returns: An instance of novaclient.base.TupleWithMeta
"""
return self._evacuate(server, host,
on_shared_storage=on_shared_storage,
password=password,
force=None)
@api_versions.wraps("2.14", "2.28")
def evacuate(self, server, host=None, password=None):
"""
Evacuate a server instance.
:param server: The :class:`Server` (or its ID) to evacuate to.
:param host: Name of the target host.
:param password: string to set as password on the evacuated server.
:returns: An instance of novaclient.base.TupleWithMeta
"""
return self._evacuate(server, host,
on_shared_storage=None,
password=password,
force=None)
@api_versions.wraps("2.29", "2.67")
def evacuate(self, server, host=None, password=None, force=None):
"""
Evacuate a server instance.
:param server: The :class:`Server` (or its ID) to evacuate to.
:param host: Name of the target host.
:param password: string to set as password on the evacuated server.
:param force: forces to bypass the scheduler if host is provided.
:returns: An instance of novaclient.base.TupleWithMeta
"""
return self._evacuate(server, host,
on_shared_storage=None,
password=password,
force=force)
@api_versions.wraps("2.68")
def evacuate(self, server, host=None, password=None):
"""
Evacuate a server instance.
:param server: The :class:`Server` (or its ID) to evacuate to.
:param host: Name of the target host.
:param password: string to set as password on the evacuated server.
:returns: An instance of novaclient.base.TupleWithMeta
"""
return self._evacuate(server, host,
on_shared_storage=None,
password=password,
force=None)
def interface_list(self, server): def interface_list(self, server):
""" """
List attached network interfaces List attached network interfaces

View File

@ -3464,7 +3464,8 @@ def _print_aggregate_details(cs, aggregate):
'actually live migrate the server to the specified host. It is ' 'actually live migrate the server to the specified host. It is '
'recommended to either not specify a host so that the scheduler ' 'recommended to either not specify a host so that the scheduler '
'will pick one, or specify a host without --force.'), 'will pick one, or specify a host without --force.'),
start_version='2.30') start_version='2.30',
end_version='2.67')
def do_live_migration(cs, args): def do_live_migration(cs, args):
"""Migrate running server to a new machine.""" """Migrate running server to a new machine."""
@ -4453,10 +4454,12 @@ def do_quota_class_update(cs, args):
'actually evacuate the server to the specified host. It is ' 'actually evacuate the server to the specified host. It is '
'recommended to either not specify a host so that the scheduler ' 'recommended to either not specify a host so that the scheduler '
'will pick one, or specify a host without --force.'), 'will pick one, or specify a host without --force.'),
start_version='2.29') start_version='2.29',
end_version='2.67')
def do_evacuate(cs, args): def do_evacuate(cs, args):
"""Evacuate server from failed host.""" """Evacuate server from failed host."""
# TODO(stephenfin): Simply call '_server_evacuate' instead?
server = _find_server(cs, args.server) server = _find_server(cs, args.server)
on_shared_storage = getattr(args, 'on_shared_storage', None) on_shared_storage = getattr(args, 'on_shared_storage', None)
force = getattr(args, 'force', None) force = getattr(args, 'force', None)
@ -4843,8 +4846,11 @@ def _server_evacuate(cs, server, args):
success = True success = True
error_message = "" error_message = ""
try: try:
if api_versions.APIVersion("2.29") <= cs.api_version: if api_versions.APIVersion('2.68') <= cs.api_version:
# if microversion >= 2.29 # if microversion >= 2.68
cs.servers.evacuate(server=server['uuid'], host=args.target_host)
elif api_versions.APIVersion('2.29') <= cs.api_version:
# if microversion 2.29 - 2.67
force = getattr(args, 'force', None) force = getattr(args, 'force', None)
cs.servers.evacuate(server=server['uuid'], host=args.target_host, cs.servers.evacuate(server=server['uuid'], host=args.target_host,
force=force) force=force)
@ -4910,7 +4916,8 @@ def _hyper_servers(cs, host, strict):
'actually evacuate the server to the specified host. It is ' 'actually evacuate the server to the specified host. It is '
'recommended to either not specify a host so that the scheduler ' 'recommended to either not specify a host so that the scheduler '
'will pick one, or specify a host without --force.'), 'will pick one, or specify a host without --force.'),
start_version='2.29') start_version='2.29',
end_version='2.67')
@utils.arg( @utils.arg(
'--strict', '--strict',
dest='strict', dest='strict',
@ -5000,7 +5007,8 @@ def _server_live_migrate(cs, server, args):
'actually live migrate the servers to the specified host. It is ' 'actually live migrate the servers to the specified host. It is '
'recommended to either not specify a host so that the scheduler ' 'recommended to either not specify a host so that the scheduler '
'will pick one, or specify a host without --force.'), 'will pick one, or specify a host without --force.'),
start_version='2.30') start_version='2.30',
end_version='2.67')
@utils.arg( @utils.arg(
'--strict', '--strict',
dest='strict', dest='strict',

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
Added support for `microversion 2.68`_, which removes the ``--force`` option
from the ``nova evacuate``, ``nova live-migration``, ``nova host-evacuate``
and ``nova host-evacuate-live`` commands.
.. _microversion 2.68: https://docs.openstack.org/nova/latest/api_microversion_history.html#id61