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.
Files changed (208) 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.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +183 -29
  17. swift/cli/manage_shard_ranges.py +708 -37
  18. swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.2.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 +198 -127
  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 +396 -147
  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 -81
  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 +52 -19
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +192 -174
  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.2.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} +2191 -2762
  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 +555 -536
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +552 -227
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +397 -176
  135. swift/container/sharder.py +1580 -639
  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 +213 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +653 -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 +452 -86
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1009 -490
  156. swift/proxy/server.py +185 -112
  157. swift-2.35.0.dist-info/AUTHORS +501 -0
  158. swift-2.35.0.dist-info/LICENSE +202 -0
  159. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
  160. swift-2.35.0.dist-info/RECORD +201 -0
  161. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  162. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  163. swift-2.35.0.dist-info/pbr.json +1 -0
  164. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  165. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  166. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  167. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  168. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  169. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  170. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  171. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  172. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  173. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  174. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  175. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  176. swift-2.23.2.data/scripts/swift-account-auditor +0 -23
  177. swift-2.23.2.data/scripts/swift-account-info +0 -51
  178. swift-2.23.2.data/scripts/swift-account-reaper +0 -23
  179. swift-2.23.2.data/scripts/swift-account-replicator +0 -34
  180. swift-2.23.2.data/scripts/swift-account-server +0 -23
  181. swift-2.23.2.data/scripts/swift-container-auditor +0 -23
  182. swift-2.23.2.data/scripts/swift-container-info +0 -51
  183. swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
  184. swift-2.23.2.data/scripts/swift-container-replicator +0 -34
  185. swift-2.23.2.data/scripts/swift-container-sharder +0 -33
  186. swift-2.23.2.data/scripts/swift-container-sync +0 -23
  187. swift-2.23.2.data/scripts/swift-container-updater +0 -23
  188. swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
  189. swift-2.23.2.data/scripts/swift-form-signature +0 -20
  190. swift-2.23.2.data/scripts/swift-init +0 -119
  191. swift-2.23.2.data/scripts/swift-object-auditor +0 -29
  192. swift-2.23.2.data/scripts/swift-object-expirer +0 -33
  193. swift-2.23.2.data/scripts/swift-object-info +0 -60
  194. swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
  195. swift-2.23.2.data/scripts/swift-object-relinker +0 -41
  196. swift-2.23.2.data/scripts/swift-object-replicator +0 -37
  197. swift-2.23.2.data/scripts/swift-object-server +0 -27
  198. swift-2.23.2.data/scripts/swift-object-updater +0 -23
  199. swift-2.23.2.data/scripts/swift-proxy-server +0 -23
  200. swift-2.23.2.data/scripts/swift-recon +0 -24
  201. swift-2.23.2.data/scripts/swift-ring-builder +0 -24
  202. swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
  203. swift-2.23.2.data/scripts/swift-ring-composer +0 -22
  204. swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
  205. swift-2.23.2.dist-info/RECORD +0 -220
  206. swift-2.23.2.dist-info/metadata.json +0 -1
  207. swift-2.23.2.dist-info/pbr.json +0 -1
  208. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
swift/proxy/server.py CHANGED
@@ -19,7 +19,6 @@ import socket
19
19
 
20
20
  from collections import defaultdict
21
21
 
22
- from swift import gettext_ as _
23
22
  from random import shuffle
24
23
  from time import time
25
24
  import functools
@@ -29,24 +28,30 @@ from eventlet import Timeout
29
28
 
30
29
  from swift import __canonical_version__ as swift_version
31
30
  from swift.common import constraints
32
- from swift.common.http import is_server_error
31
+ from swift.common.http import is_server_error, HTTP_INSUFFICIENT_STORAGE
33
32
  from swift.common.storage_policy import POLICIES
34
33
  from swift.common.ring import Ring
35
- from swift.common.utils import cache_from_env, get_logger, \
34
+ from swift.common.error_limiter import ErrorLimiter
35
+ from swift.common.utils import Watchdog, get_logger, \
36
36
  get_remote_client, split_path, config_true_value, generate_trans_id, \
37
37
  affinity_key_function, affinity_locality_predicate, list_from_csv, \
