From c225f0af798cdfba80e2b807e273297b72df379f Mon Sep 17 00:00:00 2001 From: Dina Saparbaeva Date: Mon, 1 Feb 2021 23:30:55 +0600 Subject: [PATCH] [OSC] Implement user messages commands In this patch the following openstack user messages commands are added: manila message-delete --> openstack share message delete manila message-list --> openstack share message list manila message-show --> openstack share message show Partially-implements bp openstack-client-support Change-Id: Ib1e4cca4f869d4e832e7b1814d48c838f75b42e1 --- doc/source/cli/osc/v2/index.rst | 7 + manilaclient/osc/v2/messages.py | 194 ++++++++++++++++ manilaclient/tests/unit/osc/v2/fakes.py | 57 +++++ .../tests/unit/osc/v2/test_messages.py | 208 ++++++++++++++++++ setup.cfg | 3 + 5 files changed, 469 insertions(+) create mode 100644 manilaclient/osc/v2/messages.py create mode 100644 manilaclient/tests/unit/osc/v2/test_messages.py diff --git a/doc/source/cli/osc/v2/index.rst b/doc/source/cli/osc/v2/index.rst index 983f1f502..9f697c605 100644 --- a/doc/source/cli/osc/v2/index.rst +++ b/doc/source/cli/osc/v2/index.rst @@ -72,3 +72,10 @@ share snapshots .. autoprogram-cliff:: openstack.share.v2 :command: share snapshot * + +=============== +user messages +=============== + +.. autoprogram-cliff:: openstack.share.v2 + :command: share message * diff --git a/manilaclient/osc/v2/messages.py b/manilaclient/osc/v2/messages.py new file mode 100644 index 000000000..ab067007c --- /dev/null +++ b/manilaclient/osc/v2/messages.py @@ -0,0 +1,194 @@ +# 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 osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as oscutils + +from manilaclient import api_versions +from manilaclient.common._i18n import _ +from manilaclient.common.apiclient import utils as apiutils + + +LOG = logging.getLogger(__name__) + +MESSAGE_ATTRIBUTES = [ + 'id', + 'resource_type', + 'resource_id', + 'action_id', + 'user_message', + 'message_level', + 'detail_id', + 'created_at', + 'expires_at', + 'request_id' +] + + +class DeleteMessage(command.Command): + """Remove one or more messages.""" + _description = _("Remove one or more messages") + + log = logging.getLogger(__name__ + ".DeleteMessage") + + def get_parser(self, prog_name): + parser = super(DeleteMessage, self).get_parser(prog_name) + parser.add_argument( + 'message', + metavar='', + nargs='+', + help=_('ID of the message(s).')) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + failure_count = 0 + + for message in parsed_args.message: + try: + message_ref = apiutils.find_resource( + share_client.messages, + message) + share_client.messages.delete(message_ref) + except Exception as e: + failure_count += 1 + LOG.error(_( + "Delete for message %(message)s failed: %(e)s"), + {'message': message, 'e': e}) + + if failure_count > 0: + raise exceptions.CommandError(_( + "Unable to delete some or all of the specified messages.")) + + +class ListMessage(command.Lister): + """Lists all messages.""" + _description = _("Lists all messages") + + log = logging.getLogger(__name__ + ".ListMessage") + + def get_parser(self, prog_name): + parser = super(ListMessage, self).get_parser(prog_name) + parser.add_argument( + '--resource-id', + metavar='', + default=None, + help=_('Filters results by a resource uuid. Default=None.')) + parser.add_argument( + '--resource-type', + metavar='', + default=None, + help=_('Filters results by a resource type. Default=None. ' + 'Example: "openstack message list --resource-type share"')) + parser.add_argument( + '--action-id', + metavar='', + default=None, + help=_('Filters results by action id. Default=None.')) + parser.add_argument( + '--detail-id', + metavar='', + default=None, + help=_('Filters results by detail id. Default=None.')) + parser.add_argument( + '--request-id', + metavar='', + default=None, + help=_('Filters results by request id. Default=None.')) + parser.add_argument( + '--message-level', + metavar='', + default=None, + help=_('Filters results by the message level. Default=None. ' + 'Example: "openstack message list --message-level ERROR".')) + parser.add_argument( + '--limit', + metavar='', + type=int, + default=None, + help=_('Maximum number of messages to return. (Default=None)')) + parser.add_argument( + '--since', + metavar='', + default=None, + help=_('Return only user messages created since given date. ' + 'The date format must be conforming to ISO8601. ' + 'Available only for microversion >= 2.52.')) + parser.add_argument( + '--before', + metavar='', + default=None, + help=_('Return only user messages created before given date. ' + 'The date format must be conforming to ISO8601. ' + 'Available only for microversion >= 2.52.')) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + + search_opts = { + 'limit': parsed_args.limit, + 'request_id': parsed_args.request_id, + 'resource_type': parsed_args.resource_type, + 'resource_id': parsed_args.resource_id, + 'action_id': parsed_args.action_id, + 'detail_id': parsed_args.detail_id, + 'message_level': parsed_args.message_level + } + + if share_client.api_version < api_versions.APIVersion("2.52"): + if getattr(parsed_args, 'since') or getattr(parsed_args, 'before'): + raise exceptions.CommandError(_( + "Filtering messages by 'since' and 'before'" + " is possible only with Manila API version >=2.52")) + else: + search_opts['created_since'] = parsed_args.since + search_opts['created_before'] = parsed_args.before + + messages = share_client.messages.list(search_opts=search_opts) + columns = [ + 'ID', + 'Resource Type', + 'Resource ID', + 'Action ID', + 'User Message', + 'Detail ID', + 'Created At'] + + return (columns, (oscutils.get_item_properties + (m, columns) for m in messages)) + + +class ShowMessage(command.ShowOne): + """Show details about a message.""" + _description = _("Show details about a message") + + def get_parser(self, prog_name): + parser = super(ShowMessage, self).get_parser(prog_name) + parser.add_argument( + 'message', + metavar='', + help=_('ID of the message.')) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + + message = apiutils.find_resource( + share_client.messages, + parsed_args.message) + + return (MESSAGE_ATTRIBUTES, oscutils.get_dict_properties( + message._info, MESSAGE_ATTRIBUTES)) diff --git a/manilaclient/tests/unit/osc/v2/fakes.py b/manilaclient/tests/unit/osc/v2/fakes.py index 8f65df180..43155f844 100644 --- a/manilaclient/tests/unit/osc/v2/fakes.py +++ b/manilaclient/tests/unit/osc/v2/fakes.py @@ -40,6 +40,7 @@ class FakeShareClient(object): self.share_export_locations = mock.Mock() self.share_export_locations.resource_class = ( osc_fakes.FakeResource(None, {})) + self.messages = mock.Mock() class ManilaParseException(Exception): @@ -534,3 +535,59 @@ class FakeSnapshotExportLocation(object): attrs)) return export_locations + + +class FakeMessage(object): + """Fake message""" + + @staticmethod + def create_one_message(attrs=None): + """Create a fake message + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with project_id, resource and so on + """ + + attrs = attrs or {} + + message = { + 'id': 'message-id-' + uuid.uuid4().hex, + 'action_id': '001', + 'detail_id': '002', + 'user_message': 'user message', + 'message_level': 'ERROR', + 'resource_type': 'SHARE', + 'resource_id': 'resource-id-' + uuid.uuid4().hex, + 'created_at': datetime.datetime.now().isoformat(), + 'expires_at': ( + datetime.datetime.now() + datetime.timedelta(days=30) + ).isoformat(), + 'request_id': 'req-' + uuid.uuid4().hex, + } + + message.update(attrs) + message = osc_fakes.FakeResource(info=copy.deepcopy( + message), + loaded=True) + return message + + @staticmethod + def create_messages(attrs={}, count=2): + """Create multiple fake messages. + + :param Dictionary attrs: + A dictionary with all attributes + :param Integer count: + The number of share types to be faked + :return: + A list of FakeResource objects + """ + + messages = [] + for n in range(0, count): + messages.append( + FakeMessage.create_one_message(attrs)) + + return messages diff --git a/manilaclient/tests/unit/osc/v2/test_messages.py b/manilaclient/tests/unit/osc/v2/test_messages.py new file mode 100644 index 000000000..dbfef9bd2 --- /dev/null +++ b/manilaclient/tests/unit/osc/v2/test_messages.py @@ -0,0 +1,208 @@ +# 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 osc_lib import exceptions +from osc_lib import utils as oscutils + +from manilaclient import api_versions +from manilaclient.osc.v2 import messages as osc_messages +from manilaclient.tests.unit.osc import osc_utils +from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes + +COLUMNS = [ + 'ID', + 'Resource Type', + 'Resource ID', + 'Action ID', + 'User Message', + 'Detail ID', + 'Created At', +] + + +class TestMessage(manila_fakes.TestShare): + + def setUp(self): + super(TestMessage, self).setUp() + + self.messages_mock = self.app.client_manager.share.messages + self.messages_mock.reset_mock() + + self.app.client_manager.share.api_version = api_versions.APIVersion( + api_versions.MAX_VERSION) + + +class TestMessageDelete(TestMessage): + + def setUp(self): + super(TestMessageDelete, self).setUp() + + self.message = ( + manila_fakes.FakeMessage.create_one_message()) + + self.messages_mock.get.return_value = self.message + + self.cmd = osc_messages.DeleteMessage(self.app, None) + + def test_message_delete_missing_args(self): + arglist = [] + verifylist = [] + + self.assertRaises(osc_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_message_delete(self): + arglist = [ + self.message.id + ] + verifylist = [ + ('message', [self.message.id]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.messages_mock.delete.assert_called_with(self.message) + self.assertIsNone(result) + + def test_message_delete_multiple(self): + messages = ( + manila_fakes.FakeMessage.create_messages( + count=2)) + arglist = [ + messages[0].id, + messages[1].id + ] + verifylist = [ + ('message', [messages[0].id, messages[1].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertEqual(self.messages_mock.delete.call_count, + len(messages)) + self.assertIsNone(result) + + def test_message_delete_exception(self): + arglist = [ + self.message.id + ] + verifylist = [ + ('message', [self.message.id]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.messages_mock.delete.side_effect = exceptions.CommandError() + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + +class TestMessageShow(TestMessage): + + def setUp(self): + super(TestMessageShow, self).setUp() + + self.message = ( + manila_fakes.FakeMessage.create_one_message()) + self.messages_mock.get.return_value = self.message + + self.cmd = osc_messages.ShowMessage(self.app, None) + + self.data = self.message._info.values() + self.columns = self.message._info.keys() + + def test_message_show_missing_args(self): + arglist = [] + verifylist = [] + + self.assertRaises(osc_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_message_show(self): + arglist = [ + self.message.id + ] + verifylist = [ + ('message', self.message.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.messages_mock.get.assert_called_with(self.message.id) + self.assertCountEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + +class TestMessageList(TestMessage): + + def setUp(self): + super(TestMessageList, self).setUp() + + self.messages = ( + manila_fakes.FakeMessage.create_messages( + count=2)) + + self.messages_mock.list.return_value = self.messages + + self.values = (oscutils.get_dict_properties( + m._info, COLUMNS) for m in self.messages) + + self.cmd = osc_messages.ListMessage(self.app, None) + + def test_list_messages(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.messages_mock.list.assert_called_with( + search_opts={ + 'limit': None, + 'request_id': None, + 'resource_type': None, + 'resource_id': None, + 'action_id': None, + 'detail_id': None, + 'message_level': None, + 'created_since': None, + 'created_before': None + }) + + self.assertEqual(COLUMNS, columns) + self.assertEqual(list(self.values), list(data)) + + def test_list_messages_api_version_exception(self): + self.app.client_manager.share.api_version = api_versions.APIVersion( + "2.50") + + arglist = [ + '--before', '2021-02-06T09:49:58-05:00', + '--since', '2021-02-05T09:49:58-05:00' + ] + verifylist = [ + ('before', '2021-02-06T09:49:58-05:00'), + ('since', '2021-02-05T09:49:58-05:00') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) diff --git a/setup.cfg b/setup.cfg index ad2b2a81a..03be77ea5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -78,6 +78,9 @@ openstack.share.v2 = share_snapshot_access_list = manilaclient.osc.v2.share_snapshots:ShareSnapshotAccessList share_snapshot_export_location_list = manilaclient.osc.v2.share_snapshots:ShareSnapshotListExportLocation share_snapshot_export_location_show = manilaclient.osc.v2.share_snapshots:ShareSnapshotShowExportLocation + share_message_delete = manilaclient.osc.v2.messages:DeleteMessage + share_message_list = manilaclient.osc.v2.messages:ListMessage + share_message_show = manilaclient.osc.v2.messages:ShowMessage [coverage:run] omit = manilaclient/tests/*