swift 2.23.2__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 (208) 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.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +183 -29
  17. swift/cli/manage_shard_ranges.py +708 -37
  18. swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.2.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 +198 -127
  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 +396 -147
  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 -81
  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 +52 -19
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +192 -174
  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.2.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} +2191 -2762
  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 +555 -536
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +552 -227
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +397 -176
  135. swift/container/sharder.py +1580 -639
  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 +213 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +653 -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 +452 -86
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1009 -490
  156. swift/proxy/server.py +185 -112
  157. swift-2.35.0.dist-info/AUTHORS +501 -0
  158. swift-2.35.0.dist-info/LICENSE +202 -0
  159. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
  160. swift-2.35.0.dist-info/RECORD +201 -0
  161. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  162. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  163. swift-2.35.0.dist-info/pbr.json +1 -0
  164. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  165. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  166. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  167. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  168. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  169. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  170. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  171. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  172. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  173. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  174. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  175. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  176. swift-2.23.2.data/scripts/swift-account-auditor +0 -23
  177. swift-2.23.2.data/scripts/swift-account-info +0 -51
  178. swift-2.23.2.data/scripts/swift-account-reaper +0 -23
  179. swift-2.23.2.data/scripts/swift-account-replicator +0 -34
  180. swift-2.23.2.data/scripts/swift-account-server +0 -23
  181. swift-2.23.2.data/scripts/swift-container-auditor +0 -23
  182. swift-2.23.2.data/scripts/swift-container-info +0 -51
  183. swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
  184. swift-2.23.2.data/scripts/swift-container-replicator +0 -34
  185. swift-2.23.2.data/scripts/swift-container-sharder +0 -33
  186. swift-2.23.2.data/scripts/swift-container-sync +0 -23
  187. swift-2.23.2.data/scripts/swift-container-updater +0 -23
  188. swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
  189. swift-2.23.2.data/scripts/swift-form-signature +0 -20
  190. swift-2.23.2.data/scripts/swift-init +0 -119
  191. swift-2.23.2.data/scripts/swift-object-auditor +0 -29
  192. swift-2.23.2.data/scripts/swift-object-expirer +0 -33
  193. swift-2.23.2.data/scripts/swift-object-info +0 -60
  194. swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
  195. swift-2.23.2.data/scripts/swift-object-relinker +0 -41
  196. swift-2.23.2.data/scripts/swift-object-replicator +0 -37
  197. swift-2.23.2.data/scripts/swift-object-server +0 -27
  198. swift-2.23.2.data/scripts/swift-object-updater +0 -23
  199. swift-2.23.2.data/scripts/swift-proxy-server +0 -23
  200. swift-2.23.2.data/scripts/swift-recon +0 -24
  201. swift-2.23.2.data/scripts/swift-ring-builder +0 -24
  202. swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
  203. swift-2.23.2.data/scripts/swift-ring-composer +0 -22
  204. swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
  205. swift-2.23.2.dist-info/RECORD +0 -220
  206. swift-2.23.2.dist-info/metadata.json +0 -1
  207. swift-2.23.2.dist-info/pbr.json +0 -1
  208. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,7 @@
14
14
  # limitations under the License.
15
15
 
16
16
  import re
17
- from collections import MutableMapping
17
+ from collections.abc import MutableMapping
18
18
  from functools import partial
19
19
 
20
20
  from swift.common import header_key_dict
@@ -25,6 +25,8 @@ from swift.common.request_helpers import is_sys_meta
25
25
  from swift.common.middleware.s3api.utils import snake_to_camel, \
26
26
  sysmeta_prefix, sysmeta_header
27
27
  from swift.common.middleware.s3api.etree import Element, SubElement, tostring
28
+ from swift.common.middleware.versioned_writes.object_versioning import \
29
+ DELETE_MARKER_CONTENT_TYPE
28
30
 
29
31
 
30
32
  class HeaderKeyDict(header_key_dict.HeaderKeyDict):
@@ -44,6 +46,57 @@ class HeaderKeyDict(header_key_dict.HeaderKeyDict):
44
46
  return s
45
47
 
46
48
 
