swift 2.23.3__py3-none-any.whl → 2.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. swift/__init__.py +29 -50
  2. swift/account/auditor.py +21 -118
  3. swift/account/backend.py +33 -28
  4. swift/account/reaper.py +37 -28
  5. swift/account/replicator.py +22 -0
  6. swift/account/server.py +60 -26
  7. swift/account/utils.py +28 -11
  8. swift-2.23.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +154 -14
  17. swift/cli/manage_shard_ranges.py +705 -37
  18. swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.3.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  23. swift/cli/relinker.py +807 -126
  24. swift/cli/reload.py +135 -0
  25. swift/cli/ringbuilder.py +217 -20
  26. swift/cli/ringcomposer.py +0 -1
  27. swift/cli/shard-info.py +4 -3
  28. swift/common/base_storage_server.py +9 -20
  29. swift/common/bufferedhttp.py +48 -74
  30. swift/common/constraints.py +20 -15
  31. swift/common/container_sync_realms.py +9 -11
  32. swift/common/daemon.py +25 -8
  33. swift/common/db.py +195 -128
  34. swift/common/db_auditor.py +168 -0
  35. swift/common/db_replicator.py +95 -55
  36. swift/common/digest.py +141 -0
  37. swift/common/direct_client.py +144 -33
  38. swift/common/error_limiter.py +93 -0
  39. swift/common/exceptions.py +25 -1
  40. swift/common/header_key_dict.py +2 -9
  41. swift/common/http_protocol.py +373 -0
  42. swift/common/internal_client.py +129 -59
  43. swift/common/linkat.py +3 -4
  44. swift/common/manager.py +284 -67
  45. swift/common/memcached.py +390 -145
  46. swift/common/middleware/__init__.py +4 -0
  47. swift/common/middleware/account_quotas.py +211 -46
  48. swift/common/middleware/acl.py +3 -8
  49. swift/common/middleware/backend_ratelimit.py +230 -0
  50. swift/common/middleware/bulk.py +22 -34
  51. swift/common/middleware/catch_errors.py +1 -3
  52. swift/common/middleware/cname_lookup.py +6 -11
  53. swift/common/middleware/container_quotas.py +1 -1
  54. swift/common/middleware/container_sync.py +39 -17
  55. swift/common/middleware/copy.py +12 -0
  56. swift/common/middleware/crossdomain.py +22 -9
  57. swift/common/middleware/crypto/__init__.py +2 -1
  58. swift/common/middleware/crypto/crypto_utils.py +11 -15
  59. swift/common/middleware/crypto/decrypter.py +28 -11
  60. swift/common/middleware/crypto/encrypter.py +12 -17
  61. swift/common/middleware/crypto/keymaster.py +8 -15
  62. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  63. swift/common/middleware/dlo.py +15 -11
  64. swift/common/middleware/domain_remap.py +5 -4
  65. swift/common/middleware/etag_quoter.py +128 -0
  66. swift/common/middleware/formpost.py +73 -70
  67. swift/common/middleware/gatekeeper.py +8 -1
  68. swift/common/middleware/keystoneauth.py +33 -3
  69. swift/common/middleware/list_endpoints.py +4 -4
  70. swift/common/middleware/listing_formats.py +85 -49
  71. swift/common/middleware/memcache.py +4 -95
  72. swift/common/middleware/name_check.py +3 -2
  73. swift/common/middleware/proxy_logging.py +160 -92
  74. swift/common/middleware/ratelimit.py +17 -10
  75. swift/common/middleware/read_only.py +6 -4
  76. swift/common/middleware/recon.py +59 -22
  77. swift/common/middleware/s3api/acl_handlers.py +25 -3
  78. swift/common/middleware/s3api/acl_utils.py +6 -1
  79. swift/common/middleware/s3api/controllers/__init__.py +6 -0
  80. swift/common/middleware/s3api/controllers/acl.py +3 -2
  81. swift/common/middleware/s3api/controllers/bucket.py +242 -137
  82. swift/common/middleware/s3api/controllers/logging.py +2 -2
  83. swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
  84. swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
  85. swift/common/middleware/s3api/controllers/obj.py +112 -8
  86. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  87. swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
  88. swift/common/middleware/s3api/controllers/tagging.py +57 -0
  89. swift/common/middleware/s3api/controllers/versioning.py +36 -7
  90. swift/common/middleware/s3api/etree.py +22 -9
  91. swift/common/middleware/s3api/exception.py +0 -4
  92. swift/common/middleware/s3api/s3api.py +113 -41
  93. swift/common/middleware/s3api/s3request.py +384 -218
  94. swift/common/middleware/s3api/s3response.py +126 -23
  95. swift/common/middleware/s3api/s3token.py +16 -17
  96. swift/common/middleware/s3api/schema/delete.rng +1 -1
  97. swift/common/middleware/s3api/subresource.py +7 -10
  98. swift/common/middleware/s3api/utils.py +27 -10
  99. swift/common/middleware/slo.py +665 -358
  100. swift/common/middleware/staticweb.py +64 -37
  101. swift/common/middleware/symlink.py +51 -18
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +191 -173
  104. swift/common/middleware/versioned_writes/__init__.py +51 -0
  105. swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
  106. swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
  107. swift/common/middleware/x_profile/exceptions.py +1 -4
  108. swift/common/middleware/x_profile/html_viewer.py +18 -19
  109. swift/common/middleware/x_profile/profile_model.py +1 -2
  110. swift/common/middleware/xprofile.py +10 -10
  111. swift-2.23.3.data/scripts/swift-container-server → swift/common/recon.py +13 -8
  112. swift/common/registry.py +147 -0
  113. swift/common/request_helpers.py +324 -57
  114. swift/common/ring/builder.py +67 -25
  115. swift/common/ring/composite_builder.py +1 -1
  116. swift/common/ring/ring.py +177 -51
  117. swift/common/ring/utils.py +1 -1
  118. swift/common/splice.py +10 -6
  119. swift/common/statsd_client.py +205 -0
  120. swift/common/storage_policy.py +49 -44
  121. swift/common/swob.py +86 -102
  122. swift/common/{utils.py → utils/__init__.py} +2163 -2772
  123. swift/common/utils/base.py +131 -0
  124. swift/common/utils/config.py +433 -0
  125. swift/common/utils/ipaddrs.py +256 -0
  126. swift/common/utils/libc.py +345 -0
  127. swift/common/utils/logs.py +859 -0
  128. swift/common/utils/timestamp.py +412 -0
  129. swift/common/wsgi.py +553 -535
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +490 -231
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +358 -165
  135. swift/container/sharder.py +1540 -684
  136. swift/container/sync.py +94 -88
  137. swift/container/updater.py +53 -32
  138. swift/obj/auditor.py +153 -35
  139. swift/obj/diskfile.py +466 -217
  140. swift/obj/expirer.py +406 -124
  141. swift/obj/mem_diskfile.py +7 -4
  142. swift/obj/mem_server.py +1 -0
  143. swift/obj/reconstructor.py +523 -262
  144. swift/obj/replicator.py +249 -188
  145. swift/obj/server.py +207 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +652 -139
  149. swift/obj/watchers/__init__.py +0 -0
  150. swift/obj/watchers/dark_data.py +213 -0
  151. swift/proxy/controllers/account.py +11 -11
  152. swift/proxy/controllers/base.py +848 -604
  153. swift/proxy/controllers/container.py +433 -92
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1000 -489
  156. swift/proxy/server.py +185 -112
  157. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
  158. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
  159. swift-2.35.0.dist-info/RECORD +201 -0
  160. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  161. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  162. swift-2.35.0.dist-info/pbr.json +1 -0
  163. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  164. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  165. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  166. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  167. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  168. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  169. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  170. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  171. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  172. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  173. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  174. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  175. swift-2.23.3.data/scripts/swift-account-auditor +0 -23
  176. swift-2.23.3.data/scripts/swift-account-info +0 -51
  177. swift-2.23.3.data/scripts/swift-account-reaper +0 -23
  178. swift-2.23.3.data/scripts/swift-account-replicator +0 -34
  179. swift-2.23.3.data/scripts/swift-account-server +0 -23
  180. swift-2.23.3.data/scripts/swift-container-auditor +0 -23
  181. swift-2.23.3.data/scripts/swift-container-info +0 -55
  182. swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
  183. swift-2.23.3.data/scripts/swift-container-replicator +0 -34
  184. swift-2.23.3.data/scripts/swift-container-sharder +0 -37
  185. swift-2.23.3.data/scripts/swift-container-sync +0 -23
  186. swift-2.23.3.data/scripts/swift-container-updater +0 -23
  187. swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
  188. swift-2.23.3.data/scripts/swift-form-signature +0 -20
  189. swift-2.23.3.data/scripts/swift-init +0 -119
  190. swift-2.23.3.data/scripts/swift-object-auditor +0 -29
  191. swift-2.23.3.data/scripts/swift-object-expirer +0 -33
  192. swift-2.23.3.data/scripts/swift-object-info +0 -60
  193. swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
  194. swift-2.23.3.data/scripts/swift-object-relinker +0 -41
  195. swift-2.23.3.data/scripts/swift-object-replicator +0 -37
  196. swift-2.23.3.data/scripts/swift-object-server +0 -27
  197. swift-2.23.3.data/scripts/swift-object-updater +0 -23
  198. swift-2.23.3.data/scripts/swift-proxy-server +0 -23
  199. swift-2.23.3.data/scripts/swift-recon +0 -24
  200. swift-2.23.3.data/scripts/swift-ring-builder +0 -24
  201. swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
  202. swift-2.23.3.data/scripts/swift-ring-composer +0 -22
  203. swift-2.23.3.dist-info/RECORD +0 -220
  204. swift-2.23.3.dist-info/pbr.json +0 -1
  205. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
  206. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
