From 18a6199ed0dc9d173ff18302b85615143077a887 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Thu, 18 May 2023 11:14:54 +0000 Subject: [PATCH] Adding image stores info command This is an equivalent for ``glance stores-info``. Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/883493 Change-Id: I15d2c2c523ace1cfb32045328ecee8ce8beea63f --- openstackclient/image/v2/image.py | 45 +++++++++++++++++++ openstackclient/tests/unit/image/v2/fakes.py | 33 ++++++++++++++ .../tests/unit/image/v2/test_image.py | 40 +++++++++++++++++ .../add-stores-info-9f1488dd29013767.yaml | 3 ++ setup.cfg | 3 ++ 5 files changed, 124 insertions(+) create mode 100644 releasenotes/notes/add-stores-info-9f1488dd29013767.yaml diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 89212c6925..48b2639265 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -1836,3 +1836,48 @@ class ImportImage(command.ShowOne): info = _format_image(image) return zip(*sorted(info.items())) + + +class StoresInfo(command.Lister): + _description = _( + "Get available backends (only valid with Multi-Backend support)" + ) + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--detail", + action='store_true', + default=None, + help=_( + 'Shows details of stores (admin only) ' + '(requires --os-image-api-version 2.15 or later)' + ), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + try: + columns = ("id", "description", "is_default") + column_headers = ("ID", "Description", "Default") + if parsed_args.detail: + columns += ("properties",) + column_headers += ("Properties",) + + data = list(image_client.stores(details=parsed_args.detail)) + except sdk_exceptions.ResourceNotFound: + msg = _('Multi Backend support not enabled') + raise exceptions.CommandError(msg) + else: + return ( + column_headers, + ( + utils.get_item_properties( + store, + columns, + formatters=_formatters, + ) + for store in data + ), + ) diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 706ee44ae0..81c8b5076a 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -57,6 +57,7 @@ class FakeImagev2Client: self.get_task = mock.Mock() self.get_import_info = mock.Mock() + self.stores = mock.Mock() self.auth_token = kwargs['token'] self.management_url = kwargs['endpoint'] @@ -171,6 +172,38 @@ def create_one_import_info(attrs=None): return _service_info.Import(**import_info) +def create_one_stores_info(attrs=None): + """Create a fake stores info. + + :param attrs: A dictionary with all attributes of stores + :type attrs: dict + :return: A fake Store object list. + :rtype: `openstack.image.v2.service_info.Store` + """ + attrs = attrs or {} + + stores_info = { + "stores": [ + { + "id": "reliable", + "description": "More expensive store with data redundancy", + }, + { + "id": "fast", + "description": "Provides quick access to your image data", + "default": True, + }, + { + "id": "cheap", + "description": "Less expensive store for seldom-used images", + }, + ] + } + stores_info.update(attrs) + + return _service_info.Store(**stores_info) + + def create_one_task(attrs=None): """Create a fake task. diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index ec6dc9527d..c179032553 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -2101,3 +2101,43 @@ class TestImageGetData(TestImage): test_fd = _image.get_data_from_stdin() self.assertIsNone(test_fd) + + +class TestStoresInfo(TestImage): + stores_info = image_fakes.create_one_stores_info() + + def setUp(self): + super().setUp() + + self.client.stores.return_value = self.stores_info + + self.cmd = _image.StoresInfo(self.app, None) + + def test_stores_info(self): + arglist = [] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + + self.client.stores.assert_called() + + def test_stores_info_with_detail(self): + arglist = ['--detail'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + + self.client.stores.assert_called_with(details=True) + + def test_stores_info_neg(self): + arglist = [] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.client.stores.side_effect = sdk_exceptions.ResourceNotFound() + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertIn( + "Multi Backend support not enabled", + str(exc), + ) diff --git a/releasenotes/notes/add-stores-info-9f1488dd29013767.yaml b/releasenotes/notes/add-stores-info-9f1488dd29013767.yaml new file mode 100644 index 0000000000..36eef8b4c8 --- /dev/null +++ b/releasenotes/notes/add-stores-info-9f1488dd29013767.yaml @@ -0,0 +1,3 @@ +features: + - | + Add ``image stores info`` command, allowing users to know available backends. diff --git a/setup.cfg b/setup.cfg index 819dc23b71..6989489fc1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -388,6 +388,9 @@ openstack.image.v2 = image_task_show = openstackclient.image.v2.task:ShowTask image_task_list = openstackclient.image.v2.task:ListTask image_import_info = openstackclient.image.v2.info:ImportInfo + image_import = openstackclient.image.v2.image:ImportImage + image_stores_list = openstackclient.image.v2.image:StoresInfo + image_metadef_namespace_create = openstackclient.image.v2.metadef_namespaces:CreateMetadefNameSpace image_metadef_namespace_delete = openstackclient.image.v2.metadef_namespaces:DeleteMetadefNameSpace