Add "--wait" option for creating/promoting/deleting a share replicas

1) Creating a share replica
$ manila share-replica-create share1 --az manila-zone-1 --wait
<CLI waits on the share replica to become available before providing replica details>
(The CLI does not wait for "replica_state" to become "in_sync" - it only waits until the replica reaches an "available"
status)

2) Promoting a share replica
$ manila share-replica-promote 9b6b909b-3790-4a65-a89d-f9437496f171 --wait
<CLI waits on the share replica's "replica_state" to become "active" before returning to the prompt>

3) Deleting a share replica
$ manila share-replica-delete 9b6b909b-3790-4a65-a89d-f9437496f171 --wait
<CLI waits on the share replica to be deleted before returning to the prompt>

Closes-Bug: #1898310
Change-Id: If269c708c894756c0223e3bfa173670bcc6ef763
This commit is contained in:
tspyderboy 2024-03-26 18:12:46 +05:30
parent 3103b8c56a
commit 789e65bee3
5 changed files with 132 additions and 1 deletions

View File

@ -356,6 +356,12 @@ class PromoteShareReplica(command.Command):
help=_('Quiesce wait time in seconds. Available for '
'microversion >= 2.75')
)
parser.add_argument(
'--wait',
action='store_true',
default=False,
help=_('Wait for share replica promotion')
)
return parser
def take_action(self, parsed_args):
@ -377,6 +383,15 @@ class PromoteShareReplica(command.Command):
try:
share_client.share_replicas.promote(*args)
if parsed_args.wait:
if not osc_utils.wait_for_status(
status_f=share_client.share_replicas.get,
res_id=replica.id,
success_status=['active'],
status_field='replica_state'
):
LOG.error(_("ERROR: Share replica is in error state."))
except Exception as e:
raise exceptions.CommandError(_(
"Failed to promote replica to 'active': %s" % (e)))

View File

@ -617,7 +617,10 @@ class TestShareReplicaPromote(TestShareReplica):
super(TestShareReplicaPromote, self).setUp()
self.share_replica = (
manila_fakes.FakeShareReplica.create_one_replica()
manila_fakes.FakeShareReplica.create_one_replica(
attrs={
'status': 'available'}
)
)
self.replicas_mock.get.return_value = self.share_replica
@ -630,6 +633,7 @@ class TestShareReplicaPromote(TestShareReplica):
]
verifylist = [
('replica', self.share_replica.id),
('wait', False)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -658,6 +662,28 @@ class TestShareReplicaPromote(TestShareReplica):
wait_time)
self.assertIsNone(result)
@mock.patch.object(osc_share_replicas.osc_utils, 'wait_for_status',
mock.Mock())
def test_share_replica_promote_wait(self):
arglist = [
self.share_replica.id,
'--wait'
]
verifylist = [
('replica', self.share_replica.id),
('wait', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.replicas_mock.promote.assert_called_with(
self.share_replica)
self.assertIsNone(result)
osc_share_replicas.osc_utils.wait_for_status.assert_called_once_with(
status_f=self.replicas_mock.get, res_id=self.share_replica.id,
success_status=['active'], status_field='replica_state')
def test_share_replica_promote_exception(self):
arglist = [
self.share_replica.id,

View File

@ -3400,6 +3400,7 @@ class ShellTest(test_utils.TestCase):
@ddt.data(True, False)
@mock.patch.object(shell_v2, '_find_share_replica', mock.Mock())
@mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock())
def test_share_replica_delete_force(self, force):
fake_replica = type('FakeShareReplica', (object,), {'id': '1234'})
@ -3413,6 +3414,23 @@ class ShellTest(test_utils.TestCase):
body={'force_delete': None})
else:
self.assert_called('DELETE', '/share-replicas/1234')
self.assertEqual(0, shell_v2._wait_for_resource_status.call_count)
@mock.patch.object(shell_v2, '_find_share_replica', mock.Mock())
@mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock())
def test_share_replica_delete_with_wait(self):
fake_replica = type('FakeShareReplica', (object,), {'id': '1234'})
shell_v2._find_share_replica.return_value = fake_replica
self.run_command('share-replica-delete fake-replica --wait')
self.assert_called('DELETE', '/share-replicas/1234')
# _wait_for_resource_status should be triggered once
shell_v2._wait_for_resource_status.assert_called_once_with(
self.shell.cs, mock.ANY, resource_type='share_replica',
expected_status='deleted')
@ddt.data([1, 0], [1, 1], [2, 0], [2, 1], [2, 2])
@ddt.unpack
@ -3484,6 +3502,7 @@ class ShellTest(test_utils.TestCase):
'fake-share-id --availability-zone fake-az',
)
@mock.patch.object(shell_v2, '_find_share', mock.Mock())
@mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock())
def test_share_replica_create(self, data):
fshare = type('FakeShare', (object,), {'id': 'fake-share-id'})
@ -3495,6 +3514,26 @@ class ShellTest(test_utils.TestCase):
shell_v2._find_share.assert_called_with(mock.ANY, fshare.id)
self.assert_called('POST', '/share-replicas')
self.assertEqual(0, shell_v2._wait_for_resource_status.call_count)
@mock.patch.object(shell_v2, '_find_share', mock.Mock())
@mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock())
def test_share_replica_create_with_wait(self):
fshare = type('FakeShare', (object,), {'id': 'fake-share-id'})
shell_v2._find_share.return_value = fshare
cmd = ('share-replica-create fake-share-id '
'--availability-zone fake-az --wait')
self.run_command(cmd)
shell_v2._find_share.assert_called_with(mock.ANY, fshare.id)
self.assert_called('POST', '/share-replicas')
# _wait_for_resource_status should be triggered once
shell_v2._wait_for_resource_status.assert_called_once_with(
self.shell.cs, mock.ANY, resource_type='share_replica',
expected_status='available')
def test_share_replica_show(self):
@ -3503,6 +3542,7 @@ class ShellTest(test_utils.TestCase):
self.assert_called_anytime('GET', '/share-replicas/5678')
@mock.patch.object(shell_v2, '_find_share_replica', mock.Mock())
@mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock())
def test_share_replica_promote_quiesce_wait_time(self):
fake_replica = type('FakeShareReplica', (object,), {'id': '1234'})
shell_v2._find_share_replica.return_value = fake_replica
@ -3512,6 +3552,21 @@ class ShellTest(test_utils.TestCase):
self.assert_called(
'POST', '/share-replicas/1234/action',
body={'promote': {'quiesce_wait_time': '5'}})
self.assertEqual(0, shell_v2._wait_for_resource_status.call_count)
@mock.patch.object(shell_v2, '_find_share_replica', mock.Mock())
@mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock())
def test_share_replica_promote_with_wait_(self):
fake_replica = type('FakeShareReplica', (object,), {'id': '1234'})
shell_v2._find_share_replica.return_value = fake_replica
cmd = ('share-replica-promote ' + fake_replica.id + ' --wait')
self.run_command(cmd)
self.assert_called(
'POST', '/share-replicas/1234/action')
# _wait_for_resource_status should be triggered once
shell_v2._wait_for_resource_status.assert_called_once_with(
self.shell.cs, mock.ANY, resource_type='share_replica',
expected_status='active', status_attr='replica_state')
@ddt.data('promote', 'resync')
@mock.patch.object(shell_v2, '_find_share_replica', mock.Mock())