49
+ def translate_swift_to_s3(key, val):
50
+ _key = swob.bytes_to_wsgi(swob.wsgi_to_bytes(key).lower())
51
+
52
+ def translate_meta_key(_key):
53
+ if not _key.startswith('x-object-meta-'):
54
+ return _key
55
+ # Note that AWS allows user-defined metadata with underscores in the
56
+ # header, while WSGI (and other protocols derived from CGI) does not
57
+ # differentiate between an underscore and a dash. Fortunately,
58
+ # eventlet exposes the raw headers from the client, so we could
59
+ # translate '_' to '=5F' on the way in. Now, we translate back.
60
+ return 'x-amz-meta-' + _key[14:].replace('=5f', '_')
61
+
62
+ if _key.startswith('x-object-meta-'):
63
+ return translate_meta_key(_key), val
64
+ elif _key in ('accept-ranges', 'content-length', 'content-type',
65
+ 'content-range', 'content-encoding',
66
+ 'content-disposition', 'content-language',
67
+ 'etag', 'last-modified', 'x-robots-tag',
68
+ 'cache-control', 'expires'):
69
+ return key, val
70
+ elif _key == 'x-object-version-id':
71
+ return 'x-amz-version-id', val
72
+ elif _key == 'x-parts-count':
73
+ return 'x-amz-mp-parts-count', val
74
+ elif _key == 'x-copied-from-version-id':
75
+ return 'x-amz-copy-source-version-id', val
76
+ elif _key == 'x-backend-content-type' and \
77
+ val == DELETE_MARKER_CONTENT_TYPE:
78
+ return 'x-amz-delete-marker', 'true'
79
+ elif _key == 'access-control-expose-headers':
80
+ exposed_headers = val.split(', ')
81
+ exposed_headers.extend([
82
+ 'x-amz-request-id',
83
+ 'x-amz-id-2',
84
+ ])
85
+ return 'access-control-expose-headers', ', '.join(
86
+ translate_meta_key(h) for h in exposed_headers)
87
+ elif _key == 'access-control-allow-methods':
88
+ methods = val.split(', ')
89
+ try:
90
+ methods.remove('COPY') # that's not a thing in S3
91
+ except ValueError:
92
+ pass # not there? don't worry about it
93
+ return key, ', '.join(methods)
94
+ elif _key.startswith('access-control-'):
95
+ return key, val
96
+ # else, drop the header
97
+ return None
98
+
99
+
47
100
  class S3ResponseBase(object):
48
101
  """
49
102
  Base class for swift3 responses.
@@ -57,6 +110,7 @@ class S3Response(S3ResponseBase, swob.Response):
57
110
  headers instead of Swift's HeaderKeyDict. This also translates Swift
58
111
  specific headers to S3 headers.
59
112
  """
113
+
60
114
  def __init__(self, *args, **kwargs):
61
115
  swob.Response.__init__(self, *args, **kwargs)
62
116
 
@@ -96,22 +150,13 @@ class S3Response(S3ResponseBase, swob.Response):
96
150
 
97
151
  # Handle swift headers
98
152
  for key, val in sw_headers.items():
99
- _key = swob.bytes_to_wsgi(swob.wsgi_to_bytes(key).lower())
100
-
101
- if _key.startswith('x-object-meta-'):
102
- # Note that AWS ignores user-defined headers with '=' in the
103
- # header name. We translated underscores to '=5F' on the way
104
- # in, though.
105
- headers['x-amz-meta-' + _key[14:].replace('=5f', '_')] = val
106
- elif _key in ('content-length', 'content-type',
107
- 'content-range', 'content-encoding',
108
- 'content-disposition', 'content-language',
109
- 'etag', 'last-modified', 'x-robots-tag',
110
- 'cache-control', 'expires'):
111
- headers[key] = val
112
- elif _key == 'x-static-large-object':
113
- # for delete slo
114
- self.is_slo = config_true_value(val)
153
+ s3_pair = translate_swift_to_s3(key, val)
154
+ if s3_pair is None:
155
+ continue
156
+ headers[s3_pair[0]] = s3_pair[1]
157
+
158
+ self.is_slo = config_true_value(sw_headers.get(
159
+ 'x-static-large-object'))
115
160
 
116
161
  # Check whether we stored the AWS-style etag on upload