@@ -13,15 +13,24 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ from io import BytesIO
17
+ import json
18
+
19
+ from swift.common import constraints
16
20
  from swift.common.http import HTTP_OK, HTTP_PARTIAL_CONTENT, HTTP_NO_CONTENT
17
21
  from swift.common.request_helpers import update_etag_is_at_header
18
- from swift.common.swob import Range, content_range_header_value
22
+ from swift.common.swob import Range, content_range_header_value, \
23
+ normalize_etag
19
24
  from swift.common.utils import public, list_from_csv
25
+ from swift.common.registry import get_swift_info
20
26
 
27
+ from swift.common.middleware.versioned_writes.object_versioning import \
28
+ DELETE_MARKER_CONTENT_TYPE
21
29
  from swift.common.middleware.s3api.utils import S3Timestamp, sysmeta_header
22
30
  from swift.common.middleware.s3api.controllers.base import Controller
23
31
  from swift.common.middleware.s3api.s3response import S3NotImplemented, \
24
- InvalidRange, NoSuchKey, InvalidArgument, HTTPNoContent
32
+ InvalidRange, NoSuchKey, NoSuchVersion, InvalidArgument, HTTPNoContent, \
33
+ PreconditionFailed, KeyTooLongError
25
34
 
26
35
 
27
36
  class ObjectController(Controller):
