diff --git a/senlinclient/osc/v1/policy.py b/senlinclient/osc/v1/policy.py new file mode 100644 index 0000000..a206a51 --- /dev/null +++ b/senlinclient/osc/v1/policy.py @@ -0,0 +1,99 @@ +# 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 policy 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 ListPolicy(lister.Lister): + """List policies that meet the criteria.""" + + log = logging.getLogger(__name__ + ".ListPolicy") + + def get_parser(self, prog_name): + parser = super(ListPolicy, self).get_parser(prog_name) + parser.add_argument( + '--limit', + metavar='', + help=_('Limit the number of policies returned') + ) + parser.add_argument( + '--marker', + metavar='', + help=_('Only return policies that appear after the given 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: ['type', 'name', 'created_at', " + "'updated_at']") + ) + parser.add_argument( + '--filters', + metavar='', + help=_("Filter parameters to apply on returned policies. " + "This can be specified multiple times, or once with " + "parameters separated by a semicolon. The valid filter " + "keys are: ['type', 'name']"), + action='append' + ) + parser.add_argument( + '--global-project', + default=False, + action="store_true", + help=_('Indicate that the list should include policies from' + ' all projects. This option is subject to access policy ' + 'checking. Default is False') + ) + 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', 'name', 'type', 'created_at'] + queries = { + 'limit': parsed_args.limit, + 'marker': parsed_args.marker, + 'sort': parsed_args.sort, + 'global_project': parsed_args.global_project, + } + if parsed_args.filters: + queries.update(senlin_utils.format_parameters(parsed_args.filters)) + + policies = senlin_client.policies(**queries) + formatters = {} + if not parsed_args.full_id: + formatters = { + 'id': lambda x: x[:8] + } + return ( + columns, + (utils.get_item_properties(p, columns, formatters=formatters) + for p in policies) + ) diff --git a/senlinclient/tests/unit/osc/v1/test_policy.py b/senlinclient/tests/unit/osc/v1/test_policy.py new file mode 100644 index 0000000..3d50e0f --- /dev/null +++ b/senlinclient/tests/unit/osc/v1/test_policy.py @@ -0,0 +1,138 @@ +# 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 policy as sdk_policy +from openstack import exceptions as sdk_exc + +from senlinclient.osc.v1 import policy as osc_policy +from senlinclient.tests.unit.osc.v1 import fakes + + +class TestPolicy(fakes.TestClusteringv1): + def setUp(self): + super(TestPolicy, self).setUp() + self.mock_client = self.app.client_manager.clustering + + +class TestPolicyList(TestPolicy): + columns = ['id', 'name', 'type', 'created_at'] + response = {"policies": [ + { + "created_at": "2015-02-15T08:33:13.000000", + "data": {}, + "domain": 'null', + "id": "7192d8df-73be-4e98-ab99-1cf6d5066729", + "name": "test_policy_1", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "description": "A test policy", + "properties": { + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": True, + "grace_period": 60, + "reduce_desired_capacity": False + }, + "type": "senlin.policy.deletion", + "version": "1.0" + }, + "type": "senlin.policy.deletion-1.0", + "updated_at": 'null', + "user": "5e5bf8027826429c96af157f68dc9072" + } + ]} + defaults = { + 'global_project': False, + 'marker': None, + 'limit': None, + 'sort': None, + } + + def setUp(self): + super(TestPolicyList, self).setUp() + self.cmd = osc_policy.ListPolicy(self.app, None) + self.mock_client.policies = mock.Mock( + return_value=sdk_policy.Policy(None, {})) + + def test_policy_list_defaults(self): + arglist = [] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.policies.assert_called_with(**self.defaults) + self.assertEqual(self.columns, columns) + + def test_policy_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.policies.assert_called_with(**self.defaults) + self.assertEqual(self.columns, columns) + + def test_policy_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.policies.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) + + def test_policy_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.policies.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) + + def test_policy_list_sort_invalid_key(self): + self.mock_client.policies = 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.policies.side_effect = sdk_exc.HttpException() + self.assertRaises(sdk_exc.HttpException, + self.cmd.take_action, parsed_args) + + def test_policy_list_sort_invalid_direction(self): + self.mock_client.policies = 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.policies.side_effect = sdk_exc.HttpException() + self.assertRaises(sdk_exc.HttpException, + self.cmd.take_action, parsed_args) + + def test_policy_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.policies.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) + + def test_policy_list_filter(self): + kwargs = copy.deepcopy(self.defaults) + kwargs['name'] = 'my_policy' + arglist = ['--filter', 'name=my_policy'] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + self.mock_client.policies.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) diff --git a/setup.cfg b/setup.cfg index 8b628e2..7b351df 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,6 +35,7 @@ openstack.clustering.v1 = cluster_node_list = senlinclient.osc.v1.node:ListNode cluster_node_show = senlinclient.osc.v1.node:ShowNode cluster_node_update = senlinclient.osc.v1.node:UpdateNode + cluster_policy_list = senlinclient.osc.v1.policy:ListPolicy cluster_profile_create = senlinclient.osc.v1.profile:CreateProfile cluster_profile_delete = senlinclient.osc.v1.profile:DeleteProfile cluster_profile_list = senlinclient.osc.v1.profile:ListProfile