Add two consistency group commands

Add commands:
    consistency group add volume
    consistency group remove volume
in volume v2 (v2 only)

Change-Id: I70ff287d3b5df78196b8f4b9e50402c471aef284
Implements: bp cinder-command-support
Closes-Bug: #1613964
This commit is contained in:
zhiyong.dai 2016-12-06 19:08:49 +08:00
parent 29587eaa66
commit 2f2603d908
5 changed files with 351 additions and 1 deletions

View File

@ -4,6 +4,28 @@ consistency group
Block Storage v2
consistency group add volume
----------------------------
Add volume(s) to consistency group.
.. program:: consistency group add volume
.. code:: bash
os consistency group add volume
<consistency-group>
<volume> [<volume> ...]
.. _consistency_group_add_volume:
.. describe:: <consistency-group>
Consistency group to contain <volume> (name or ID)
.. describe:: <volume>
Volume(s) to add to <consistency-group> (name or ID)
(repeat option to add multiple volumes)
consistency group create
------------------------
@ -86,13 +108,35 @@ List consistency groups.
List additional fields in output
consistency group remove volume
-------------------------------
Remove volume(s) from consistency group.
.. program:: consistency group remove volume
.. code:: bash
os consistency group remove volume
<consistency-group>
<volume> [<volume> ...]
.. _consistency_group_remove_volume:
.. describe:: <consistency-group>
Consistency group containing <volume> (name or ID)
.. describe:: <volume>
Volume(s) to remove from <consistency-group> (name or ID)
(repeat option to remove multiple volumes)
consistency group set
---------------------
Set consistency group properties.
.. program:: consistency group set
.. code:: bash
.. code:: bash
os consistency group set
[--name <name>]

View File