@@ -68,8 +77,7 @@ class ObjectController(Controller):
68
77
  continue
69
78
  had_match = True
70
79
  for value in list_from_csv(req.headers[match_header]):
71
- if value.startswith('"') and value.endswith('"'):
72
- value = value[1:-1]
80
+ value = normalize_etag(value)
73
81
  if value.endswith('-N'):
74
82
  # Deal with fake S3-like etags for SLOs uploaded via Swift
75
83
  req.headers[match_header] += ', ' + value[:-2]
@@ -78,11 +86,47 @@ class ObjectController(Controller):
78
86
  # Update where to look
79
87
  update_etag_is_at_header(req, sysmeta_header('object', 'etag'))
80
88
 
81
- resp = req.get_response(self.app)
89
+ object_name = req.object_name
90
+ version_id = req.params.get('versionId')
91
+ if version_id not in ('null', None) and \
92
+ 'object_versioning' not in get_swift_info():
93
+ raise S3NotImplemented()
94
+ part_number = req.validate_part_number(check_max=False)
95
+
96
+ query = {}
97
+ if version_id is not None:
98
+ query['version-id'] = version_id
99
+ if part_number is not None:
100
+ query['part-number'] = part_number
101
+
102
+ if version_id not in ('null', None):
103
+ container_info = req.get_container_info(self.app)
104
+ if not container_info.get(
105
+ 'sysmeta', {}).get('versions-container', ''):
106
+ # Versioning has never been enabled
107
+ raise NoSuchVersion(object_name, version_id)
108
+
109
+ resp = req.get_response(self.app, query=query)
110
+
111
+ if not resp.is_slo:
112
+ # SLO ignores part_number for non-slo objects, but s3api only
113
+ # allows the query param for non-MPU if it's exactly 1.
114
+ part_number = req.validate_part_number(parts_count=1)
115
+ if part_number == 1:
116
+ # When the query param *is* exactly 1 the response status code
117
+ # and headers are updated.
118
+ resp.status = HTTP_PARTIAL_CONTENT
119
+ resp.headers['Content-Range'] = \
120
+ 'bytes 0-%d/%s' % (int(resp.headers['Content-Length']) - 1,
121
+ resp.headers['Content-Length'])
122
+ # else: part_number is None
82
123
 
