Microversion 2.56 - Enable cold migration with target host
Change-Id: I4deea811ffae3e7944d5ec10ca0bbf2bfa056a7c Implements: blueprint cold-migration-with-target-queens
This commit is contained in:
parent
d18436ca5d
commit
e5e8cebc81
|
@ -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.55")
|
API_MAX_VERSION = api_versions.APIVersion("2.56")
|
||||||
|
|
|
@ -452,6 +452,8 @@ class V1(Base):
|
||||||
# but we can not specify version in data_fixture now and this is
|
# but we can not specify version in data_fixture now and this is
|
||||||
# V1 data, so just let it pass
|
# V1 data, so just let it pass
|
||||||
pass
|
pass
|
||||||
|
elif action == 'migrate':
|
||||||
|
return None
|
||||||
elif action == 'rebuild':
|
elif action == 'rebuild':
|
||||||
body = body[action]
|
body = body[action]
|
||||||
adminPass = body.get('adminPass', 'randompassword')
|
adminPass = body.get('adminPass', 'randompassword')
|
||||||
|
|
|
@ -687,7 +687,7 @@ class FakeSessionClient(base_client.SessionClient):
|
||||||
# Server actions
|
# Server actions
|
||||||
#
|
#
|
||||||
|
|
||||||
none_actions = ['revertResize', 'migrate', 'os-stop', 'os-start',
|
none_actions = ['revertResize', 'os-stop', 'os-start',
|
||||||
'forceDelete', 'restore', 'pause', 'unpause', 'unlock',
|
'forceDelete', 'restore', 'pause', 'unpause', 'unlock',
|
||||||
'unrescue', 'resume', 'suspend', 'lock', 'shelve',
|
'unrescue', 'resume', 'suspend', 'lock', 'shelve',
|
||||||
'shelveOffload', 'unshelve', 'resetNetwork']
|
'shelveOffload', 'unshelve', 'resetNetwork']
|
||||||
|
@ -749,6 +749,15 @@ class FakeSessionClient(base_client.SessionClient):
|
||||||
if self.api_version < api_versions.APIVersion("2.25"):
|
if self.api_version < api_versions.APIVersion("2.25"):
|
||||||
expected.add('disk_over_commit')
|
expected.add('disk_over_commit')
|
||||||
assert set(body[action].keys()) == expected
|
assert set(body[action].keys()) == expected
|
||||||
|
elif action == 'migrate':
|
||||||
|
if self.api_version < api_versions.APIVersion("2.56"):
|
||||||
|
assert body[action] is None
|
||||||
|
else:
|
||||||
|
expected = set()
|
||||||
|
if 'host' in body[action].keys():
|
||||||
|
# host can be optional
|
||||||
|
expected.add('host')
|
||||||
|
assert set(body[action].keys()) == expected
|
||||||
elif action == 'rebuild':
|
elif action == 'rebuild':
|
||||||
body = body[action]
|
body = body[action]
|
||||||
adminPass = body.get('adminPass', 'randompassword')
|
adminPass = body.get('adminPass', 'randompassword')
|
||||||
|
|
|
@ -1581,3 +1581,26 @@ class ServersV254Test(ServersV252Test):
|
||||||
'1234', fakes.FAKE_IMAGE_UUID_1,
|
'1234', fakes.FAKE_IMAGE_UUID_1,
|
||||||
key_name='test_keypair')
|
key_name='test_keypair')
|
||||||
self.assertIn('key_name', six.text_type(ex.message))
|
self.assertIn('key_name', six.text_type(ex.message))
|
||||||
|
|
||||||
|
|
||||||
|
class ServersV256Test(ServersV254Test):
|
||||||
|
|
||||||
|
api_version = "2.56"
|
||||||
|
|
||||||
|
def test_migrate_server(self):
|
||||||
|
s = self.cs.servers.get(1234)
|
||||||
|
ret = s.migrate()
|
||||||
|
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
|
{'migrate': {}})
|
||||||
|
ret = s.migrate(host='target-host')
|
||||||
|
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
|
{'migrate': {'host': 'target-host'}})
|
||||||
|
|
||||||
|
def test_migrate_server_pre_256_fails(self):
|
||||||
|
self.cs.api_version = api_versions.APIVersion('2.55')
|
||||||
|
s = self.cs.servers.get(1234)
|
||||||
|
ex = self.assertRaises(TypeError,
|
||||||
|
s.migrate, host='target-host')
|
||||||
|
self.assertIn('host', six.text_type(ex))
|
||||||
|
|
|
@ -1628,6 +1628,23 @@ class ShellTest(utils.TestCase):
|
||||||
self.run_command('migrate sample-server')
|
self.run_command('migrate sample-server')
|
||||||
self.assert_called('POST', '/servers/1234/action', {'migrate': None})
|
self.assert_called('POST', '/servers/1234/action', {'migrate': None})
|
||||||
|
|
||||||
|
def test_migrate_pre_v256(self):
|
||||||
|
self.assertRaises(SystemExit,
|
||||||
|
self.run_command,
|
||||||
|
'migrate --host target-host sample-server',
|
||||||
|
api_version='2.55')
|
||||||
|
|
||||||
|
def test_migrate_v256(self):
|
||||||
|
self.run_command('migrate sample-server',
|
||||||
|
api_version='2.56')
|
||||||
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
|
{'migrate': {}})
|
||||||
|
|
||||||
|
self.run_command('migrate --host target-host sample-server',
|
||||||
|
api_version='2.56')
|
||||||
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
|
{'migrate': {'host': 'target-host'}})
|
||||||
|
|
||||||
def test_resize(self):
|
def test_resize(self):
|
||||||
self.run_command('resize sample-server 1')
|
self.run_command('resize sample-server 1')
|
||||||
self.assert_called('POST', '/servers/1234/action',
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
|
|
|
@ -327,6 +327,7 @@ class Server(base.Resource):
|
||||||
"""Diagnostics -- Retrieve server diagnostics."""
|
"""Diagnostics -- Retrieve server diagnostics."""
|
||||||
return self.manager.diagnostics(self)
|
return self.manager.diagnostics(self)
|
||||||
|
|
||||||
|
@api_versions.wraps("2.0", "2.55")
|
||||||
def migrate(self):
|
def migrate(self):
|
||||||
"""
|
"""
|
||||||
Migrate a server to a new host.
|
Migrate a server to a new host.
|
||||||
|
@ -335,6 +336,16 @@ class Server(base.Resource):
|
||||||
"""
|
"""
|
||||||
return self.manager.migrate(self)
|
return self.manager.migrate(self)
|
||||||
|
|
||||||
|
@api_versions.wraps("2.56")
|
||||||
|
def migrate(self, host=None):
|
||||||
|
"""
|
||||||
|
Migrate a server to a new host.
|
||||||
|
|
||||||
|
:param host: (Optional) The target host.
|
||||||
|
:returns: An instance of novaclient.base.TupleWithMeta
|
||||||
|
"""
|
||||||
|
return self.manager.migrate(self, host=host)
|
||||||
|
|
||||||
def remove_fixed_ip(self, address):
|
def remove_fixed_ip(self, address):
|
||||||
"""
|
"""
|
||||||
Remove an IP address.
|
Remove an IP address.
|
||||||
|
@ -1545,6 +1556,7 @@ class ServerManager(base.BootingManagerWithFind):
|
||||||
body, **kwargs)
|
body, **kwargs)
|
||||||
return Server(self, body['server'], resp=resp)
|
return Server(self, body['server'], resp=resp)
|
||||||
|
|
||||||
|
@api_versions.wraps("2.0", "2.55")
|
||||||
def migrate(self, server):
|
def migrate(self, server):
|
||||||
"""
|
"""
|
||||||
Migrate a server to a new host.
|
Migrate a server to a new host.
|
||||||
|
@ -1554,6 +1566,22 @@ class ServerManager(base.BootingManagerWithFind):
|
||||||
"""
|
"""
|
||||||
return self._action('migrate', server)
|
return self._action('migrate', server)
|
||||||
|
|
||||||
|
@api_versions.wraps("2.56")
|
||||||
|
def migrate(self, server, host=None):
|
||||||
|
"""
|
||||||
|
Migrate a server to a new host.
|
||||||
|
|
||||||
|
:param server: The :class:`Server` (or its ID).
|
||||||
|
:param host: (Optional) The target host.
|
||||||
|
:returns: An instance of novaclient.base.TupleWithMeta
|
||||||
|
"""
|
||||||
|
info = {}
|
||||||
|
|
||||||
|
if host:
|
||||||
|
info['host'] = host
|
||||||
|
|
||||||
|
return self._action('migrate', server, info)
|
||||||
|
|
||||||
def resize(self, server, flavor, disk_config=None, **kwargs):
|
def resize(self, server, flavor, disk_config=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Resize a server's resources.
|
Resize a server's resources.
|
||||||
|
|
|
@ -1950,6 +1950,12 @@ def do_resize_revert(cs, args):
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||||
|
@utils.arg(
|
||||||
|
'--host',
|
||||||
|
metavar='<host>',
|
||||||
|
default=None,
|
||||||
|
help=_('Destination host name.'),
|
||||||
|
start_version='2.56')
|
||||||
@utils.arg(
|
@utils.arg(
|
||||||
'--poll',
|
'--poll',
|
||||||
dest='poll',
|
dest='poll',
|
||||||
|
@ -1957,9 +1963,13 @@ def do_resize_revert(cs, args):
|
||||||
default=False,
|
default=False,
|
||||||
help=_('Report the server migration progress until it completes.'))
|
help=_('Report the server migration progress until it completes.'))
|
||||||
def do_migrate(cs, args):
|
def do_migrate(cs, args):
|
||||||
"""Migrate a server. The new host will be selected by the scheduler."""
|
"""Migrate a server."""
|
||||||
|
update_kwargs = {}
|
||||||
|
if 'host' in args and args.host:
|
||||||
|
update_kwargs['host'] = args.host
|
||||||
|
|
||||||
server = _find_server(cs, args.server)
|
server = _find_server(cs, args.server)
|
||||||
server.migrate()
|
server.migrate(**update_kwargs)
|
||||||
|
|
||||||
if args.poll:
|
if args.poll:
|
||||||
_poll_for_status(cs.servers.get, server.id, 'migrating',
|
_poll_for_status(cs.servers.get, server.id, 'migrating',
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added a new ``--host`` option to ``nova migrate`` command
|
||||||
|
in microversion 2.56. It enables administrators to specify
|
||||||
|
a target host when cold migating a server. The target host will be
|
||||||
|
validated by the scheduler. The target host cannot be the same as
|
||||||
|
the current host on which the server is running and must be in the
|
||||||
|
same cell that the server is currently in.
|
Loading…
Reference in New Issue