diff --git a/senlinclient/osc/v1/event.py b/senlinclient/osc/v1/event.py new file mode 100644 index 0000000..333ea44 --- /dev/null +++ b/senlinclient/osc/v1/event.py @@ -0,0 +1,102 @@ +# 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. + +"""Clustering v1 event action implementations""" + +import logging + +from cliff import lister +from openstackclient.common import utils + +from senlinclient.common.i18n import _ +from senlinclient.common import utils as senlin_utils + + +class ListEvent(lister.Lister): + """List events.""" + + log = logging.getLogger(__name__ + ".ListEvent") + + def get_parser(self, prog_name): + parser = super(ListEvent, self).get_parser(prog_name) + parser.add_argument( + '--filters', + metavar='', + help=_("Filter parameters to apply on returned events. " + "This can be specified multiple times, or once with " + "parameters separated by a semicolon. The valid filter " + "keys are: ['level', 'obj_type', 'obj_id' ,'cluster_id', " + "'obj_name', 'action']"), + action='append' + ) + parser.add_argument( + '--limit', + metavar='', + help=_('Limit the number of events returned') + ) + parser.add_argument( + '--marker', + metavar='', + help=_('Only return events that appear after the given event ID') + ) + parser.add_argument( + '--sort', + metavar='', + help=_("Sorting option which is a string containing a list of " + "keys separated by commas. Each key can be optionally " + "appended by a sort direction (:asc or :desc). The valid " + "sort keys are: ['timestamp', 'level', 'obj_type', " + "'obj_name', 'user', 'action', 'status']") + ) + parser.add_argument( + '--global-project', + default=False, + action="store_true", + help=_('Whether events from all projects should be listed. ' + ' Default to False. Setting this to True may demand ' + 'for an admin privilege') + ) + parser.add_argument( + '--full-id', + default=False, + action="store_true", + help=_('Print full IDs in list') + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + + senlin_client = self.app.client_manager.clustering + columns = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name', + 'action', 'status', 'status_reason', 'level'] + queries = { + 'sort': parsed_args.sort, + 'limit': parsed_args.limit, + 'marker': parsed_args.marker, + 'global_project': parsed_args.global_project, + } + + if parsed_args.filters: + queries.update(senlin_utils.format_parameters(parsed_args.filters)) + + formatters = {} + if not parsed_args.full_id: + formatters['id'] = lambda x: x[:8] + formatters['obj_id'] = lambda x: x[:8] if x else '' + + events = senlin_client.events(**queries) + return ( + columns, + (utils.get_item_properties(e, columns, formatters=formatters) + for e in events) + ) diff --git a/senlinclient/tests/unit/osc/v1/test_event.py b/senlinclient/tests/unit/osc/v1/test_event.py new file mode 100644 index 0000000..74eef65 --- /dev/null +++ b/senlinclient/tests/unit/osc/v1/test_event.py @@ -0,0 +1,134 @@ +# 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 copy +import mock + +from openstack.cluster.v1 import event as sdk_event +from openstack import exceptions as sdk_exc + +from senlinclient.osc.v1 import event as osc_event +from senlinclient.tests.unit.osc.v1 import fakes + + +class TestEvent(fakes.TestClusteringv1): + def setUp(self): + super(TestEvent, self).setUp() + self.mock_client = self.app.client_manager.clustering + + +class TestEventList(TestEvent): + + columns = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name', 'action', + 'status', 'status_reason', 'level'] + + response = {"events": [ + { + "action": "create", + "cluster_id": 'null', + "id": "2d255b9c-8f36-41a2-a137-c0175ccc29c3", + "level": "20", + "obj_id": "0df0931b-e251-4f2e-8719-4ebfda3627ba", + "obj_name": "node009", + "obj_type": "NODE", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "CREATING", + "status_reason": "Initializing", + "timestamp": "2015-03-05T08:53:15", + "user": "a21ded6060534d99840658a777c2af5a" + } + ]} + + defaults = { + 'global_project': False, + 'marker': None, + 'limit': None, + 'sort': None, + } + + def setUp(self): + super(TestEventList, self).setUp() + self.cmd = osc_event.ListEvent(self.app, None) + self.mock_client.events = mock.Mock( + return_value=sdk_event.Event(None, {})) + + def test_event_list_defaults(self): + arglist = [] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.events.assert_called_with(**self.defaults) + self.assertEqual(self.columns, columns) + + def test_event_list_full_id(self): + arglist = ['--full-id'] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.events.assert_called_with(**self.defaults) + self.assertEqual(self.columns, columns) + + def test_event_list_limit(self): + kwargs = copy.deepcopy(self.defaults) + kwargs['limit'] = '3' + arglist = ['--limit', '3'] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.events.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) + + def test_event_list_sort(self): + kwargs = copy.deepcopy(self.defaults) + kwargs['sort'] = 'name:asc' + arglist = ['--sort', 'name:asc'] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.events.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) + + def test_event_list_sort_invalid_key(self): + self.mock_client.events = mock.Mock( + return_value=self.response) + kwargs = copy.deepcopy(self.defaults) + kwargs['sort'] = 'bad_key' + arglist = ['--sort', 'bad_key'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.mock_client.events.side_effect = sdk_exc.HttpException() + self.assertRaises(sdk_exc.HttpException, + self.cmd.take_action, parsed_args) + + def test_event_list_sort_invalid_direction(self): + self.mock_client.events = mock.Mock( + return_value=self.response) + kwargs = copy.deepcopy(self.defaults) + kwargs['sort'] = 'name:bad_direction' + arglist = ['--sort', 'name:bad_direction'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.mock_client.events.side_effect = sdk_exc.HttpException() + self.assertRaises(sdk_exc.HttpException, + self.cmd.take_action, parsed_args) + + def test_event_list_filter(self): + kwargs = copy.deepcopy(self.defaults) + kwargs['name'] = 'my_event' + arglist = ['--filter', 'name=my_event'] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.events.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) + + def test_event_list_marker(self): + kwargs = copy.deepcopy(self.defaults) + kwargs['marker'] = 'a9448bf6' + arglist = ['--marker', 'a9448bf6'] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.events.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) diff --git a/setup.cfg b/setup.cfg index 852e97c..e6a2621 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,7 @@ openstack.cli.extension = openstack.clustering.v1 = cluster_create = senlinclient.osc.v1.cluster:CreateCluster cluster_delete = senlinclient.osc.v1.cluster:DeleteCluster + cluster_event_list = senlinclient.osc.v1.event:ListEvent cluster_list = senlinclient.osc.v1.cluster:ListCluster cluster_resize = senlinclient.osc.v1.cluster:ResizeCluster cluster_scale_in = senlinclient.osc.v1.cluster:ScaleInCluster