38
- register_swift_info, readconf, config_auto_int_value
38
+ parse_prefixed_conf, config_auto_int_value, node_to_string, \
39
+ config_request_node_count_value, config_percent_value, cap_length, \
40
+ parse_options
41
+ from swift.common.registry import register_swift_info
39
42
  from swift.common.constraints import check_utf8, valid_api_version
40
43
  from swift.proxy.controllers import AccountController, ContainerController, \
41
44
  ObjectControllerRouter, InfoController
42
- from swift.proxy.controllers.base import get_container_info, NodeIter, \
45
+ from swift.proxy.controllers.base import get_container_info, \
43
46
  DEFAULT_RECHECK_CONTAINER_EXISTENCE, DEFAULT_RECHECK_ACCOUNT_EXISTENCE, \
44
- DEFAULT_RECHECK_UPDATING_SHARD_RANGES
47
+ DEFAULT_RECHECK_UPDATING_SHARD_RANGES, DEFAULT_RECHECK_LISTING_SHARD_RANGES
45
48
  from swift.common.swob import HTTPBadRequest, HTTPForbidden, \
46
49
  HTTPMethodNotAllowed, HTTPNotFound, HTTPPreconditionFailed, \
47
50
  HTTPServerError, HTTPException, Request, HTTPServiceUnavailable, \
48
51
  wsgi_to_str
49
52
  from swift.common.exceptions import APIVersionError
53
+ from swift.common.wsgi import run_wsgi
54
+ from swift.obj import expirer
50
55
 
51
56
 
52
57
  # List of entry points for mandatory middlewares.
@@ -101,7 +106,8 @@ class ProxyOverrideOptions(object):
101
106
  :param conf: the proxy-server config dict.
102
107
  :param override_conf: a dict of overriding configuration options.