83
124
  if req.method == 'HEAD':
84
125
  resp.app_iter = None
85
126
 
127
+ if 'x-amz-meta-deleted' in resp.headers:
128
+ raise NoSuchKey(object_name)
129
+
86
130
  for key in ('content-type', 'content-language', 'expires',
87
131
  'cache-control', 'content-disposition',
88
132
  'content-encoding'):
@@ -116,6 +160,8 @@ class ObjectController(Controller):
116
160
  """
117
161
  Handle PUT Object and PUT Object (Copy) request
118
162
  """
163
+ if len(req.object_name) > constraints.MAX_OBJECT_NAME_LENGTH:
164
+ raise KeyTooLongError()
119
165
  # set X-Timestamp by s3api to use at copy resp body
120
166
  req_timestamp = S3Timestamp.now()
121
167
  req.headers['X-Timestamp'] = req_timestamp.internal
@@ -125,12 +171,14 @@ class ObjectController(Controller):
125
171
  req.headers['X-Amz-Copy-Source-Range'],
126
172
  'Illegal copy header')
127
173
  req.check_copy_source(self.app)
174
+ if not req.headers.get('Content-Type'):
175
+ # can't setdefault because it can be None for some reason
176
+ req.headers['Content-Type'] = 'binary/octet-stream'
128
177
  resp = req.get_response(self.app)
129
178
 
130
179
  if 'X-Amz-Copy-Source' in req.headers:
131
180
  resp.append_copy_resp_body(req.controller_name,
132
181
  req_timestamp.s3xmlformat)
133
-
134
182
  # delete object metadata from response
135
183
  for key in list(resp.headers.keys()):
136
184
  if key.lower().startswith('x-amz-meta-'):
@@ -143,20 +191,76 @@ class ObjectController(Controller):
143
191
  def POST(self, req):
144
192
  raise S3NotImplemented()
145
193
 
194
+ def _restore_on_delete(self, req):
195
+ resp = req.get_response(self.app, 'GET', req.container_name, '',
196
+ query={'prefix': req.object_name,
197
+ 'versions': True})
198
+ if resp.status_int != HTTP_OK:
199
+ return resp
200
+ old_versions = json.loads(resp.body)
201
+ resp = None
202
+ for item in old_versions:
203
+ if item['content_type'] == DELETE_MARKER_CONTENT_TYPE:
204
+ resp = None
205
+ break
206
+ try:
207
+ resp = req.get_response(self.app, 'PUT', query={
208
+ 'version-id': item['version_id']})
209
+ except PreconditionFailed:
210
+ self.logger.debug('skipping failed PUT?version-id=%s' %
211
+ item['version_id'])
212
+ continue
213
+ # if that worked, we'll go ahead and fix up the status code
214
+ resp.status_int = HTTP_NO_CONTENT
215
+ break
216
+ return resp
217
+
146
218
  @public
