Add options to create volume group from source

This patch adds ``--source-group`` and ``--group-snapshot`` options
to the ``volume group create`` command to allow creating group from
a source group or a group snapshot.

Change-Id: I87482a5dd43c519dfdcf981635aa879914a70a5c
This commit is contained in:
whoami-rajat 2023-02-06 23:09:03 +05:30
parent 73b4ce88eb
commit ec01268ea9
4 changed files with 226 additions and 30 deletions

View File

@ -45,7 +45,7 @@ freeze-host,volume host set --disable,Freeze and disable the specified cinder-vo
get-capabilities,volume backend capability show,Show capabilities of a volume backend. Admin only.
get-pools,volume backend pool list,Show pool information for backends. Admin only.
group-create,volume group create,Creates a group. (Supported by API versions 3.13 - 3.latest)
group-create-from-src,,Creates a group from a group snapshot or a source group. (Supported by API versions 3.14 - 3.latest)
group-create-from-src,volume group create [--source-group|--group-snapshot],Creates a group from a group snapshot or a source group. (Supported by API versions 3.14 - 3.latest)
group-delete,volume group delete,Removes one or more groups. (Supported by API versions 3.13 - 3.latest)
group-disable-replication,volume group set --disable-replication,Disables replication for group. (Supported by API versions 3.38 - 3.latest)
group-enable-replication,volume group set --enable-replication,Enables replication for group. (Supported by API versions 3.38 - 3.latest)

1 absolute-limits limits show --absolute Lists absolute limits for a user.
45 get-capabilities volume backend capability show Show capabilities of a volume backend. Admin only.
46 get-pools volume backend pool list Show pool information for backends. Admin only.
47 group-create volume group create Creates a group. (Supported by API versions 3.13 - 3.latest)
48 group-create-from-src volume group create [--source-group|--group-snapshot] Creates a group from a group snapshot or a source group. (Supported by API versions 3.14 - 3.latest)
49 group-delete volume group delete Removes one or more groups. (Supported by API versions 3.13 - 3.latest)
50 group-disable-replication volume group set --disable-replication Disables replication for group. (Supported by API versions 3.38 - 3.latest)
51 group-enable-replication volume group set --enable-replication Enables replication for group. (Supported by API versions 3.38 - 3.latest)

View File

