From d6022f96dfd608b83a4ff41483336f024aeacb16 Mon Sep 17 00:00:00 2001 From: Simon Merrick Date: Tue, 18 Feb 2020 12:48:02 +1300 Subject: [PATCH] Add storage policy option to create container command + Add CLI option to specify swift storage policy + Add CLI flag to specify container uses public read ACLS + Show storage policy in container show data Change-Id: I08ffa0d98bd39d467aa415771675f59bd77768ff --- openstackclient/api/object_store_v1.py | 27 ++++++- openstackclient/object/v1/container.py | 12 ++++ .../tests/unit/api/test_object_store_v1.py | 2 + .../unit/object/v1/test_container_all.py | 72 +++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index 44ff7a01f..c8514a572 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -24,6 +24,11 @@ from six.moves import urllib from openstackclient.api import api +GLOBAL_READ_ACL = ".r:*" +LIST_CONTENTS_ACL = ".rlistings" +PUBLIC_CONTAINER_ACLS = [GLOBAL_READ_ACL, LIST_CONTENTS_ACL] + + class APIv1(api.BaseAPI): """Object Store v1 API""" @@ -33,15 +38,32 @@ class APIv1(api.BaseAPI): def container_create( self, container=None, + public=False, + storage_policy=None ): """Create a container :param string container: name of container to create + :param bool public: + Boolean value indicating if the container should be publicly + readable. Defaults to False. + :param string storage_policy: + Name of the a specific storage policy to use. If not specified + swift will use its default storage policy. :returns: dict of returned headers """ - response = self.create(urllib.parse.quote(container), method='PUT') + + headers = {} + if public: + headers['x-container-read'] = ",".join(PUBLIC_CONTAINER_ACLS) + if storage_policy is not None: + headers['x-storage-policy'] = storage_policy + + response = self.create( + urllib.parse.quote(container), method='PUT', headers=headers) + data = { 'account': self._find_account_id(), 'container': container, @@ -173,7 +195,8 @@ class APIv1(api.BaseAPI): 'object_count': response.headers.get( 'x-container-object-count' ), - 'bytes_used': response.headers.get('x-container-bytes-used') + 'bytes_used': response.headers.get('x-container-bytes-used'), + 'storage_policy': response.headers.get('x-storage-policy'), } if 'x-container-read' in response.headers: diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index 47ca5bc1d..917e41c02 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -33,6 +33,16 @@ class CreateContainer(command.Lister): def get_parser(self, prog_name): parser = super(CreateContainer, self).get_parser(prog_name) + parser.add_argument( + '--public', + action='store_true', + default=False, + help="Make the container publicly accessible" + ) + parser.add_argument( + '--storage-policy', + help="Specify a particular storage policy to use." + ) parser.add_argument( 'containers', metavar='', @@ -51,6 +61,8 @@ class CreateContainer(command.Lister): ' is 256'), len(container)) data = self.app.client_manager.object_store.container_create( container=container, + public=parsed_args.public, + storage_policy=parsed_args.storage_policy ) results.append(data) diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py index 1c55be719..96c68d5a1 100644 --- a/openstackclient/tests/unit/api/test_object_store_v1.py +++ b/openstackclient/tests/unit/api/test_object_store_v1.py @@ -151,12 +151,14 @@ class TestContainer(TestObjectAPIv1): 'X-Container-Meta-Owner': FAKE_ACCOUNT, 'x-container-object-count': '1', 'x-container-bytes-used': '577', + 'x-storage-policy': 'o1--sr-r3' } resp = { 'account': FAKE_ACCOUNT, 'container': 'qaz', 'object_count': '1', 'bytes_used': '577', + 'storage_policy': 'o1--sr-r3', 'properties': {'Owner': FAKE_ACCOUNT}, } self.requests_mock.register_uri( diff --git a/openstackclient/tests/unit/object/v1/test_container_all.py b/openstackclient/tests/unit/object/v1/test_container_all.py index 58c90e36d..654cfbc74 100644 --- a/openstackclient/tests/unit/object/v1/test_container_all.py +++ b/openstackclient/tests/unit/object/v1/test_container_all.py @@ -70,6 +70,75 @@ class TestContainerCreate(TestContainerAll): )] self.assertEqual(datalist, list(data)) + def test_object_create_container_storage_policy(self): + self.requests_mock.register_uri( + 'PUT', + object_fakes.ENDPOINT + '/ernie', + headers={ + 'x-trans-id': '314159', + 'x-storage-policy': 'o1--sr-r3' + }, + status_code=200, + ) + + arglist = [ + 'ernie', + '--storage-policy', + 'o1--sr-r3' + ] + verifylist = [ + ('containers', ['ernie']), + ('storage_policy', 'o1--sr-r3') + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = [( + object_fakes.ACCOUNT_ID, + 'ernie', + '314159', + )] + self.assertEqual(datalist, list(data)) + + def test_object_create_container_public(self): + self.requests_mock.register_uri( + 'PUT', + object_fakes.ENDPOINT + '/ernie', + headers={ + 'x-trans-id': '314159', + 'x-container-read': '.r:*,.rlistings' + }, + status_code=200, + ) + + arglist = [ + 'ernie', + '--public' + ] + verifylist = [ + ('containers', ['ernie']), + ('public', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = [( + object_fakes.ACCOUNT_ID, + 'ernie', + '314159', + )] + self.assertEqual(datalist, list(data)) + def test_object_create_container_more(self): self.requests_mock.register_uri( 'PUT', @@ -300,6 +369,7 @@ class TestContainerShow(TestContainerAll): 'x-container-write': 'wsx', 'x-container-sync-to': 'edc', 'x-container-sync-key': 'rfv', + 'x-storage-policy': 'o1--sr-r3' } self.requests_mock.register_uri( 'HEAD', @@ -327,6 +397,7 @@ class TestContainerShow(TestContainerAll): 'container', 'object_count', 'read_acl', + 'storage_policy', 'sync_key', 'sync_to', 'write_acl', @@ -338,6 +409,7 @@ class TestContainerShow(TestContainerAll): 'ernie', '42', 'qaz', + 'o1--sr-r3', 'rfv', 'edc', 'wsx',