@ -36,10 +36,115 @@ class TestConsistencyGroup(volume_fakes.TestVolume):
self.app.client_manager.volume.cgsnapshots)
self.cgsnapshots_mock.reset_mock()
self.volumes_mock = (
self.app.client_manager.volume.volumes)
self.volumes_mock.reset_mock()
self.types_mock = self.app.client_manager.volume.volume_types
self.types_mock.reset_mock()
class TestConsistencyGroupAddVolume(TestConsistencyGroup):
_consistency_group = (
volume_fakes.FakeConsistencyGroup.create_one_consistency_group())
def setUp(self):
super(TestConsistencyGroupAddVolume, self).setUp()
self.consistencygroups_mock.get.return_value = (
self._consistency_group)
# Get the command object to test
self.cmd = \
consistency_group.AddVolumeToConsistencyGroup(self.app, None)
def test_add_one_volume_to_consistency_group(self):
volume = volume_fakes.FakeVolume.create_one_volume()
self.volumes_mock.get.return_value = volume
arglist = [
self._consistency_group.id,
volume.id,
]
verifylist = [
('consistency_group', self._consistency_group.id),
('volumes', [volume.id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'add_volumes': volume.id,
}
self.consistencygroups_mock.update.assert_called_once_with(
self._consistency_group.id,
**kwargs
)
self.assertIsNone(result)
def test_add_multiple_volumes_to_consistency_group(self):
volumes = volume_fakes.FakeVolume.create_volumes(count=2)
self.volumes_mock.get = volume_fakes.FakeVolume.get_volumes(volumes)
arglist = [
self._consistency_group.id,
volumes[0].id,
volumes[1].id,
]
verifylist = [
('consistency_group', self._consistency_group.id),
('volumes', [volumes[0].id, volumes[1].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'add_volumes': volumes[0].id + ',' + volumes[1].id,
}
self.consistencygroups_mock.update.assert_called_once_with(
self._consistency_group.id,
**kwargs
)
self.assertIsNone(result)
@mock.patch.object(consistency_group.LOG, 'error')
def test_add_multiple_volumes_to_consistency_group_with_exception(
self, mock_error):
volume = volume_fakes.FakeVolume.create_one_volume()
arglist = [
self._consistency_group.id,
volume.id,
'unexist_volume',
]
verifylist = [
('consistency_group', self._consistency_group.id),
('volumes', [volume.id, 'unexist_volume']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
find_mock_result = [volume,
exceptions.CommandError,
self._consistency_group]
with mock.patch.object(utils, 'find_resource',
side_effect=find_mock_result) as find_mock:
result = self.cmd.take_action(parsed_args)
mock_error.assert_called_with("1 of 2 volumes failed to add.")
self.assertIsNone(result)
find_mock.assert_any_call(self.consistencygroups_mock,
self._consistency_group.id)
find_mock.assert_any_call(self.volumes_mock,
volume.id)
find_mock.assert_any_call(self.volumes_mock,
'unexist_volume')
self.assertEqual(3, find_mock.call_count)
self.consistencygroups_mock.update.assert_called_once_with(
self._consistency_group.id, add_volumes=volume.id
)
class TestConsistencyGroupCreate(TestConsistencyGroup):
volume_type = volume_fakes.FakeType.create_one_type()
@ -394,6 +499,107 @@ class TestConsistencyGroupList(TestConsistencyGroup):
self.assertEqual(self.data_long, list(data))
class TestConsistencyGroupRemoveVolume(TestConsistencyGroup):
_consistency_group = (
volume_fakes.FakeConsistencyGroup.create_one_consistency_group())
def setUp(self):
super(TestConsistencyGroupRemoveVolume, self).setUp()
self.consistencygroups_mock.get.return_value = (
self._consistency_group)
# Get the command object to test
self.cmd = \
consistency_group.RemoveVolumeFromConsistencyGroup(self.app, None)
def test_remove_one_volume_from_consistency_group(self):
volume = volume_fakes.FakeVolume.create_one_volume()
self.volumes_mock.get.return_value = volume
arglist = [
self._consistency_group.id,
volume.id,
]
verifylist = [
('consistency_group', self._consistency_group.id),
('volumes', [volume.id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'remove_volumes': volume.id,
}
self.consistencygroups_mock.update.assert_called_once_with(
self._consistency_group.id,
**kwargs
)
self.assertIsNone(result)
def test_remove_multi_volumes_from_consistency_group(self):
volumes = volume_fakes.FakeVolume.create_volumes(count=2)
self.volumes_mock.get = volume_fakes.FakeVolume.get_volumes(volumes)
arglist = [
self._consistency_group.id,
volumes[0].id,
volumes[1].id,
]
verifylist = [
('consistency_group', self._consistency_group.id),
('volumes', [volumes[0].id, volumes[1].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'remove_volumes': volumes[0].id + ',' + volumes[1].id,
}
self.consistencygroups_mock.update.assert_called_once_with(
self._consistency_group.id,
**kwargs
)
self.assertIsNone(result)
@mock.patch.object(consistency_group.LOG, 'error')
def test_remove_multiple_volumes_from_consistency_group_with_exception(
self, mock_error):
volume = volume_fakes.FakeVolume.create_one_volume()
arglist = [
self._consistency_group.id,
volume.id,
'unexist_volume',
]
verifylist = [
('consistency_group', self._consistency_group.id),
('volumes', [volume.id, 'unexist_volume']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
find_mock_result = [volume,
exceptions.CommandError,
self._consistency_group]
with mock.patch.object(utils, 'find_resource',
side_effect=find_mock_result) as find_mock:
result = self.cmd.take_action(parsed_args)
mock_error.assert_called_with("1 of 2 volumes failed to remove.")
self.assertIsNone(result)
find_mock.assert_any_call(self.consistencygroups_mock,
self._consistency_group.id)
find_mock.assert_any_call(self.volumes_mock,
volume.id)
find_mock.assert_any_call(self.volumes_mock,
'unexist_volume')
self.assertEqual(3, find_mock.call_count)
self.consistencygroups_mock.update.assert_called_once_with(
self._consistency_group.id, remove_volumes=volume.id
)
class TestConsistencyGroupSet(TestConsistencyGroup):
consistency_group = (

View File

@ -27,6 +27,60 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
def _find_volumes(parsed_args_volumes, volume_client):
result = 0
uuid = ''
for volume in parsed_args_volumes:
try:
volume_id = utils.find_resource(
volume_client.volumes, volume).id
uuid += volume_id + ','
except Exception as e:
result += 1
LOG.error(_("Failed to find volume with "
"name or ID '%(volume)s':%(e)s")
% {'volume': volume, 'e': e})
return result, uuid
class AddVolumeToConsistencyGroup(command.Command):
_description = _("Add volume(s) to consistency group")
def get_parser(self, prog_name):
parser = super(AddVolumeToConsistencyGroup, self).get_parser(prog_name)
parser.add_argument(
'consistency_group',
metavar="<consistency-group>",
help=_('Consistency group to contain <volume> (name or ID)'),
)
parser.add_argument(
'volumes',
metavar='<volume>',
nargs='+',
help=_('Volume(s) to add to <consistency-group> (name or ID) '
'(repeat option to add multiple volumes)'),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
result, add_uuid = _find_volumes(parsed_args.volumes, volume_client)
if result > 0:
total = len(parsed_args.volumes)
LOG.error(_("%(result)s of %(total)s volumes failed "
"to add.") % {'result': result, 'total': total})
if add_uuid:
add_uuid = add_uuid.rstrip(',')
consistency_group_id = utils.find_resource(
volume_client.consistencygroups,
parsed_args.consistency_group).id
volume_client.consistencygroups.update(
consistency_group_id, add_volumes=add_uuid)
class CreateConsistencyGroup(command.ShowOne):
_description = _("Create new consistency group.")
@ -188,6 +242,44 @@ class ListConsistencyGroup(command.Lister):
for s in consistency_groups))
class RemoveVolumeFromConsistencyGroup(command.Command):
_description = _("Remove volume(s) from consistency group")
def get_parser(self, prog_name):
parser = \
super(RemoveVolumeFromConsistencyGroup, self).get_parser(prog_name)
parser.add_argument(
'consistency_group',
metavar="<consistency-group>",
help=_('Consistency group containing <volume> (name or ID)'),
)
parser.add_argument(
'volumes',
metavar='<volume>',
nargs='+',
help=_('Volume(s) to remove from <consistency-group> (name or ID) '
'(repeat option to remove multiple volumes)'),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
result, remove_uuid = _find_volumes(parsed_args.volumes, volume_client)
if result > 0:
total = len(parsed_args.volumes)
LOG.error(_("%(result)s of %(total)s volumes failed "
"to remove.") % {'result': result, 'total': total})
if remove_uuid:
remove_uuid = remove_uuid.rstrip(',')
consistency_group_id = utils.find_resource(
volume_client.consistencygroups,
parsed_args.consistency_group).id
volume_client.consistencygroups.update(
consistency_group_id, remove_volumes=remove_uuid)
class SetConsistencyGroup(command.Command):
_description = _("Set consistency group properties")

View File

@ -0,0 +1,6 @@
---
features:
- Add ``consistency group add volume`` and ``consistency group remove volume`` commands
in volume v2.
[Bug `1642238 <https://bugs.launchpad.net/python-openstackclient/+bug/1642238>`_]

View File

@ -515,9 +515,11 @@ openstack.volume.v2 =
backup_restore = openstackclient.volume.v2.backup:RestoreBackup
backup_show = openstackclient.volume.v2.backup:ShowBackup
consistency_group_add_volume = openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup
consistency_group_create = openstackclient.volume.v2.consistency_group:CreateConsistencyGroup
consistency_group_delete = openstackclient.volume.v2.consistency_group:DeleteConsistencyGroup
consistency_group_list = openstackclient.volume.v2.consistency_group:ListConsistencyGroup
consistency_group_remove_volume = openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup
consistency_group_set = openstackclient.volume.v2.consistency_group:SetConsistencyGroup
consistency_group_show = openstackclient.volume.v2.consistency_group:ShowConsistencyGroup