From c6496789914e2d88984a65350df1d70e75a4d759 Mon Sep 17 00:00:00 2001 From: Sylvain Bauza Date: Tue, 21 Jun 2016 18:08:06 +0200 Subject: [PATCH] Add support for microversion 2.30 Now the os-migrateLive API supports a new body argument called 'force' which helps the operators to bypass the scheduler call in case they provide a host. Also modifies the host_evacuate_live helper method in the contrib tree to make sure operators also have the force flag in case they need it for a global call. Change-Id: Id7fcd88ad060390ac6d1a21510d84363ed643957 Implements: blueprint check-destination-on-migrations-newton --- novaclient/__init__.py | 2 +- novaclient/tests/unit/v2/fakes.py | 13 ++++---- novaclient/tests/unit/v2/test_servers.py | 22 +++++++++++++ novaclient/tests/unit/v2/test_shell.py | 26 +++++++++++++++ novaclient/v2/contrib/host_evacuate_live.py | 19 ++++++++--- novaclient/v2/servers.py | 36 +++++++++++++++++++-- novaclient/v2/shell.py | 20 ++++++++---- 7 files changed, 118 insertions(+), 20 deletions(-) diff --git a/novaclient/__init__.py b/novaclient/__init__.py index 727328f47..56b62db24 100644 --- a/novaclient/__init__.py +++ b/novaclient/__init__.py @@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1") # when client supported the max version, and bumped sequentially, otherwise # the client may break due to server side new version may include some # backward incompatible change. -API_MAX_VERSION = api_versions.APIVersion("2.29") +API_MAX_VERSION = api_versions.APIVersion("2.30") diff --git a/novaclient/tests/unit/v2/fakes.py b/novaclient/tests/unit/v2/fakes.py index 13681e02a..baf345236 100644 --- a/novaclient/tests/unit/v2/fakes.py +++ b/novaclient/tests/unit/v2/fakes.py @@ -715,13 +715,14 @@ class FakeHTTPClient(base_client.HTTPClient): # raise AssertionError if we didn't find 'action' at all. pass elif action == 'os-migrateLive': + expected = set(['host', 'block_migration']) + if self.api_version >= api_versions.APIVersion("2.30"): + if 'force' in body[action].keys(): + # force can be optional + expected.add('force') if self.api_version < api_versions.APIVersion("2.25"): - assert set(body[action].keys()) == set(['host', - 'block_migration', - 'disk_over_commit']) - else: - assert set(body[action].keys()) == set(['host', - 'block_migration']) + expected.add('disk_over_commit') + assert set(body[action].keys()) == expected elif action == 'rebuild': body = body[action] adminPass = body.get('adminPass', 'randompassword') diff --git a/novaclient/tests/unit/v2/test_servers.py b/novaclient/tests/unit/v2/test_servers.py index 70b06f220..38bf1a4ba 100644 --- a/novaclient/tests/unit/v2/test_servers.py +++ b/novaclient/tests/unit/v2/test_servers.py @@ -1195,3 +1195,25 @@ class ServersV229Test(ServersV226Test): self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'fake_target_host', 'force': True}}) + + +class ServersV230Test(ServersV229Test): + def setUp(self): + super(ServersV230Test, self).setUp() + self.cs.api_version = api_versions.APIVersion("2.30") + + 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', + force=True) + self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) + self.assert_called('POST', '/servers/1234/action', + {'os-migrateLive': {'host': 'hostname', + 'block_migration': 'auto', + 'force': True}}) diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index d4a552698..8790b2a2b 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -1758,6 +1758,19 @@ class ShellTest(utils.TestCase): {'os-migrateLive': {'host': None, 'block_migration': 'auto'}}) + def test_live_migration_v2_30(self): + self.run_command('live-migration sample-server hostname', + api_version='2.30') + self.assert_called('POST', '/servers/1234/action', + {'os-migrateLive': {'host': 'hostname', + 'block_migration': 'auto'}}) + self.run_command('live-migration --force sample-server hostname', + api_version='2.30') + self.assert_called('POST', '/servers/1234/action', + {'os-migrateLive': {'host': 'hostname', + 'block_migration': 'auto', + 'force': True}}) + def test_live_migration_force_complete(self): self.run_command('live-migration-force-complete sample-server 1', api_version='2.22') @@ -1811,6 +1824,19 @@ class ShellTest(utils.TestCase): self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_2_30(self): + self.run_command('host-evacuate-live --force hyper ' + '--target-host hostname', + api_version='2.30') + self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) + body = {'os-migrateLive': {'host': 'hostname', + 'block_migration': 'auto', + 'force': True}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=1) + self.assert_called('POST', '/servers/uuid2/action', body, pos=2) + self.assert_called('POST', '/servers/uuid3/action', body, pos=3) + self.assert_called('POST', '/servers/uuid4/action', body, pos=4) + def test_host_evacuate_live_with_block_migration(self): self.run_command('host-evacuate-live --block-migrate hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) diff --git a/novaclient/v2/contrib/host_evacuate_live.py b/novaclient/v2/contrib/host_evacuate_live.py index badc4d1b3..f55dda1a4 100644 --- a/novaclient/v2/contrib/host_evacuate_live.py +++ b/novaclient/v2/contrib/host_evacuate_live.py @@ -26,14 +26,16 @@ def _server_live_migrate(cs, server, args): self.error_message = error_message success = True error_message = "" + update_kwargs = {} try: + # API >= 2.30 + if 'force' in args and args.force: + update_kwargs['force'] = args.force # API 2.0->2.24 if 'disk_over_commit' in args: - cs.servers.live_migrate(server['uuid'], args.target_host, - args.block_migrate, args.disk_over_commit) - else: # API 2.25+ - cs.servers.live_migrate(server['uuid'], args.target_host, - args.block_migrate) + update_kwargs['disk_over_commit'] = args.disk_over_commit + cs.servers.live_migrate(server['uuid'], args.target_host, + args.block_migrate, **update_kwargs) except Exception as e: success = False error_message = _("Error while live migrating instance: %s") % e @@ -72,6 +74,13 @@ def _server_live_migrate(cs, server, args): dest='max_servers', metavar='', help='Maximum number of servers to live migrate simultaneously') +@utils.arg( + '--force', + dest='force', + action='store_true', + default=False, + help=_('Force to not verify the scheduler if a host is provided.'), + start_version='2.30') def do_host_evacuate_live(cs, args): """Live migrate all instances of the specified host to other available hosts. diff --git a/novaclient/v2/servers.py b/novaclient/v2/servers.py index fd672af20..1ddbc721b 100644 --- a/novaclient/v2/servers.py +++ b/novaclient/v2/servers.py @@ -432,7 +432,7 @@ class Server(base.Resource): block_migration, disk_over_commit) - @api_versions.wraps("2.25") + @api_versions.wraps("2.25", "2.29") def live_migrate(self, host=None, block_migration=None): """ Migrates a running instance to a new machine. @@ -446,6 +446,21 @@ class Server(base.Resource): block_migration = "auto" return self.manager.live_migrate(self, host, block_migration) + @api_versions.wraps("2.30") + def live_migrate(self, host=None, block_migration=None, force=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'. + :param force: force to bypass the scheduler if host is provided. + :returns: An instance of novaclient.base.TupleWithMeta + """ + if block_migration is None: + block_migration = "auto" + return self.manager.live_migrate(self, host, block_migration, force) + def reset_state(self, state='error'): """ Reset the state of an instance to active or error. @@ -1578,7 +1593,7 @@ class ServerManager(base.BootingManagerWithFind): 'block_migration': block_migration, 'disk_over_commit': disk_over_commit}) - @api_versions.wraps('2.25') + @api_versions.wraps('2.25', '2.29') def live_migrate(self, server, host, block_migration): """ Migrates a running instance to a new machine. @@ -1593,6 +1608,23 @@ class ServerManager(base.BootingManagerWithFind): {'host': host, 'block_migration': block_migration}) + @api_versions.wraps('2.30') + def live_migrate(self, server, host, block_migration, force=None): + """ + 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' + :param force: forces to bypass the scheduler if host is provided. + :returns: An instance of novaclient.base.TupleWithMeta + """ + body = {'host': host, 'block_migration': block_migration} + if force: + body['force'] = force + return self._action('os-migrateLive', server, body) + def reset_state(self, server, state='error'): """ Reset the state of an instance to active or error. diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index 7e1241640..d2ed16e7f 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -3768,16 +3768,24 @@ def _print_aggregate_details(aggregate): 'novaclient 3.3.0.') % '--disk-over-commit', help=argparse.SUPPRESS, start_version="2.0", end_version="2.24") +@utils.arg( + '--force', + dest='force', + action='store_true', + default=False, + help=_('Force to not verify the scheduler if a host is provided.'), + start_version='2.30') def do_live_migration(cs, args): """Migrate running server to a new machine.""" + update_kwargs = {} if 'disk_over_commit' in args: - _find_server(cs, args.server).live_migrate(args.host, - args.block_migrate, - args.disk_over_commit) - else: - _find_server(cs, args.server).live_migrate(args.host, - args.block_migrate) + update_kwargs['disk_over_commit'] = args.disk_over_commit + if 'force' in args and args.force: + update_kwargs['force'] = args.force + + _find_server(cs, args.server).live_migrate(args.host, args.block_migrate, + **update_kwargs) @api_versions.wraps("2.22")