Add commands for user messages
Allows listing, showing and deleting of user messages. Change-Id: I5ffb840a271c518f62ee1accfd8e20a97f45594d Partially-implements: blueprint user-messages Depends-On: Ia0cc524e0bfb2ca5e495e575e17e9911c746690b
This commit is contained in:
parent
757bb793a8
commit
8bf5f9b7e0
@ -27,7 +27,7 @@ from manilaclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_VERSION = '2.33'
|
||||
MAX_VERSION = '2.37'
|
||||
MIN_VERSION = '2.0'
|
||||
DEPRECATED_VERSION = '1.0'
|
||||
_VERSIONED_METHOD_MAP = {}
|
||||
|
@ -81,3 +81,8 @@ V2_SERVICE_TYPE = 'sharev2'
|
||||
SERVICE_TYPES = {'1': V1_SERVICE_TYPE, '2': V2_SERVICE_TYPE}
|
||||
|
||||
EXTENSION_PLUGIN_NAMESPACE = 'manilaclient.common.apiclient.auth'
|
||||
MESSAGE_SORT_KEY_VALUES = (
|
||||
'id', 'project_id', 'request_id', 'resource_type', 'action_id',
|
||||
'detail_id', 'resource_id', 'message_level', 'expires_at',
|
||||
'request_id', 'created_at'
|
||||
)
|
||||
|
@ -17,6 +17,7 @@ import traceback
|
||||
|
||||
from oslo_log import log
|
||||
from tempest.lib.cli import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
from manilaclient import config
|
||||
@ -324,3 +325,40 @@ class BaseTestCase(base.ClientTestBase):
|
||||
if wait_for_creation:
|
||||
client.wait_for_snapshot_status(snapshot['id'], 'available')
|
||||
return snapshot
|
||||
|
||||
@classmethod
|
||||
def create_message(cls, client=None, wait_for_creation=True,
|
||||
cleanup_in_class=False, microversion=None):
|
||||
"""Trigger a 'no valid host' situation to generate a message."""
|
||||
if client is None:
|
||||
client = cls.get_admin_client()
|
||||
|
||||
extra_specs = {
|
||||
'vendor_name': 'foobar',
|
||||
}
|
||||
share_type_name = data_utils.rand_name("share-type")
|
||||
cls.create_share_type(
|
||||
name=share_type_name, extra_specs=extra_specs,
|
||||
driver_handles_share_servers=False, client=client,
|
||||
cleanup_in_class=cleanup_in_class, microversion=microversion)
|
||||
|
||||
share_name = data_utils.rand_name("share")
|
||||
share = cls.create_share(
|
||||
name=share_name, share_type=share_type_name,
|
||||
cleanup_in_class=cleanup_in_class, microversion=microversion,
|
||||
wait_for_creation=False, client=client)
|
||||
|
||||
client.wait_for_share_status(share['id'], "error")
|
||||
message = client.wait_for_message(share['id'])
|
||||
|
||||
resource = {
|
||||
"type": "message",
|
||||
"id": message["ID"],
|
||||
"client": client,
|
||||
"microversion": microversion,
|
||||
}
|
||||
if cleanup_in_class:
|
||||
cls.class_resources.insert(0, resource)
|
||||
else:
|
||||
cls.method_resources.insert(0, resource)
|
||||
return message
|
||||
|
@ -29,6 +29,7 @@ from manilaclient.tests.functional import exceptions
|
||||
from manilaclient.tests.functional import utils
|
||||
|
||||
CONF = config.CONF
|
||||
MESSAGE = 'message'
|
||||
SHARE = 'share'
|
||||
SHARE_TYPE = 'share_type'
|
||||
SHARE_NETWORK = 'share_network'
|
||||
@ -133,6 +134,8 @@ class ManilaCLIClient(base.CLIClient):
|
||||
func = self.is_share_deleted
|
||||
elif res_type == SNAPSHOT:
|
||||
func = self.is_snapshot_deleted
|
||||
elif res_type == MESSAGE:
|
||||
func = self.is_message_deleted
|
||||
else:
|
||||
raise exceptions.InvalidResource(message=res_type)
|
||||
|
||||
@ -1363,3 +1366,70 @@ class ManilaCLIClient(base.CLIClient):
|
||||
self.wait_for_resource_deletion(
|
||||
SHARE_SERVER, res_id=share_server, interval=3, timeout=60,
|
||||
microversion=microversion)
|
||||
|
||||
# user messages
|
||||
|
||||
def wait_for_message(self, resource_id):
|
||||
"""Waits until a message for a resource with given id exists"""
|
||||
start = int(time.time())
|
||||
message = None
|
||||
|
||||
while not message:
|
||||
time.sleep(self.build_interval)
|
||||
for msg in self.list_messages():
|
||||
if msg['Resource ID'] == resource_id:
|
||||
return msg
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('No message for resource with id %s was created in'
|
||||
' the required time (%s s).' %
|
||||
(resource_id, self.build_timeout))
|
||||
raise tempest_lib_exc.TimeoutException(message)
|
||||
|
||||
def list_messages(self, columns=None, microversion=None):
|
||||
"""List messages.
|
||||
|
||||
:param columns: str -- comma separated string of columns.
|
||||
Example, "--columns id,resource_id".
|
||||
:param microversion: API microversion to be used for request.
|
||||
"""
|
||||
cmd = "message-list"
|
||||
if columns is not None:
|
||||
cmd += " --columns " + columns
|
||||
messages_raw = self.manila(cmd, microversion=microversion)
|
||||
messages = utils.listing(messages_raw)
|
||||
return messages
|
||||
|
||||
@not_found_wrapper
|
||||
def get_message(self, message, microversion=None):
|
||||
"""Returns share server by its Name or ID."""
|
||||
message_raw = self.manila(
|
||||
'message-show %s' % message, microversion=microversion)
|
||||
message = output_parser.details(message_raw)
|
||||
return message
|
||||
|
||||
@not_found_wrapper
|
||||
def delete_message(self, message, microversion=None):
|
||||
"""Deletes message by its ID."""
|
||||
return self.manila('message-delete %s' % message,
|
||||
microversion=microversion)
|
||||
|
||||
def is_message_deleted(self, message, microversion=None):
|
||||
"""Indicates whether message is deleted or not.
|
||||
|
||||
:param message: str -- ID of message
|
||||
"""
|
||||
try:
|
||||
self.get_message(message, microversion=microversion)
|
||||
return False
|
||||
except tempest_lib_exc.NotFound:
|
||||
return True
|
||||
|
||||
def wait_for_message_deletion(self, message, microversion=None):
|
||||
"""Wait for message deletion by its ID.
|
||||
|
||||
:param message: text -- ID of message
|
||||
"""
|
||||
self.wait_for_resource_deletion(
|
||||
MESSAGE, res_id=message, interval=3, timeout=60,
|
||||
microversion=microversion)
|
||||
|
71
manilaclient/tests/functional/test_messages.py
Normal file
71
manilaclient/tests/functional/test_messages.py
Normal file
@ -0,0 +1,71 @@
|
||||
# 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 ddt
|
||||
|
||||
from manilaclient.tests.functional import base
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MessagesReadOnlyTest(base.BaseTestCase):
|
||||
|
||||
@ddt.data(
|
||||
("admin", "2.37"),
|
||||
("user", "2.37"),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_message_list(self, role, microversion):
|
||||
self.skip_if_microversion_not_supported(microversion)
|
||||
self.clients[role].manila("message-list", microversion=microversion)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MessagesReadWriteTest(base.BaseTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(MessagesReadWriteTest, cls).setUpClass()
|
||||
cls.message = cls.create_message(cleanup_in_class=True)
|
||||
|
||||
def test_list_messages(self):
|
||||
self.skip_if_microversion_not_supported('2.37')
|
||||
messages = self.admin_client.list_messages()
|
||||
self.assertTrue(any(m['ID'] is not None for m in messages))
|
||||
self.assertTrue(any(m['User Message'] is not None for m in messages))
|
||||
self.assertTrue(any(m['Resource ID'] is not None for m in messages))
|
||||
self.assertTrue(any(m['Action ID'] is not None for m in messages))
|
||||
self.assertTrue(any(m['Detail ID'] is not None for m in messages))
|
||||
self.assertTrue(any(m['Resource Type'] is not None for m in messages))
|
||||
|
||||
@ddt.data(
|
||||
'id', 'action_id', 'resource_id', 'action_id', 'detail_id',
|
||||
'resource_type', 'created_at', 'action_id,detail_id,resource_id',
|
||||
)
|
||||
def test_list_share_type_select_column(self, columns):
|
||||
self.skip_if_microversion_not_supported('2.37')
|
||||
self.admin_client.list_messages(columns=columns)
|
||||
|
||||
def test_get_message(self):
|
||||
self.skip_if_microversion_not_supported('2.37')
|
||||
message = self.admin_client.get_message(self.message['ID'])
|
||||
expected_keys = (
|
||||
'id', 'action_id', 'resource_id', 'action_id', 'detail_id',
|
||||
'resource_type', 'created_at', 'created_at',
|
||||
)
|
||||
for key in expected_keys:
|
||||
self.assertIn(key, message)
|
||||
|
||||
def test_delete_message(self):
|
||||
self.skip_if_microversion_not_supported('2.37')
|
||||
message = self.create_message(cleanup_in_class=False)
|
||||
self.admin_client.delete_message(message['ID'])
|
||||
self.admin_client.wait_for_message_deletion(message['ID'])
|
@ -1095,6 +1095,35 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
||||
}
|
||||
return 200, {}, sg_type_access
|
||||
|
||||
fake_message = {
|
||||
'id': 'fake message id',
|
||||
'action_id': '001',
|
||||
'detail_id': '002',
|
||||
'user_message': 'user message',
|
||||
'message_level': 'ERROR',
|
||||
'resource_type': 'SHARE',
|
||||
'resource_id': 'resource id',
|
||||
'created_at': '2015-08-27T09:49:58-05:00',
|
||||
'expires_at': '2015-09-27T09:49:58-05:00',
|
||||
'request_id': 'req-936666d2-4c8f-4e41-9ac9-237b43f8b848',
|
||||
}
|
||||
|
||||
def get_messages(self, **kw):
|
||||
messages = {
|
||||
'messages': [self.fake_message],
|
||||
}
|
||||
return 200, {}, messages
|
||||
|
||||
def get_messages_1234(self, **kw):
|
||||
message = {'message': self.fake_message}
|
||||
return 200, {}, message
|
||||
|
||||
def delete_messages_1234(self, **kw):
|
||||
return 202, {}, None
|
||||
|
||||
def delete_messages_5678(self, **kw):
|
||||
return 202, {}, None
|
||||
|
||||
|
||||
def fake_create(url, body, response_key):
|
||||
return {'url': url, 'body': body, 'resp_key': response_key}
|
||||
@ -1151,3 +1180,16 @@ class ShareGroupSnapshot(object):
|
||||
share_network_id = ShareNetwork().id
|
||||
name = 'fake name'
|
||||
description = 'fake description'
|
||||
|
||||
|
||||
class Message(object):
|
||||
id = 'fake message id'
|
||||
action_id = '001'
|
||||
detail_id = '002'
|
||||
user_message = 'user message'
|
||||
message_level = 'ERROR'
|
||||
resource_type = 'SHARE'
|
||||
resource_id = 'resource id'
|
||||
created_at = '2015-08-27T09:49:58-05:00'
|
||||
expires_at = '2015-09-27T09:49:58-05:00'
|
||||
request_id = 'req-936666d2-4c8f-4e41-9ac9-237b43f8b848'
|
||||
|
132
manilaclient/tests/unit/v2/test_messages.py
Normal file
132
manilaclient/tests/unit/v2/test_messages.py
Normal file
@ -0,0 +1,132 @@
|
||||
# Copyright 2017 Red Hat
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
import ddt
|
||||
import six
|
||||
|
||||
from manilaclient.tests.unit import utils
|
||||
from manilaclient.tests.unit.v2 import fakes as fake
|
||||
from manilaclient.v2 import messages
|
||||
|
||||
|
||||
class MessageTest(utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(MessageTest, self).setUp()
|
||||
self.manager = messages.MessageManager(fake.FakeClient())
|
||||
self.message = messages.Message(
|
||||
self.manager, {'id': 'fake_id'})
|
||||
self.fake_kwargs = {'key': 'value'}
|
||||
|
||||
def test_repr(self):
|
||||
result = six.text_type(self.message)
|
||||
|
||||
self.assertEqual('<Message: fake_id>', result)
|
||||
|
||||
def test_delete(self):
|
||||
mock_manager_delete = self.mock_object(self.manager, 'delete')
|
||||
|
||||
self.message.delete()
|
||||
|
||||
mock_manager_delete.assert_called_once_with(self.message)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MessageManagerTest(utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(MessageManagerTest, self).setUp()
|
||||
self.manager = messages.MessageManager(fake.FakeClient())
|
||||
|
||||
def test_get(self):
|
||||
fake_message = fake.Message()
|
||||
mock_get = self.mock_object(
|
||||
self.manager, '_get', mock.Mock(return_value=fake_message))
|
||||
|
||||
result = self.manager.get(fake.Message.id)
|
||||
|
||||
self.assertIs(fake_message, result)
|
||||
mock_get.assert_called_once_with(
|
||||
messages.RESOURCE_PATH % fake.Message.id,
|
||||
messages.RESOURCE_NAME)
|
||||
|
||||
def test_list(self):
|
||||
fake_message = fake.Message()
|
||||
mock_list = self.mock_object(
|
||||
self.manager, '_list', mock.Mock(return_value=[fake_message]))
|
||||
|
||||
result = self.manager.list()
|
||||
|
||||
self.assertEqual([fake_message], result)
|
||||
mock_list.assert_called_once_with(
|
||||
messages.RESOURCES_PATH,
|
||||
messages.RESOURCES_NAME)
|
||||
|
||||
@ddt.data(
|
||||
({'action_id': 1, 'resource_type': 'share'},
|
||||
'?action_id=1&resource_type=share'),
|
||||
({'action_id': 1}, '?action_id=1'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_list_with_filters(self, filters, filters_path):
|
||||
fake_message = fake.Message()
|
||||
mock_list = self.mock_object(
|
||||
self.manager, '_list', mock.Mock(return_value=[fake_message]))
|
||||
|
||||
result = self.manager.list(search_opts=filters)
|
||||
|
||||
self.assertEqual([fake_message], result)
|
||||
expected_path = (messages.RESOURCES_PATH + filters_path)
|
||||
mock_list.assert_called_once_with(
|
||||
expected_path, messages.RESOURCES_NAME)
|
||||
|
||||
@ddt.data('id', 'project_id', 'request_id', 'resource_type', 'action_id',
|
||||
'detail_id', 'resource_id', 'message_level', 'expires_at',
|
||||
'request_id', 'created_at')
|
||||
def test_list_with_sorting(self, key):
|
||||
fake_message = fake.Message()
|
||||
mock_list = self.mock_object(
|
||||
self.manager, '_list', mock.Mock(return_value=[fake_message]))
|
||||
|
||||
result = self.manager.list(sort_dir='asc', sort_key=key)
|
||||
|
||||
self.assertEqual([fake_message], result)
|
||||
expected_path = (
|
||||
messages.RESOURCES_PATH + '?sort_dir=asc&sort_key=' +
|
||||
key)
|
||||
mock_list.assert_called_once_with(
|
||||
expected_path, messages.RESOURCES_NAME)
|
||||
|
||||
@ddt.data(
|
||||
('name', 'invalid'),
|
||||
('invalid', 'asc'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_list_with_invalid_sorting(self, sort_key, sort_dir):
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.manager.list, sort_dir=sort_dir, sort_key=sort_key)
|
||||
|
||||
def test_delete(self):
|
||||
mock_delete = self.mock_object(self.manager, '_delete')
|
||||
mock_post = self.mock_object(self.manager.api.client, 'post')
|
||||
|
||||
self.manager.delete(fake.Message())
|
||||
|
||||
mock_delete.assert_called_once_with(
|
||||
messages.RESOURCE_PATH % fake.Message.id)
|
||||
self.assertFalse(mock_post.called)
|
@ -30,6 +30,7 @@ from manilaclient import exceptions
|
||||
from manilaclient import shell
|
||||
from manilaclient.tests.unit import utils as test_utils
|
||||
from manilaclient.tests.unit.v2 import fakes
|
||||
from manilaclient.v2 import messages
|
||||
from manilaclient.v2 import security_services
|
||||
from manilaclient.v2 import share_instances
|
||||
from manilaclient.v2 import share_networks
|
||||
@ -2623,3 +2624,51 @@ class ShellTest(test_utils.TestCase):
|
||||
self.assert_called_anytime(
|
||||
'DELETE', '/share-servers/%s' % server.id,
|
||||
clear_callstack=False)
|
||||
|
||||
@mock.patch.object(cliutils, 'print_list', mock.Mock())
|
||||
def test_message_list(self):
|
||||
self.run_command('message-list')
|
||||
|
||||
self.assert_called('GET', '/messages')
|
||||
cliutils.print_list.assert_called_once_with(
|
||||
mock.ANY, fields=['ID', 'Resource Type', 'Resource ID',
|
||||
'Action ID', 'User Message', 'Detail ID',
|
||||
'Created At'], sortby_index=None)
|
||||
|
||||
@mock.patch.object(cliutils, 'print_list', mock.Mock())
|
||||
def test_message_list_select_column(self):
|
||||
self.run_command('message-list --columns id,resource_type')
|
||||
|
||||
self.assert_called('GET', '/messages')
|
||||
cliutils.print_list.assert_called_once_with(
|
||||
mock.ANY, fields=['Id', 'Resource_Type'], sortby_index=None)
|
||||
|
||||
def test_message_list_with_filters(self):
|
||||
self.run_command('message-list --limit 10 --offset 0')
|
||||
|
||||
self.assert_called(
|
||||
'GET', '/messages?limit=10&offset=0')
|
||||
|
||||
def test_message_show(self):
|
||||
self.run_command('message-show 1234')
|
||||
|
||||
self.assert_called('GET', '/messages/1234')
|
||||
|
||||
@ddt.data(('1234', ), ('1234', '5678'))
|
||||
def test_message_delete(self, ids):
|
||||
fake_messages = [
|
||||
messages.Message('fake', {'id': mid}, True) for mid in ids
|
||||
]
|
||||
self.mock_object(
|
||||
shell_v2, '_find_message',
|
||||
mock.Mock(side_effect=fake_messages))
|
||||
|
||||
self.run_command('message-delete %s' % ' '.join(ids))
|
||||
|
||||
shell_v2._find_message.assert_has_calls([
|
||||
mock.call(self.shell.cs, mid) for mid in ids
|
||||
])
|
||||
for fake_message in fake_messages:
|
||||
self.assert_called_anytime(
|
||||
'DELETE', '/messages/%s' % fake_message.id,
|
||||
clear_callstack=False)
|
||||
|
@ -23,6 +23,7 @@ from manilaclient.common import httpclient
|
||||
from manilaclient import exceptions
|
||||
from manilaclient.v2 import availability_zones
|
||||
from manilaclient.v2 import limits
|
||||
from manilaclient.v2 import messages
|
||||
from manilaclient.v2 import quota_classes
|
||||
from manilaclient.v2 import quotas
|
||||
from manilaclient.v2 import scheduler_stats
|
||||
@ -212,6 +213,7 @@ class Client(object):
|
||||
self.availability_zones = availability_zones.AvailabilityZoneManager(
|
||||
self)
|
||||
self.limits = limits.LimitsManager(self)
|
||||
self.messages = messages.MessageManager(self)
|
||||
self.services = services.ServiceManager(self)
|
||||
self.security_services = security_services.SecurityServiceManager(self)
|
||||
self.share_networks = share_networks.ShareNetworkManager(self)
|
||||
|
96
manilaclient/v2/messages.py
Normal file
96
manilaclient/v2/messages.py
Normal file
@ -0,0 +1,96 @@
|
||||
# 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.
|
||||
|
||||
"""Asynchronous User Message interface."""
|
||||
|
||||
try:
|
||||
from urllib import urlencode # noqa
|
||||
except ImportError:
|
||||
from urllib.parse import urlencode # noqa
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient import base
|
||||
from manilaclient.common.apiclient import base as common_base
|
||||
from manilaclient.common import constants
|
||||
|
||||
RESOURCES_PATH = '/messages'
|
||||
RESOURCE_PATH = '/messages/%s'
|
||||
RESOURCES_NAME = 'messages'
|
||||
RESOURCE_NAME = 'message'
|
||||
|
||||
|
||||
class Message(common_base.Resource):
|
||||
NAME_ATTR = 'id'
|
||||
|
||||
def __repr__(self):
|
||||
return "<Message: %s>" % self.id
|
||||
|
||||
def delete(self):
|
||||
"""Delete this message."""
|
||||
return self.manager.delete(self)
|
||||
|
||||
|
||||
class MessageManager(base.ManagerWithFind):
|
||||
"""Manage :class:`Message` resources."""
|
||||
resource_class = Message
|
||||
|
||||
@api_versions.wraps('2.37')
|
||||
def get(self, message_id):
|
||||
"""Get a message.
|
||||
|
||||
:param message_id: The ID of the message to get.
|
||||
:rtype: :class:`Message`
|
||||
"""
|
||||
return self._get(RESOURCE_PATH % message_id, RESOURCE_NAME)
|
||||
|
||||
@api_versions.wraps('2.37')
|
||||
def list(self, search_opts=None, sort_key=None, sort_dir=None):
|
||||
"""Lists all messages.
|
||||
|
||||
:param search_opts: Search options to filter out messages.
|
||||
:rtype: list of :class:`Message`
|
||||
"""
|
||||
search_opts = search_opts or {}
|
||||
|
||||
if sort_key is not None:
|
||||
if sort_key in constants.MESSAGE_SORT_KEY_VALUES:
|
||||
search_opts['sort_key'] = sort_key
|
||||
else:
|
||||
raise ValueError(
|
||||
'sort_key must be one of the following: %s.'
|
||||
% ', '.join(constants.MESSAGE_SORT_KEY_VALUES))
|
||||
|
||||
if sort_dir is not None:
|
||||
if sort_dir in constants.SORT_DIR_VALUES:
|
||||
search_opts['sort_dir'] = sort_dir
|
||||
else:
|
||||
raise ValueError('sort_dir must be one of the following: %s.'
|
||||
% ', '.join(constants.SORT_DIR_VALUES))
|
||||
|
||||
if search_opts:
|
||||
query_string = urlencode(
|
||||
sorted([(k, v) for (k, v) in list(search_opts.items()) if v]))
|
||||
if query_string:
|
||||
query_string = "?%s" % (query_string,)
|
||||
else:
|
||||
query_string = ''
|
||||
|
||||
path = RESOURCES_PATH + query_string
|
||||
return self._list(path, RESOURCES_NAME)
|
||||
|
||||
@api_versions.wraps('2.37')
|
||||
def delete(self, message):
|
||||
"""Delete a message."""
|
||||
|
||||
loc = RESOURCE_PATH % common_base.getid(message)
|
||||
|
||||
return self._delete(loc)
|
@ -269,6 +269,11 @@ def _find_share_server(cs, share_server):
|
||||
return apiclient_utils.find_resource(cs.share_servers, share_server)
|
||||
|
||||
|
||||
def _find_message(cs, message):
|
||||
"""Get a message by ID."""
|
||||
return apiclient_utils.find_resource(cs.messages, message)
|
||||
|
||||
|
||||
def _translate_keys(collection, convert):
|
||||
for item in collection:
|
||||
keys = item.__dict__
|
||||
@ -4650,3 +4655,170 @@ def do_share_replica_resync(cs, args):
|
||||
"""
|
||||
replica = _find_share_replica(cs, args.replica)
|
||||
cs.share_replicas.resync(replica)
|
||||
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# User Messages
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
@api_versions.wraps("2.37")
|
||||
@cliutils.arg(
|
||||
'--resource_id',
|
||||
'--resource-id',
|
||||
'--resource',
|
||||
metavar='<resource_id>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Filters results by a resource uuid. Default=None.')
|
||||
@cliutils.arg(
|
||||
'--resource_type',
|
||||
'--resource-type',
|
||||
metavar='<type>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Filters results by a resource type. Default=None. '
|
||||
'Example: "manila message-list --resource_type share"')
|
||||
@cliutils.arg(
|
||||
'--action_id',
|
||||
'--action-id',
|
||||
'--action',
|
||||
metavar='<id>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Filters results by action id. Default=None.')
|
||||
@cliutils.arg(
|
||||
'--detail_id',
|
||||
'--detail-id',
|
||||
'--detail',
|
||||
metavar='<id>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Filters results by detail id. Default=None.')
|
||||
@cliutils.arg(
|
||||
'--request_id',
|
||||
'--request-id',
|
||||
'--request',
|
||||
metavar='<request_id>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Filters results by request id. Default=None.')
|
||||
@cliutils.arg(
|
||||
'--level',
|
||||
'--message_level',
|
||||
'--message-level',
|
||||
metavar='<level>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Filters results by the message level. Default=None. '
|
||||
'Example: "manila message-list --level ERROR".')
|
||||
@cliutils.arg(
|
||||
'--limit',
|
||||
metavar='<limit>',
|
||||
type=int,
|
||||
default=None,
|
||||
help='Maximum number of messages to return. (Default=None)')
|
||||
@cliutils.arg(
|
||||
'--offset',
|
||||
metavar="<offset>",
|
||||
default=None,
|
||||
help='Start position of message listing.')
|
||||
@cliutils.arg(
|
||||
'--sort-key', '--sort_key',
|
||||
metavar='<sort_key>',
|
||||
type=str,
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Key to be sorted, available keys are %(keys)s. Default=desc.' % {
|
||||
'keys': constants.MESSAGE_SORT_KEY_VALUES})
|
||||
@cliutils.arg(
|
||||
'--sort-dir', '--sort_dir',
|
||||
metavar='<sort_dir>',
|
||||
type=str,
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help='Sort direction, available values are %(values)s. '
|
||||
'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES})
|
||||
@cliutils.arg(
|
||||
'--columns',
|
||||
metavar='<columns>',
|
||||
type=str,
|
||||
default=None,
|
||||
help='Comma separated list of columns to be displayed '
|
||||
'example --columns "resource_id,user_message".')
|
||||
def do_message_list(cs, args):
|
||||
"""Lists all messages."""
|
||||
if args.columns is not None:
|
||||
list_of_keys = _split_columns(columns=args.columns)
|
||||
else:
|
||||
list_of_keys = ['ID', 'Resource Type', 'Resource ID', 'Action ID',
|
||||
'User Message', 'Detail ID', 'Created At']
|
||||
|
||||
search_opts = {
|
||||
'offset': args.offset,
|
||||
'limit': args.limit,
|
||||
'request_id': args.request_id,
|
||||
'resource_type': args.resource_type,
|
||||
'resource_id': args.resource_id,
|
||||
'action_id': args.action_id,
|
||||
'detail_id': args.detail_id,
|
||||
'message_level': args.level
|
||||
}
|
||||
messages = cs.messages.list(
|
||||
search_opts=search_opts, sort_key=args.sort_key,
|
||||
sort_dir=args.sort_dir)
|
||||
cliutils.print_list(messages, fields=list_of_keys, sortby_index=None)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'message',
|
||||
metavar='<message>',
|
||||
help='ID of the message.')
|
||||
@api_versions.wraps("2.37")
|
||||
def do_message_show(cs, args):
|
||||
"""Show details about a message."""
|
||||
|
||||
message = cs.messages.get(args.message)
|
||||
_print_message(message)
|
||||
|
||||
|
||||
@api_versions.wraps("2.37")
|
||||
@cliutils.arg(
|
||||
'message',
|
||||
metavar='<message>',
|
||||
nargs='+',
|
||||
help='ID of the message(s).')
|
||||
def do_message_delete(cs, args):
|
||||
"""Remove one or more messages."""
|
||||
failure_count = 0
|
||||
|
||||
for message in args.message:
|
||||
try:
|
||||
message_ref = _find_message(cs, message)
|
||||
cs.messages.delete(message_ref)
|
||||
except Exception as e:
|
||||
failure_count += 1
|
||||
print("Delete for message %s failed: %s" % (message, e),
|
||||
file=sys.stderr)
|
||||
|
||||
if failure_count == len(args.message):
|
||||
raise exceptions.CommandError("Unable to delete any of the specified "
|
||||
"messages.")
|
||||
|
||||
|
||||
def _print_message(message):
|
||||
message_dict = {
|
||||
'id': message.id,
|
||||
'resource_type': message.resource_type,
|
||||
'resource_id': message.resource_id,
|
||||
'action_id': message.action_id,
|
||||
'user_message': message.user_message,
|
||||
'message_level': message.message_level,
|
||||
'detail_id': message.detail_id,
|
||||
'created_at': message.created_at,
|
||||
'expires_at': message.expires_at,
|
||||
'request_id': message.request_id,
|
||||
}
|
||||
cliutils.print_dict(message_dict)
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Added new subcommands message-list, message-show and message-delete.
|
Loading…
x
Reference in New Issue
Block a user