147
219
  def DELETE(self, req):
148
220
  """
149
221
  Handle DELETE Object request
150
222
  """
223
+ if 'versionId' in req.params and \
224
+ req.params['versionId'] != 'null' and \
225
+ 'object_versioning' not in get_swift_info():
226
+ raise S3NotImplemented()
227
+
228
+ version_id = req.params.get('versionId')
229
+ if version_id not in ('null', None):
230
+ container_info = req.get_container_info(self.app)
231
+ if not container_info.get(
232
+ 'sysmeta', {}).get('versions-container', ''):
233
+ # Versioning has never been enabled
234
+ return HTTPNoContent(headers={'x-amz-version-id': version_id})
235
+
151
236
  try:
152
- query = req.gen_multipart_manifest_delete_query(self.app)
237
+ try:
238
+ query = req.gen_multipart_manifest_delete_query(
239
+ self.app, version=version_id)
240
+ except NoSuchKey:
241
+ query = {}
242
+
153
243
  req.headers['Content-Type'] = None # Ignore client content-type
244
+
245
+ if version_id is not None:
246
+ query['version-id'] = version_id
247
+ query['symlink'] = 'get'
248
+
154
249
  resp = req.get_response(self.app, query=query)
155
- if query and resp.status_int == HTTP_OK:
250
+ # If we're going to continue using this request, we need to
251
+ # replace the now-spent body
252
+ req.environ['wsgi.input'] = BytesIO(b'')
253
+ req.headers['content-length'] = '0'
254
+ req.headers.pop('transfer-encoding', None)
255
+ if query.get('multipart-manifest') and resp.status_int == HTTP_OK:
156
256
  for chunk in resp.app_iter:
157
257
  pass # drain the bulk-deleter response
158
258
  resp.status = HTTP_NO_CONTENT
159
259
  resp.body = b''
260
+ if resp.sw_headers.get('X-Object-Current-Version-Id') == 'null':
261
+ new_resp = self._restore_on_delete(req)
262
+ if new_resp:
263
+ resp = new_resp
160
264
  except NoSuchKey:
161
265
  # expect to raise NoSuchBucket when the bucket doesn't exist
162
266
  req.get_container_info(self.app)
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2010-2023 OpenStack Foundation
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
+ # implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from swift.common.utils import public
17
+
18
+ from swift.common.middleware.s3api.controllers.base import Controller, \
19
+ bucket_operation, S3NotImplemented
20
+ from swift.common.middleware.s3api.s3response import \
21
+ ObjectLockConfigurationNotFoundError
22
+
23
+
24
+ class ObjectLockController(Controller):
25
+ """
26
+ Handles GET object-lock request, which always returns
27
+ <ObjectLockEnabled>Disabled</ObjectLockEnabled>
28
+ """
29
+ @public
30
+ @bucket_operation
31
+ def GET(self, req):
32
+ """
33
+ Handles GET object-lock param calls.
34
+ """
35
+ raise ObjectLockConfigurationNotFoundError(req.container_name)
36
+
37
+ @public
38
+ @bucket_operation
39
+ def PUT(self, req):
40
+ """
41
+ Handles PUT object-lock param calls.
42
+ """
43
+ # Basically we don't support it, so return a 501
44
+ raise S3NotImplemented('The requested resource is not implemented')
@@ -13,7 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- from six.moves.urllib.parse import quote
16
+ from urllib.parse import quote
17
17
  from swift.common.utils import public
18
18
 
19
19
  from swift.common.middleware.s3api.controllers.base import Controller
@@ -37,7 +37,7 @@ class S3AclController(Controller):
37
37
  """
38
38
  Handles GET Bucket acl and GET Object acl.
39
39
  """
40
- resp = req.get_response(self.app)
40
+ resp = req.get_response(self.app, method='HEAD')
41
41
 
42
42
  acl = resp.object_acl if req.is_object_request else resp.bucket_acl
