From becbdeae4126bdb75327e95b661b1c7d2d7b69f9 Mon Sep 17 00:00:00 2001 From: enthurohini Date: Sun, 6 Mar 2016 07:46:41 +0530 Subject: [PATCH] Add Category support for openstack CLI Adding category commands to murano openstack-client $ openstack category list $ openstack category show $ openstack category create $ openstack category delete [ ...] Partially implements: blueprint openstack-client-plugin-support Change-Id: I5623b2fd18508e795115a5e646eb44ad3e82b836 --- muranoclient/osc/v1/category.py | 150 ++++++++++++++++++ .../tests/unit/osc/v1/test_category.py | 141 ++++++++++++++++ ...ded-category-command-01cb9dab21ab4a7b.yaml | 6 + setup.cfg | 5 + 4 files changed, 302 insertions(+) create mode 100644 muranoclient/osc/v1/category.py create mode 100644 muranoclient/tests/unit/osc/v1/test_category.py create mode 100644 releasenotes/notes/added-category-command-01cb9dab21ab4a7b.yaml diff --git a/muranoclient/osc/v1/category.py b/muranoclient/osc/v1/category.py new file mode 100644 index 00000000..89a6523e --- /dev/null +++ b/muranoclient/osc/v1/category.py @@ -0,0 +1,150 @@ +# 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. + +"""Application-catalog v1 category action implementation""" + +import textwrap + +from cliff import lister +from cliff import show +from muranoclient.openstack.common.apiclient import exceptions +from openstackclient.common import utils +from oslo_log import log as logging + +LOG = logging.getLogger(__name__) + + +class ListCategories(lister.Lister): + """List all available categories.""" + + def take_action(self, parsed_args=None): + LOG.debug("take_action({0})".format(parsed_args)) + client = self.app.client_manager.application_catalog + + if parsed_args is None: + parsed_args = {} + + data = client.categories.list() + + fields = ["id", "name"] + field_labels = ["ID", "Name"] + + return ( + field_labels, + list(utils.get_item_properties( + s, + fields, + ) for s in data) + ) + + +class ShowCategory(show.ShowOne): + """Display category details.""" + + def get_parser(self, prog_name): + parser = super(ShowCategory, self).get_parser(prog_name) + parser.add_argument( + "id", + metavar="", + help=("ID of a category(s) to show."), + ) + + return parser + + def take_action(self, parsed_args): + LOG.debug("take_action({0})".format(parsed_args)) + client = self.app.client_manager.application_catalog + + category = client.categories.get(parsed_args.id) + to_display = dict(id=category.id, + name=category.name, + packages=', '.join(p['name'] + for p in category.packages)) + + to_display['packages'] = '\n'.join(textwrap.wrap(to_display['packages'] + or '', 55)) + + return self.dict2columns(to_display) + + +class CreateCategory(lister.Lister): + """Create a category.""" + + def get_parser(self, prog_name): + parser = super(CreateCategory, self).get_parser(prog_name) + parser.add_argument( + "name", + metavar="", + help=("Category name."), + ) + + return parser + + def take_action(self, parsed_args): + LOG.debug("take_action({0})".format(parsed_args)) + client = self.app.client_manager.application_catalog + + data = [client.categories.add({"name": parsed_args.name})] + + fields = ["id", "name"] + field_labels = ["ID", "Name"] + + return ( + field_labels, + list(utils.get_item_properties( + s, + fields, + ) for s in data) + ) + + +class DeleteCategory(lister.Lister): + """Delete a category.""" + + def get_parser(self, prog_name): + parser = super(DeleteCategory, self).get_parser(prog_name) + parser.add_argument( + "id", + metavar="", + nargs="+", + help=("ID of a category(ies) to delete."), + ) + + return parser + + def take_action(self, parsed_args): + LOG.debug("take_action({0})".format(parsed_args)) + client = self.app.client_manager.application_catalog + + failure_count = 0 + for category_id in parsed_args.id: + try: + client.categories.delete(category_id) + except Exception: + failure_count += 1 + print("Failed to delete '{0}'; category not found". + format(category_id)) + if failure_count == len(parsed_args.id): + raise exceptions.CommandError("Unable to find and delete any of " + "the specified categories.") + data = client.categories.list() + + fields = ["id", "name"] + field_labels = ["ID", "Name"] + + return ( + field_labels, + list(utils.get_item_properties( + s, + fields, + ) for s in data) + ) diff --git a/muranoclient/tests/unit/osc/v1/test_category.py b/muranoclient/tests/unit/osc/v1/test_category.py new file mode 100644 index 00000000..8be8e30f --- /dev/null +++ b/muranoclient/tests/unit/osc/v1/test_category.py @@ -0,0 +1,141 @@ +# 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 + +from muranoclient.osc.v1 import category as osc_category +from muranoclient.tests.unit.osc.v1 import fakes +from muranoclient.v1 import categories as api_category + +CATEGORY_INFO = {'id': 'xyz123', + 'name': 'fake1', + 'packages': [{'name': 'package1'}, {'name': 'package2'}]} + + +class TestCategory(fakes.TestApplicationCatalog): + def setUp(self): + super(TestCategory, self).setUp() + self.category_mock = self.app.client_manager.application_catalog.\ + categories + self.category_mock.reset_mock() + + +class TestListCategories(TestCategory): + def setUp(self): + super(TestListCategories, self).setUp() + self.category_mock.list.return_value = [api_category.Category(None, + CATEGORY_INFO)] + + # Command to test + self.cmd = osc_category.ListCategories(self.app, None) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_category_list(self, mock_util): + mock_util.return_value = ('xyz123', 'fake1') + + columns, data = self.cmd.take_action(parsed_args=None) + + # Check that columns are correct + expected_columns = ['ID', 'Name'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('xyz123', 'fake1')] + self.assertEqual(expected_data, data) + + +class TestShowCategory(TestCategory): + def setUp(self): + super(TestShowCategory, self).setUp() + self.category_mock.get.return_value = api_category.\ + Category(None, CATEGORY_INFO) + + # Command to test + self.cmd = osc_category.ShowCategory(self.app, None) + + @mock.patch('textwrap.wrap') + def test_category_show(self, mock_wrap): + arglist = ['xyz123'] + verifylist = [('id', 'xyz123')] + + mock_wrap.return_value = ['package1, package2'] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ('id', 'name', 'packages') + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = ('xyz123', 'fake1', 'package1, package2') + self.assertEqual(expected_data, data) + + +class TestCreateCategory(TestCategory): + def setUp(self): + super(TestCreateCategory, self).setUp() + self.category_mock.add.return_value = [api_category.Category(None, + CATEGORY_INFO)] + + # Command to test + self.cmd = osc_category.CreateCategory(self.app, None) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_category_list(self, mock_util): + arglist = ['fake1'] + verifylist = [('name', 'fake1')] + + mock_util.return_value = ('xyz123', 'fake1') + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ['ID', 'Name'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('xyz123', 'fake1')] + self.assertEqual(expected_data, data) + + +class TestDeleteCategory(TestCategory): + def setUp(self): + super(TestDeleteCategory, self).setUp() + self.category_mock.delete.return_value = None + self.category_mock.list.return_value = [api_category.Category(None, + CATEGORY_INFO)] + + # Command to test + self.cmd = osc_category.DeleteCategory(self.app, None) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_category_list(self, mock_util): + arglist = ['abc123', '123abc'] + verifylist = [('id', ['abc123', '123abc'])] + + mock_util.return_value = ('xyz123', 'fake1') + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ['ID', 'Name'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('xyz123', 'fake1')] + self.assertEqual(expected_data, data) diff --git a/releasenotes/notes/added-category-command-01cb9dab21ab4a7b.yaml b/releasenotes/notes/added-category-command-01cb9dab21ab4a7b.yaml new file mode 100644 index 00000000..40a19b5c --- /dev/null +++ b/releasenotes/notes/added-category-command-01cb9dab21ab4a7b.yaml @@ -0,0 +1,6 @@ +--- +features: + - New OSC command ``category list`` + - New OSC command ``category show `` + - New OSC command ``category create `` + - New OSC command ``category delete [ ...]`` diff --git a/setup.cfg b/setup.cfg index 98657b4b..36b6c4f5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,6 +39,11 @@ openstack.application_catalog.v1 = environment_rename = muranoclient.osc.v1.environment:RenameEnvironment environment_session_create = muranoclient.osc.v1.environment:EnvironmentSessionCreate + category_list = muranoclient.osc.v1.category:ListCategories + category_show = muranoclient.osc.v1.category:ShowCategory + category_create = muranoclient.osc.v1.category:CreateCategory + category_delete = muranoclient.osc.v1.category:DeleteCategory + [global] setup-hooks = pbr.hooks.setup_hook