117
162
  override_etag = s3_sysmeta_headers.get(
@@ -184,16 +229,17 @@ class ErrorResponse(S3ResponseBase, swob.HTTPException):
184
229
  _code = ''
185
230
  xml_declaration = True
186
231
 
187
- def __init__(self, msg=None, *args, **kwargs):
232
+ def __init__(self, msg=None, reason=None, *args, **kwargs):
188
233
  if msg:
189
234
  self._msg = msg
190
235
  if not self._code:
191
236
  self._code = self.__class__.__name__
237
+ self.reason = reason
192
238
 
193
239
  self.info = kwargs.copy()
194
240
  for reserved_key in ('headers', 'body'):
195
241
  if self.info.get(reserved_key):
196
- del(self.info[reserved_key])
242
+ del (self.info[reserved_key])
197
243
 
198
244
  swob.HTTPException.__init__(
199
245
  self, status=kwargs.pop('status', self._status),
@@ -202,6 +248,19 @@ class ErrorResponse(S3ResponseBase, swob.HTTPException):
202
248
  **kwargs)
203
249
  self.headers = HeaderKeyDict(self.headers)
204
250
 
251
+ @property
252
+ def summary(self):
253
+ """Provide a summary of the error code and reason."""
254
+ if self.reason:
255
+ summary = '.'.join([self._code, self.reason])
256
+ else:
257
+ summary = self._code
258
+ return summary.replace(' ', '_')
259
+
260
+ @property
261
+ def metric_name(self):
262
+ return '.'.join([str(self.status_int), self.summary])
263
+
205
264
  def _body_iter(self):
206
265
  error_elem = Element('Error')
207
266
  SubElement(error_elem, 'Code').text = self._code
@@ -217,7 +276,7 @@ class ErrorResponse(S3ResponseBase, swob.HTTPException):
217
276
 
218
277
  def _dict_to_etree(self, parent, d):
219
278
  for key, value in d.items():
220
- tag = re.sub('\W', '', snake_to_camel(key))
279
+ tag = re.sub(r'\W', '', snake_to_camel(key))
221
280
  elem = SubElement(parent, tag)
222
281
 
223
282
  if isinstance(value, (dict, MutableMapping)):
@@ -265,6 +324,12 @@ class BadDigest(ErrorResponse):
265
324
  _msg = 'The Content-MD5 you specified did not match what we received.'
266
325
 
267
326
 
327
+ class XAmzContentSHA256Mismatch(ErrorResponse):
328
+ _status = '400 Bad Request'
329
+ _msg = "The provided 'x-amz-content-sha256' header does not match what " \
330
+ "was computed."
331
+
332
+
268
333
  class BucketAlreadyExists(ErrorResponse):
269
334
  _status = '409 Conflict'
270
335
  _msg = 'The requested bucket name is not available. The bucket ' \
@@ -289,6 +354,12 @@ class BucketNotEmpty(ErrorResponse):
289
354
  _msg = 'The bucket you tried to delete is not empty'
290
355
 
291
356
 
357
+ class VersionedBucketNotEmpty(BucketNotEmpty):
358
+ _msg = 'The bucket you tried to delete is not empty. ' \
359
+ 'You must delete all versions in the bucket.'
360
+ _code = 'BucketNotEmpty'
361
+
362
+
292
363
  class CredentialsNotSupported(ErrorResponse):
293
364
  _status = '400 Bad Request'
294
365
  _msg = 'This request does not support credentials.'
@@ -375,7 +446,7 @@ class InvalidBucketState(ErrorResponse):
375
446
 
376
447
  class InvalidDigest(ErrorResponse):
377
448
  _status = '400 Bad Request'
378
- _msg = 'The Content-MD5 you specified was an invalid.'
449
+ _msg = 'The Content-MD5 you specified was invalid.'
379
450
 
380
451
 
381
452
  class InvalidLocationConstraint(ErrorResponse):
@@ -388,6 +459,17 @@ class InvalidObjectState(ErrorResponse):
388
459
  _msg = 'The operation is not valid for the current state of the object.'
389
460
 
390
461
 
462
+ class InvalidPartArgument(InvalidArgument):
463
+ _code = 'InvalidArgument'
464
+
465
+ def __init__(self, max_parts, value):
466
+ err_msg = ('Part number must be an integer between '
467
+ '1 and %s, inclusive' % max_parts)
468
+ super(InvalidArgument, self).__init__(err_msg,
469
+ argument_name='partNumber',
470
+ argument_value=value)
471
+
472
+
391
473
  class InvalidPart(ErrorResponse):
392
474
  _status = '400 Bad Request'
393
475
  _msg = 'One or more of the specified parts could not be found. The part ' \
@@ -417,6 +499,11 @@ class InvalidRange(ErrorResponse):
417
499
  _msg = 'The requested range cannot be satisfied.'
418
500
 
419
501
 
502
+ class InvalidPartNumber(ErrorResponse):
503
+ _status = '416 Requested Range Not Satisfiable'
504
+ _msg = 'The requested partnumber is not satisfiable'
505
+
506
+
420
507
  class InvalidRequest(ErrorResponse):
421
508
  _status = '400 Bad Request'
422
509
  _msg = 'Invalid Request.'
@@ -461,7 +548,7 @@ class InvalidURI(ErrorResponse):
461
548
  ErrorResponse.__init__(self, msg, uri=uri, *args, **kwargs)
462
549
 
463
550
 
464
- class KeyTooLong(ErrorResponse):
551
+ class KeyTooLongError(ErrorResponse):
465
552
  _status = '400 Bad Request'
466
553
  _msg = 'Your key is too long.'
467
554
 
@@ -481,7 +568,7 @@ class MalformedPOSTRequest(ErrorResponse):
481
568
  class MalformedXML(ErrorResponse):
482
569
  _status = '400 Bad Request'
483
570
  _msg = 'The XML you provided was not well-formed or did not validate ' \
484
- 'against our published schema.'
571
+ 'against our published schema'
485
572
 
486
573
 
487
574
  class MaxMessageLengthExceeded(ErrorResponse):
@@ -553,6 +640,16 @@ class NoSuchKey(ErrorResponse):
553
640
  ErrorResponse.__init__(self, msg, key=key, *args, **kwargs)
554
641
 
555
642
 
643
+ class ObjectLockConfigurationNotFoundError(ErrorResponse):
644
+ _status = '404 Not found'
645
+ _msg = 'Object Lock configuration does not exist for this bucket'
646
+
647
+ def __init__(self, bucket, msg=None, *args, **kwargs):
648
+ if not bucket:
649
+ raise InternalError()
650
+ ErrorResponse.__init__(self, msg, bucket_name=bucket, *args, **kwargs)
651
+
652
+
556
653
  class NoSuchLifecycleConfiguration(ErrorResponse):
557
654
  _status = '404 Not Found'
558
655
  _msg = 'The lifecycle configuration does not exist. .'
@@ -689,3 +786,9 @@ class UserKeyMustBeSpecified(ErrorResponse):
689
786
  _status = '400 Bad Request'
690
787
  _msg = 'The bucket POST must contain the specified field name. If it is ' \
691
788
  'specified, please check the order of the fields.'
789
+
790
+
791
+ class BrokenMPU(ErrorResponse):
792
+ # This is very much a Swift-ism, and we wish we didn't need it
793
+ _status = '409 Conflict'
794
+ _msg = 'Multipart upload has broken segment data.'
@@ -61,11 +61,10 @@ from keystoneclient.v3 import client as keystone_client
61
61
  from keystoneauth1 import session as keystone_session
62
62
  from keystoneauth1 import loading as keystone_loading
63
63
  import requests
64
- import six
65
- from six.moves import urllib
64
+ import urllib
66
65
 
67
66
  from swift.common.swob import Request, HTTPBadRequest, HTTPUnauthorized, \
68
- HTTPException
67
+ HTTPException, str_to_wsgi
69
68
  from swift.common.utils import config_true_value, split_path, get_logger, \
70
69
  cache_from_env, append_underscore
71
70
  from swift.common.wsgi import ConfigFileError
@@ -179,10 +178,12 @@ class S3Token(object):
179
178
  self._verify = None
180
179
 
181
180
  self._secret_cache_duration = int(conf.get('secret_cache_duration', 0))
182
- if self._secret_cache_duration > 0:
181
+ if self._secret_cache_duration < 0:
182
+ raise ValueError('secret_cache_duration must be non-negative')
183
+ if self._secret_cache_duration:
183
184
  try:
184
185
  auth_plugin = keystone_loading.get_plugin_loader(
185
- conf.get('auth_type'))
186
+ conf.get('auth_type', 'password'))
186
187
  available_auth_options = auth_plugin.get_options()