43
43
 
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2014 OpenStack Foundation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
+ # implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from swift.common.utils import public
17
+
18
+ from swift.common.middleware.s3api.controllers.base import Controller, \
19
+ S3NotImplemented
20
+ from swift.common.middleware.s3api.s3response import HTTPOk
21
+ from swift.common.middleware.s3api.etree import Element, tostring, \
22
+ SubElement
23
+
24
+
25
+ class TaggingController(Controller):
26
+ """
27
+ Handles the following APIs:
28
+
29
+ * GET Bucket and Object tagging
30
+ * PUT Bucket and Object tagging
31
+ * DELETE Bucket and Object tagging
32
+
33
+ """
34
+ @public
35
+ def GET(self, req):
36
+ """
37
+ Handles GET Bucket and Object tagging.
38
+ """
39
+ elem = Element('Tagging')
40
+ SubElement(elem, 'TagSet')
41
+ body = tostring(elem)
42
+
43
+ return HTTPOk(body=body, content_type=None)
44
+
45
+ @public
46
+ def PUT(self, req):
47
+ """
48
+ Handles PUT Bucket and Object tagging.
49
+ """
50
+ raise S3NotImplemented('The requested resource is not implemented')
51
+
52
+ @public
53
+ def DELETE(self, req):
54
+ """
55
+ Handles DELETE Bucket and Object tagging.
56
+ """
57
+ raise S3NotImplemented('The requested resource is not implemented')
@@ -13,12 +13,17 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- from swift.common.utils import public
16
+ from swift.common.utils import public, config_true_value
17
+ from swift.common.registry import get_swift_info
17
18
 
18
19
  from swift.common.middleware.s3api.controllers.base import Controller, \
19
20
  bucket_operation
20
- from swift.common.middleware.s3api.etree import Element, tostring
21
- from swift.common.middleware.s3api.s3response import HTTPOk, S3NotImplemented
21
+ from swift.common.middleware.s3api.etree import Element, tostring, \
22
+ fromstring, XMLSyntaxError, DocumentInvalid, SubElement
23
+ from swift.common.middleware.s3api.s3response import HTTPOk, \
24
+ S3NotImplemented, MalformedXML
25
+
26
+ MAX_PUT_VERSIONING_BODY_SIZE = 10240
22
27
 
23
28
 
24
29
  class VersioningController(Controller):
@@ -36,13 +41,16 @@ class VersioningController(Controller):
36
41
  """
37
42
  Handles GET Bucket versioning.
38
43
  """
39
- req.get_response(self.app, method='HEAD')
44
+ sysmeta = req.get_container_info(self.app).get('sysmeta', {})
40
45
 
41
- # Just report there is no versioning configured here.
42
46
  elem = Element('VersioningConfiguration')
47
+ if sysmeta.get('versions-enabled'):
48
+ SubElement(elem, 'Status').text = (
49
+ 'Enabled' if config_true_value(sysmeta['versions-enabled'])
50
+ else 'Suspended')
43
51
  body = tostring(elem)
44
52
 
45
- return HTTPOk(body=body, content_type="text/plain")
53
+ return HTTPOk(body=body, content_type=None)
46
54
 
47
55
  @public
48
56
  @bucket_operation
@@ -50,4 +58,25 @@ class VersioningController(Controller):
50
58
  """
51
59
  Handles PUT Bucket versioning.
52
60
  """
53
- raise S3NotImplemented()
61
+ if 'object_versioning' not in get_swift_info():
62
+ raise S3NotImplemented()
63
+
64
+ xml = req.xml(MAX_PUT_VERSIONING_BODY_SIZE)
65
+ try:
66
+ elem = fromstring(xml, 'VersioningConfiguration')
67
+ status = elem.find('./Status').text
68
+ except (XMLSyntaxError, DocumentInvalid):
69
+ raise MalformedXML()
70
+ except Exception as e:
71
+ self.logger.error(e)
72
+ raise
73
+
74
+ if status not in ['Enabled', 'Suspended']:
75
+ raise MalformedXML()
76
+
77
+ # Set up versioning
78
+ # NB: object_versioning responsible for ensuring its container exists
79
+ req.headers['X-Versions-Enabled'] = str(status == 'Enabled').lower()
80
+ req.get_response(self.app, 'POST')
81
+
82
+ return HTTPOk()
@@ -15,13 +15,22 @@
15
15
 