103
108
  """
104
- def __init__(self, base_conf, override_conf):
109
+ def __init__(self, base_conf, override_conf, app):
110
+
105
111
  def get(key, default):
106
112
  return override_conf.get(key, base_conf.get(key, default))
107
113
 
@@ -147,14 +153,28 @@ class ProxyOverrideOptions(object):
147
153
  get('write_affinity_handoff_delete_count', 'auto'), None
148
154
  )
149
155
 
156
+ self.rebalance_missing_suppression_count = int(get(
157
+ 'rebalance_missing_suppression_count', 1))
158
+ self.concurrent_gets = config_true_value(get('concurrent_gets', False))
159
+ self.concurrency_timeout = float(get(
160
+ 'concurrency_timeout', app.conn_timeout))
161
+ self.concurrent_ec_extra_requests = int(get(
162
+ 'concurrent_ec_extra_requests', 0))
163
+
150
164
  def __repr__(self):
151
- return '%s({}, {%s})' % (self.__class__.__name__, ', '.join(
152
- '%r: %r' % (k, getattr(self, k)) for k in (
153
- 'sorting_method',
154
- 'read_affinity',
155
- 'write_affinity',
156
- 'write_affinity_node_count',
157
- 'write_affinity_handoff_delete_count')))
165
+ return '%s({}, {%s}, app)' % (
166
+ self.__class__.__name__, ', '.join(
167
+ '%r: %r' % (k, getattr(self, k)) for k in (
168
+ 'sorting_method',
169
+ 'read_affinity',
170
+ 'write_affinity',
171
+ 'write_affinity_node_count',
172
+ 'write_affinity_handoff_delete_count',
173
+ 'rebalance_missing_suppression_count',
174
+ 'concurrent_gets',
175
+ 'concurrency_timeout',
176
+ 'concurrent_ec_extra_requests',
177
+ )))
158
178
 
159
179
  def __eq__(self, other):
160
180
  if not isinstance(other, ProxyOverrideOptions):
@@ -164,25 +184,31 @@ class ProxyOverrideOptions(object):
164
184
  'read_affinity',
165
185
  'write_affinity',
166
186
  'write_affinity_node_count',
167
- 'write_affinity_handoff_delete_count'))
187
+ 'write_affinity_handoff_delete_count',
188
+ 'rebalance_missing_suppression_count',
189
+ 'concurrent_gets',
190
+ 'concurrency_timeout',
191
+ 'concurrent_ec_extra_requests',
192
+ ))
168
193
 
169
194
 
170
195
  class Application(object):
171
196
  """WSGI application for the proxy server."""
172
197
 
173
- def __init__(self, conf, memcache=None, logger=None, account_ring=None,
198
+ def __init__(self, conf, logger=None, account_ring=None,
174
199
  container_ring=None):
200
+ # This is for the sake of tests which instantiate an Application
201
+ # directly rather than via loadapp().
202
+ self._pipeline_final_app = self
203
+
175
204
  if conf is None:
176
205
  conf = {}
177
206
  if logger is None:
178
- self.logger = get_logger(conf, log_route='proxy-server')
207
+ self.logger = get_logger(conf, log_route='proxy-server',
208
+ statsd_tail_prefix='proxy-server')
179
209
  else:
180
210
  self.logger = logger
181
- self._override_options = self._load_per_policy_config(conf)
182
- self.sorts_by_timing = any(pc.sorting_method == 'timing'
183
- for pc in self._override_options.values())
184
-
185
- self._error_limiting = {}
211
+ self.backend_user_agent = 'proxy-server %s' % os.getpid()
186
212
 
187
213
  swift_dir = conf.get('swift_dir', '/etc/swift')
188
214
  self.swift_dir = swift_dir
@@ -190,25 +216,39 @@ class Application(object):
190
216
  self.recoverable_node_timeout = float(
191
217
  conf.get('recoverable_node_timeout', self.node_timeout))
192
218
  self.conn_timeout = float(conf.get('conn_timeout', 0.5))
193
- self.client_timeout = int(conf.get('client_timeout', 60))
194
- self.put_queue_depth = int(conf.get('put_queue_depth', 10))
219
+ self.client_timeout = float(conf.get('client_timeout', 60))
195
220
  self.object_chunk_size = int(conf.get('object_chunk_size', 65536))
196
221
  self.client_chunk_size = int(conf.get('client_chunk_size', 65536))
197
222
  self.trans_id_suffix = conf.get('trans_id_suffix', '')
198
223
  self.post_quorum_timeout = float(conf.get('post_quorum_timeout', 0.5))
199
- self.error_suppression_interval = \
200
- int(conf.get('error_suppression_interval', 60))
201
- self.error_suppression_limit = \
224
+ error_suppression_interval = \
225
+ float(conf.get('error_suppression_interval', 60))
226
+ error_suppression_limit = \
202
227
  int(conf.get('error_suppression_limit', 10))
228
+ self.error_limiter = ErrorLimiter(error_suppression_interval,
229
+ error_suppression_limit)
203
230
  self.recheck_container_existence = \
204
231
  int(conf.get('recheck_container_existence',
205
232
  DEFAULT_RECHECK_CONTAINER_EXISTENCE))
206
233
  self.recheck_updating_shard_ranges = \
207
234
  int(conf.get('recheck_updating_shard_ranges',
208
235
  DEFAULT_RECHECK_UPDATING_SHARD_RANGES))
236
+ self.recheck_listing_shard_ranges = \
237
+ int(conf.get('recheck_listing_shard_ranges',
238
+ DEFAULT_RECHECK_LISTING_SHARD_RANGES))
209
239
  self.recheck_account_existence = \
210
240
  int(conf.get('recheck_account_existence',
211
241
  DEFAULT_RECHECK_ACCOUNT_EXISTENCE))
242
+ self.container_existence_skip_cache = config_percent_value(
243
+ conf.get('container_existence_skip_cache_pct', 0))
244
+ self.container_updating_shard_ranges_skip_cache = \
245
+ config_percent_value(conf.get(
246
+ 'container_updating_shard_ranges_skip_cache_pct', 0))
247
+ self.container_listing_shard_ranges_skip_cache = \
248
+ config_percent_value(conf.get(
249
+ 'container_listing_shard_ranges_skip_cache_pct', 0))
250
+ self.account_existence_skip_cache = config_percent_value(
251
+ conf.get('account_existence_skip_cache_pct', 0))
212
252
  self.allow_account_management = \
213
253
  config_true_value(conf.get('allow_account_management', 'no'))
214
254
  self.container_ring = container_ring or Ring(swift_dir,
@@ -219,17 +259,14 @@ class Application(object):
219
259
  for policy in POLICIES:
220
260
  policy.load_ring(swift_dir)
221
261
  self.obj_controller_router = ObjectControllerRouter()
222
- self.memcache = memcache
223
262
  mimetypes.init(mimetypes.knownfiles +
224
263
  [os.path.join(swift_dir, 'mime.types')])
225
264
  self.account_autocreate = \
226
265
  config_true_value(conf.get('account_autocreate', 'no'))
227
- self.auto_create_account_prefix = (
228
- conf.get('auto_create_account_prefix') or '.')
229
- self.expiring_objects_account = self.auto_create_account_prefix + \
230
- (conf.get('expiring_objects_account_name') or 'expiring_objects')
231
- self.expiring_objects_container_divisor = \
232
- int(conf.get('expiring_objects_container_divisor') or 86400)
266
+ self.auto_create_account_prefix = \
267
+ constraints.AUTO_CREATE_ACCOUNT_PREFIX
268
+ self.expirer_config = expirer.ExpirerConfig(
269
+ conf, container_ring=self.container_ring, logger=self.logger)
233
270
  self.max_containers_per_account = \
234
271
  int(conf.get('max_containers_per_account') or 0)
235
272
  self.max_containers_whitelist = [
@@ -250,21 +287,12 @@ class Application(object):
250
287
  if a.strip()]
251
288
  self.strict_cors_mode = config_true_value(
252
289
  conf.get('strict_cors_mode', 't'))
290
+ self.allow_open_expired = config_true_value(
291
+ conf.get('allow_open_expired', 'f'))
253
292
  self.node_timings = {}
254
293
  self.timing_expiry = int(conf.get('timing_expiry', 300))
255
- self.concurrent_gets = config_true_value(conf.get('concurrent_gets'))
256
- self.concurrency_timeout = float(conf.get('concurrency_timeout',
257
- self.conn_timeout))
258
- value = conf.get('request_node_count', '2 * replicas').lower().split()
259
- if len(value) == 1:
260
- rnc_value = int(value[0])
261
- self.request_node_count = lambda replicas: rnc_value
262
- elif len(value) == 3 and value[1] == '*' and value[2] == 'replicas':
263
- rnc_value = int(value[0])
264
- self.request_node_count = lambda replicas: rnc_value * replicas
265
- else:
266
- raise ValueError(
267
- 'Invalid request_node_count value: %r' % ''.join(value))
294
+ value = conf.get('request_node_count', '2 * replicas')
295
+ self.request_node_count = config_request_node_count_value(value)
268
296
  # swift_owner_headers are stripped by the account and container
269
297
  # controllers; we should extend header stripping to object controller
270
298
  # when a privileged object header is implemented.
@@ -278,6 +306,17 @@ class Application(object):
278
306
  self.swift_owner_headers = [
279
307
  name.strip().title()
280
308
  for name in swift_owner_headers.split(',') if name.strip()]
309
+
310
+ # When upgrading from liberasurecode<=1.5.0, you may want to continue
311
+ # writing legacy CRCs until all nodes are upgraded and capabale of
312
+ # reading fragments with zlib CRCs.
313
+ # See https://bugs.launchpad.net/liberasurecode/+bug/1886088 for more
314
+ # information.
315
+ if 'write_legacy_ec_crc' in conf:
316
+ os.environ['LIBERASURECODE_WRITE_LEGACY_CRC'] = \
317
+ '1' if config_true_value(conf['write_legacy_ec_crc']) else '0'
318
+ # else, assume operators know what they're doing and leave env alone
319
+
281
320
  # Initialization was successful, so now apply the client chunk size
282
321
  # parameter as the default read / write buffer size for the network
283
322
  # sockets.
@@ -296,20 +335,30 @@ class Application(object):
296
335
  self.expose_info = config_true_value(
297
336
  conf.get('expose_info', 'yes'))
298
337
  self.disallowed_sections = list_from_csv(
299
- conf.get('disallowed_sections', 'swift.valid_api_versions'))
338
+ conf.get('disallowed_sections', ', '.join([
339
+ 'swift.auto_create_account_prefix',
340
+ 'swift.valid_api_versions',
341
+ ])))
300
342
  self.admin_key = conf.get('admin_key', None)
343
+ self._override_options = self._load_per_policy_config(conf)
344
+ self.sorts_by_timing = any(pc.sorting_method == 'timing'
345
+ for pc in self._override_options.values())
346
+
301
347
  register_swift_info(
302
348
  version=swift_version,
303
349
  strict_cors_mode=self.strict_cors_mode,
304
350
  policies=POLICIES.get_policy_info(),
305
351
  allow_account_management=self.allow_account_management,
306
352
  account_autocreate=self.account_autocreate,
353
+ allow_open_expired=self.allow_open_expired,
307
354
  **constraints.EFFECTIVE_CONSTRAINTS)
355
+ self.watchdog = Watchdog()
356
+ self.watchdog.spawn()
308
357
 
309
358
  def _make_policy_override(self, policy, conf, override_conf):
310
359
  label_for_policy = _label_for_policy(policy)
311
360
  try:
312
- override = ProxyOverrideOptions(conf, override_conf)
361
+ override = ProxyOverrideOptions(conf, override_conf, self)
313
362
  self.logger.debug("Loaded override config for %s: %r" %
314
363
  (label_for_policy, override))
315
364
  return override
@@ -367,8 +416,8 @@ class Application(object):
367
416
  else POLICIES.get_by_index(policy_idx))
368
417
  if options.read_affinity and options.sorting_method != 'affinity':
369
418
  self.logger.warning(
370
- _("sorting_method is set to '%(method)s', not 'affinity'; "
371
- "%(label)s read_affinity setting will have no effect."),
419
+ "sorting_method is set to '%(method)s', not 'affinity'; "
420
+ "%(label)s read_affinity setting will have no effect.",
372
421
  {'label': _label_for_policy(policy),
373
422
  'method': options.sorting_method})
374
423
 
@@ -440,8 +489,6 @@ class Application(object):
440
489
  :param start_response: WSGI callable
441
490
  """
