Add a --wait flag to share manage/unmanage

This option enables "manage" and "unmanage"
commands to wait until the action has completed.

Partially-implements: bp add-wait-to-async-commands
Closes-Bug: #1898309
Change-Id: I03b87f2f27b045e0a83c3a25cc482901bacffa15
This commit is contained in:
paulali 2021-05-19 16:45:25 +00:00 committed by kiwi-dot
parent f9aee522ef
commit 36aee019cd
3 changed files with 89 additions and 11 deletions

View File

@ -691,7 +691,8 @@ class ShellTest(test_utils.TestCase):
'share_type': None, 'share_type': None,
'share_server_id': None, 'share_server_id': None,
}}, }},
{'cmd_args': '--public', {'cmd_args': '--public'
' --wait',
'valid_params': { 'valid_params': {
'driver_options': {}, 'driver_options': {},
'share_type': None, 'share_type': None,
@ -710,7 +711,8 @@ class ShellTest(test_utils.TestCase):
'version': '--os-share-api-version 2.8', 'version': '--os-share-api-version 2.8',
}, },
{'cmd_args': '--driver_options opt1=opt1 opt2=opt2' {'cmd_args': '--driver_options opt1=opt1 opt2=opt2'
' --share_type fake_share_type', ' --share_type fake_share_type'
' --wait',
'valid_params': { 'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
'share_type': 'fake_share_type', 'share_type': 'fake_share_type',
@ -730,7 +732,8 @@ class ShellTest(test_utils.TestCase):
}, },
{'cmd_args': '--driver_options opt1=opt1 opt2=opt2' {'cmd_args': '--driver_options opt1=opt1 opt2=opt2'
' --share_type fake_share_type' ' --share_type fake_share_type'
' --share_server_id fake_server', ' --share_server_id fake_server'
' --wait',
'valid_params': { 'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
'share_type': 'fake_share_type', 'share_type': 'fake_share_type',
@ -740,6 +743,16 @@ class ShellTest(test_utils.TestCase):
@ddt.unpack @ddt.unpack
def test_manage(self, cmd_args, valid_params, is_public=False, def test_manage(self, cmd_args, valid_params, is_public=False,
version=None): version=None):
share_to_be_managed = shares.Share(
'fake_share', {
'id': 'fake'
}
)
self.mock_object(
shell_v2, '_wait_for_resource_status',
mock.Mock(return_value=share_to_be_managed)
)
if version is not None: if version is not None:
self.run_command(version self.run_command(version
+ ' manage fake_service fake_protocol ' + ' manage fake_service fake_protocol '
@ -761,8 +774,16 @@ class ShellTest(test_utils.TestCase):
} }
} }
expected['share'].update(valid_params) expected['share'].update(valid_params)
self.assert_called('POST', '/shares/manage', body=expected) self.assert_called('POST', '/shares/manage', body=expected)
if '--wait' in cmd_args:
shell_v2._wait_for_resource_status.assert_called_once_with(
self.shell.cs, share_to_be_managed, resource_type='share',
expected_status='available')
else:
shell_v2._wait_for_resource_status.assert_not_called()
def test_manage_invalid_param_share_server_id(self): def test_manage_invalid_param_share_server_id(self):
self.assertRaises( self.assertRaises(
exceptions.CommandError, exceptions.CommandError,
@ -903,9 +924,40 @@ class ShellTest(test_utils.TestCase):
expected = {'reset_status': {'status': status}} expected = {'reset_status': {'status': status}}
self.assert_called('POST', '/share-servers/1234/action', body=expected) self.assert_called('POST', '/share-servers/1234/action', body=expected)
def test_unmanage(self): @ddt.data('--wait', '')
self.run_command('unmanage 1234') def test_unmanage(self, wait_option):
self.assert_called('POST', '/shares/1234/action') version = api_versions.APIVersion('2.46')
api = mock.Mock(api_version=version)
manager = shares.ShareManager(api=api)
fake_share = shares.Share(
manager, {
'id': 'xyzzyspoon',
'api_version': version,
'status': 'available',
}
)
share_not_found_error = ("ERROR: No share with "
"a name or ID of '%s' exists.")
share_not_found_error = exceptions.CommandError(
share_not_found_error % (fake_share.id)
)
self.mock_object(
shell_v2, '_find_share',
mock.Mock(side_effect=([fake_share, fake_share, fake_share,
share_not_found_error])))
self.mock_object(
shares.ShareManager, 'get',
mock.Mock(return_value=fake_share))
self.run_command('unmanage %s xyzzyspoon' % wait_option)
expected_get_share_calls = 4 if wait_option else 1
shell_v2._find_share.assert_has_calls(
[mock.call(self.shell.cs, fake_share.id)] *
expected_get_share_calls
)
uri = '/shares/%s/action' % fake_share.id
api.client.post.assert_called_once_with(uri, body={'unmanage': None})
def test_share_server_unmanage(self): def test_share_server_unmanage(self):
self.run_command('share-server-unmanage 1234') self.run_command('share-server-unmanage 1234')

View File

@ -79,6 +79,8 @@ def _wait_for_resource_status(cs,
"state.") "state.")
deleted_message = ("%(resource_type)s %(resource)s has been successfully " deleted_message = ("%(resource_type)s %(resource)s has been successfully "
"deleted.") "deleted.")
unmanaged_message = ("%(resource_type)s %(resource)s has been "
"successfully unmanaged.")
message_payload = { message_payload = {
'resource_type': resource_type.capitalize(), 'resource_type': resource_type.capitalize(),
'resource': resource.id, 'resource': resource.id,
@ -94,10 +96,13 @@ def _wait_for_resource_status(cs,
try: try:
resource = find_resource[resource_type](cs, resource.id) resource = find_resource[resource_type](cs, resource.id)
except exceptions.CommandError as e: except exceptions.CommandError as e:
if (re.search(not_found_regex, str(e), flags=re.IGNORECASE) if (re.search(not_found_regex, str(e), flags=re.IGNORECASE)):
and 'deleted' in expected_status): if 'deleted' in expected_status:
print(deleted_message % message_payload) print(deleted_message % message_payload)
break break
if 'unmanaged' in expected_status:
print(unmanaged_message % message_payload)
break
else: else:
raise e raise e
@ -1523,6 +1528,10 @@ def do_share_export_location_show(cs, args):
help="Share server associated with share when using a share type with " help="Share server associated with share when using a share type with "
"'driver_handles_share_servers' extra_spec set to True. Available " "'driver_handles_share_servers' extra_spec set to True. Available "
"only for microversion >= 2.49. (Default=None)") "only for microversion >= 2.49. (Default=None)")
@cliutils.arg(
'--wait',
action='store_true',
help='Wait for share management')
def do_manage(cs, args): def do_manage(cs, args):
"""Manage share not handled by Manila (Admin only).""" """Manage share not handled by Manila (Admin only)."""
driver_options = _extract_key_value_options(args, 'driver_options') driver_options = _extract_key_value_options(args, 'driver_options')
@ -1545,6 +1554,11 @@ def do_manage(cs, args):
name=args.name, description=args.description, name=args.name, description=args.description,
is_public=args.public) is_public=args.public)
if args.wait:
share = _wait_for_resource_status(
cs, share, resource_type='share',
expected_status='available'
)
_print_share(cs, share) _print_share(cs, share)
@ -1682,10 +1696,16 @@ def do_snapshot_manage(cs, args):
'share', 'share',
metavar='<share>', metavar='<share>',
help='Name or ID of the share(s).') help='Name or ID of the share(s).')
@cliutils.arg(
'--wait',
action='store_true',
help='Wait for share unmanagement')
def do_unmanage(cs, args): def do_unmanage(cs, args):
"""Unmanage share (Admin only).""" """Unmanage share (Admin only)."""
share_ref = _find_share(cs, args.share) share_ref = _find_share(cs, args.share)
share_ref.unmanage() share_ref.unmanage()
if args.wait:
_wait_for_share_status(cs, share_ref, expected_status='unmanaged')
@api_versions.wraps("2.49") @api_versions.wraps("2.49")
@ -1775,7 +1795,6 @@ def do_delete(cs, args):
"""Remove one or more shares.""" """Remove one or more shares."""
failure_count = 0 failure_count = 0
shares_to_delete = [] shares_to_delete = []
for share in args.share: for share in args.share:
try: try:
share_ref = _find_share(cs, share) share_ref = _find_share(cs, share)

View File

@ -0,0 +1,7 @@
---
features:
- |
The commands "manila manage" and "manila unmanage"
now accept an optional "--wait" flag that allows
users to let the client poll for the completion
of the operation.