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.
- swift/__init__.py +29 -50
- swift/account/auditor.py +21 -118
- swift/account/backend.py +33 -28
- swift/account/reaper.py +37 -28
- swift/account/replicator.py +22 -0
- swift/account/server.py +60 -26
- swift/account/utils.py +28 -11
- swift-2.23.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +183 -29
- swift/cli/manage_shard_ranges.py +708 -37
- swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.2.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +807 -126
- swift/cli/reload.py +135 -0
- swift/cli/ringbuilder.py +217 -20
- swift/cli/ringcomposer.py +0 -1
- swift/cli/shard-info.py +4 -3
- swift/common/base_storage_server.py +9 -20
- swift/common/bufferedhttp.py +48 -74
- swift/common/constraints.py +20 -15
- swift/common/container_sync_realms.py +9 -11
- swift/common/daemon.py +25 -8
- swift/common/db.py +198 -127
- swift/common/db_auditor.py +168 -0
- swift/common/db_replicator.py +95 -55
- swift/common/digest.py +141 -0
- swift/common/direct_client.py +144 -33
- swift/common/error_limiter.py +93 -0
- swift/common/exceptions.py +25 -1
- swift/common/header_key_dict.py +2 -9
- swift/common/http_protocol.py +373 -0
- swift/common/internal_client.py +129 -59
- swift/common/linkat.py +3 -4
- swift/common/manager.py +284 -67
- swift/common/memcached.py +396 -147
- swift/common/middleware/__init__.py +4 -0
- swift/common/middleware/account_quotas.py +211 -46
- swift/common/middleware/acl.py +3 -8
- swift/common/middleware/backend_ratelimit.py +230 -0
- swift/common/middleware/bulk.py +22 -34
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +6 -11
- swift/common/middleware/container_quotas.py +1 -1
- swift/common/middleware/container_sync.py +39 -17
- swift/common/middleware/copy.py +12 -0
- swift/common/middleware/crossdomain.py +22 -9
- swift/common/middleware/crypto/__init__.py +2 -1
- swift/common/middleware/crypto/crypto_utils.py +11 -15
- swift/common/middleware/crypto/decrypter.py +28 -11
- swift/common/middleware/crypto/encrypter.py +12 -17
- swift/common/middleware/crypto/keymaster.py +8 -15
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/dlo.py +15 -11
- swift/common/middleware/domain_remap.py +5 -4
- swift/common/middleware/etag_quoter.py +128 -0
- swift/common/middleware/formpost.py +73 -70
- swift/common/middleware/gatekeeper.py +8 -1
- swift/common/middleware/keystoneauth.py +33 -3
- swift/common/middleware/list_endpoints.py +4 -4
- swift/common/middleware/listing_formats.py +85 -49
- swift/common/middleware/memcache.py +4 -81
- swift/common/middleware/name_check.py +3 -2
- swift/common/middleware/proxy_logging.py +160 -92
- swift/common/middleware/ratelimit.py +17 -10
- swift/common/middleware/read_only.py +6 -4
- swift/common/middleware/recon.py +59 -22
- swift/common/middleware/s3api/acl_handlers.py +25 -3
- swift/common/middleware/s3api/acl_utils.py +6 -1
- swift/common/middleware/s3api/controllers/__init__.py +6 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/bucket.py +242 -137
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
- swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
- swift/common/middleware/s3api/controllers/obj.py +112 -8
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
- swift/common/middleware/s3api/controllers/tagging.py +57 -0
- swift/common/middleware/s3api/controllers/versioning.py +36 -7
- swift/common/middleware/s3api/etree.py +22 -9
- swift/common/middleware/s3api/exception.py +0 -4
- swift/common/middleware/s3api/s3api.py +113 -41
- swift/common/middleware/s3api/s3request.py +384 -218
- swift/common/middleware/s3api/s3response.py +126 -23
- swift/common/middleware/s3api/s3token.py +16 -17
- swift/common/middleware/s3api/schema/delete.rng +1 -1
- swift/common/middleware/s3api/subresource.py +7 -10
- swift/common/middleware/s3api/utils.py +27 -10
- swift/common/middleware/slo.py +665 -358
- swift/common/middleware/staticweb.py +64 -37
- swift/common/middleware/symlink.py +52 -19
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +192 -174
- swift/common/middleware/versioned_writes/__init__.py +51 -0
- swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
- swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +18 -19
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +10 -10
- swift-2.23.2.data/scripts/swift-container-server → swift/common/recon.py +13 -8
- swift/common/registry.py +147 -0
- swift/common/request_helpers.py +324 -57
- swift/common/ring/builder.py +67 -25
- swift/common/ring/composite_builder.py +1 -1
- swift/common/ring/ring.py +177 -51
- swift/common/ring/utils.py +1 -1
- swift/common/splice.py +10 -6
- swift/common/statsd_client.py +205 -0
- swift/common/storage_policy.py +49 -44
- swift/common/swob.py +86 -102
- swift/common/{utils.py → utils/__init__.py} +2191 -2762
- swift/common/utils/base.py +131 -0
- swift/common/utils/config.py +433 -0
- swift/common/utils/ipaddrs.py +256 -0
- swift/common/utils/libc.py +345 -0
- swift/common/utils/logs.py +859 -0
- swift/common/utils/timestamp.py +412 -0
- swift/common/wsgi.py +555 -536
- swift/container/auditor.py +14 -100
- swift/container/backend.py +552 -227
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +397 -176
- swift/container/sharder.py +1580 -639
- swift/container/sync.py +94 -88
- swift/container/updater.py +53 -32
- swift/obj/auditor.py +153 -35
- swift/obj/diskfile.py +466 -217
- swift/obj/expirer.py +406 -124
- swift/obj/mem_diskfile.py +7 -4
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +523 -262
- swift/obj/replicator.py +249 -188
- swift/obj/server.py +213 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +653 -139
- swift/obj/watchers/__init__.py +0 -0
- swift/obj/watchers/dark_data.py +213 -0
- swift/proxy/controllers/account.py +11 -11
- swift/proxy/controllers/base.py +848 -604
- swift/proxy/controllers/container.py +452 -86
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1009 -490
- swift/proxy/server.py +185 -112
- swift-2.35.0.dist-info/AUTHORS +501 -0
- swift-2.35.0.dist-info/LICENSE +202 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
- swift-2.35.0.dist-info/pbr.json +1 -0
- swift/locale/de/LC_MESSAGES/swift.po +0 -1216
- swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
- swift/locale/es/LC_MESSAGES/swift.po +0 -1085
- swift/locale/fr/LC_MESSAGES/swift.po +0 -909
- swift/locale/it/LC_MESSAGES/swift.po +0 -894
- swift/locale/ja/LC_MESSAGES/swift.po +0 -965
- swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
- swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
- swift/locale/ru/LC_MESSAGES/swift.po +0 -891
- swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
- swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
- swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
- swift-2.23.2.data/scripts/swift-account-auditor +0 -23
- swift-2.23.2.data/scripts/swift-account-info +0 -51
- swift-2.23.2.data/scripts/swift-account-reaper +0 -23
- swift-2.23.2.data/scripts/swift-account-replicator +0 -34
- swift-2.23.2.data/scripts/swift-account-server +0 -23
- swift-2.23.2.data/scripts/swift-container-auditor +0 -23
- swift-2.23.2.data/scripts/swift-container-info +0 -51
- swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.2.data/scripts/swift-container-replicator +0 -34
- swift-2.23.2.data/scripts/swift-container-sharder +0 -33
- swift-2.23.2.data/scripts/swift-container-sync +0 -23
- swift-2.23.2.data/scripts/swift-container-updater +0 -23
- swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.2.data/scripts/swift-form-signature +0 -20
- swift-2.23.2.data/scripts/swift-init +0 -119
- swift-2.23.2.data/scripts/swift-object-auditor +0 -29
- swift-2.23.2.data/scripts/swift-object-expirer +0 -33
- swift-2.23.2.data/scripts/swift-object-info +0 -60
- swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.2.data/scripts/swift-object-relinker +0 -41
- swift-2.23.2.data/scripts/swift-object-replicator +0 -37
- swift-2.23.2.data/scripts/swift-object-server +0 -27
- swift-2.23.2.data/scripts/swift-object-updater +0 -23
- swift-2.23.2.data/scripts/swift-proxy-server +0 -23
- swift-2.23.2.data/scripts/swift-recon +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.2.data/scripts/swift-ring-composer +0 -22
- swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
- swift-2.23.2.dist-info/RECORD +0 -220
- swift-2.23.2.dist-info/metadata.json +0 -1
- swift-2.23.2.dist-info/pbr.json +0 -1
- {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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
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
|
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
|
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
|
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(
|
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,
|
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,
|
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,
|
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,
|
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(
|
403
|
-
|
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
|
|
@@ -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
|
-
|
183
|
+
grantee_type, value = grantee.split('=', 1)
|
184
|
+
grantee_type = grantee_type.lower()
|
186
185
|
value = value.strip('"\'')
|
187
|
-
if
|
186
|
+
if grantee_type == 'id':
|
188
187
|
return User(value)
|
189
|
-
elif
|
188
|
+
elif grantee_type == 'emailaddress':
|
190
189
|
raise S3NotImplemented()
|
191
|
-
elif
|
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(
|
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,
|
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
|
-
|
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
|
|