442
491
  try:
443
- if self.memcache is None:
444
- self.memcache = cache_from_env(env, True)
445
492
  req = self.update_request(Request(env))
446
493
  return self.handle_request(req)(env, start_response)
447
494
  except UnicodeError:
@@ -474,14 +521,14 @@ class Application(object):
474
521
  :param req: swob.Request object
475
522
  """
476
523
  try:
477
- self.logger.set_statsd_prefix('proxy-server')
478
524
  if req.content_length and req.content_length < 0:
479
525
  self.logger.increment('errors')
480
526
  return HTTPBadRequest(request=req,
481
527
  body='Invalid Content-Length')
482
528
 
483
529
  try:
484
- if not check_utf8(wsgi_to_str(req.path_info)):
530
+ if not check_utf8(wsgi_to_str(req.path_info),
531
+ internal=req.allow_reserved_names):
485
532
  self.logger.increment('errors')
486
533
  return HTTPPreconditionFailed(
487
534
  request=req, body='Invalid UTF8 or contains NULL')
@@ -505,8 +552,6 @@ class Application(object):
505
552
  req.host.split(':')[0] in self.deny_host_headers:
506
553
  return HTTPForbidden(request=req, body='Invalid host header')
507
554
 
508
- self.logger.set_statsd_prefix('proxy-server.' +
509
- controller.server_type.lower())
510
555
  controller = controller(self, **path_parts)
511
556
  if 'swift.trans_id' not in req.environ:
512
557
  # if this wasn't set by an earlier middleware, set it now
@@ -561,7 +606,7 @@ class Application(object):
561
606
  except HTTPException as error_response:
562
607
  return error_response
563
608
  except (Exception, Timeout):
564
- self.logger.exception(_('ERROR Unhandled exception in request'))
609
+ self.logger.exception('ERROR Unhandled exception in request')
565
610
  return HTTPServerError(request=req)
566
611
 
567
612
  def sort_nodes(self, nodes, policy=None):
@@ -597,9 +642,6 @@ class Application(object):
597
642
  timing = round(timing, 3) # sort timings to the millisecond
598
643
  self.node_timings[node['ip']] = (timing, now + self.timing_expiry)
599
644
 
600
- def _error_limit_node_key(self, node):
601
- return "{ip}:{port}/{device}".format(**node)
602
-
603
645
  def error_limited(self, node):
604
646
  """
