diff --git a/novaclient/tests/v1_1/test_shell.py b/novaclient/tests/v1_1/test_shell.py index eae395a83..ec0b2aa74 100644 --- a/novaclient/tests/v1_1/test_shell.py +++ b/novaclient/tests/v1_1/test_shell.py @@ -1389,6 +1389,51 @@ class ShellTest(utils.TestCase): 'block_migration': True, 'disk_over_commit': True}}) + def test_host_evacuate_live_with_no_target_host(self): + self.run_command('host-evacuate-live hyper') + self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) + body = {'os-migrateLive': {'host': None, + 'block_migration': False, + 'disk_over_commit': False}} + 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_target_host(self): + self.run_command('host-evacuate-live hyper ' + '--target-host hostname') + self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) + body = {'os-migrateLive': {'host': 'hostname', + 'block_migration': False, + 'disk_over_commit': False}} + 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) + body = {'os-migrateLive': {'host': None, + 'block_migration': True, + 'disk_over_commit': False}} + 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_disk_over_commit(self): + self.run_command('host-evacuate-live --disk-over-commit hyper') + self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) + body = {'os-migrateLive': {'host': None, + 'block_migration': False, + 'disk_over_commit': 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_reset_state(self): self.run_command('reset-state sample-server') self.assert_called('POST', '/servers/1234/action', diff --git a/novaclient/tests/v3/fakes.py b/novaclient/tests/v3/fakes.py index 23f1e7cbd..8300f16f7 100644 --- a/novaclient/tests/v3/fakes.py +++ b/novaclient/tests/v3/fakes.py @@ -335,6 +335,10 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient): # Hypervisors # def get_os_hypervisors_search(self, **kw): + if kw['query'] == 'hyper1': + return (200, {}, {'hypervisors': [ + {'id': 1234, 'hypervisor_hostname': 'hyper1'}, + ]}) return (200, {}, {'hypervisors': [ {'id': 1234, 'hypervisor_hostname': 'hyper1'}, {'id': 5678, 'hypervisor_hostname': 'hyper2'} diff --git a/novaclient/tests/v3/test_shell.py b/novaclient/tests/v3/test_shell.py index 569ddabcb..306070cc2 100644 --- a/novaclient/tests/v3/test_shell.py +++ b/novaclient/tests/v3/test_shell.py @@ -644,6 +644,47 @@ class ShellTest(utils.TestCase): self.assert_called('GET', '/flavors/2', pos=3) self.assert_called('GET', '/flavors/2/flavor-extra-specs', pos=4) + def test_host_evacuate_live_with_no_target_host(self): + self.run_command('host-evacuate-live hyper1') + self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) + self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) + body = {'migrate_live': {'host': None, + 'block_migration': False, + 'disk_over_commit': False}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=2) + self.assert_called('POST', '/servers/uuid2/action', body, pos=3) + + def test_host_evacuate_live_with_target_host(self): + self.run_command('host-evacuate-live hyper1 ' + '--target-host hostname') + self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) + self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) + body = {'migrate_live': {'host': 'hostname', + 'block_migration': False, + 'disk_over_commit': False}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=2) + self.assert_called('POST', '/servers/uuid2/action', body, pos=3) + + def test_host_evacuate_live_with_block_migration(self): + self.run_command('host-evacuate-live --block-migrate hyper1') + self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) + self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) + body = {'migrate_live': {'host': None, + 'block_migration': True, + 'disk_over_commit': False}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=2) + self.assert_called('POST', '/servers/uuid2/action', body, pos=3) + + def test_host_evacuate_live_with_disk_over_commit(self): + self.run_command('host-evacuate-live --disk-over-commit hyper1') + self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) + self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) + body = {'migrate_live': {'host': None, + 'block_migration': False, + 'disk_over_commit': True}} + self.assert_called('POST', '/servers/uuid1/action', body, pos=2) + self.assert_called('POST', '/servers/uuid2/action', body, pos=3) + def test_delete(self): self.run_command('delete 1234') self.assert_called('DELETE', '/servers/1234') diff --git a/novaclient/v1_1/contrib/host_evacuate_live.py b/novaclient/v1_1/contrib/host_evacuate_live.py new file mode 100644 index 000000000..6ac4a018f --- /dev/null +++ b/novaclient/v1_1/contrib/host_evacuate_live.py @@ -0,0 +1,64 @@ +# Copyright 2014 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from novaclient.openstack.common.gettextutils import _ +from novaclient import utils + + +def _server_live_migrate(cs, server, args): + class HostEvacuateLiveResponse(object): + def __init__(self, server_uuid, live_migration_accepted, + error_message): + self.server_uuid = server_uuid + self.live_migration_accepted = live_migration_accepted + self.error_message = error_message + success = True + error_message = "" + try: + cs.servers.live_migrate(server['uuid'], args.target_host, + args.block_migrate, args.disk_over_commit) + except Exception as e: + success = False + error_message = _("Error while live migrating instance: %s") % e + return HostEvacuateLiveResponse(server['uuid'], + success, + error_message) + + +@utils.arg('host', metavar='', help='Name of host.') +@utils.arg('--target-host', + metavar='', + default=None, + help=_('Name of target host.')) +@utils.arg('--block-migrate', + action='store_true', + default=False, + help=_('Enable block migration.')) +@utils.arg('--disk-over-commit', + action='store_true', + default=False, + help=_('Enable disk overcommit.')) +def do_host_evacuate_live(cs, args): + """Live migrate all instances of the specified host + to other available hosts. + """ + hypervisors = cs.hypervisors.search(args.host, servers=True) + response = [] + for hyper in hypervisors: + for server in getattr(hyper, 'servers', []): + response.append(_server_live_migrate(cs, server, args)) + + utils.print_list(response, ["Server UUID", "Live Migration Accepted", + "Error Message"]) diff --git a/novaclient/v3/shell.py b/novaclient/v3/shell.py index f97331e0f..9c4d09a65 100644 --- a/novaclient/v3/shell.py +++ b/novaclient/v3/shell.py @@ -2392,6 +2392,53 @@ def do_live_migration(cs, args): args.disk_over_commit) +def _server_live_migrate(cs, server, args): + class HostServersLiveMigrateResponse(object): + def __init__(self, server_uuid, live_migration_accepted, + error_message): + self.server_uuid = server_uuid + self.live_migration_accepted = live_migration_accepted + self.error_message = error_message + success = True + error_message = "" + try: + cs.servers.live_migrate(server['id'], args.target_host, + args.block_migrate, args.disk_over_commit) + except Exception as e: + success = False + error_message = "Error while live migrating instance: %s" % e + return HostServersLiveMigrateResponse(server['id'], + success, + error_message) + + +@utils.arg('host', metavar='', help='Name of host.') +@utils.arg('--target-host', + metavar='', + default=None, + help='Name of target host.') +@utils.arg('--block-migrate', + action='store_true', + default=False, + help='Enable block migration.') +@utils.arg('--disk-over-commit', + action='store_true', + default=False, + help='Enable disk overcommit.') +def do_host_evacuate_live(cs, args): + """Live Migrate all instances of the specified host + to other available hosts. + """ + hypervisors = cs.hypervisors.search(args.host) + response = [] + for hyper in hypervisors: + servers = getattr(cs.hypervisors.servers(hyper.id), 'servers', []) + for server in servers: + response.append(_server_live_migrate(cs, server, args)) + utils.print_list(response, ["Server UUID", "Live Migration Accepted", + "Error Message"]) + + @utils.arg('server', metavar='', nargs='+', help='Name or ID of server(s).') @utils.arg('--active', action='store_const', dest='state',