16
16
  import lxml.etree
17
17
  from copy import deepcopy
18
- from pkg_resources import resource_stream # pylint: disable-msg=E0611
19
- import six
18
+ try:
19
+ # importlib.resources was introduced in py37, but couldn't handle
20
+ # resources in subdirectories (which we use); files() added support
21
+ from importlib.resources import files
22
+ del files
23
+ except ImportError:
24
+ # python < 3.9
25
+ from pkg_resources import resource_stream # pylint: disable-msg=E0611
26
+ else:
27
+ import importlib.resources
28
+ resource_stream = None
20
29
 
21
30
  from swift.common.utils import get_logger
22
31
  from swift.common.middleware.s3api.exception import S3Exception
23
32
  from swift.common.middleware.s3api.utils import camel_to_snake, \
24
- utf8encode, utf8decode
33
+ utf8decode
25
34
 
26
35
  XMLNS_S3 = 'http://s3.amazonaws.com/doc/2006-03-01/'
27
36
  XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
@@ -41,7 +50,7 @@ def cleanup_namespaces(elem):
41
50
  tag = tag[len('{%s}' % ns):]
42
51
  return tag
43
52
 
44
- if not isinstance(elem.tag, six.string_types):
53
+ if not isinstance(elem.tag, str):
45
54
  # elem is a comment element.
46
55
  return
47
56
 
@@ -70,7 +79,13 @@ def fromstring(text, root_tag=None, logger=None):
70
79
  # validate XML
71
80
  try:
72
81
  path = 'schema/%s.rng' % camel_to_snake(root_tag)
73
- with resource_stream(__name__, path) as rng:
82
+ if resource_stream:
83
+ # python < 3.9
84
+ stream = resource_stream(__name__, path)
85
+ else:
86
+ stream = importlib.resources.files(
87
+ __name__.rsplit('.', 1)[0]).joinpath(path).open('rb')
88
+ with stream as rng:
74
89
  lxml.etree.RelaxNG(file=rng).assertValid(elem)
75
90
  except IOError as e:
76
91
  # Probably, the schema file doesn't exist.
@@ -92,7 +107,7 @@ def tostring(tree, use_s3ns=True, xml_declaration=True):
92
107
 
93
108
  root = Element(tree.tag, attrib=tree.attrib, nsmap=nsmap)
94
109
  root.text = tree.text
95
- root.extend(deepcopy(tree.getchildren()))
110
+ root.extend(deepcopy(list(tree)))
96
111
  tree = root
97
112
 
98
113
  return lxml.etree.tostring(tree, xml_declaration=xml_declaration,
@@ -120,8 +135,6 @@ class _Element(lxml.etree.ElementBase):
120
135
  """
121
136
  utf-8 wrapper property of lxml.etree.Element.text
122
137
  """
123
- if six.PY2:
124
- return utf8encode(lxml.etree.ElementBase.text.__get__(self))
125
138
  return lxml.etree.ElementBase.text.__get__(self)
126
139
 
127
140
  @text.setter
@@ -130,7 +143,7 @@ class _Element(lxml.etree.ElementBase):
130
143
 
131
144
 
132
145
  parser_lookup = lxml.etree.ElementDefaultClassLookup(element=_Element)
133
- parser = lxml.etree.XMLParser()
146
+ parser = lxml.etree.XMLParser(resolve_entities=False, no_network=True)
134
147
  parser.set_element_class_lookup(parser_lookup)
135
148
 
136
149
  Element = parser.makeelement
@@ -22,10 +22,6 @@ class NotS3Request(S3Exception):
22
22
  pass
23
23
 
24
24
 
25
- class BadSwiftRequest(S3Exception):
26
- pass
27
-
28
-
29
25
  class ACLError(S3Exception):
30
26
  pass
31
27