605
647
  Check if the node is currently error limited.
@@ -607,20 +649,11 @@ class Application(object):
607
649
  :param node: dictionary of node to check
608
650
  :returns: True if error limited, False otherwise
609
651
  """
610
- now = time()
611
- node_key = self._error_limit_node_key(node)
612
- error_stats = self._error_limiting.get(node_key)
613
-
614
- if error_stats is None or 'errors' not in error_stats:
615
- return False
616
- if 'last_error' in error_stats and error_stats['last_error'] < \
617
- now - self.error_suppression_interval:
618
- self._error_limiting.pop(node_key, None)
619
- return False
620
- limited = error_stats['errors'] > self.error_suppression_limit
652
+ limited = self.error_limiter.is_limited(node)
621
653
  if limited:
654
+ self.logger.increment('error_limiter.is_limited')
622
655
  self.logger.debug(
623
- _('Node error limited %(ip)s:%(port)s (%(device)s)'), node)
656
+ 'Node is error limited: %s', node_to_string(node))
624
657
  return limited
625
658
 
626
659
  def error_limit(self, node, msg):
@@ -628,24 +661,30 @@ class Application(object):
628
661
  Mark a node as error limited. This immediately pretends the
629
662
  node received enough errors to trigger error suppression. Use
630
663
  this for errors like Insufficient Storage. For other errors
631
- use :func:`error_occurred`.
664
+ use :func:`increment`.
632
665
 
633
666
  :param node: dictionary of node to error limit
634
667
  :param msg: error message
635
668
  """
