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.
Files changed (206) hide show
  1. swift/__init__.py +29 -50
  2. swift/account/auditor.py +21 -118
  3. swift/account/backend.py +33 -28
  4. swift/account/reaper.py +37 -28
  5. swift/account/replicator.py +22 -0
  6. swift/account/server.py +60 -26
  7. swift/account/utils.py +28 -11
  8. swift-2.23.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +154 -14
  17. swift/cli/manage_shard_ranges.py +705 -37
  18. swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.3.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  23. swift/cli/relinker.py +807 -126
  24. swift/cli/reload.py +135 -0
  25. swift/cli/ringbuilder.py +217 -20
  26. swift/cli/ringcomposer.py +0 -1
  27. swift/cli/shard-info.py +4 -3
  28. swift/common/base_storage_server.py +9 -20
  29. swift/common/bufferedhttp.py +48 -74
  30. swift/common/constraints.py +20 -15
  31. swift/common/container_sync_realms.py +9 -11
  32. swift/common/daemon.py +25 -8
  33. swift/common/db.py +195 -128
  34. swift/common/db_auditor.py +168 -0
  35. swift/common/db_replicator.py +95 -55
  36. swift/common/digest.py +141 -0
  37. swift/common/direct_client.py +144 -33
  38. swift/common/error_limiter.py +93 -0
  39. swift/common/exceptions.py +25 -1
  40. swift/common/header_key_dict.py +2 -9
  41. swift/common/http_protocol.py +373 -0
  42. swift/common/internal_client.py +129 -59
  43. swift/common/linkat.py +3 -4
  44. swift/common/manager.py +284 -67
  45. swift/common/memcached.py +390 -145
  46. swift/common/middleware/__init__.py +4 -0
  47. swift/common/middleware/account_quotas.py +211 -46
  48. swift/common/middleware/acl.py +3 -8
  49. swift/common/middleware/backend_ratelimit.py +230 -0
  50. swift/common/middleware/bulk.py +22 -34
  51. swift/common/middleware/catch_errors.py +1 -3
  52. swift/common/middleware/cname_lookup.py +6 -11
  53. swift/common/middleware/container_quotas.py +1 -1
  54. swift/common/middleware/container_sync.py +39 -17
  55. swift/common/middleware/copy.py +12 -0
  56. swift/common/middleware/crossdomain.py +22 -9
  57. swift/common/middleware/crypto/__init__.py +2 -1
  58. swift/common/middleware/crypto/crypto_utils.py +11 -15
  59. swift/common/middleware/crypto/decrypter.py +28 -11
  60. swift/common/middleware/crypto/encrypter.py +12 -17
  61. swift/common/middleware/crypto/keymaster.py +8 -15
  62. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  63. swift/common/middleware/dlo.py +15 -11
  64. swift/common/middleware/domain_remap.py +5 -4
  65. swift/common/middleware/etag_quoter.py +128 -0
  66. swift/common/middleware/formpost.py +73 -70
  67. swift/common/middleware/gatekeeper.py +8 -1
  68. swift/common/middleware/keystoneauth.py +33 -3
  69. swift/common/middleware/list_endpoints.py +4 -4
  70. swift/common/middleware/listing_formats.py +85 -49
  71. swift/common/middleware/memcache.py +4 -95
  72. swift/common/middleware/name_check.py +3 -2
  73. swift/common/middleware/proxy_logging.py +160 -92
  74. swift/common/middleware/ratelimit.py +17 -10
  75. swift/common/middleware/read_only.py +6 -4
  76. swift/common/middleware/recon.py +59 -22
  77. swift/common/middleware/s3api/acl_handlers.py +25 -3
  78. swift/common/middleware/s3api/acl_utils.py +6 -1
  79. swift/common/middleware/s3api/controllers/__init__.py +6 -0
  80. swift/common/middleware/s3api/controllers/acl.py +3 -2
  81. swift/common/middleware/s3api/controllers/bucket.py +242 -137
  82. swift/common/middleware/s3api/controllers/logging.py +2 -2
  83. swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
  84. swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
  85. swift/common/middleware/s3api/controllers/obj.py +112 -8
  86. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  87. swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
  88. swift/common/middleware/s3api/controllers/tagging.py +57 -0
  89. swift/common/middleware/s3api/controllers/versioning.py +36 -7
  90. swift/common/middleware/s3api/etree.py +22 -9
  91. swift/common/middleware/s3api/exception.py +0 -4
  92. swift/common/middleware/s3api/s3api.py +113 -41
  93. swift/common/middleware/s3api/s3request.py +384 -218
  94. swift/common/middleware/s3api/s3response.py +126 -23
  95. swift/common/middleware/s3api/s3token.py +16 -17
  96. swift/common/middleware/s3api/schema/delete.rng +1 -1
  97. swift/common/middleware/s3api/subresource.py +7 -10
  98. swift/common/middleware/s3api/utils.py +27 -10
  99. swift/common/middleware/slo.py +665 -358
  100. swift/common/middleware/staticweb.py +64 -37
  101. swift/common/middleware/symlink.py +51 -18
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +191 -173
  104. swift/common/middleware/versioned_writes/__init__.py +51 -0
  105. swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
  106. swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
  107. swift/common/middleware/x_profile/exceptions.py +1 -4
  108. swift/common/middleware/x_profile/html_viewer.py +18 -19
  109. swift/common/middleware/x_profile/profile_model.py +1 -2
  110. swift/common/middleware/xprofile.py +10 -10
  111. swift-2.23.3.data/scripts/swift-container-server → swift/common/recon.py +13 -8
  112. swift/common/registry.py +147 -0
  113. swift/common/request_helpers.py +324 -57
  114. swift/common/ring/builder.py +67 -25
  115. swift/common/ring/composite_builder.py +1 -1
  116. swift/common/ring/ring.py +177 -51
  117. swift/common/ring/utils.py +1 -1
  118. swift/common/splice.py +10 -6
  119. swift/common/statsd_client.py +205 -0
  120. swift/common/storage_policy.py +49 -44
  121. swift/common/swob.py +86 -102
  122. swift/common/{utils.py → utils/__init__.py} +2163 -2772
  123. swift/common/utils/base.py +131 -0
  124. swift/common/utils/config.py +433 -0
  125. swift/common/utils/ipaddrs.py +256 -0
  126. swift/common/utils/libc.py +345 -0
  127. swift/common/utils/logs.py +859 -0
  128. swift/common/utils/timestamp.py +412 -0
  129. swift/common/wsgi.py +553 -535
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +490 -231
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +358 -165
  135. swift/container/sharder.py +1540 -684
  136. swift/container/sync.py +94 -88
  137. swift/container/updater.py +53 -32
  138. swift/obj/auditor.py +153 -35
  139. swift/obj/diskfile.py +466 -217
  140. swift/obj/expirer.py +406 -124
  141. swift/obj/mem_diskfile.py +7 -4
  142. swift/obj/mem_server.py +1 -0
  143. swift/obj/reconstructor.py +523 -262
  144. swift/obj/replicator.py +249 -188
  145. swift/obj/server.py +207 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +652 -139
  149. swift/obj/watchers/__init__.py +0 -0
  150. swift/obj/watchers/dark_data.py +213 -0
  151. swift/proxy/controllers/account.py +11 -11
  152. swift/proxy/controllers/base.py +848 -604
  153. swift/proxy/controllers/container.py +433 -92
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1000 -489
  156. swift/proxy/server.py +185 -112
  157. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
  158. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
  159. swift-2.35.0.dist-info/RECORD +201 -0
  160. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  161. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  162. swift-2.35.0.dist-info/pbr.json +1 -0
  163. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  164. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  165. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  166. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  167. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  168. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  169. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  170. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  171. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  172. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  173. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  174. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  175. swift-2.23.3.data/scripts/swift-account-auditor +0 -23
  176. swift-2.23.3.data/scripts/swift-account-info +0 -51
  177. swift-2.23.3.data/scripts/swift-account-reaper +0 -23
  178. swift-2.23.3.data/scripts/swift-account-replicator +0 -34
  179. swift-2.23.3.data/scripts/swift-account-server +0 -23
  180. swift-2.23.3.data/scripts/swift-container-auditor +0 -23
  181. swift-2.23.3.data/scripts/swift-container-info +0 -55
  182. swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
  183. swift-2.23.3.data/scripts/swift-container-replicator +0 -34
  184. swift-2.23.3.data/scripts/swift-container-sharder +0 -37
  185. swift-2.23.3.data/scripts/swift-container-sync +0 -23
  186. swift-2.23.3.data/scripts/swift-container-updater +0 -23
  187. swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
  188. swift-2.23.3.data/scripts/swift-form-signature +0 -20
  189. swift-2.23.3.data/scripts/swift-init +0 -119
  190. swift-2.23.3.data/scripts/swift-object-auditor +0 -29
  191. swift-2.23.3.data/scripts/swift-object-expirer +0 -33
  192. swift-2.23.3.data/scripts/swift-object-info +0 -60
  193. swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
  194. swift-2.23.3.data/scripts/swift-object-relinker +0 -41
  195. swift-2.23.3.data/scripts/swift-object-replicator +0 -37
  196. swift-2.23.3.data/scripts/swift-object-server +0 -27
  197. swift-2.23.3.data/scripts/swift-object-updater +0 -23
  198. swift-2.23.3.data/scripts/swift-proxy-server +0 -23
  199. swift-2.23.3.data/scripts/swift-recon +0 -24
  200. swift-2.23.3.data/scripts/swift-ring-builder +0 -24
  201. swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
  202. swift-2.23.3.data/scripts/swift-ring-composer +0 -22
  203. swift-2.23.3.dist-info/RECORD +0 -220
  204. swift-2.23.3.dist-info/pbr.json +0 -1
  205. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
  206. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
