volume: Add 'volume message *' commands
This patch implements the necessary commands to utilize the Messages API introduced in Cinder API version 3.3. Version 3.5 built upon this by implementing pagination support for these commands which is present in this patch as well. volume message get volume message list volume message delete Change-Id: I64aa0b4a8d4468baa8c63e5e30ee31de68df999d
This commit is contained in:
parent
6dc94e1fb8
commit
0eddab36e5
8
doc/source/cli/command-objects/volume-message.rst
Normal file
8
doc/source/cli/command-objects/volume-message.rst
Normal file
@ -0,0 +1,8 @@
|
||||
==============
|
||||
volume message
|
||||
==============
|
||||
|
||||
Block Storage v3
|
||||
|
||||
.. autoprogram-cliff:: openstack.volume.v3
|
||||
:command: volume message *
|
@ -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 host``: (**Volume**) the physical computer for volumes
|
||||
* ``volume message``: (**Volume**) volume API internal messages detailing volume failure messages
|
||||
* ``volume qos``: (**Volume**) quality-of-service (QoS) specification for volumes
|
||||
* ``volume snapshot``: (**Volume**) a point-in-time copy of a volume
|
||||
* ``volume type``: (**Volume**) deployment-specific types of volumes available
|
||||
|
@ -72,9 +72,9 @@ list,volume list,Lists all volumes.
|
||||
list-filters,,List enabled filters. (Supported by API versions 3.33 - 3.latest)
|
||||
manage,volume create --remote-source k=v,Manage an existing volume.
|
||||
manageable-list,,Lists all manageable volumes. (Supported by API versions 3.8 - 3.latest)
|
||||
message-delete,,Removes one or more messages. (Supported by API versions 3.3 - 3.latest)
|
||||
message-list,,Lists all messages. (Supported by API versions 3.3 - 3.latest)
|
||||
message-show,,Shows message details. (Supported by API versions 3.3 - 3.latest)
|
||||
message-delete,volume message delete,Removes one or more messages. (Supported by API versions 3.3 - 3.latest)
|
||||
message-list,volume message list,Lists all messages. (Supported by API versions 3.3 - 3.latest)
|
||||
message-show,volume message show,Shows message details. (Supported by API versions 3.3 - 3.latest)
|
||||
metadata,volume set --property k=v / volume unset --property k,Sets or deletes volume metadata.
|
||||
metadata-show,volume show,Shows volume metadata.
|
||||
metadata-update-all,volume set --property k=v,Updates volume metadata.
|
||||
|
|
@ -32,6 +32,8 @@ class FakeVolumeClient(object):
|
||||
|
||||
self.attachments = mock.Mock()
|
||||
self.attachments.resource_class = fakes.FakeResource(None, {})
|
||||
self.messages = mock.Mock()
|
||||
self.messages.resource_class = fakes.FakeResource(None, {})
|
||||
self.volumes = mock.Mock()
|
||||
self.volumes.resource_class = fakes.FakeResource(None, {})
|
||||
|
||||
@ -59,6 +61,72 @@ class TestVolume(utils.TestCommand):
|
||||
FakeVolume = volume_v2_fakes.FakeVolume
|
||||
|
||||
|
||||
class FakeVolumeMessage:
|
||||
"""Fake one or more volume messages."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_volume_message(attrs=None):
|
||||
"""Create a fake message.
|
||||
|
||||
:param attrs: A dictionary with all attributes of message
|
||||
:return: A FakeResource object with id, name, status, etc.
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attribute
|
||||
message_info = {
|
||||
'created_at': '2016-02-11T11:17:37.000000',
|
||||
'event_id': f'VOLUME_{random.randint(1, 999999):06d}',
|
||||
'guaranteed_until': '2016-02-11T11:17:37.000000',
|
||||
'id': uuid.uuid4().hex,
|
||||
'message_level': 'ERROR',
|
||||
'request_id': f'req-{uuid.uuid4().hex}',
|
||||
'resource_type': 'VOLUME',
|
||||
'resource_uuid': uuid.uuid4().hex,
|
||||
'user_message': f'message-{uuid.uuid4().hex}',
|
||||
}
|
||||
|
||||
# Overwrite default attributes if there are some attributes set
|
||||
message_info.update(attrs)
|
||||
|
||||
message = fakes.FakeResource(
|
||||
None,
|
||||
message_info,
|
||||
loaded=True)
|
||||
return message
|
||||
|
||||
@staticmethod
|
||||
def create_volume_messages(attrs=None, count=2):
|
||||
"""Create multiple fake messages.
|
||||
|
||||
:param attrs: A dictionary with all attributes of message
|
||||
:param count: The number of messages to be faked
|
||||
:return: A list of FakeResource objects
|
||||
"""
|
||||
messages = []
|
||||
for n in range(0, count):
|
||||
messages.append(FakeVolumeMessage.create_one_volume_message(attrs))
|
||||
|
||||
return messages
|
||||
|
||||
@staticmethod
|
||||
def get_volume_messages(messages=None, count=2):
|
||||
"""Get an iterable MagicMock object with a list of faked messages.
|
||||
|
||||
If messages list is provided, then initialize the Mock object with the
|
||||
list. Otherwise create one.
|
||||
|
||||
:param messages: A list of FakeResource objects faking messages
|
||||
:param count: The number of messages to be faked
|
||||
:return An iterable Mock object with side_effect set to a list of faked
|
||||
messages
|
||||
"""
|
||||
if messages is None:
|
||||
messages = FakeVolumeMessage.create_messages(count)
|
||||
|
||||
return mock.Mock(side_effect=messages)
|
||||
|
||||
|
||||
class FakeVolumeAttachment:
|
||||
"""Fake one or more volume attachments."""
|
||||
|
||||
|
324
openstackclient/tests/unit/volume/v3/test_volume_message.py
Normal file
324
openstackclient/tests/unit/volume/v3/test_volume_message.py
Normal file
@ -0,0 +1,324 @@
|
||||
# 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 unittest.mock import call
|
||||
|
||||
from cinderclient import api_versions
|
||||
from osc_lib import exceptions
|
||||
|
||||
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
|
||||
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
|
||||
from openstackclient.volume.v3 import volume_message
|
||||
|
||||
|
||||
class TestVolumeMessage(volume_fakes.TestVolume):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.projects_mock = self.app.client_manager.identity.projects
|
||||
self.projects_mock.reset_mock()
|
||||
|
||||
self.volume_messages_mock = self.app.client_manager.volume.messages
|
||||
self.volume_messages_mock.reset_mock()
|
||||
|
||||
|
||||
class TestVolumeMessageDelete(TestVolumeMessage):
|
||||
|
||||
fake_messages = volume_fakes.FakeVolumeMessage.create_volume_messages(
|
||||
count=2)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.volume_messages_mock.get = \
|
||||
volume_fakes.FakeVolumeMessage.get_volume_messages(
|
||||
self.fake_messages)
|
||||
self.volume_messages_mock.delete.return_value = None
|
||||
|
||||
# Get the command object to mock
|
||||
self.cmd = volume_message.DeleteMessage(self.app, None)
|
||||
|
||||
def test_message_delete(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.3')
|
||||
|
||||
arglist = [
|
||||
self.fake_messages[0].id,
|
||||
]
|
||||
verifylist = [
|
||||
('message_ids', [self.fake_messages[0].id]),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.volume_messages_mock.delete.assert_called_with(
|
||||
self.fake_messages[0].id)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_message_delete_multiple_messages(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.3')
|
||||
|
||||
arglist = [
|
||||
self.fake_messages[0].id,
|
||||
self.fake_messages[1].id,
|
||||
]
|
||||
verifylist = [
|
||||
('message_ids', arglist),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
calls = []
|
||||
for m in self.fake_messages:
|
||||
calls.append(call(m.id))
|
||||
self.volume_messages_mock.delete.assert_has_calls(calls)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_message_delete_multiple_messages_with_exception(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.3')
|
||||
|
||||
arglist = [
|
||||
self.fake_messages[0].id,
|
||||
'invalid_message',
|
||||
]
|
||||
verifylist = [
|
||||
('message_ids', arglist),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.volume_messages_mock.delete.side_effect = [
|
||||
self.fake_messages[0], exceptions.CommandError]
|
||||
|
||||
exc = self.assertRaises(
|
||||
exceptions.CommandError,
|
||||
self.cmd.take_action, parsed_args)
|
||||
self.assertEqual('Failed to delete 1 of 2 messages.', str(exc))
|
||||
|
||||
self.volume_messages_mock.delete.assert_any_call(
|
||||
self.fake_messages[0].id)
|
||||
self.volume_messages_mock.delete.assert_any_call('invalid_message')
|
||||
|
||||
self.assertEqual(2, self.volume_messages_mock.delete.call_count)
|
||||
|
||||
def test_message_delete_pre_v33(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.2')
|
||||
|
||||
arglist = [
|
||||
self.fake_messages[0].id,
|
||||
]
|
||||
verifylist = [
|
||||
('message_ids', [self.fake_messages[0].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.3 or greater is required',
|
||||
str(exc))
|
||||
|
||||
|
||||
class TestVolumeMessageList(TestVolumeMessage):
|
||||
|
||||
fake_project = identity_fakes.FakeProject.create_one_project()
|
||||
fake_messages = volume_fakes.FakeVolumeMessage.create_volume_messages(
|
||||
count=3)
|
||||
|
||||
columns = (
|
||||
'ID',
|
||||
'Event ID',
|
||||
'Resource Type',
|
||||
'Resource UUID',
|
||||
'Message Level',
|
||||
'User Message',
|
||||
'Request ID',
|
||||
'Created At',
|
||||
'Guaranteed Until',
|
||||
)
|
||||
data = []
|
||||
for fake_message in fake_messages:
|
||||
data.append((
|
||||
fake_message.id,
|
||||
fake_message.event_id,
|
||||
fake_message.resource_type,
|
||||
fake_message.resource_uuid,
|
||||
fake_message.message_level,
|
||||
fake_message.user_message,
|
||||
fake_message.request_id,
|
||||
fake_message.created_at,
|
||||
fake_message.guaranteed_until,
|
||||
))
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.projects_mock.get.return_value = self.fake_project
|
||||
self.volume_messages_mock.list.return_value = self.fake_messages
|
||||
# Get the command to test
|
||||
self.cmd = volume_message.ListMessages(self.app, None)
|
||||
|
||||
def test_message_list(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.3')
|
||||
|
||||
arglist = []
|
||||
verifylist = [
|
||||
('project', None),
|
||||
('marker', None),
|
||||
('limit', None),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
search_opts = {
|
||||
'project_id': None,
|
||||
}
|
||||
self.volume_messages_mock.list.assert_called_with(
|
||||
search_opts=search_opts,
|
||||
marker=None,
|
||||
limit=None,
|
||||
)
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertItemsEqual(self.data, list(data))
|
||||
|
||||
def test_message_list_with_options(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.3')
|
||||
|
||||
arglist = [
|
||||
'--project', self.fake_project.name,
|
||||
'--marker', self.fake_messages[0].id,
|
||||
'--limit', '3',
|
||||
]
|
||||
verifylist = [
|
||||
('project', self.fake_project.name),
|
||||
('marker', self.fake_messages[0].id),
|
||||
('limit', 3),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
search_opts = {
|
||||
'project_id': self.fake_project.id,
|
||||
}
|
||||
self.volume_messages_mock.list.assert_called_with(
|
||||
search_opts=search_opts,
|
||||
marker=self.fake_messages[0].id,
|
||||
limit=3,
|
||||
)
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertItemsEqual(self.data, list(data))
|
||||
|
||||
def test_message_list_pre_v33(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.2')
|
||||
|
||||
arglist = []
|
||||
verifylist = [
|
||||
('project', None),
|
||||
('marker', None),
|
||||
('limit', 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.3 or greater is required',
|
||||
str(exc))
|
||||
|
||||
|
||||
class TestVolumeMessageShow(TestVolumeMessage):
|
||||
|
||||
fake_message = volume_fakes.FakeVolumeMessage.create_one_volume_message()
|
||||
|
||||
columns = (
|
||||
'created_at',
|
||||
'event_id',
|
||||
'guaranteed_until',
|
||||
'id',
|
||||
'message_level',
|
||||
'request_id',
|
||||
'resource_type',
|
||||
'resource_uuid',
|
||||
'user_message',
|
||||
)
|
||||
data = (
|
||||
fake_message.created_at,
|
||||
fake_message.event_id,
|
||||
fake_message.guaranteed_until,
|
||||
fake_message.id,
|
||||
fake_message.message_level,
|
||||
fake_message.request_id,
|
||||
fake_message.resource_type,
|
||||
fake_message.resource_uuid,
|
||||
fake_message.user_message,
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.volume_messages_mock.get.return_value = self.fake_message
|
||||
# Get the command object to test
|
||||
self.cmd = volume_message.ShowMessage(self.app, None)
|
||||
|
||||
def test_message_show(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.3')
|
||||
|
||||
arglist = [
|
||||
self.fake_message.id
|
||||
]
|
||||
verifylist = [
|
||||
('message_id', self.fake_message.id)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.volume_messages_mock.get.assert_called_with(self.fake_message.id)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.data, data)
|
||||
|
||||
def test_message_show_pre_v33(self):
|
||||
self.app.client_manager.volume.api_version = \
|
||||
api_versions.APIVersion('3.2')
|
||||
|
||||
arglist = [
|
||||
self.fake_message.id
|
||||
]
|
||||
verifylist = [
|
||||
('message_id', self.fake_message.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.3 or greater is required',
|
||||
str(exc))
|
165
openstackclient/volume/v3/volume_message.py
Normal file
165
openstackclient/volume/v3/volume_message.py
Normal file
@ -0,0 +1,165 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""Volume V3 Messages implementations"""
|
||||
|
||||
import logging as LOG
|
||||
|
||||
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 _
|
||||
from openstackclient.identity import common as identity_common
|
||||
|
||||
|
||||
class DeleteMessage(command.Command):
|
||||
_description = _('Delete a volume failure message')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super().get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'message_ids',
|
||||
metavar='<message-id>',
|
||||
nargs='+',
|
||||
help=_('Message(s) to delete (ID)')
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
|
||||
if volume_client.api_version < api_versions.APIVersion('3.3'):
|
||||
msg = _(
|
||||
"--os-volume-api-version 3.3 or greater is required to "
|
||||
"support the 'volume message delete' command"
|
||||
)
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
errors = 0
|
||||
for message_id in parsed_args.message_ids:
|
||||
try:
|
||||
volume_client.messages.delete(message_id)
|
||||
except Exception:
|
||||
LOG.error(_('Failed to delete message: %s'), message_id)
|
||||
errors += 1
|
||||
|
||||
if errors > 0:
|
||||
total = len(parsed_args.message_ids)
|
||||
msg = _('Failed to delete %(errors)s of %(total)s messages.') % {
|
||||
'errors': errors, 'total': total,
|
||||
}
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
class ListMessages(command.Lister):
|
||||
_description = _('List volume failure messages')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super().get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'--project',
|
||||
metavar='<project>',
|
||||
help=_('Filter results by project (name or ID) (admin only)'),
|
||||
)
|
||||
identity_common.add_project_domain_option_to_parser(parser)
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
metavar='<message-id>',
|
||||
help=_('The last message ID of the previous page'),
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
metavar='<limit>',
|
||||
help=_('Maximum number of messages to display'),
|
||||
default=None,
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
identity_client = self.app.client_manager.identity
|
||||
|
||||
if volume_client.api_version < api_versions.APIVersion('3.3'):
|
||||
msg = _(
|
||||
"--os-volume-api-version 3.3 or greater is required to "
|
||||
"support the 'volume message list' command"
|
||||
)
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
column_headers = (
|
||||
'ID',
|
||||
'Event ID',
|
||||
'Resource Type',
|
||||
'Resource UUID',
|
||||
'Message Level',
|
||||
'User Message',
|
||||
'Request ID',
|
||||
'Created At',
|
||||
'Guaranteed Until',
|
||||
)
|
||||
|
||||
project_id = None
|
||||
if parsed_args.project:
|
||||
project_id = identity_common.find_project(
|
||||
identity_client,
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain).id
|
||||
|
||||
search_opts = {
|
||||
'project_id': project_id,
|
||||
}
|
||||
data = volume_client.messages.list(
|
||||
search_opts=search_opts,
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit)
|
||||
|
||||
return (
|
||||
column_headers,
|
||||
(utils.get_item_properties(s, column_headers) for s in data)
|
||||
)
|
||||
|
||||
|
||||
class ShowMessage(command.ShowOne):
|
||||
_description = _('Show a volume failure message')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowMessage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'message_id',
|
||||
metavar='<message-id>',
|
||||
help=_('Message to show (ID).')
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
|
||||
if volume_client.api_version < api_versions.APIVersion('3.3'):
|
||||
msg = _(
|
||||
"--os-volume-api-version 3.3 or greater is required to "
|
||||
"support the 'volume message show' command"
|
||||
)
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
message = volume_client.messages.get(parsed_args.message_id)
|
||||
|
||||
return zip(*sorted(message._info.items()))
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add ``volume message list``, ``volume message get`` and
|
||||
``volume message delete`` commands, to list, get and delete volume
|
||||
failure messages, respectively.
|
@ -716,6 +716,10 @@ openstack.volume.v3 =
|
||||
|
||||
volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost
|
||||
|
||||
volume_message_delete = openstackclient.volume.v3.volume_message:DeleteMessage
|
||||
volume_message_list = openstackclient.volume.v3.volume_message:ListMessages
|
||||
volume_message_show = openstackclient.volume.v3.volume_message:ShowMessage
|
||||
|
||||
volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot
|
||||
volume_snapshot_delete = openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot
|
||||
volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot
|
||||
|
Loading…
Reference in New Issue
Block a user