636
- node_key = self._error_limit_node_key(node)
637
- error_stats = self._error_limiting.setdefault(node_key, {})
638
- error_stats['errors'] = self.error_suppression_limit + 1
639
- error_stats['last_error'] = time()
640
- self.logger.error(_('%(msg)s %(ip)s:%(port)s/%(device)s'),
641
- {'msg': msg, 'ip': node['ip'],
642
- 'port': node['port'], 'device': node['device']})
643
-
644
- def _incr_node_errors(self, node):
645
- node_key = self._error_limit_node_key(node)
646
- error_stats = self._error_limiting.setdefault(node_key, {})
647
- error_stats['errors'] = error_stats.get('errors', 0) + 1
648
- error_stats['last_error'] = time()
669
+ self.error_limiter.limit(node)
670
+ self.logger.increment('error_limiter.forced_limit')
671
+ self.logger.error(
672
+ 'Node will be error limited for %.2fs: %s, error: %s',
673
+ self.error_limiter.suppression_interval, node_to_string(node),
674
+ msg)
675
+
676
+ def _error_increment(self, node):
677
+ """
678
+ Call increment() on error limiter once, emit metrics and log if error
679
+ suppression will be triggered.
680
+
681
+ :param node: dictionary of node to handle errors for
682
+ """
683
+ if self.error_limiter.increment(node):
684
+ self.logger.increment('error_limiter.incremented_limit')
685
+ self.logger.error(
686
+ 'Node will be error limited for %.2fs: %s',
687
+ self.error_limiter.suppression_interval, node_to_string(node))
649
688
 
650
689
  def error_occurred(self, node, msg):
651
690
  """
@@ -654,16 +693,49 @@ class Application(object):
654
693
  :param node: dictionary of node to handle errors for
655
694
  :param msg: error message
656
695
  """
657
- self._incr_node_errors(node)
658
696
  if isinstance(msg, bytes):
659
697
  msg = msg.decode('utf-8')
660
- self.logger.error(_('%(msg)s %(ip)s:%(port)s/%(device)s'),
661
- {'msg': msg, 'ip': node['ip'],
662
- 'port': node['port'], 'device': node['device']})
698
+ self.logger.error('%(msg)s %(node)s',
699
+ {'msg': msg, 'node': node_to_string(node)})
700
+ self._error_increment(node)
663
701
 
664
- def iter_nodes(self, ring, partition, node_iter=None, policy=None):
665
- return NodeIter(self, ring, partition, node_iter=node_iter,
666
- policy=policy)
702
+ def check_response(self, node, server_type, response, method, path,
703
+ body=None):
704
+ """
705
+ Check response for error status codes and update error limiters as
706
+ required.
707
+
708
+ :param node: a dict describing a node
709
+ :param server_type: the type of server from which the response was
710
+ received (e.g. 'Object').
711
+ :param response: an instance of HTTPResponse.
712
+ :param method: the request method.
713
+ :param path: the request path.
714
+ :param body: an optional response body. If given, up to 1024 of the
715
+ start of the body will be included in any log message.
716
+ :return True: if the response status code is less than 500, False
717
+ otherwise.
718
+ """
719
+ ok = False
720
+ if response.status == HTTP_INSUFFICIENT_STORAGE:
721
+ self.error_limit(node, 'ERROR Insufficient Storage')
722
+ elif is_server_error(response.status):
723
+ values = {'status': response.status,
724
+ 'method': method,
725
+ 'path': path,
726
+ 'type': server_type}
727
+ if body is None:
728
+ fmt = 'ERROR %(status)d Trying to %(method)s ' \
729
+ '%(path)s From %(type)s Server'
730
+ else:
731
+ fmt = 'ERROR %(status)d %(body)s Trying to %(method)s ' \
732
+ '%(path)s From %(type)s Server'
733
+ values['body'] = cap_length(body, 1024)
734
+ self.error_occurred(node, fmt % values)
735
+ else:
736
+ ok = True
737
+
738
+ return ok
667
739
 