View File

@ -6416,6 +6416,11 @@ def do_share_replica_create(cs, args): # noqa
action='single_alias',
help='Optional network info ID or name. '
'Available only for microversion >= 2.72')
@cliutils.arg(
'--wait',
action='store_true',
default=False,
help='Wait for share replica to be created')
def do_share_replica_create(cs, args): # noqa
"""Create a share replica."""
share = _find_share(cs, args.share)
@ -6444,6 +6449,10 @@ def do_share_replica_create(cs, args): # noqa
body['share_network'] = share_network
replica = cs.share_replicas.create(**body)
if args.wait:
_wait_for_resource_status(
cs, replica, resource_type='share_replica',
expected_status='available')
_print_share_replica(cs, replica)
@ -6485,6 +6494,11 @@ def do_share_replica_show(cs, args): # noqa
help='Attempt to force deletion of a replica on its backend. Using '
'this option will purge the replica from Manila even if it '
'is not cleaned up on the backend. Defaults to False.')
@cliutils.arg(
'--wait',
action='store_true',
default=False,
help='Wait for share replica to be deleted')
@api_versions.wraps("2.11")
def do_share_replica_delete(cs, args):
"""Remove one or more share replicas."""
@ -6497,6 +6511,10 @@ def do_share_replica_delete(cs, args):
try:
replica_ref = _find_share_replica(cs, replica)
cs.share_replicas.delete(replica_ref, **kwargs)
if args.wait:
_wait_for_resource_status(
cs, replica_ref, resource_type='share_replica',
expected_status='deleted')
except Exception as e:
failure_count += 1
print("Delete for share replica %s failed: %s" % (replica, e),
@ -6528,6 +6546,11 @@ def do_share_replica_promote(cs, args):
default=None,
help='Quiesce wait time in seconds. Available for '
'microversion >= 2.75')
@cliutils.arg(
'--wait',
action='store_true',
default=False,
help='Wait for share replica to be promoted')
@api_versions.wraps("2.75") # noqa
def do_share_replica_promote(cs, args): # noqa
"""Promote specified replica to 'active' replica_state."""
@ -6537,6 +6560,12 @@ def do_share_replica_promote(cs, args): # noqa
if args.quiesce_wait_time:
quiesce_wait_time = args.quiesce_wait_time
cs.share_replicas.promote(replica, quiesce_wait_time)
if args.wait:
_wait_for_resource_status(
cs, replica,
resource_type='share_replica',
expected_status='active',
status_attr='replica_state')
@api_versions.wraps("2.47")

View File

@ -0,0 +1,6 @@
---
features:
- |
The commands "share-replica-create", "share-replica-promote", and
"share-replica-delete" now accept an optional "--wait" option that
allows users to let the client poll for the completion of the operation.