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
swift/common/direct_client.py
CHANGED
@@ -23,17 +23,18 @@ import os
|
|
23
23
|
import socket
|
24
24
|
|
25
25
|
from eventlet import sleep, Timeout
|
26
|
-
import
|
27
|
-
|
28
|
-
from six.moves.http_client import HTTPException
|
26
|
+
import pickle # nosec: B403
|
27
|
+
from http.client import HTTPException
|
29
28
|
|
30
|
-
from swift.common.bufferedhttp import http_connect
|
29
|
+
from swift.common.bufferedhttp import http_connect, http_connect_raw
|
31
30
|
from swift.common.exceptions import ClientException
|
32
|
-
from swift.common.
|
31
|
+
from swift.common.request_helpers import USE_REPLICATION_NETWORK_HEADER, \
|
32
|
+
get_ip_port
|
33
|
+
from swift.common.swob import normalize_etag
|
34
|
+
from swift.common.utils import Timestamp, FileLikeIter, quote
|
33
35
|
from swift.common.http import HTTP_NO_CONTENT, HTTP_INSUFFICIENT_STORAGE, \
|
34
36
|
is_success, is_server_error
|
35
37
|
from swift.common.header_key_dict import HeaderKeyDict
|
36
|
-
from swift.common.utils import quote
|
37
38
|
|
38
39
|
|
39
40
|
class DirectClientException(ClientException):
|
@@ -42,7 +43,7 @@ class DirectClientException(ClientException):
|
|
42
43
|
# host can be used to override the node ip and port reported in
|
43
44
|
# the exception
|
44
45
|
host = host if host is not None else node
|
45
|
-
if
|
46
|
+
if isinstance(path, bytes):
|
46
47
|
path = path.decode("utf-8")
|
47
48
|
full_path = quote('/%s/%s%s' % (node['device'], part, path))
|
48
49
|
msg = '%s server %s:%s direct %s %r gave status %s' % (
|
@@ -54,9 +55,23 @@ class DirectClientException(ClientException):
|
|
54
55
|
http_reason=resp.reason, http_headers=headers)
|
55
56
|
|
56
57
|
|
58
|
+
class DirectClientReconException(ClientException):
|
59
|
+
|
60
|
+
def __init__(self, method, node, path, resp):
|
61
|
+
if isinstance(path, bytes):
|
62
|
+
path = path.decode("utf-8")
|
63
|
+
msg = 'server %s:%s direct %s %r gave status %s' % (
|
64
|
+
node['ip'], node['port'], method, path, resp.status)
|
65
|
+
headers = HeaderKeyDict(resp.getheaders())
|
66
|
+
super(DirectClientReconException, self).__init__(
|
67
|
+
msg, http_host=node['ip'], http_port=node['port'],
|
68
|
+
http_status=resp.status, http_reason=resp.reason,
|
69
|
+
http_headers=headers)
|
70
|
+
|
71
|
+
|
57
72
|
def _make_path(*components):
|
58
73
|
return u'/' + u'/'.join(
|
59
|
-
x.decode('utf-8') if isinstance(x,
|
74
|
+
x.decode('utf-8') if isinstance(x, bytes) else x
|
60
75
|
for x in components)
|
61
76
|
|
62
77
|
|
@@ -95,13 +110,15 @@ def _make_req(node, part, method, path, headers, stype,
|
|
95
110
|
content_length = int(v)
|
96
111
|
if not contents:
|
97
112
|
headers['Content-Length'] = '0'
|
98
|
-
if isinstance(contents,
|
113
|
+
if isinstance(contents, str):
|
99
114
|
contents = [contents]
|
100
115
|
if content_length is None:
|
101
116
|
headers['Transfer-Encoding'] = 'chunked'
|
102
117
|
|
118
|
+
ip, port = get_ip_port(node, headers)
|
119
|
+
headers.setdefault('X-Backend-Allow-Reserved-Names', 'true')
|
103
120
|
with Timeout(conn_timeout):
|
104
|
-
conn = http_connect(
|
121
|
+
conn = http_connect(ip, port, node['device'], part,
|
105
122
|
method, path, headers=headers)
|
106
123
|
|
107
124
|
if contents is not None:
|
@@ -138,28 +155,50 @@ def _get_direct_account_container(path, stype, node, part,
|
|
138
155
|
marker=None, limit=None,
|
139
156
|
prefix=None, delimiter=None,
|
140
157
|
conn_timeout=5, response_timeout=15,
|
141
|
-
end_marker=None, reverse=None, headers=None
|
142
|
-
|
158
|
+
end_marker=None, reverse=None, headers=None,
|
159
|
+
extra_params=None):
|
160
|
+
"""Base function for get direct account and container.
|
143
161
|
|
144
|
-
Do not use directly use the
|
145
|
-
|
162
|
+
Do not use directly use the direct_get_account or
|
163
|
+
direct_get_container instead.
|
146
164
|
"""
|
147
|
-
|
165
|
+
if headers is None:
|
166
|
+
headers = {}
|
167
|
+
|
168
|
+
params = {'format': 'json'}
|
169
|
+
if extra_params:
|
170
|
+
for key, value in extra_params.items():
|
171
|
+
if value is not None:
|
172
|
+
params[key] = value
|
148
173
|
if marker:
|
149
|
-
|
174
|
+
if 'marker' in params:
|
175
|
+
raise TypeError('duplicate values for keyword arg: marker')
|
176
|
+
params['marker'] = quote(marker)
|
150
177
|
if limit:
|
151
|
-
|
178
|
+
if 'limit' in params:
|
179
|
+
raise TypeError('duplicate values for keyword arg: limit')
|
180
|
+
params['limit'] = '%d' % limit
|
152
181
|
if prefix:
|
153
|
-
|
182
|
+
if 'prefix' in params:
|
183
|
+
raise TypeError('duplicate values for keyword arg: prefix')
|
184
|
+
params['prefix'] = quote(prefix)
|
154
185
|
if delimiter:
|
155
|
-
|
186
|
+
if 'delimiter' in params:
|
187
|
+
raise TypeError('duplicate values for keyword arg: delimiter')
|
188
|
+
params['delimiter'] = quote(delimiter)
|
156
189
|
if end_marker:
|
157
|
-
|
190
|
+
if 'end_marker' in params:
|
191
|
+
raise TypeError('duplicate values for keyword arg: end_marker')
|
192
|
+
params['end_marker'] = quote(end_marker)
|
158
193
|
if reverse:
|
159
|
-
|
160
|
-
|
194
|
+
if 'reverse' in params:
|
195
|
+
raise TypeError('duplicate values for keyword arg: reverse')
|
196
|
+
params['reverse'] = quote(reverse)
|
197
|
+
qs = '&'.join('%s=%s' % (k, v) for k, v in params.items())
|
198
|
+
|
199
|
+
ip, port = get_ip_port(node, headers)
|
161
200
|
with Timeout(conn_timeout):
|
162
|
-
conn = http_connect(
|
201
|
+
conn = http_connect(ip, port, node['device'], part,
|
163
202
|
'GET', path, query_string=qs,
|
164
203
|
headers=gen_headers(hdrs_in=headers))
|
165
204
|
with Timeout(response_timeout):
|
@@ -193,12 +232,14 @@ def gen_headers(hdrs_in=None, add_ts=True):
|
|
193
232
|
hdrs_out['X-Timestamp'] = Timestamp.now().internal
|
194
233
|
if 'user-agent' not in hdrs_out:
|
195
234
|
hdrs_out['User-Agent'] = 'direct-client %s' % os.getpid()
|
235
|
+
hdrs_out.setdefault('X-Backend-Allow-Reserved-Names', 'true')
|
196
236
|
return hdrs_out
|
197
237
|
|
198
238
|
|
199
239
|
def direct_get_account(node, part, account, marker=None, limit=None,
|
200
240
|
prefix=None, delimiter=None, conn_timeout=5,
|
201
|
-
response_timeout=15, end_marker=None, reverse=None
|
241
|
+
response_timeout=15, end_marker=None, reverse=None,
|
242
|
+
headers=None):
|
202
243
|
"""
|
203
244
|
Get listings directly from the account server.
|
204
245
|
|
@@ -218,6 +259,7 @@ def direct_get_account(node, part, account, marker=None, limit=None,
|
|
218
259
|
"""
|
219
260
|
path = _make_path(account)
|
220
261
|
return _get_direct_account_container(path, "Account", node, part,
|
262
|
+
headers=headers,
|
221
263
|
marker=marker,
|
222
264
|
limit=limit, prefix=prefix,
|
223
265
|
delimiter=delimiter,
|
@@ -238,7 +280,7 @@ def direct_delete_account(node, part, account, conn_timeout=5,
|
|
238
280
|
|
239
281
|
|
240
282
|
def direct_head_container(node, part, account, container, conn_timeout=5,
|
241
|
-
response_timeout=15):
|
283
|
+
response_timeout=15, headers=None):
|
242
284
|
"""
|
243
285
|
Request container information directly from the container server.
|
244
286
|
|
@@ -251,8 +293,11 @@ def direct_head_container(node, part, account, container, conn_timeout=5,
|
|
251
293
|
:returns: a dict containing the response's headers in a HeaderKeyDict
|
252
294
|
:raises ClientException: HTTP HEAD request failed
|
253
295
|
"""
|
296
|
+
if headers is None:
|
297
|
+
headers = {}
|
298
|
+
|
254
299
|
path = _make_path(account, container)
|
255
|
-
resp = _make_req(node, part, 'HEAD', path, gen_headers(),
|
300
|
+
resp = _make_req(node, part, 'HEAD', path, gen_headers(headers),
|
256
301
|
'Container', conn_timeout, response_timeout)
|
257
302
|
|
258
303
|
resp_headers = HeaderKeyDict()
|
@@ -264,7 +309,7 @@ def direct_head_container(node, part, account, container, conn_timeout=5,
|
|
264
309
|
def direct_get_container(node, part, account, container, marker=None,
|
265
310
|
limit=None, prefix=None, delimiter=None,
|
266
311
|
conn_timeout=5, response_timeout=15, end_marker=None,
|
267
|
-
reverse=None, headers=None):
|
312
|
+
reverse=None, headers=None, extra_params=None):
|
268
313
|
"""
|
269
314
|
Get container listings directly from the container server.
|
270
315
|
|
@@ -281,6 +326,12 @@ def direct_get_container(node, part, account, container, marker=None,
|
|
281
326
|
:param end_marker: end_marker query
|
282
327
|
:param reverse: reverse the returned listing
|
283
328
|
:param headers: headers to be included in the request
|
329
|
+
:param extra_params: a dict of extra parameters to be included in the
|
330
|
+
request. It can be used to pass additional parameters, e.g,
|
331
|
+
{'states':'updating'} can be used with shard_range/namespace listing.
|
332
|
+
It can also be used to pass the existing keyword args, like 'marker' or
|
333
|
+
'limit', but if the same parameter appears twice in both keyword arg
|
334
|
+
(not None) and extra_params, this function will raise TypeError.
|
284
335
|
:returns: a tuple of (response headers, a list of objects) The response
|
285
336
|
headers will be a HeaderKeyDict.
|
286
337
|
"""
|
@@ -293,7 +344,8 @@ def direct_get_container(node, part, account, container, marker=None,
|
|
293
344
|
reverse=reverse,
|
294
345
|
conn_timeout=conn_timeout,
|
295
346
|
response_timeout=response_timeout,
|
296
|
-
headers=headers
|
347
|
+
headers=headers,
|
348
|
+
extra_params=extra_params)
|
297
349
|
|
298
350
|
|
299
351
|
def direct_delete_container(node, part, account, container, conn_timeout=5,
|
@@ -349,6 +401,31 @@ def direct_put_container(node, part, account, container, conn_timeout=5,
|
|
349
401
|
content_length=content_length, chunk_size=chunk_size)
|
350
402
|
|
351
403
|
|
404
|
+
def direct_post_container(node, part, account, container, conn_timeout=5,
|
405
|
+
response_timeout=15, headers=None):
|
406
|
+
"""
|
407
|
+
Make a POST request to a container server.
|
408
|
+
|
409
|
+
:param node: node dictionary from the ring
|
410
|
+
:param part: partition the container is on
|
411
|
+
:param account: account name
|
412
|
+
:param container: container name
|
413
|
+
:param conn_timeout: timeout in seconds for establishing the connection
|
414
|
+
:param response_timeout: timeout in seconds for getting the response
|
415
|
+
:param headers: additional headers to include in the request
|
416
|
+
:raises ClientException: HTTP PUT request failed
|
417
|
+
"""
|
418
|
+
if headers is None:
|
419
|
+
headers = {}
|
420
|
+
|
421
|
+
lower_headers = set(k.lower() for k in headers)
|
422
|
+
headers_out = gen_headers(headers,
|
423
|
+
add_ts='x-timestamp' not in lower_headers)
|
424
|
+
path = _make_path(account, container)
|
425
|
+
return _make_req(node, part, 'POST', path, headers_out, 'Container',
|
426
|
+
conn_timeout, response_timeout)
|
427
|
+
|
428
|
+
|
352
429
|
def direct_put_container_object(node, part, account, container, obj,
|
353
430
|
conn_timeout=5, response_timeout=15,
|
354
431
|
headers=None):
|
@@ -429,9 +506,10 @@ def direct_get_object(node, part, account, container, obj, conn_timeout=5,
|
|
429
506
|
if headers is None:
|
430
507
|
headers = {}
|
431
508
|
|
509
|
+
ip, port = get_ip_port(node, headers)
|
432
510
|
path = _make_path(account, container, obj)
|
433
511
|
with Timeout(conn_timeout):
|
434
|
-
conn = http_connect(
|
512
|
+
conn = http_connect(ip, port, node['device'], part,
|
435
513
|
'GET', path, headers=gen_headers(headers))
|
436
514
|
with Timeout(response_timeout):
|
437
515
|
resp = conn.getresponse()
|
@@ -483,7 +561,7 @@ def direct_put_object(node, part, account, container, name, contents,
|
|
483
561
|
if headers is None:
|
484
562
|
headers = {}
|
485
563
|
if etag:
|
486
|
-
headers['ETag'] = etag
|
564
|
+
headers['ETag'] = normalize_etag(etag)
|
487
565
|
if content_type is not None:
|
488
566
|
headers['Content-Type'] = content_type
|
489
567
|
else:
|
@@ -496,7 +574,7 @@ def direct_put_object(node, part, account, container, name, contents,
|
|
496
574
|
'Object', conn_timeout, response_timeout, contents=contents,
|
497
575
|
content_length=content_length, chunk_size=chunk_size)
|
498
576
|
|
499
|
-
return resp.getheader('etag')
|
577
|
+
return normalize_etag(resp.getheader('etag'))
|
500
578
|
|
501
579
|
|
502
580
|
def direct_post_object(node, part, account, container, name, headers,
|
@@ -549,6 +627,9 @@ def direct_get_suffix_hashes(node, part, suffixes, conn_timeout=5,
|
|
549
627
|
"""
|
550
628
|
Get suffix hashes directly from the object server.
|
551
629
|
|
630
|
+
Note that unlike other ``direct_client`` functions, this one defaults
|
631
|
+
to using the replication network to make requests.
|
632
|
+
|
552
633
|
:param node: node dictionary from the ring
|
553
634
|
:param part: partition the container is on
|
554
635
|
:param conn_timeout: timeout in seconds for establishing the connection
|
@@ -560,9 +641,11 @@ def direct_get_suffix_hashes(node, part, suffixes, conn_timeout=5,
|
|
560
641
|
if headers is None:
|
561
642
|
headers = {}
|
562
643
|
|
644
|
+
headers.setdefault(USE_REPLICATION_NETWORK_HEADER, 'true')
|
645
|
+
ip, port = get_ip_port(node, headers)
|
563
646
|
path = '/%s' % '-'.join(suffixes)
|
564
647
|
with Timeout(conn_timeout):
|
565
|
-
conn = http_connect(
|
648
|
+
conn = http_connect(ip, port,
|
566
649
|
node['device'], part, 'REPLICATE', path,
|
567
650
|
headers=gen_headers(headers))
|
568
651
|
with Timeout(response_timeout):
|
@@ -573,7 +656,7 @@ def direct_get_suffix_hashes(node, part, suffixes, conn_timeout=5,
|
|
573
656
|
host={'ip': node['replication_ip'],
|
574
657
|
'port': node['replication_port']}
|
575
658
|
)
|
576
|
-
return pickle.loads(resp.read())
|
659
|
+
return pickle.loads(resp.read()) # nosec: B301
|
577
660
|
|
578
661
|
|
579
662
|
def retry(func, *args, **kwargs):
|
@@ -619,3 +702,31 @@ def retry(func, *args, **kwargs):
|
|
619
702
|
http_device=args[0]['device'])
|
620
703
|
else:
|
621
704
|
raise ClientException('Raise too many retries')
|
705
|
+
|
706
|
+
|
707
|
+
def direct_get_recon(node, recon_command, conn_timeout=5, response_timeout=15,
|
708
|
+
headers=None):
|
709
|
+
"""
|
710
|
+
Get recon json directly from the storage server.
|
711
|
+
|
712
|
+
:param node: node dictionary from the ring
|
713
|
+
:param recon_command: recon string (post /recon/)
|
714
|
+
:param conn_timeout: timeout in seconds for establishing the connection
|
715
|
+
:param response_timeout: timeout in seconds for getting the response
|
716
|
+
:param headers: dict to be passed into HTTPConnection headers
|
717
|
+
:returns: deserialized json response
|
718
|
+
:raises DirectClientReconException: HTTP GET request failed
|
719
|
+
"""
|
720
|
+
if headers is None:
|
721
|
+
headers = {}
|
722
|
+
|
723
|
+
ip, port = get_ip_port(node, headers)
|
724
|
+
path = '/recon/%s' % recon_command
|
725
|
+
with Timeout(conn_timeout):
|
726
|
+
conn = http_connect_raw(ip, port, 'GET', path,
|
727
|
+
headers=gen_headers(headers))
|
728
|
+
with Timeout(response_timeout):
|
729
|
+
resp = conn.getresponse()
|
730
|
+
if not is_success(resp.status):
|
731
|
+
raise DirectClientReconException('GET', node, path, resp)
|
732
|
+
return json.loads(resp.read())
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Copyright (c) 2021 NVIDIA
|
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
|
+
import collections
|
16
|
+
from time import time
|
17
|
+
|
18
|
+
from swift.common.utils import node_to_string
|
19
|
+
|
20
|
+
|
21
|
+
class ErrorLimiter(object):
|
22
|
+
"""
|
23
|
+
Tracks the number of errors that have occurred for nodes. A node will be
|
24
|
+
considered to be error-limited for a given interval of time after it has
|
25
|
+
accumulated more errors than a given limit.
|
26
|
+
|
27
|
+
:param suppression_interval: The number of seconds for which a node is
|
28
|
+
error-limited once it has accumulated more than ``suppression_limit``
|
29
|
+
errors. Should be a float value.
|
30
|
+
:param suppression_limit: The number of errors that a node must accumulate
|
31
|
+
before it is considered to be error-limited. Should be an int value.
|
32
|
+
"""
|
33
|
+
def __init__(self, suppression_interval, suppression_limit):
|
34
|
+
self.suppression_interval = float(suppression_interval)
|
35
|
+
self.suppression_limit = int(suppression_limit)
|
36
|
+
self.stats = collections.defaultdict(dict)
|
37
|
+
|
38
|
+
def node_key(self, node):
|
39
|
+
"""
|
40
|
+
Get the key under which a node's error stats will be stored.
|
41
|
+
|
42
|
+
:param node: dictionary describing a node.
|
43
|
+
:return: string key.
|
44
|
+
"""
|
45
|
+
return node_to_string(node)
|
46
|
+
|
47
|
+
def is_limited(self, node):
|
48
|
+
"""
|
49
|
+
Check if the node is currently error limited.
|
50
|
+
|
51
|
+
:param node: dictionary of node to check
|
52
|
+
:returns: True if error limited, False otherwise
|
53
|
+
"""
|
54
|
+
now = time()
|
55
|
+
node_key = self.node_key(node)
|
56
|
+
error_stats = self.stats.get(node_key)
|
57
|
+
|
58
|
+
if error_stats is None or 'errors' not in error_stats:
|
59
|
+
return False
|
60
|
+
|
61
|
+
if 'last_error' in error_stats and error_stats['last_error'] < \
|
62
|
+
now - self.suppression_interval:
|
63
|
+
self.stats.pop(node_key)
|
64
|
+
return False
|
65
|
+
return error_stats['errors'] > self.suppression_limit
|
66
|
+
|
67
|
+
def limit(self, node):
|
68
|
+
"""
|
69
|
+
Mark a node as error limited. This immediately pretends the
|
70
|
+
node received enough errors to trigger error suppression. Use
|
71
|
+
this for errors like Insufficient Storage. For other errors
|
72
|
+
use :func:`increment`.
|
73
|
+
|
74
|
+
:param node: dictionary of node to error limit
|
75
|
+
"""
|
76
|
+
node_key = self.node_key(node)
|
77
|
+
error_stats = self.stats[node_key]
|
78
|
+
error_stats['errors'] = self.suppression_limit + 1
|
79
|
+
error_stats['last_error'] = time()
|
80
|
+
|
81
|
+
def increment(self, node):
|
82
|
+
"""
|
83
|
+
Increment the error count and update the time of the last error for
|
84
|
+
the given ``node``.
|
85
|
+
|
86
|
+
:param node: dictionary describing a node.
|
87
|
+
:returns: True if suppression_limit is exceeded, False otherwise
|
88
|
+
"""
|
89
|
+
node_key = self.node_key(node)
|
90
|
+
error_stats = self.stats[node_key]
|
91
|
+
error_stats['errors'] = error_stats.get('errors', 0) + 1
|
92
|
+
error_stats['last_error'] = time()
|
93
|
+
return error_stats['errors'] > self.suppression_limit
|
swift/common/exceptions.py
CHANGED
@@ -113,7 +113,11 @@ class DeviceUnavailable(SwiftException):
|
|
113
113
|
pass
|
114
114
|
|
115
115
|
|
116
|
-
class
|
116
|
+
class DatabaseAuditorException(SwiftException):
|
117
|
+
pass
|
118
|
+
|
119
|
+
|
120
|
+
class InvalidAccountInfo(DatabaseAuditorException):
|
117
121
|
pass
|
118
122
|
|
119
123
|
|
@@ -215,6 +219,10 @@ class ReplicationLockTimeout(LockTimeout):
|
|
215
219
|
pass
|
216
220
|
|
217
221
|
|
222
|
+
class PartitionLockTimeout(LockTimeout):
|
223
|
+
pass
|
224
|
+
|
225
|
+
|
218
226
|
class MimeInvalid(SwiftException):
|
219
227
|
pass
|
220
228
|
|
@@ -231,6 +239,22 @@ class UnknownSecretIdError(EncryptionException):
|
|
231
239
|
pass
|
232
240
|
|
233
241
|
|
242
|
+
class QuarantineRequest(SwiftException):
|
243
|
+
pass
|
244
|
+
|
245
|
+
|
246
|
+
class MemcacheConnectionError(Exception):
|
247
|
+
pass
|
248
|
+
|
249
|
+
|
250
|
+
class MemcacheIncrNotFoundError(MemcacheConnectionError):
|
251
|
+
pass
|
252
|
+
|
253
|
+
|
254
|
+
class MemcachePoolTimeout(Timeout):
|
255
|
+
pass
|
256
|
+
|
257
|
+
|
234
258
|
class ClientException(Exception):
|
235
259
|
|
236
260
|
def __init__(self, msg, http_scheme='', http_host='', http_port='',
|
swift/common/header_key_dict.py
CHANGED
@@ -13,8 +13,6 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
import six
|
17
|
-
|
18
16
|
|
19
17
|
class HeaderKeyDict(dict):
|
20
18
|
"""
|
@@ -31,10 +29,7 @@ class HeaderKeyDict(dict):
|
|
31
29
|
|
32
30
|
@staticmethod
|
33
31
|
def _title(s):
|
34
|
-
|
35
|
-
return s.title()
|
36
|
-
else:
|
37
|
-
return s.encode('latin1').title().decode('latin1')
|
32
|
+
return s.encode('latin1').title().decode('latin1')
|
38
33
|
|
39
34
|
def update(self, other):
|
40
35
|
if hasattr(other, 'keys'):
|
@@ -51,9 +46,7 @@ class HeaderKeyDict(dict):
|
|
51
46
|
key = self._title(key)
|
52
47
|
if value is None:
|
53
48
|
self.pop(key, None)
|
54
|
-
elif
|
55
|
-
return dict.__setitem__(self, key, value.encode('utf-8'))
|
56
|
-
elif six.PY3 and isinstance(value, six.binary_type):
|
49
|
+
elif isinstance(value, bytes):
|
57
50
|
return dict.__setitem__(self, key, value.decode('latin-1'))
|
58
51
|
else:
|
59
52
|
return dict.__setitem__(self, key, str(value))
|