668
740
  def exception_occurred(self, node, typ, additional_info,
669
741
  **kwargs):
@@ -674,7 +746,6 @@ class Application(object):
674
746
  :param typ: server type
675
747
  :param additional_info: additional information to log
676
748
  """
677
- self._incr_node_errors(node)
678
749
  if 'level' in kwargs:
679
750
  log = functools.partial(self.logger.log, kwargs.pop('level'))
680
751
  if 'exc_info' not in kwargs:
@@ -683,12 +754,12 @@ class Application(object):
683
754
  log = self.logger.exception
684
755
  if isinstance(additional_info, bytes):
685
756
  additional_info = additional_info.decode('utf-8')
686
- log(_('ERROR with %(type)s server %(ip)s:%(port)s/%(device)s'
687
- ' re: %(info)s'),
688
- {'type': typ, 'ip': node['ip'],
689
- 'port': node['port'], 'device': node['device'],
757
+ log('ERROR with %(type)s server %(node)s'
758
+ ' re: %(info)s',
759
+ {'type': typ, 'node': node_to_string(node),
690
760
  'info': additional_info},
691
761
  **kwargs)
762
+ self._error_increment(node)
692
763
 
693
764
  def modify_wsgi_pipeline(self, pipe):
694
765
  """
@@ -709,18 +780,18 @@ class Application(object):
709
780
  except ValueError: # not in pipeline; ignore it
710
781
  pass
711
782
  self.logger.info(
712
- _('Adding required filter %(filter_name)s to pipeline at '
713
- 'position %(insert_at)d'),
783
+ 'Adding required filter %(filter_name)s to pipeline at '
784
+ 'position %(insert_at)d',
714
785
  {'filter_name': filter_name, 'insert_at': insert_at})
715
786
  ctx = pipe.create_filter(filter_name)
716
787
  pipe.insert_filter(ctx, index=insert_at)
717
788
  pipeline_was_modified = True
718
789
 
719
790
  if pipeline_was_modified:
720
- self.logger.info(_("Pipeline was modified. "
721
- "New pipeline is \"%s\"."), pipe)
791
+ self.logger.info("Pipeline was modified. "
792
+ "New pipeline is \"%s\".", pipe)
722
793
  else:
723
- self.logger.debug(_("Pipeline is \"%s\""), pipe)
794
+ self.logger.debug("Pipeline is \"%s\"", pipe)
724
795
 
725
796
 
726
797
  def parse_per_policy_config(conf):
@@ -733,15 +804,8 @@ def parse_per_policy_config(conf):
733
804
  :return: a dict mapping policy reference -> dict of policy options
734
805
  :raises ValueError: if a policy config section has an invalid name
735
806
  """
736
- policy_config = {}
737
- all_conf = readconf(conf['__file__'])
738
807
  policy_section_prefix = conf['__name__'] + ':policy:'
739
- for section, options in all_conf.items():
740
- if not section.startswith(policy_section_prefix):
741
- continue
742
- policy_ref = section[len(policy_section_prefix):]
743
- policy_config[policy_ref] = options
744
- return policy_config
808
+ return parse_prefixed_conf(conf['__file__'], policy_section_prefix)
745
809
 
746
810
 
747
811
  def app_factory(global_conf, **local_conf):
@@ -755,3 +819,12 @@ def app_factory(global_conf, **local_conf):
755
819
  app = Application(conf)
756
820
  app.check_config()
757
821
  return app
822
+
823
+
824
+ def main():
825
+ conf_file, options = parse_options(test_config=True)
826
+ sys.exit(run_wsgi(conf_file, 'proxy-server', **options))
827
+
828
+
829
+ if __name__ == '__main__':
830
+ main()