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
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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.
|
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=
|
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
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
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,
|
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
|
-
|
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
|
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
|