4 files changed
@@ -24,6 +24,11 @@ | |||
| 24 | 24 | from openstackclient.api import api | |
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | + GLOBAL_READ_ACL = ".r:*" | ||
| 28 | + LIST_CONTENTS_ACL = ".rlistings" | ||
| 29 | + PUBLIC_CONTAINER_ACLS = [GLOBAL_READ_ACL, LIST_CONTENTS_ACL] | ||
| 30 | + | ||
| 31 | + | ||
| 27 | 32 | class APIv1(api.BaseAPI): | |
| 28 | 33 | """Object Store v1 API""" | |
| 29 | 34 | ||
@@ -33,15 +38,32 @@ def __init__(self, **kwargs): | |||
| 33 | 38 | def container_create( | |
| 34 | 39 | self, | |
| 35 | 40 | container=None, | |
| 41 | + public=False, | ||
| 42 | + storage_policy=None | ||
| 36 | 43 | ): | |
| 37 | 44 | """Create a container | |
| 38 | 45 | ||
| 39 | 46 | :param string container: | |
| 40 | 47 | name of container to create | |
| 48 | + :param bool public: | ||
| 49 | + Boolean value indicating if the container should be publicly | ||
| 50 | + readable. Defaults to False. | ||
| 51 | + :param string storage_policy: | ||
| 52 | + Name of the a specific storage policy to use. If not specified | ||
| 53 | + swift will use its default storage policy. | ||
| 41 | 54 | :returns: | |
| 42 | 55 | dict of returned headers | |
| 43 | 56 | """ | |
| 44 | - response = self.create(urllib.parse.quote(container), method='PUT') | ||
| 57 | + | ||
| 58 | + headers = {} | ||
| 59 | + if public: | ||
| 60 | + headers['x-container-read'] = ",".join(PUBLIC_CONTAINER_ACLS) | ||
| 61 | + if storage_policy is not None: | ||
| 62 | + headers['x-storage-policy'] = storage_policy | ||
| 63 | + | ||
| 64 | + response = self.create( | ||
| 65 | + urllib.parse.quote(container), method='PUT', headers=headers) | ||
| 66 | + | ||
| 45 | 67 | data = { | |
| 46 | 68 | 'account': self._find_account_id(), | |
| 47 | 69 | 'container': container, | |
@@ -173,7 +195,8 @@ def container_show( | |||
| 173 | 195 | 'object_count': response.headers.get( | |
| 174 | 196 | 'x-container-object-count' | |
| 175 | 197 | ), | |
| 176 | - 'bytes_used': response.headers.get('x-container-bytes-used') | ||
| 198 | + 'bytes_used': response.headers.get('x-container-bytes-used'), | ||
| 199 | + 'storage_policy': response.headers.get('x-storage-policy'), | ||
| 177 | 200 | } | |
| 178 | 201 | ||
| 179 | 202 | if 'x-container-read' in response.headers: | |
@@ -33,6 +33,16 @@ class CreateContainer(command.Lister): | |||
| 33 | 33 | ||
| 34 | 34 | def get_parser(self, prog_name): | |
| 35 | 35 | parser = super(CreateContainer, self).get_parser(prog_name) | |
| 36 | + parser.add_argument( | ||
| 37 | + '--public', | ||
| 38 | + action='store_true', | ||
| 39 | + default=False, | ||
| 40 | + help="Make the container publicly accessible" | ||
| 41 | + ) | ||
| 42 | + parser.add_argument( | ||
| 43 | + '--storage-policy', | ||
| 44 | + help="Specify a particular storage policy to use." | ||
| 45 | + ) | ||
| 36 | 46 | parser.add_argument( | |
| 37 | 47 | 'containers', | |
| 38 | 48 | metavar='<container-name>', | |
@@ -51,6 +61,8 @@ def take_action(self, parsed_args): | |||
| 51 | 61 | ' is 256'), len(container)) | |
| 52 | 62 | data = self.app.client_manager.object_store.container_create( | |
| 53 | 63 | container=container, | |
| 64 | + public=parsed_args.public, | ||
| 65 | + storage_policy=parsed_args.storage_policy | ||
| 54 | 66 | ) | |
| 55 | 67 | results.append(data) | |
| 56 | 68 | ||
@@ -151,12 +151,14 @@ def test_container_show(self): | |||
| 151 | 151 | 'X-Container-Meta-Owner': FAKE_ACCOUNT, | |
| 152 | 152 | 'x-container-object-count': '1', | |
| 153 | 153 | 'x-container-bytes-used': '577', | |
| 154 | + 'x-storage-policy': 'o1--sr-r3' | ||
| 154 | 155 | } | |
| 155 | 156 | resp = { | |
| 156 | 157 | 'account': FAKE_ACCOUNT, | |
| 157 | 158 | 'container': 'qaz', | |
| 158 | 159 | 'object_count': '1', | |
| 159 | 160 | 'bytes_used': '577', | |
| 161 | + 'storage_policy': 'o1--sr-r3', | ||
| 160 | 162 | 'properties': {'Owner': FAKE_ACCOUNT}, | |
| 161 | 163 | } | |
| 162 | 164 | self.requests_mock.register_uri( | |
@@ -70,6 +70,75 @@ def test_object_create_container_single(self): | |||
| 70 | 70 | )] | |
| 71 | 71 | self.assertEqual(datalist, list(data)) | |
| 72 | 72 | ||
| 73 | + def test_object_create_container_storage_policy(self): | ||
| 74 | + self.requests_mock.register_uri( | ||
| 75 | + 'PUT', | ||
| 76 | + object_fakes.ENDPOINT + '/ernie', | ||
| 77 | + headers={ | ||
| 78 | + 'x-trans-id': '314159', | ||
| 79 | + 'x-storage-policy': 'o1--sr-r3' | ||
| 80 | + }, | ||
| 81 | + status_code=200, | ||
| 82 | + ) | ||
| 83 | + | ||
| 84 | + arglist = [ | ||
| 85 | + 'ernie', | ||
| 86 | + '--storage-policy', | ||
| 87 | + 'o1--sr-r3' | ||
| 88 | + ] | ||
| 89 | + verifylist = [ | ||
| 90 | + ('containers', ['ernie']), | ||
| 91 | + ('storage_policy', 'o1--sr-r3') | ||
| 92 | + ] | ||
| 93 | + parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 94 | + | ||
| 95 | + # In base command class ShowOne in cliff, abstract method take_action() | ||
| 96 | + # returns a two-part tuple with a tuple of column names and a tuple of | ||
| 97 | + # data to be shown. | ||
| 98 | + columns, data = self.cmd.take_action(parsed_args) | ||
| 99 | + | ||
| 100 | + self.assertEqual(self.columns, columns) | ||
| 101 | + datalist = [( | ||
| 102 | + object_fakes.ACCOUNT_ID, | ||
| 103 | + 'ernie', | ||
| 104 | + '314159', | ||
| 105 | + )] | ||
| 106 | + self.assertEqual(datalist, list(data)) | ||
| 107 | + | ||
| 108 | + def test_object_create_container_public(self): | ||
| 109 | + self.requests_mock.register_uri( | ||
| 110 | + 'PUT', | ||
| 111 | + object_fakes.ENDPOINT + '/ernie', | ||
| 112 | + headers={ | ||
| 113 | + 'x-trans-id': '314159', | ||
| 114 | + 'x-container-read': '.r:*,.rlistings' | ||
| 115 | + }, | ||
| 116 | + status_code=200, | ||
| 117 | + ) | ||
| 118 | + | ||
| 119 | + arglist = [ | ||
| 120 | + 'ernie', | ||
| 121 | + '--public' | ||
| 122 | + ] | ||
| 123 | + verifylist = [ | ||
| 124 | + ('containers', ['ernie']), | ||
| 125 | + ('public', True) | ||
| 126 | + ] | ||
| 127 | + parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 128 | + | ||
| 129 | + # In base command class ShowOne in cliff, abstract method take_action() | ||
| 130 | + # returns a two-part tuple with a tuple of column names and a tuple of | ||
| 131 | + # data to be shown. | ||
| 132 | + columns, data = self.cmd.take_action(parsed_args) | ||
| 133 | + | ||
| 134 | + self.assertEqual(self.columns, columns) | ||
| 135 | + datalist = [( | ||
| 136 | + object_fakes.ACCOUNT_ID, | ||
| 137 | + 'ernie', | ||
| 138 | + '314159', | ||
| 139 | + )] | ||
| 140 | + self.assertEqual(datalist, list(data)) | ||
| 141 | + | ||
| 73 | 142 | def test_object_create_container_more(self): | |
| 74 | 143 | self.requests_mock.register_uri( | |
| 75 | 144 | 'PUT', | |
@@ -300,6 +369,7 @@ def test_object_show_container(self): | |||
| 300 | 369 | 'x-container-write': 'wsx', | |
| 301 | 370 | 'x-container-sync-to': 'edc', | |
| 302 | 371 | 'x-container-sync-key': 'rfv', | |
| 372 | + 'x-storage-policy': 'o1--sr-r3' | ||
| 303 | 373 | } | |
| 304 | 374 | self.requests_mock.register_uri( | |
| 305 | 375 | 'HEAD', | |
@@ -327,6 +397,7 @@ def test_object_show_container(self): | |||
| 327 | 397 | 'container', | |
| 328 | 398 | 'object_count', | |
| 329 | 399 | 'read_acl', | |
| 400 | + 'storage_policy', | ||
| 330 | 401 | 'sync_key', | |
| 331 | 402 | 'sync_to', | |
| 332 | 403 | 'write_acl', | |
@@ -338,6 +409,7 @@ def test_object_show_container(self): | |||
| 338 | 409 | 'ernie', | |
| 339 | 410 | '42', | |
| 340 | 411 | 'qaz', | |
| 412 | + 'o1--sr-r3', | ||
| 341 | 413 | 'rfv', | |
| 342 | 414 | 'edc', | |
| 343 | 415 | 'wsx', | |
0 commit comments