Browse Source

Merge "volume: Add 'volume group snapshot *' commands"

changes/68/797368/1
Zuul 3 months ago
committed by Gerrit Code Review
parent
commit
3f3d8829ce
  1. 8
      doc/source/cli/command-objects/volume-group-snapshot.rst
  2. 1
      doc/source/cli/commands.rst
  3. 8
      doc/source/cli/data/cinder.csv
  4. 53
      openstackclient/tests/unit/volume/v3/fakes.py
  5. 262
      openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py
  6. 234
      openstackclient/volume/v3/volume_group_snapshot.py
  7. 6
      releasenotes/notes/add-volume-group-snapshot-commands-27fa8920d55f6bdb.yaml
  8. 5
      setup.cfg

8
doc/source/cli/command-objects/volume-group-snapshot.rst

@ -0,0 +1,8 @@
=====================
volume group snapshot
=====================
Block Storage v3
.. autoprogram-cliff:: openstack.volume.v3
:command: volume group snapshot *

1
doc/source/cli/commands.rst

@ -160,6 +160,7 @@ referring to both Compute and Volume quotas.
* ``volume backup record``: (**Volume**) volume record that can be imported or exported
* ``volume backend``: (**Volume**) volume backend storage
* ``volume group``: (**Volume**) group of volumes
* ``volume group snapshot``: (**Volume**) a point-in-time copy of a volume group
* ``volume group type``: (**Volume**) deployment-specific types of volumes groups available
* ``volume host``: (**Volume**) the physical computer for volumes
* ``volume message``: (**Volume**) volume API internal messages detailing volume failure messages

8
doc/source/cli/data/cinder.csv

@ -53,10 +53,10 @@ group-failover-replication,volume group failover,Fails over replication for grou
group-list,volume group list,Lists all groups. (Supported by API versions 3.13 - 3.latest)
group-list-replication-targets,volume group list --replication-targets,Lists replication targets for group. (Supported by API versions 3.38 - 3.latest)
group-show,volume group show,Shows details of a group. (Supported by API versions 3.13 - 3.latest)
group-snapshot-create,,Creates a group snapshot. (Supported by API versions 3.14 - 3.latest)
group-snapshot-delete,,Removes one or more group snapshots. (Supported by API versions 3.14 - 3.latest)
group-snapshot-list,,Lists all group snapshots. (Supported by API versions 3.14 - 3.latest)
group-snapshot-show,,Shows group snapshot details. (Supported by API versions 3.14 - 3.latest)
group-snapshot-create,volume group snapshot create,Creates a group snapshot. (Supported by API versions 3.14 - 3.latest)
group-snapshot-delete,volume group snapshot delete,Removes one or more group snapshots. (Supported by API versions 3.14 - 3.latest)
group-snapshot-list,volume group snapshot list,Lists all group snapshots. (Supported by API versions 3.14 - 3.latest)
group-snapshot-show,volume group snapshot show,Shows group snapshot details. (Supported by API versions 3.14 - 3.latest)
group-specs-list,volume group type list,Lists current group types and specs. (Supported by API versions 3.11 - 3.latest)
group-type-create,volume group type create,Creates a group type. (Supported by API versions 3.11 - 3.latest)
group-type-default,volume group type list --default,List the default group type. (Supported by API versions 3.11 - 3.latest)

53
openstackclient/tests/unit/volume/v3/fakes.py

@ -34,6 +34,8 @@ class FakeVolumeClient(object):
self.attachments.resource_class = fakes.FakeResource(None, {})
self.groups = mock.Mock()
self.groups.resource_class = fakes.FakeResource(None, {})
self.group_snapshots = mock.Mock()
self.group_snapshots.resource_class = fakes.FakeResource(None, {})
self.group_types = mock.Mock()
self.group_types.resource_class = fakes.FakeResource(None, {})
self.messages = mock.Mock()
@ -125,6 +127,57 @@ class FakeVolumeGroup:
return groups
class FakeVolumeGroupSnapshot:
"""Fake one or more volume group snapshots."""
@staticmethod
def create_one_volume_group_snapshot(attrs=None, methods=None):
"""Create a fake group snapshot.
:param attrs: A dictionary with all attributes
:param methods: A dictionary with all methods
:return: A FakeResource object with id, name, description, etc.
"""
attrs = attrs or {}
# Set default attribute
group_snapshot_info = {
'id': uuid.uuid4().hex,
'name': f'group-snapshot-{uuid.uuid4().hex}',
'description': f'description-{uuid.uuid4().hex}',
'status': random.choice(['available']),
'group_id': uuid.uuid4().hex,
'group_type_id': uuid.uuid4().hex,
'project_id': uuid.uuid4().hex,
}
# Overwrite default attributes if there are some attributes set
group_snapshot_info.update(attrs)
group_snapshot = fakes.FakeResource(
None,
group_snapshot_info,
methods=methods,
loaded=True)
return group_snapshot
@staticmethod
def create_volume_group_snapshots(attrs=None, count=2):
"""Create multiple fake group snapshots.
:param attrs: A dictionary with all attributes of group snapshot
:param count: The number of group snapshots to be faked
:return: A list of FakeResource objects
"""
group_snapshots = []
for n in range(0, count):
group_snapshots.append(
FakeVolumeGroupSnapshot.create_one_volume_group_snapshot(attrs)
)
return group_snapshots
class FakeVolumeGroupType:
"""Fake one or more volume group types."""

