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.
- 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.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +154 -14
- swift/cli/manage_shard_ranges.py +705 -37
- swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.3.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 +195 -128
- 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 +390 -145
- 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 -95
- 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 +51 -18
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +191 -173
- 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.3.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} +2163 -2772
- 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 +553 -535
- swift/container/auditor.py +14 -100
- swift/container/backend.py +490 -231
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +358 -165
- swift/container/sharder.py +1540 -684
- 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 +207 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +652 -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 +433 -92
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1000 -489
- swift/proxy/server.py +185 -112
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.3.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.3.data/scripts/swift-account-auditor +0 -23
- swift-2.23.3.data/scripts/swift-account-info +0 -51
- swift-2.23.3.data/scripts/swift-account-reaper +0 -23
- swift-2.23.3.data/scripts/swift-account-replicator +0 -34
- swift-2.23.3.data/scripts/swift-account-server +0 -23
- swift-2.23.3.data/scripts/swift-container-auditor +0 -23
- swift-2.23.3.data/scripts/swift-container-info +0 -55
- swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.3.data/scripts/swift-container-replicator +0 -34
- swift-2.23.3.data/scripts/swift-container-sharder +0 -37
- swift-2.23.3.data/scripts/swift-container-sync +0 -23
- swift-2.23.3.data/scripts/swift-container-updater +0 -23
- swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.3.data/scripts/swift-form-signature +0 -20
- swift-2.23.3.data/scripts/swift-init +0 -119
- swift-2.23.3.data/scripts/swift-object-auditor +0 -29
- swift-2.23.3.data/scripts/swift-object-expirer +0 -33
- swift-2.23.3.data/scripts/swift-object-info +0 -60
- swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.3.data/scripts/swift-object-relinker +0 -41
- swift-2.23.3.data/scripts/swift-object-replicator +0 -37
- swift-2.23.3.data/scripts/swift-object-server +0 -27
- swift-2.23.3.data/scripts/swift-object-updater +0 -23
- swift-2.23.3.data/scripts/swift-proxy-server +0 -23
- swift-2.23.3.data/scripts/swift-recon +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.3.data/scripts/swift-ring-composer +0 -22
- swift-2.23.3.dist-info/RECORD +0 -220
- swift-2.23.3.dist-info/pbr.json +0 -1
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
swift/container/server.py
CHANGED
@@ -15,15 +15,13 @@
|
|
15
15
|
|
16
16
|
import json
|
17
17
|
import os
|
18
|
+
import sys
|
18
19
|
import time
|
19
20
|
import traceback
|
20
|
-
import math
|
21
|
-
from swift import gettext_ as _
|
22
21
|
|
23
22
|
from eventlet import Timeout
|
24
23
|
|
25
|
-
import
|
26
|
-
from six.moves.urllib.parse import quote
|
24
|
+
from urllib.parse import quote
|
27
25
|
|
28
26
|
import swift.common.db
|
29
27
|
from swift.container.sync_store import ContainerSyncStore
|
@@ -32,16 +30,17 @@ from swift.container.backend import ContainerBroker, DATADIR, \
|
|
32
30
|
from swift.container.replicator import ContainerReplicatorRpc
|
33
31
|
from swift.common.db import DatabaseAlreadyExists
|
34
32
|
from swift.common.container_sync_realms import ContainerSyncRealms
|
35
|
-
from swift.common.request_helpers import
|
36
|
-
|
33
|
+
from swift.common.request_helpers import split_and_validate_path, \
|
34
|
+
is_sys_or_user_meta, validate_internal_container, validate_internal_obj, \
|
35
|
+
validate_container_params
|
37
36
|
from swift.common.utils import get_logger, hash_path, public, \
|
38
37
|
Timestamp, storage_directory, validate_sync_to, \
|
39
38
|
config_true_value, timing_stats, replication, \
|
40
39
|
override_bytes_from_content_type, get_log_line, \
|
41
40
|
config_fallocate_value, fs_has_free_space, list_from_csv, \
|
42
|
-
ShardRange
|
43
|
-
from swift.common.constraints import valid_timestamp, check_utf8,
|
44
|
-
|
41
|
+
ShardRange, parse_options
|
42
|
+
from swift.common.constraints import valid_timestamp, check_utf8, \
|
43
|
+
check_drive, AUTO_CREATE_ACCOUNT_PREFIX
|
45
44
|
from swift.common.bufferedhttp import http_connect
|
46
45
|
from swift.common.exceptions import ConnectionTimeout
|
47
46
|
from swift.common.http import HTTP_NO_CONTENT, HTTP_NOT_FOUND, is_success
|
@@ -54,6 +53,7 @@ from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPConflict, \
|
|
54
53
|
HTTPPreconditionFailed, HTTPMethodNotAllowed, Request, Response, \
|
55
54
|
HTTPInsufficientStorage, HTTPException, HTTPMovedPermanently, \
|
56
55
|
wsgi_to_str, str_to_wsgi
|
56
|
+
from swift.common.wsgi import run_wsgi
|
57
57
|
|
58
58
|
|
59
59
|
def gen_resp_headers(info, is_deleted=False):
|
@@ -84,6 +84,33 @@ def gen_resp_headers(info, is_deleted=False):
|
|
84
84
|
return headers
|
85
85
|
|
86
86
|
|
87
|
+
def get_container_name_and_placement(req):
|
88
|
+
"""
|
89
|
+
Split and validate path for a container.
|
90
|
+
|
91
|
+
:param req: a swob request
|
92
|
+
|
93
|
+
:returns: a tuple of path parts as strings
|
94
|
+
"""
|
95
|
+
drive, part, account, container = split_and_validate_path(req, 4)
|
96
|
+
validate_internal_container(account, container)
|
97
|
+
return drive, part, account, container
|
98
|
+
|
99
|
+
|
100
|
+
def get_obj_name_and_placement(req):
|
101
|
+
"""
|
102
|
+
Split and validate path for an object.
|
103
|
+
|
104
|
+
:param req: a swob request
|
105
|
+
|
106
|
+
:returns: a tuple of path parts as strings
|
107
|
+
"""
|
108
|
+
drive, part, account, container, obj = split_and_validate_path(
|
109
|
+
req, 4, 5, True)
|
110
|
+
validate_internal_obj(account, container, obj)
|
111
|
+
return drive, part, account, container, obj
|
112
|
+
|
113
|
+
|
87
114
|
class ContainerController(BaseStorageServer):
|
88
115
|
"""WSGI Controller for the container server."""
|
89
116
|
|
@@ -115,8 +142,7 @@ class ContainerController(BaseStorageServer):
|
|
115
142
|
self.replicator_rpc = ContainerReplicatorRpc(
|
116
143
|
self.root, DATADIR, ContainerBroker, self.mount_check,
|
117
144
|
logger=self.logger)
|
118
|
-
self.auto_create_account_prefix =
|
119
|
-
conf.get('auto_create_account_prefix') or '.'
|
145
|
+
self.auto_create_account_prefix = AUTO_CREATE_ACCOUNT_PREFIX
|
120
146
|
self.shards_account_prefix = (
|
121
147
|
self.auto_create_account_prefix + 'shards_')
|
122
148
|
if config_true_value(conf.get('allow_versions', 'f')):
|
@@ -128,6 +154,8 @@ class ContainerController(BaseStorageServer):
|
|
128
154
|
'be ignored in a future release.')
|
129
155
|
swift.common.db.DB_PREALLOCATION = \
|
130
156
|
config_true_value(conf.get('db_preallocation', 'f'))
|
157
|
+
swift.common.db.QUERY_LOGGING = \
|
158
|
+
config_true_value(conf.get('db_query_logging', 'f'))
|
131
159
|
self.sync_store = ContainerSyncStore(self.root,
|
132
160
|
self.logger,
|
133
161
|
self.mount_check)
|
@@ -199,19 +227,16 @@ class ContainerController(BaseStorageServer):
|
|
199
227
|
if len(account_hosts) != len(account_devices):
|
200
228
|
# This shouldn't happen unless there's a bug in the proxy,
|
201
229
|
# but if there is, we want to know about it.
|
202
|
-
self.logger.error(
|
230
|
+
self.logger.error(
|
203
231
|
'ERROR Account update failed: different '
|
204
232
|
'numbers of hosts and devices in request: '
|
205
|
-
'"%(hosts)s" vs "%(devices)s"'
|
233
|
+
'"%(hosts)s" vs "%(devices)s"', {
|
206
234
|
'hosts': req.headers.get('X-Account-Host', ''),
|
207
235
|
'devices': req.headers.get('X-Account-Device', '')})
|
208
236
|
return HTTPBadRequest(req=req)
|
209
237
|
|
210
238
|
if account_partition:
|
211
|
-
# zip is lazy
|
212
|
-
# On py2 it's an extra list copy, but the list is so small
|
213
|
-
# (one element per replica in account ring, usually 3) that it
|
214
|
-
# doesn't matter.
|
239
|
+
# zip is lazy, but we need a list, so force evaluation.
|
215
240
|
updates = list(zip(account_hosts, account_devices))
|
216
241
|
else:
|
217
242
|
updates = []
|
@@ -245,18 +270,18 @@ class ContainerController(BaseStorageServer):
|
|
245
270
|
if account_response.status == HTTP_NOT_FOUND:
|
246
271
|
account_404s += 1
|
247
272
|
elif not is_success(account_response.status):
|
248
|
-
self.logger.error(
|
273
|
+
self.logger.error(
|
249
274
|
'ERROR Account update failed '
|
250
275
|
'with %(ip)s:%(port)s/%(device)s (will retry '
|
251
|
-
'later): Response %(status)s %(reason)s'
|
276
|
+
'later): Response %(status)s %(reason)s',
|
252
277
|
{'ip': account_ip, 'port': account_port,
|
253
278
|
'device': account_device,
|
254
279
|
'status': account_response.status,
|
255
280
|
'reason': account_response.reason})
|
256
281
|
except (Exception, Timeout):
|
257
|
-
self.logger.exception(
|
282
|
+
self.logger.exception(
|
258
283
|
'ERROR account update failed with '
|
259
|
-
'%(ip)s:%(port)s/%(device)s (will retry later)'
|
284
|
+
'%(ip)s:%(port)s/%(device)s (will retry later)',
|
260
285
|
{'ip': account_ip, 'port': account_port,
|
261
286
|
'device': account_device})
|
262
287
|
if updates and account_404s == len(updates):
|
@@ -327,8 +352,7 @@ class ContainerController(BaseStorageServer):
|
|
327
352
|
@timing_stats()
|
328
353
|
def DELETE(self, req):
|
329
354
|
"""Handle HTTP DELETE request."""
|
330
|
-
drive, part, account, container, obj =
|
331
|
-
req, 4, 5, True)
|
355
|
+
drive, part, account, container, obj = get_obj_name_and_placement(req)
|
332
356
|
req_timestamp = valid_timestamp(req)
|
333
357
|
try:
|
334
358
|
check_drive(self.root, drive, self.mount_check)
|
@@ -461,8 +485,7 @@ class ContainerController(BaseStorageServer):
|
|
461
485
|
@timing_stats()
|
462
486
|
def PUT(self, req):
|
463
487
|
"""Handle HTTP PUT request."""
|
464
|
-
drive, part, account, container, obj =
|
465
|
-
req, 4, 5, True)
|
488
|
+
drive, part, account, container, obj = get_obj_name_and_placement(req)
|
466
489
|
req_timestamp = valid_timestamp(req)
|
467
490
|
if 'x-container-sync-to' in req.headers:
|
468
491
|
err, sync_to, realm, realm_key = validate_sync_to(
|
@@ -476,59 +499,42 @@ class ContainerController(BaseStorageServer):
|
|
476
499
|
return HTTPInsufficientStorage(drive=drive, request=req)
|
477
500
|
if not self.check_free_space(drive):
|
478
501
|
return HTTPInsufficientStorage(drive=drive, request=req)
|
479
|
-
requested_policy_index = self.get_and_validate_policy_index(req)
|
480
|
-
broker = self._get_container_broker(drive, part, account, container)
|
481
|
-
if obj: # put container object
|
482
|
-
# obj put expects the policy_index header, default is for
|
483
|
-
# legacy support during upgrade.
|
484
|
-
obj_policy_index = requested_policy_index or 0
|
485
|
-
self._maybe_autocreate(
|
486
|
-
broker, req_timestamp, account, obj_policy_index, req)
|
487
|
-
# redirect if a shard exists for this object name
|
488
|
-
response = self._redirect_to_shard(req, broker, obj)
|
489
|
-
if response:
|
490
|
-
return response
|
491
|
-
|
492
|
-
broker.put_object(obj, req_timestamp.internal,
|
493
|
-
int(req.headers['x-size']),
|
494
|
-
wsgi_to_str(req.headers['x-content-type']),
|
495
|
-
wsgi_to_str(req.headers['x-etag']), 0,
|
496
|
-
obj_policy_index,
|
497
|
-
wsgi_to_str(req.headers.get(
|
498
|
-
'x-content-type-timestamp')),
|
499
|
-
wsgi_to_str(req.headers.get('x-meta-timestamp')))
|
500
|
-
return HTTPCreated(request=req)
|
501
502
|
|
503
|
+
broker = self._get_container_broker(drive, part, account, container)
|
504
|
+
if obj:
|
505
|
+
return self.PUT_object(req, broker, account, obj, req_timestamp)
|
502
506
|
record_type = req.headers.get('x-backend-record-type', '').lower()
|
503
507
|
if record_type == RECORD_TYPE_SHARD:
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
508
|
+
return self.PUT_shard(req, broker, account, req_timestamp)
|
509
|
+
else:
|
510
|
+
return self.PUT_container(req, broker, account,
|
511
|
+
container, req_timestamp)
|
512
|
+
|
513
|
+
@timing_stats()
|
514
|
+
def PUT_object(self, req, broker, account, obj, req_timestamp):
|
515
|
+
"""Put object into container."""
|
516
|
+
# obj put expects the policy_index header, default is for
|
517
|
+
# legacy support during upgrade.
|
518
|
+
requested_policy_index = self.get_and_validate_policy_index(req)
|
519
|
+
obj_policy_index = requested_policy_index or 0
|
520
|
+
self._maybe_autocreate(
|
521
|
+
broker, req_timestamp, account, obj_policy_index, req)
|
522
|
+
# redirect if a shard exists for this object name
|
523
|
+
response = self._redirect_to_shard(req, broker, obj)
|
524
|
+
if response:
|
525
|
+
return response
|
526
|
+
|
527
|
+
broker.put_object(obj, req_timestamp.internal,
|
528
|
+
int(req.headers['x-size']),
|
529
|
+
wsgi_to_str(req.headers['x-content-type']),
|
530
|
+
wsgi_to_str(req.headers['x-etag']), 0,
|
531
|
+
obj_policy_index,
|
532
|
+
wsgi_to_str(req.headers.get(
|
533
|
+
'x-content-type-timestamp')),
|
534
|
+
wsgi_to_str(req.headers.get('x-meta-timestamp')))
|
535
|
+
return HTTPCreated(request=req)
|
536
|
+
|
537
|
+
def _create_ok_resp(self, req, broker, created):
|
532
538
|
if created:
|
533
539
|
return HTTPCreated(request=req,
|
534
540
|
headers={'x-backend-storage-policy-index':
|
@@ -538,12 +544,50 @@ class ContainerController(BaseStorageServer):
|
|
538
544
|
headers={'x-backend-storage-policy-index':
|
539
545
|
broker.storage_policy_index})
|
540
546
|
|
547
|
+
@timing_stats()
|
548
|
+
def PUT_shard(self, req, broker, account, req_timestamp):
|
549
|
+
"""Put shards into container."""
|
550
|
+
requested_policy_index = self.get_and_validate_policy_index(req)
|
551
|
+
try:
|
552
|
+
# validate incoming data...
|
553
|
+
shard_ranges = [ShardRange.from_dict(sr)
|
554
|
+
for sr in json.loads(req.body)]
|
555
|
+
except (ValueError, KeyError, TypeError) as err:
|
556
|
+
return HTTPBadRequest('Invalid body: %r' % err)
|
557
|
+
created = self._maybe_autocreate(
|
558
|
+
broker, req_timestamp, account, requested_policy_index, req)
|
559
|
+
self._update_metadata(req, broker, req_timestamp, 'PUT')
|
560
|
+
if shard_ranges:
|
561
|
+
# TODO: consider writing the shard ranges into the pending
|
562
|
+
# file, but if so ensure an all-or-none semantic for the write
|
563
|
+
broker.merge_shard_ranges(shard_ranges)
|
564
|
+
return self._create_ok_resp(req, broker, created)
|
565
|
+
|
566
|
+
@timing_stats()
|
567
|
+
def PUT_container(self, req, broker, account, container, req_timestamp):
|
568
|
+
"""Update or create container."""
|
569
|
+
requested_policy_index = self.get_and_validate_policy_index(req)
|
570
|
+
if requested_policy_index is None:
|
571
|
+
# use the default index sent by the proxy if available
|
572
|
+
new_container_policy = req.headers.get(
|
573
|
+
'X-Backend-Storage-Policy-Default', int(POLICIES.default))
|
574
|
+
else:
|
575
|
+
new_container_policy = requested_policy_index
|
576
|
+
created = self._update_or_create(req, broker,
|
577
|
+
req_timestamp.internal,
|
578
|
+
new_container_policy,
|
579
|
+
requested_policy_index)
|
580
|
+
self._update_metadata(req, broker, req_timestamp, 'PUT')
|
581
|
+
resp = self.account_update(req, account, container, broker)
|
582
|
+
if resp:
|
583
|
+
return resp
|
584
|
+
return self._create_ok_resp(req, broker, created)
|
585
|
+
|
541
586
|
@public
|
542
587
|
@timing_stats(sample_rate=0.1)
|
543
588
|
def HEAD(self, req):
|
544
589
|
"""Handle HTTP HEAD request."""
|
545
|
-
drive, part, account, container, obj =
|
546
|
-
req, 4, 5, True)
|
590
|
+
drive, part, account, container, obj = get_obj_name_and_placement(req)
|
547
591
|
out_content_type = listing_formats.get_listing_content_type(req)
|
548
592
|
try:
|
549
593
|
check_drive(self.root, drive, self.mount_check)
|
@@ -562,33 +606,46 @@ class ContainerController(BaseStorageServer):
|
|
562
606
|
if value != '' and (key.lower() in self.save_headers or
|
563
607
|
is_sys_or_user_meta('container', key)))
|
564
608
|
headers['Content-Type'] = out_content_type
|
609
|
+
headers['Content-Length'] = 0
|
565
610
|
resp = HTTPNoContent(request=req, headers=headers, charset='utf-8')
|
566
|
-
resp.last_modified =
|
611
|
+
resp.last_modified = Timestamp(headers['X-PUT-Timestamp']).ceil()
|
567
612
|
return resp
|
568
613
|
|
569
|
-
def
|
614
|
+
def update_shard_record(self, record, shard_record_full=True):
|
615
|
+
"""
|
616
|
+
Return the shard_range database record as a dict, the keys will depend
|
617
|
+
on the database fields provided in the record.
|
618
|
+
|
619
|
+
:param record: shard entry record, either ShardRange or Namespace.
|
620
|
+
:param shard_record_full: boolean, when true the timestamp field is
|
621
|
+
added as "last_modified" in iso format.
|
622
|
+
:returns: dict suitable for listing responses
|
623
|
+
"""
|
624
|
+
response = dict(record)
|
625
|
+
if shard_record_full:
|
626
|
+
created = record.timestamp
|
627
|
+
response['last_modified'] = Timestamp(created).isoformat
|
628
|
+
return response
|
629
|
+
|
630
|
+
def update_object_record(self, record):
|
570
631
|
"""
|
571
|
-
Perform
|
572
|
-
|
632
|
+
Perform mutation to container listing records that are common to all
|
633
|
+
serialization formats, and returns it as a dict.
|
573
634
|
|
574
635
|
Converts created time to iso timestamp.
|
575
636
|
Replaces size with 'swift_bytes' content type parameter.
|
576
637
|
|
577
|
-
:
|
638
|
+
:param record: object entry record
|
578
639
|
:returns: modified record
|
579
640
|
"""
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
response = {
|
589
|
-
'bytes': size, 'hash': etag, 'name': name_,
|
590
|
-
'content_type': content_type}
|
591
|
-
override_bytes_from_content_type(response, logger=self.logger)
|
641
|
+
# record is object info
|
642
|
+
(name, created, size, content_type, etag) = record[:5]
|
643
|
+
if content_type is None:
|
644
|
+
return {'subdir': name}
|
645
|
+
response = {
|
646
|
+
'bytes': size, 'hash': etag, 'name': name,
|
647
|
+
'content_type': content_type}
|
648
|
+
override_bytes_from_content_type(response, logger=self.logger)
|
592
649
|
response['last_modified'] = Timestamp(created).isoformat
|
593
650
|
return response
|
594
651
|
|
@@ -610,6 +667,19 @@ class ContainerController(BaseStorageServer):
|
|
610
667
|
``sharded``, then the listing will be a list of shard ranges;
|
611
668
|
otherwise the response body will be a list of objects.
|
612
669
|
|
670
|
+
* Both shard range and object listings may be filtered according to
|
671
|
+
the constraints described below. However, the
|
672
|
+
``X-Backend-Ignore-Shard-Name-Filter`` header may be used to override
|
673
|
+
the application of the ``marker``, ``end_marker``, ``includes`` and
|
674
|
+
``reverse`` parameters to shard range listings. These parameters will
|
675
|
+
be ignored if the header has the value 'sharded' and the current db
|
676
|
+
sharding state is also 'sharded'. Note that this header does not
|
677
|
+
override the ``states`` constraint on shard range listings.
|
678
|
+
|
679
|
+
* The order of both shard range and object listings may be reversed by
|
680
|
+
using a ``reverse`` query string parameter with a
|
681
|
+
value in :attr:`swift.common.utils.TRUE_VALUES`.
|
682
|
+
|
613
683
|
* Both shard range and object listings may be constrained to a name
|
614
684
|
range by the ``marker`` and ``end_marker`` query string parameters.
|
615
685
|
Object listings will only contain objects whose names are greater
|
@@ -638,11 +708,11 @@ class ContainerController(BaseStorageServer):
|
|
638
708
|
either the string or integer representation of
|
639
709
|
:data:`~swift.common.utils.ShardRange.STATES`.
|
640
710
|
|
641
|
-
|
642
|
-
``listing`` will cause the listing to include all shard ranges
|
643
|
-
state suitable for contributing to an object listing
|
644
|
-
will cause the listing to include all shard ranges
|
645
|
-
suitable to accept an object update.
|
711
|
+
Alias values may be used in a ``states`` parameter value. The
|
712
|
+
``listing`` alias will cause the listing to include all shard ranges
|
713
|
+
in a state suitable for contributing to an object listing. The
|
714
|
+
``updating`` alias will cause the listing to include all shard ranges
|
715
|
+
in a state suitable to accept an object update.
|
646
716
|
|
647
717
|
If either of these aliases is used then the shard range listing will
|
648
718
|
if necessary be extended with a synthesised 'filler' range in order
|
@@ -651,6 +721,23 @@ class ContainerController(BaseStorageServer):
|
|
651
721
|
uncovered tail of the requested name range and will point back to the
|
652
722
|
same container.
|
653
723
|
|
724
|
+
The ``auditing`` alias will cause the listing to include all shard
|
725
|
+
ranges in a state useful to the sharder while auditing a shard
|
726
|
+
container. This alias will not cause a 'filler' range to be added,
|
727
|
+
but will cause the container's own shard range to be included in the
|
728
|
+
listing. For now, ``auditing`` is only supported when
|
729
|
+
'X-Backend-Record-Shard-Format' is 'full'.
|
730
|
+
|
731
|
+
* Shard range listings can be simplified to include only Namespace
|
732
|
+
only attributes (name, lower and upper) if the caller send the header
|
733
|
+
``X-Backend-Record-Shard-Format`` with value 'namespace' as a hint
|
734
|
+
that it would prefer namespaces. If this header doesn't exist or the
|
735
|
+
value is 'full', the listings will default to include all attributes
|
736
|
+
of shard ranges. But if params has includes/marker/end_marker then
|
737
|
+
the response will be full shard ranges, regardless the header of
|
738
|
+
``X-Backend-Record-Shard-Format``. The response header
|
739
|
+
``X-Backend-Record-Type`` will tell the user what type it gets back.
|
740
|
+
|
654
741
|
* Listings are not normally returned from a deleted container. However,
|
655
742
|
the ``X-Backend-Override-Deleted`` header may be used with a value in
|
656
743
|
:attr:`swift.common.utils.TRUE_VALUES` to force a shard range
|
@@ -660,23 +747,8 @@ class ContainerController(BaseStorageServer):
|
|
660
747
|
:param req: an instance of :class:`swift.common.swob.Request`
|
661
748
|
:returns: an instance of :class:`swift.common.swob.Response`
|
662
749
|
"""
|
663
|
-
drive, part, account, container, obj =
|
664
|
-
|
665
|
-
path = get_param(req, 'path')
|
666
|
-
prefix = get_param(req, 'prefix')
|
667
|
-
delimiter = get_param(req, 'delimiter')
|
668
|
-
marker = get_param(req, 'marker', '')
|
669
|
-
end_marker = get_param(req, 'end_marker')
|
670
|
-
limit = constraints.CONTAINER_LISTING_LIMIT
|
671
|
-
given_limit = get_param(req, 'limit')
|
672
|
-
reverse = config_true_value(get_param(req, 'reverse'))
|
673
|
-
if given_limit and given_limit.isdigit():
|
674
|
-
limit = int(given_limit)
|
675
|
-
if limit > constraints.CONTAINER_LISTING_LIMIT:
|
676
|
-
return HTTPPreconditionFailed(
|
677
|
-
request=req,
|
678
|
-
body='Maximum limit is %d'
|
679
|
-
% constraints.CONTAINER_LISTING_LIMIT)
|
750
|
+
drive, part, account, container, obj = get_obj_name_and_placement(req)
|
751
|
+
params = validate_container_params(req)
|
680
752
|
out_content_type = listing_formats.get_listing_content_type(req)
|
681
753
|
try:
|
682
754
|
check_drive(self.root, drive, self.mount_check)
|
@@ -687,55 +759,158 @@ class ContainerController(BaseStorageServer):
|
|
687
759
|
stale_reads_ok=True)
|
688
760
|
info, is_deleted = broker.get_info_is_deleted()
|
689
761
|
record_type = req.headers.get('x-backend-record-type', '').lower()
|
690
|
-
|
691
|
-
|
762
|
+
db_state = info.get('db_state')
|
763
|
+
if record_type == 'auto' and db_state in (SHARDING, SHARDED):
|
692
764
|
record_type = 'shard'
|
693
765
|
if record_type == 'shard':
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
766
|
+
return self.GET_shard(req, broker, container, params, info,
|
767
|
+
is_deleted, out_content_type)
|
768
|
+
else:
|
769
|
+
return self.GET_object(req, broker, container, params, info,
|
770
|
+
is_deleted, out_content_type)
|
771
|
+
|
772
|
+
@timing_stats()
|
773
|
+
def GET_shard(self, req, broker, container, params, info,
|
774
|
+
is_deleted, out_content_type):
|
775
|
+
"""
|
776
|
+
Returns a list of persisted shard ranges or namespaces in response.
|
777
|
+
|
778
|
+
:param req: swob.Request object
|
779
|
+
:param broker: container DB broker object
|
780
|
+
:param container: container name
|
781
|
+
:param params: the request params.
|
782
|
+
:param info: the global info for the container
|
783
|
+
:param is_deleted: the is_deleted status for the container.
|
784
|
+
:param out_content_type: content type as a string.
|
785
|
+
:returns: an instance of :class:`swift.common.swob.Response`
|
786
|
+
"""
|
787
|
+
override_deleted = info and config_true_value(
|
788
|
+
req.headers.get('x-backend-override-deleted', False))
|
789
|
+
resp_headers = gen_resp_headers(
|
790
|
+
info, is_deleted=is_deleted and not override_deleted)
|
791
|
+
|
792
|
+
if is_deleted and not override_deleted:
|
793
|
+
return HTTPNotFound(request=req, headers=resp_headers)
|
794
|
+
|
795
|
+
marker = params.get('marker', '')
|
796
|
+
end_marker = params.get('end_marker')
|
797
|
+
reverse = config_true_value(params.get('reverse'))
|
798
|
+
states = params.get('states')
|
799
|
+
includes = params.get('includes')
|
800
|
+
include_deleted = config_true_value(
|
801
|
+
req.headers.get('x-backend-include-deleted', False))
|
802
|
+
|
803
|
+
resp_headers['X-Backend-Record-Type'] = 'shard'
|
804
|
+
override_filter_hdr = req.headers.get(
|
805
|
+
'x-backend-override-shard-name-filter', '').lower()
|
806
|
+
if override_filter_hdr == info.get('db_state') == 'sharded':
|
807
|
+
# respect the request to send back *all* ranges if the db is in
|
808
|
+
# sharded state
|
809
|
+
resp_headers['X-Backend-Override-Shard-Name-Filter'] = 'true'
|
810
|
+
marker = end_marker = includes = None
|
811
|
+
reverse = False
|
812
|
+
fill_gaps = include_own = False
|
813
|
+
if states:
|
814
|
+
states = list_from_csv(states)
|
815
|
+
fill_gaps = any(('listing' in states, 'updating' in states))
|
816
|
+
# The 'auditing' state alias is used by the sharder during
|
817
|
+
# shard audit; if the shard is shrinking then it needs to get
|
818
|
+
# acceptor shard ranges, which may be the root container
|
819
|
+
# itself, so use include_own.
|
820
|
+
include_own = 'auditing' in states
|
821
|
+
try:
|
822
|
+
states = broker.resolve_shard_range_states(states)
|
823
|
+
except ValueError:
|
824
|
+
return HTTPBadRequest(request=req, body='Bad state')
|
825
|
+
|
826
|
+
# For record type of 'shard', user can specify an additional header
|
827
|
+
# to ask for list of Namespaces instead of full ShardRanges.
|
828
|
+
# This will allow proxy server who is going to retrieve Namespace
|
829
|
+
# to talk to older version of container servers who don't support
|
830
|
+
# Namespace yet during upgrade.
|
831
|
+
shard_format = req.headers.get(
|
832
|
+
'x-backend-record-shard-format', 'full').lower()
|
833
|
+
if shard_format == 'namespace':
|
834
|
+
resp_headers['X-Backend-Record-Shard-Format'] = 'namespace'
|
835
|
+
# Namespace GET does not support all the options of Shard Range
|
836
|
+
# GET: 'x-backend-include-deleted' cannot be supported because
|
837
|
+
# there is no way for a Namespace to indicate the deleted state;
|
838
|
+
# the 'auditing' state query parameter is not supported because it
|
839
|
+
# is specific to the sharder which only requests full shard ranges.
|
840
|
+
if include_deleted:
|
841
|
+
return HTTPBadRequest(
|
842
|
+
request=req, body='No include_deleted for namespace GET')
|
843
|
+
if include_own:
|
844
|
+
return HTTPBadRequest(
|
845
|
+
request=req, body='No auditing state for namespace GET')
|
846
|
+
shard_format_full = False
|
847
|
+
container_list = broker.get_namespaces(
|
848
|
+
marker, end_marker, includes, reverse, states, fill_gaps)
|
849
|
+
else:
|
850
|
+
resp_headers['X-Backend-Record-Shard-Format'] = 'full'
|
851
|
+
shard_format_full = True
|
713
852
|
container_list = broker.get_shard_ranges(
|
714
853
|
marker, end_marker, includes, reverse, states=states,
|
715
|
-
include_deleted=include_deleted, fill_gaps=fill_gaps
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
854
|
+
include_deleted=include_deleted, fill_gaps=fill_gaps,
|
855
|
+
include_own=include_own)
|
856
|
+
listing = [self.update_shard_record(record, shard_format_full)
|
857
|
+
for record in container_list]
|
858
|
+
return self._create_GET_response(req, out_content_type, info,
|
859
|
+
resp_headers, broker.metadata,
|
860
|
+
container, listing)
|
861
|
+
|
862
|
+
@timing_stats()
|
863
|
+
def GET_object(self, req, broker, container, params, info,
|
864
|
+
is_deleted, out_content_type):
|
865
|
+
"""
|
866
|
+
Returns a list of objects in response.
|
867
|
+
|
868
|
+
:param req: swob.Request object
|
869
|
+
:param broker: container DB broker object
|
870
|
+
:param container: container name
|
871
|
+
:param params: the request params.
|
872
|
+
:param info: the global info for the container
|
873
|
+
:param is_deleted: the is_deleted status for the container.
|
874
|
+
:param out_content_type: content type as a string.
|
875
|
+
:returns: an instance of :class:`swift.common.swob.Response`
|
876
|
+
"""
|
877
|
+
marker = params.get('marker', '')
|
878
|
+
end_marker = params.get('end_marker')
|
879
|
+
reverse = config_true_value(params.get('reverse'))
|
880
|
+
path = params.get('path')
|
881
|
+
prefix = params.get('prefix')
|
882
|
+
delimiter = params.get('delimiter')
|
883
|
+
limit = params['limit']
|
884
|
+
requested_policy_index = self.get_and_validate_policy_index(req)
|
885
|
+
resp_headers = gen_resp_headers(info, is_deleted=is_deleted)
|
886
|
+
if is_deleted:
|
887
|
+
return HTTPNotFound(request=req, headers=resp_headers)
|
888
|
+
resp_headers['X-Backend-Record-Type'] = 'object'
|
889
|
+
storage_policy_index = (
|
890
|
+
requested_policy_index if requested_policy_index is not None
|
891
|
+
else info['storage_policy_index'])
|
892
|
+
resp_headers['X-Backend-Record-Storage-Policy-Index'] = \
|
893
|
+
storage_policy_index
|
894
|
+
# Use the retired db while container is in process of sharding,
|
895
|
+
# otherwise use current db
|
896
|
+
src_broker = broker.get_brokers()[0]
|
897
|
+
container_list = src_broker.list_objects_iter(
|
898
|
+
limit, marker, end_marker, prefix, delimiter, path,
|
899
|
+
storage_policy_index=storage_policy_index,
|
900
|
+
reverse=reverse, allow_reserved=req.allow_reserved_names)
|
901
|
+
listing = [self.update_object_record(record)
|
902
|
+
for record in container_list]
|
903
|
+
return self._create_GET_response(req, out_content_type, info,
|
904
|
+
resp_headers, broker.metadata,
|
905
|
+
container, listing)
|
906
|
+
|
907
|
+
def _create_GET_response(self, req, out_content_type, info, resp_headers,
|
908
|
+
metadata, container, listing):
|
733
909
|
for key, (value, _timestamp) in metadata.items():
|
734
910
|
if value and (key.lower() in self.save_headers or
|
735
911
|
is_sys_or_user_meta('container', key)):
|
736
912
|
resp_headers[str_to_wsgi(key)] = str_to_wsgi(value)
|
737
|
-
|
738
|
-
for record in container_list]
|
913
|
+
|
739
914
|
if out_content_type.endswith('/xml'):
|
740
915
|
body = listing_formats.container_to_xml(listing, container)
|
741
916
|
elif out_content_type.endswith('/json'):
|
@@ -745,7 +920,7 @@ class ContainerController(BaseStorageServer):
|
|
745
920
|
|
746
921
|
ret = Response(request=req, headers=resp_headers, body=body,
|
747
922
|
content_type=out_content_type, charset='utf-8')
|
748
|
-
ret.last_modified =
|
923
|
+
ret.last_modified = Timestamp(resp_headers['X-PUT-Timestamp']).ceil()
|
749
924
|
if not ret.body:
|
750
925
|
ret.status_int = HTTP_NO_CONTENT
|
751
926
|
return ret
|
@@ -779,7 +954,7 @@ class ContainerController(BaseStorageServer):
|
|
779
954
|
"""
|
780
955
|
Handle HTTP UPDATE request (merge_items RPCs coming from the proxy.)
|
781
956
|
"""
|
782
|
-
drive, part, account, container =
|
957
|
+
drive, part, account, container = get_container_name_and_placement(req)
|
783
958
|
req_timestamp = valid_timestamp(req)
|
784
959
|
try:
|
785
960
|
check_drive(self.root, drive, self.mount_check)
|
@@ -802,8 +977,15 @@ class ContainerController(BaseStorageServer):
|
|
802
977
|
@public
|
803
978
|
@timing_stats()
|
804
979
|
def POST(self, req):
|
805
|
-
"""
|
806
|
-
|
980
|
+
"""
|
981
|
+
Handle HTTP POST request.
|
982
|
+
|
983
|
+
A POST request will update the container's ``put_timestamp``, unless
|
984
|
+
it has an ``X-Backend-No-Timestamp-Update`` header with a truthy value.
|
985
|
+
|
986
|
+
:param req: an instance of :class:`~swift.common.swob.Request`.
|
987
|
+
"""
|
988
|
+
drive, part, account, container = get_container_name_and_placement(req)
|
807
989
|
req_timestamp = valid_timestamp(req)
|
808
990
|
if 'x-container-sync-to' in req.headers:
|
809
991
|
err, sync_to, realm, realm_key = validate_sync_to(
|
@@ -820,7 +1002,9 @@ class ContainerController(BaseStorageServer):
|
|
820
1002
|
broker = self._get_container_broker(drive, part, account, container)
|
821
1003
|
if broker.is_deleted():
|
822
1004
|
return HTTPNotFound(request=req)
|
823
|
-
|
1005
|
+
if not config_true_value(
|
1006
|
+
req.headers.get('x-backend-no-timestamp-update', False)):
|
1007
|
+
broker.update_put_timestamp(req_timestamp.internal)
|
824
1008
|
self._update_metadata(req, broker, req_timestamp, 'POST')
|
825
1009
|
return HTTPNoContent(request=req)
|
826
1010
|
|
@@ -828,7 +1012,7 @@ class ContainerController(BaseStorageServer):
|
|
828
1012
|
start_time = time.time()
|
829
1013
|
req = Request(env)
|
830
1014
|
self.logger.txn_id = req.headers.get('x-trans-id', None)
|
831
|
-
if not check_utf8(wsgi_to_str(req.path_info)):
|
1015
|
+
if not check_utf8(wsgi_to_str(req.path_info), internal=True):
|
832
1016
|
res = HTTPPreconditionFailed(body='Invalid UTF8 or contains NULL')
|
833
1017
|
else:
|
834
1018
|
try:
|
@@ -840,8 +1024,8 @@ class ContainerController(BaseStorageServer):
|
|
840
1024
|
except HTTPException as error_response:
|
841
1025
|
res = error_response
|
842
1026
|
except (Exception, Timeout):
|
843
|
-
self.logger.exception(
|
844
|
-
'ERROR __call__ error with %(method)s %(path)s '
|
1027
|
+
self.logger.exception(
|
1028
|
+
'ERROR __call__ error with %(method)s %(path)s ',
|
845
1029
|
{'method': req.method, 'path': req.path})
|
846
1030
|
res = HTTPInternalServerError(body=traceback.format_exc())
|
847
1031
|
if self.log_requests:
|
@@ -862,3 +1046,12 @@ def app_factory(global_conf, **local_conf):
|
|
862
1046
|
conf = global_conf.copy()
|
863
1047
|
conf.update(local_conf)
|
864
1048
|
return ContainerController(conf)
|
1049
|
+
|
1050
|
+
|
1051
|
+
def main():
|
1052
|
+
conf_file, options = parse_options(test_config=True)
|
1053
|
+
sys.exit(run_wsgi(conf_file, 'container-server', **options))
|
1054
|
+
|
1055
|
+
|
1056
|
+
if __name__ == '__main__':
|
1057
|
+
main()
|