187
188
  auth_options = {}
188
189
  for option in available_auth_options:
@@ -193,7 +194,9 @@ class S3Token(object):
193
194
 
194
195
  auth = auth_plugin.load_from_options(**auth_options)
195
196
  session = keystone_session.Session(auth=auth)
196
- self.keystoneclient = keystone_client.Client(session=session)
197
+ self.keystoneclient = keystone_client.Client(
198
+ session=session,
199
+ region_name=conf.get('region_name'))
197
200
  self._logger.info("Caching s3tokens for %s seconds",
198
201
  self._secret_cache_duration)
199
202
  except Exception:
@@ -213,9 +216,7 @@ class S3Token(object):
213
216
  error_msg = ('<?xml version="1.0" encoding="UTF-8"?>\r\n'
214
217
  '<Error>\r\n <Code>%s</Code>\r\n '
215
218
  '<Message>%s</Message>\r\n</Error>\r\n' %
216
- (code, message))
217
- if six.PY3:
218
- error_msg = error_msg.encode()
219
+ (code, message)).encode()
219
220
  resp.body = error_msg
220
221
  return resp
221
222
 
@@ -262,18 +263,18 @@ class S3Token(object):
262
263
  return self._app(environ, start_response)
263
264
 
264
265
  access = s3_auth_details['access_key']