@ -10,9 +10,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from cinderclient import api_versions
from osc_lib import exceptions
from openstackclient.tests.unit import utils as tests_utils
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
from openstackclient.volume.v3 import volume_group
@ -32,6 +35,10 @@ class TestVolumeGroup(volume_fakes.TestVolume):
self.volume_types_mock = self.app.client_manager.volume.volume_types
self.volume_types_mock.reset_mock()
self.volume_group_snapshots_mock = \
self.app.client_manager.volume.group_snapshots
self.volume_group_snapshots_mock.reset_mock()
class TestVolumeGroupCreate(TestVolumeGroup):
@ -43,6 +50,8 @@ class TestVolumeGroupCreate(TestVolumeGroup):
'volume_types': [fake_volume_type.id],
},
)
fake_volume_group_snapshot = \
volume_fakes.create_one_volume_group_snapshot()
columns = (
'ID',
@ -79,6 +88,10 @@ class TestVolumeGroupCreate(TestVolumeGroup):
self.fake_volume_group_type
self.volume_groups_mock.create.return_value = self.fake_volume_group
self.volume_groups_mock.get.return_value = self.fake_volume_group
self.volume_groups_mock.create_from_src.return_value = \
self.fake_volume_group
self.volume_group_snapshots_mock.get.return_value = \
self.fake_volume_group_snapshot
self.cmd = volume_group.CreateVolumeGroup(self.app, None)
@ -115,6 +128,29 @@ class TestVolumeGroupCreate(TestVolumeGroup):
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_volume_group_create_no_volume_type(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.13')
arglist = [
self.fake_volume_group_type.id
]
verifylist = [
('volume_group_type', self.fake_volume_group_type.id),
('name', None),
('description', None),
('availability_zone', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args)
self.assertIn(
'<volume_types> is a required argument',
str(exc))
def test_volume_group_create_with_options(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.13')
@ -176,6 +212,101 @@ class TestVolumeGroupCreate(TestVolumeGroup):
'--os-volume-api-version 3.13 or greater is required',
str(exc))
def test_volume_group_create_from_source_group(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.14')
arglist = [
'--source-group', self.fake_volume_group.id,
]
verifylist = [
('source_group', self.fake_volume_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_groups_mock.get.assert_has_calls(
[mock.call(self.fake_volume_group.id),
mock.call(self.fake_volume_group.id)])
self.volume_groups_mock.create_from_src.assert_called_once_with(
None,
self.fake_volume_group.id,
None,
None,
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_volume_group_create_from_group_snapshot(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.14')
arglist = [
'--group-snapshot', self.fake_volume_group_snapshot.id,
]
verifylist = [
('group_snapshot', self.fake_volume_group_snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_group_snapshots_mock.get.assert_called_once_with(
self.fake_volume_group_snapshot.id)
self.volume_groups_mock.get.assert_called_once_with(
self.fake_volume_group.id)
self.volume_groups_mock.create_from_src.assert_called_once_with(
self.fake_volume_group_snapshot.id,
None,
None,
None,
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_volume_group_create_from_src_pre_v314(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.13')
arglist = [
'--source-group', self.fake_volume_group.id,
]
verifylist = [
('source_group', self.fake_volume_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args)
self.assertIn(
'--os-volume-api-version 3.14 or greater is required',
str(exc))
def test_volume_group_create_from_src_source_group_group_snapshot(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.14')
arglist = [
'--source-group', self.fake_volume_group.id,
'--group-snapshot', self.fake_volume_group_snapshot.id,
]
verifylist = [
('source_group', self.fake_volume_group.id),
('group_snapshot', self.fake_volume_group_snapshot.id),
]
exc = self.assertRaises(tests_utils.ParserException,
self.check_parser,
self.cmd,
arglist,
verifylist)
self.assertIn(
'--group-snapshot: not allowed with argument --source-group',
str(exc))
class TestVolumeGroupDelete(TestVolumeGroup):

View File

@ -82,15 +82,17 @@ class CreateVolumeGroup(command.ShowOne):
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
source_parser = parser.add_mutually_exclusive_group()
source_parser.add_argument(
'volume_group_type',
metavar='<volume_group_type>',
nargs='?',
help=_('Name or ID of volume group type to use.'),
)
parser.add_argument(
'volume_types',
metavar='<volume_type>',
nargs='+',
nargs='*',
default=[],
help=_('Name or ID of volume type(s) to use.'),
)
@ -107,44 +109,101 @@ class CreateVolumeGroup(command.ShowOne):
parser.add_argument(
'--availability-zone',
metavar='<availability-zone>',
help=_('Availability zone for volume group.'),
help=_('Availability zone for volume group. '
'(not available if creating group from source)'),
)
source_parser.add_argument(
'--source-group',
metavar='<source-group>',
help=_('Existing volume group (name or ID) '
'(supported by --os-volume-api-version 3.14 or later)'),
)
source_parser.add_argument(
'--group-snapshot',
metavar='<group-snapshot>',
help=_('Existing group snapshot (name or ID) '
'(supported by --os-volume-api-version 3.14 or later)'),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
if volume_client.api_version < api_versions.APIVersion('3.13'):
msg = _(
"--os-volume-api-version 3.13 or greater is required to "
"support the 'volume group create' command"
)
raise exceptions.CommandError(msg)
volume_group_type = utils.find_resource(
volume_client.group_types,
parsed_args.volume_group_type,
)
volume_types = []
for volume_type in parsed_args.volume_types:
volume_types.append(
utils.find_resource(
volume_client.volume_types,
volume_type,
if parsed_args.volume_group_type:
if volume_client.api_version < api_versions.APIVersion('3.13'):
msg = _(
"--os-volume-api-version 3.13 or greater is required to "
"support the 'volume group create' command"
)
raise exceptions.CommandError(msg)
if not parsed_args.volume_types:
msg = _(
"<volume_types> is a required argument when creating a "
"group from group type."
)
raise exceptions.CommandError(msg)
volume_group_type = utils.find_resource(
volume_client.group_types,
parsed_args.volume_group_type,
)
volume_types = []
for volume_type in parsed_args.volume_types:
volume_types.append(
utils.find_resource(
volume_client.volume_types,
volume_type,
)
)
group = volume_client.groups.create(
volume_group_type.id,
','.join(x.id for x in volume_types),
parsed_args.name,
parsed_args.description,
availability_zone=parsed_args.availability_zone)
group = volume_client.groups.create(
volume_group_type.id,
','.join(x.id for x in volume_types),
parsed_args.name,
parsed_args.description,
availability_zone=parsed_args.availability_zone)
group = volume_client.groups.get(group.id)
group = volume_client.groups.get(group.id)
return _format_group(group)
return _format_group(group)
else:
if volume_client.api_version < api_versions.APIVersion('3.14'):
msg = _(
"--os-volume-api-version 3.14 or greater is required to "
"support the 'volume group create "
"[--source-group|--group-snapshot]' command"
)
raise exceptions.CommandError(msg)
if (parsed_args.source_group is None and
parsed_args.group_snapshot is None):
msg = _(
"Either --source-group <source_group> or "
"'--group-snapshot <group_snapshot>' needs to be "
"provided to run the 'volume group create "
"[--source-group|--group-snapshot]' command"
)
raise exceptions.CommandError(msg)
if parsed_args.availability_zone:
msg = _("'--availability-zone' option will not work "
"if creating group from source.")
LOG.warning(msg)
source_group = None
if parsed_args.source_group:
source_group = utils.find_resource(volume_client.groups,
parsed_args.source_group)
group_snapshot = None
if parsed_args.group_snapshot:
group_snapshot = utils.find_resource(
volume_client.group_snapshots,
parsed_args.group_snapshot)
group = volume_client.groups.create_from_src(
group_snapshot.id if group_snapshot else None,
source_group.id if source_group else None,
parsed_args.name,
parsed_args.description)
group = volume_client.groups.get(group.id)
return _format_group(group)
class DeleteVolumeGroup(command.Command):

View File

@ -0,0 +1,6 @@
---
features:
- |
Added ``--source-group`` and ``--group-snapshot`` options to the
``volume group create`` command to allow creating group from
a source group or a group snapshot.