@@ -23,17 +23,18 @@ import os
23
23
  import socket
24
24
 
25
25
  from eventlet import sleep, Timeout
26
- import six
27
- import six.moves.cPickle as pickle
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.utils import Timestamp, FileLikeIter
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 not isinstance(path, six.text_type):
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, six.binary_type) else 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, six.string_types):
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(node['ip'], node['port'], node['device'], part,
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
- """Base class for get direct account and container.
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 get_direct_account or
145
- get_direct_container instead.
162
+ Do not use directly use the direct_get_account or
163
+ direct_get_container instead.
146
164
  """
147
- params = ['format=json']
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
- params.append('marker=%s' % quote(marker))
174
+ if 'marker' in params:
175
+ raise TypeError('duplicate values for keyword arg: marker')
176
+ params['marker'] = quote(marker)
150
177
  if limit:
151
- params.append('limit=%d' % limit)
178
+ if 'limit' in params:
179
+ raise TypeError('duplicate values for keyword arg: limit')
180
+ params['limit'] = '%d' % limit
152
181
  if prefix:
153
- params.append('prefix=%s' % quote(prefix))
182
+ if 'prefix' in params:
183
+ raise TypeError('duplicate values for keyword arg: prefix')
184
+ params['prefix'] = quote(prefix)
154
185
  if delimiter:
155
- params.append('delimiter=%s' % quote(delimiter))
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
- params.append('end_marker=%s' % quote(end_marker))
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
- params.append('reverse=%s' % quote(reverse))
160
- qs = '&'.join(params)
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(node['ip'], node['port'], node['device'], part,
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(node['ip'], node['port'], node['device'], part,
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.strip('"')
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').strip('"')
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(node['replication_ip'], node['replication_port'],
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
@@ -113,7 +113,11 @@ class DeviceUnavailable(SwiftException):
113
113
  pass
114
114
 
115
115
 
116
- class InvalidAccountInfo(SwiftException):
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='',
@@ -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
- if six.PY2:
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 six.PY2 and isinstance(value, six.text_type):
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))