Add support for instance evacuate.

This adds support for server evacuation from failed host.
Adds CLI command:

nova evacuate <server> <host> [--password pwd] [--on-shared-storage]

Depends on the approval of change:
https://review.openstack.org/#change,17991

Change-Id: Icd91c0484b2db532861e23163d043737ad04117a
This commit is contained in:
Kravchenko Pavel 2013-01-07 18:46:59 +02:00
parent 4410339a45
commit 9e319ece37
6 changed files with 87 additions and 0 deletions

View File

@ -108,6 +108,7 @@ You'll find complete documentation on the shell by running
and name.
endpoints Discover endpoints that get returned from the
authenticate services
evacuate Evacuate a server from failed host
flavor-create Create a new flavor
flavor-delete Delete a specific flavor
flavor-list Print a list of available 'flavors' (sizes of

View File

@ -281,6 +281,17 @@ class Server(base.Resource):
"""
self.manager.remove_security_group(self, security_group)
def evacuate(self, host, on_shared_storage, password=None):
"""
Evacuate an instance from failed host to specified host.
: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.
"""
return self.manager.evacuate(self, host, on_shared_storage, password)
class ServerManager(local_base.BootingManagerWithFind):
resource_class = Server
@ -707,6 +718,26 @@ class ServerManager(local_base.BootingManagerWithFind):
"""
self._action('removeSecurityGroup', server, {'name': security_group})
def evacuate(self, server, host, on_shared_storage, 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.
"""
body = {
'host': host,
'onSharedStorage': on_shared_storage,
}
if password is not None:
body['adminPass'] = password
return self._action('evacuate', server, body)
def _action(self, action, server, info=None, **kwargs):
"""
Perform a server "action" -- reboot/rebuild/resize/etc.

View File

@ -2701,3 +2701,25 @@ def do_quota_class_update(cs, args):
"""Update the quotas for a quota class."""
_quota_update(cs.quota_classes, args.class_name, args)
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
@utils.arg('host', metavar='<host>', help='Name or ID of target host.')
@utils.arg('--password',
dest='password',
metavar='<password>',
default=None,
help="Set the provided password on the evacuated instance. Not applicable "
"with on-shared-storage flag")
@utils.arg('--on-shared-storage',
dest='on_shared_storage',
action="store_true",
default=False,
help='Specifies whether instance files located on shared storage')
def do_evacuate(cs, args):
"""Evacuate server from failed host to specified one."""
server = _find_server(cs, args.server)
res = server.evacuate(args.host, args.on_shared_storage, args.password)[0]
if type(res) is dict:
utils.print_dict(res)

View File

@ -489,6 +489,11 @@ class FakeHTTPClient(base_client.HTTPClient):
assert set(body[action].keys()) == set(['name',
'backup_type',
'rotation'])
elif action == 'evacuate':
keys = body[action].keys()
if 'adminPass' in keys:
keys.remove('adminPass')
assert set(keys) == set(['host', 'onSharedStorage'])
else:
raise AssertionError("Unexpected server action: %s" % action)
return (resp, _headers, _body)

View File

@ -399,3 +399,10 @@ class ServersTest(utils.TestCase):
cs.assert_called('POST', '/servers/1234/action')
cs.servers.remove_security_group(s, 'oldsg')
cs.assert_called('POST', '/servers/1234/action')
def test_evacuate(self):
s = cs.servers.get(1234)
s.evacuate('fake_target_host', 'True')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.evacuate(s, 'fake_target_host', 'False', 'NewAdminPassword')
cs.assert_called('POST', '/servers/1234/action')

View File

@ -790,3 +790,24 @@ class ShellTest(utils.TestCase):
self.run_command('absolute-limits --reserved')
self.assert_called('GET', '/limits?reserved=1')
def test_evacuate(self):
self.run_command('evacuate sample-server new_host')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'onSharedStorage': False}})
self.run_command('evacuate sample-server new_host '
'--password NewAdminPass')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'onSharedStorage': False,
'adminPass': 'NewAdminPass'}})
self.run_command('evacuate sample-server new_host')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'onSharedStorage': False}})
self.run_command('evacuate sample-server new_host '
'--on-shared-storage')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'onSharedStorage': True}})