265
- if isinstance(access, six.binary_type):
266
+ if isinstance(access, bytes):
266
267
  access = access.decode('utf-8')
267
268
 
268
269
  signature = s3_auth_details['signature']
269
- if isinstance(signature, six.binary_type):
270
+ if isinstance(signature, bytes):
270
271
  signature = signature.decode('utf-8')
271
272
 
272
273
  string_to_sign = s3_auth_details['string_to_sign']
273
- if isinstance(string_to_sign, six.text_type):
274
+ if isinstance(string_to_sign, str):
274
275
  string_to_sign = string_to_sign.encode('utf-8')
275
276
  token = base64.urlsafe_b64encode(string_to_sign)
276
- if isinstance(token, six.binary_type):
277
+ if isinstance(token, bytes):
277
278
  token = token.decode('ascii')
278
279
 
279
280
  # NOTE(chmou): This is to handle the special case with nova
@@ -395,12 +396,10 @@ class S3Token(object):
395
396
 
396
397
  req.headers.update(headers)
397
398
  tenant_to_connect = force_tenant or tenant['id']
398
- if six.PY2 and isinstance(tenant_to_connect, six.text_type):
399
- tenant_to_connect = tenant_to_connect.encode('utf-8')
400
399
  self._logger.debug('Connecting with tenant: %s', tenant_to_connect)
401
400
  new_tenant_name = '%s%s' % (self._reseller_prefix, tenant_to_connect)
402
- environ['PATH_INFO'] = environ['PATH_INFO'].replace(account,
403
- new_tenant_name)
401
+ environ['PATH_INFO'] = environ['PATH_INFO'].replace(
402
+ str_to_wsgi(account), str_to_wsgi(new_tenant_name), 1)
404
403
  return self._app(environ, start_response)
405
404
 
406
405
 
@@ -5,7 +5,7 @@
5
5
  <interleave>
6
6
  <optional>
7
7
  <element name="Quiet">
8
- <data type="boolean"/>
8
+ <data type="string"/>
9
9
  </element>
10
10
  </optional>
11
11
  <oneOrMore>
@@ -43,8 +43,6 @@ http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html
43
43
  """
44
44
  from functools import partial
45
45
 
46
- import six
47
-
48
46
  from swift.common.utils import json
49
47
 
50
48
  from swift.common.middleware.s3api.s3response import InvalidArgument, \
@@ -182,18 +180,19 @@ class Grantee(object):
182
180
  """
183
181
  Convert a grantee string in the HTTP header to an Grantee instance.
184
182
  """
185
- type, value = grantee.split('=', 1)
183
+ grantee_type, value = grantee.split('=', 1)
184
+ grantee_type = grantee_type.lower()
186
185
  value = value.strip('"\'')
187
- if type == 'id':
186
+ if grantee_type == 'id':
188
187
  return User(value)
189
- elif type == 'emailAddress':
188
+ elif grantee_type == 'emailaddress':
190
189
  raise S3NotImplemented()
191
- elif type == 'uri':
190
+ elif grantee_type == 'uri':
192
191
  # return a subclass instance of Group class
193
192
  subclass = get_group_subclass_from_uri(value)
194
193
  return subclass()
195
194
  else:
196
- raise InvalidArgument(type, value,
195
+ raise InvalidArgument(grantee_type, value,
197
196
  'Argument format not recognized')
198
197
 
199
198
 
@@ -232,7 +231,7 @@ class Owner(object):
232
231
  """