262
openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py

@ -0,0 +1,262 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cinderclient import api_versions
from osc_lib import exceptions
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
from openstackclient.volume.v3 import volume_group_snapshot
class TestVolumeGroupSnapshot(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.volume_groups_mock = self.app.client_manager.volume.groups
self.volume_groups_mock.reset_mock()
self.volume_group_snapshots_mock = \
self.app.client_manager.volume.group_snapshots
self.volume_group_snapshots_mock.reset_mock()
class TestVolumeGroupSnapshotCreate(TestVolumeGroupSnapshot):
fake_volume_group = volume_fakes.FakeVolumeGroup.create_one_volume_group()
fake_volume_group_snapshot = \
volume_fakes.FakeVolumeGroupSnapshot.create_one_volume_group_snapshot()
columns = (
'ID',
'Status',
'Name',
'Description',
'Group',
'Group Type',
)
data = (
fake_volume_group_snapshot.id,
fake_volume_group_snapshot.status,
fake_volume_group_snapshot.name,
fake_volume_group_snapshot.description,
fake_volume_group_snapshot.group_id,
fake_volume_group_snapshot.group_type_id,
)
def setUp(self):
super().setUp()
self.volume_groups_mock.get.return_value = self.fake_volume_group
self.volume_group_snapshots_mock.create.return_value = \
self.fake_volume_group_snapshot
self.volume_group_snapshots_mock.get.return_value = \
self.fake_volume_group_snapshot
self.cmd = volume_group_snapshot.CreateVolumeGroupSnapshot(
self.app, None)
def test_volume_group_snapshot_create(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.14')
arglist = [
self.fake_volume_group.id,
]
verifylist = [
('volume_group', self.fake_volume_group.id),
('name', None),
('description', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_groups_mock.get.assert_called_once_with(
self.fake_volume_group.id)
self.volume_group_snapshots_mock.create.assert_called_once_with(
self.fake_volume_group.id, None, None,
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_volume_group_snapshot_create_with_options(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.14')
arglist = [
self.fake_volume_group.id,
'--name', 'foo',
'--description', 'hello, world',
]
verifylist = [
('volume_group', self.fake_volume_group.id),
('name', 'foo'),
('description', 'hello, world'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_groups_mock.get.assert_called_once_with(
self.fake_volume_group.id)
self.volume_group_snapshots_mock.create.assert_called_once_with(
self.fake_volume_group.id, 'foo', 'hello, world',
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_volume_group_snapshot_create_pre_v314(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.13')
arglist = [
self.fake_volume_group.id,
]
verifylist = [
('volume_group', self.fake_volume_group.id),
('name', None),
('description', None),
]
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))
class TestVolumeGroupSnapshotDelete(TestVolumeGroupSnapshot):
fake_volume_group_snapshot = \
volume_fakes.FakeVolumeGroupSnapshot.create_one_volume_group_snapshot()
def setUp(self):
super().setUp()
self.volume_group_snapshots_mock.get.return_value = \
self.fake_volume_group_snapshot
self.volume_group_snapshots_mock.delete.return_value = None
self.cmd = volume_group_snapshot.DeleteVolumeGroupSnapshot(
self.app, None)
def test_volume_group_snapshot_delete(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.14')
arglist = [
self.fake_volume_group_snapshot.id,
]
verifylist = [
('snapshot', self.fake_volume_group_snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.volume_group_snapshots_mock.delete.assert_called_once_with(
self.fake_volume_group_snapshot.id,
)
self.assertIsNone(result)
def test_volume_group_snapshot_delete_pre_v314(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.13')
arglist = [
self.fake_volume_group_snapshot.id,
]
verifylist = [
('snapshot', self.fake_volume_group_snapshot.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))
class TestVolumeGroupSnapshotList(TestVolumeGroupSnapshot):
fake_volume_group_snapshots = \
volume_fakes.FakeVolumeGroupSnapshot.create_volume_group_snapshots()
columns = (
'ID',
'Status',
'Name',
)
data = [
(
fake_volume_group_snapshot.id,
fake_volume_group_snapshot.status,
fake_volume_group_snapshot.name,
) for fake_volume_group_snapshot in fake_volume_group_snapshots
]
def setUp(self):
super().setUp()
self.volume_group_snapshots_mock.list.return_value = \
self.fake_volume_group_snapshots
self.cmd = volume_group_snapshot.ListVolumeGroupSnapshot(
self.app, None)
def test_volume_group_snapshot_list(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.14')
arglist = [
'--all-projects',
]
verifylist = [
('all_projects', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_group_snapshots_mock.list.assert_called_once_with(
search_opts={
'all_tenants': True,
},
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(tuple(self.data), data)
def test_volume_group_snapshot_list_pre_v314(self):
self.app.client_manager.volume.api_version = \
api_versions.APIVersion('3.13')
arglist = [
]
verifylist = [
('all_projects', False),
]
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))

234
openstackclient/volume/v3/volume_group_snapshot.py

@ -0,0 +1,234 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from cinderclient import api_versions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
def _format_group_snapshot(snapshot):
columns = (
'id',
'status',
'name',
'description',
'group_id',
'group_type_id',
)
column_headers = (
'ID',
'Status',
'Name',
'Description',
'Group',
'Group Type',
)
return (
column_headers,
utils.get_item_properties(
snapshot,
columns,
),
)
class CreateVolumeGroupSnapshot(command.ShowOne):
"""Create a volume group snapshot.
This command requires ``--os-volume-api-version`` 3.13 or greater.
"""
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'volume_group',
metavar='<volume_group>',
help=_('Name or ID of volume group to create a snapshot of.'),
)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Name of the volume group snapshot.'),
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('Description of a volume group snapshot.')
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
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 snapshot create' command"
)
raise exceptions.CommandError(msg)
volume_group = utils.find_resource(
volume_client.groups,
parsed_args.volume_group,
)
snapshot = volume_client.group_snapshots.create(
volume_group.id,
parsed_args.name,
parsed_args.description)
return _format_group_snapshot(snapshot)
class DeleteVolumeGroupSnapshot(command.Command):
"""Delete a volume group snapshot.
This command requires ``--os-volume-api-version`` 3.14 or greater.
"""
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'snapshot',
metavar='<snapshot>',
help=_('Name or ID of volume group snapshot to delete'),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
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 snapshot delete' command"
)
raise exceptions.CommandError(msg)
snapshot = utils.find_resource(
volume_client.group_snapshots,
parsed_args.snapshot,
)
volume_client.group_snapshots.delete(snapshot.id)
class ListVolumeGroupSnapshot(command.Lister):
"""Lists all volume group snapshot.
This command requires ``--os-volume-api-version`` 3.14 or greater.
"""
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'--all-projects',
dest='all_projects',
action='store_true',
default=utils.env('ALL_PROJECTS', default=False),
help=_('Shows details for all projects (admin only).'),
)
# TODO(stephenfin): Add once we have an equivalent command for
# 'cinder list-filters'
# parser.add_argument(
# '--filter',
# metavar='<key=value>',
# action=parseractions.KeyValueAction,
# dest='filters',
# help=_(
# "Filter key and value pairs. Use 'foo' to "
# "check enabled filters from server. Use 'key~=value' for "
# "inexact filtering if the key supports "
# "(supported by --os-volume-api-version 3.33 or above)"
# ),
# )
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
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 snapshot list' command"
)
raise exceptions.CommandError(msg)
search_opts = {
'all_tenants': parsed_args.all_projects,
}
groups = volume_client.group_snapshots.list(
search_opts=search_opts)
column_headers = (
'ID',
'Status',
'Name',
)
columns = (
'id',
'status',
'name',
)
return (
column_headers,
(
utils.get_item_properties(a, columns)
for a in groups
),
)
class ShowVolumeGroupSnapshot(command.ShowOne):
"""Show detailed information for a volume group snapshot.
This command requires ``--os-volume-api-version`` 3.14 or greater.
"""
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'snapshot',
metavar='<snapshot>',
help=_('Name or ID of volume group snapshot.'),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
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 snapshot show' command"
)
raise exceptions.CommandError(msg)
snapshot = utils.find_resource(
volume_client.group_snapshots,
parsed_args.snapshot,
)
# TODO(stephenfin): Do we need this?
snapshot = volume_client.groups.show(snapshot.id)
return _format_group_snapshot(snapshot)

6
releasenotes/notes/add-volume-group-snapshot-commands-27fa8920d55f6bdb.yaml

@ -0,0 +1,6 @@
---
features:
- |
Add ``volume group snapshot create``, ``volume group snapshot delete``,
``volume group snapshot list`` and ``volume group snapshot show`` commands
to create, delete, list, and show volume group snapshots, respectively.

5
setup.cfg

@ -728,6 +728,11 @@ openstack.volume.v3 =
volume_group_unset = openstackclient.volume.v3.volume_group:UnsetVolumeGroup
volume_group_show = openstackclient.volume.v3.volume_group:ShowVolumeGroup
volume_group_snapshot_create = openstackclient.volume.v3.volume_group_snapshot:CreateVolumeGroupSnapshot
volume_group_snapshot_delete = openstackclient.volume.v3.volume_group_snapshot:DeleteVolumeGroupSnapshot
volume_group_snapshot_list = openstackclient.volume.v3.volume_group_snapshot:ListVolumeGroupSnapshot
volume_group_snapshot_show = openstackclient.volume.v3.volume_group_snapshot:ShowVolumeGroupSnapshot
volume_group_type_create = openstackclient.volume.v3.volume_group_type:CreateVolumeGroupType
volume_group_type_delete = openstackclient.volume.v3.volume_group_type:DeleteVolumeGroupType
volume_group_type_list = openstackclient.volume.v3.volume_group_type:ListVolumeGroupType

Loading…
Cancel
Save