233
232
  def __init__(self, id, name):
234
233
  self.id = id
235
- if not (name is None or isinstance(name, six.string_types)):
234
+ if not (name is None or isinstance(name, str)):
236
235
  raise TypeError('name must be a string or None')
237
236
  self.name = name
238
237
 
@@ -428,8 +427,6 @@ class ACL(object):
428
427
  return tostring(self.elem())
429
428
 
430
429
  def __repr__(self):
431
- if six.PY2:
432
- return self.__bytes__()
433
430
  return self.__bytes__().decode('utf8')
434
431
 
435
432
  @classmethod
@@ -15,9 +15,9 @@
15
15
 
16
16
  import base64
17
17
  import calendar
18
+ import datetime
18
19
  import email.utils
19
20
  import re
20
- import six
21
21
  import time
22
22
  import uuid
23
23
 
@@ -53,8 +53,6 @@ def snake_to_camel(snake):
53
53
 
54
54
  def unique_id():
55
55
  result = base64.urlsafe_b64encode(str(uuid.uuid4()).encode('ascii'))
56
- if six.PY2:
57
- return result
58
56
  return result.decode('ascii')
59
57
 
60
58
 
@@ -95,8 +93,8 @@ def validate_bucket_name(name, dns_compliant_bucket_names):
95
93
  elif name.endswith('.'):
96
94
  # Bucket names must not end with dot
97
95
  return False
98
- elif re.match("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)"
99
- "{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
96
+ elif re.match(r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)"
97
+ r"{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
100
98
  name):
101
99
  # Bucket names cannot be formatted as an IP Address
102
100
  return False
@@ -108,9 +106,19 @@ def validate_bucket_name(name, dns_compliant_bucket_names):
108
106
 
109
107
 
110
108
  class S3Timestamp(utils.Timestamp):
109
+ S3_XML_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z"
110
+
111
111
  @property
112
112
  def s3xmlformat(self):
113
- return self.isoformat[:-7] + '.000Z'
113
+ dt = datetime.datetime.fromtimestamp(self.ceil(), utils.UTC)
114
+ return dt.strftime(self.S3_XML_FORMAT)
115
+
116
+ @classmethod
117
+ def from_s3xmlformat(cls, date_string):
118
+ dt = datetime.datetime.strptime(date_string, cls.S3_XML_FORMAT)
119
+ dt = dt.replace(tzinfo=utils.UTC)
120
+ seconds = calendar.timegm(dt.timetuple())
121
+ return cls(seconds)
114
122
 
115
123
  @property
116
124
  def amz_date_format(self):
@@ -120,10 +128,6 @@ class S3Timestamp(utils.Timestamp):
120
128
  return self.isoformat.replace(
121
129
  '-', '').replace(':', '')[:-7] + 'Z'
122
130
 
123
- @classmethod
124
- def now(cls):
125
- return cls(time.time())
126
-
127
131
 
128
132
  def mktime(timestamp_str, time_format='%Y-%m-%dT%H:%M:%S'):
129
133
  """
@@ -156,7 +160,20 @@ def mktime(timestamp_str, time_format='%Y-%m-%dT%H:%M:%S'):
156
160
 
157
161
 
158
162
  class Config(dict):
163
+ DEFAULTS = {
164
+ 'storage_domains': [],
165
+ 'location': 'us-east-1',
166
+ 'force_swift_request_proxy_log': False,
167
+ 'dns_compliant_bucket_names': True,
168
+ 'allow_multipart_uploads': True,
169
+ 'allow_no_owner': False,
170
+ 'allowable_clock_skew': 900,
171
+ 'ratelimit_as_client_error': False,
172
+ 'max_upload_part_num': 1000,
173
+ }
174
+
159
175
  def __init__(self, base=None):
176
+ self.update(self.DEFAULTS)
160
177
  if base is not None:
161
178
  self.update(base)
162
179