swift 2.33.0__py2.py3-none-any.whl → 2.34.0__py2.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 (112) hide show
  1. swift/account/auditor.py +11 -0
  2. swift/account/reaper.py +11 -1
  3. swift/account/replicator.py +22 -0
  4. swift/account/server.py +12 -1
  5. swift-2.33.0.data/scripts/swift-account-audit → swift/cli/account_audit.py +6 -2
  6. swift-2.33.0.data/scripts/swift-config → swift/cli/config.py +1 -1
  7. swift-2.33.0.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +6 -2
  8. swift-2.33.0.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +12 -3
  9. swift-2.33.0.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +6 -2
  10. swift/cli/info.py +103 -2
  11. swift-2.33.0.data/scripts/swift-oldies → swift/cli/oldies.py +6 -3
  12. swift-2.33.0.data/scripts/swift-orphans → swift/cli/orphans.py +7 -2
  13. swift/cli/recon_cron.py +5 -5
  14. swift-2.33.0.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  15. swift/cli/relinker.py +1 -1
  16. swift/cli/ringbuilder.py +24 -0
  17. swift/common/db.py +2 -1
  18. swift/common/db_auditor.py +2 -2
  19. swift/common/db_replicator.py +6 -0
  20. swift/common/exceptions.py +12 -0
  21. swift/common/manager.py +102 -0
  22. swift/common/memcached.py +6 -13
  23. swift/common/middleware/account_quotas.py +144 -43
  24. swift/common/middleware/backend_ratelimit.py +166 -24
  25. swift/common/middleware/catch_errors.py +1 -3
  26. swift/common/middleware/cname_lookup.py +3 -5
  27. swift/common/middleware/container_sync.py +6 -10
  28. swift/common/middleware/crypto/crypto_utils.py +4 -5
  29. swift/common/middleware/crypto/decrypter.py +4 -5
  30. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  31. swift/common/middleware/proxy_logging.py +22 -16
  32. swift/common/middleware/ratelimit.py +6 -7
  33. swift/common/middleware/recon.py +6 -7
  34. swift/common/middleware/s3api/acl_handlers.py +9 -0
  35. swift/common/middleware/s3api/controllers/multi_upload.py +1 -9
  36. swift/common/middleware/s3api/controllers/obj.py +20 -1
  37. swift/common/middleware/s3api/s3api.py +2 -0
  38. swift/common/middleware/s3api/s3request.py +171 -62
  39. swift/common/middleware/s3api/s3response.py +35 -6
  40. swift/common/middleware/s3api/s3token.py +2 -2
  41. swift/common/middleware/s3api/utils.py +1 -0
  42. swift/common/middleware/slo.py +153 -52
  43. swift/common/middleware/tempauth.py +6 -4
  44. swift/common/middleware/tempurl.py +2 -2
  45. swift/common/middleware/x_profile/exceptions.py +1 -4
  46. swift/common/middleware/x_profile/html_viewer.py +9 -10
  47. swift/common/middleware/x_profile/profile_model.py +1 -2
  48. swift/common/middleware/xprofile.py +1 -2
  49. swift/common/request_helpers.py +69 -0
  50. swift/common/statsd_client.py +207 -0
  51. swift/common/utils/__init__.py +97 -1635
  52. swift/common/utils/base.py +138 -0
  53. swift/common/utils/config.py +443 -0
  54. swift/common/utils/logs.py +999 -0
  55. swift/common/wsgi.py +11 -3
  56. swift/container/auditor.py +11 -0
  57. swift/container/backend.py +10 -10
  58. swift/container/reconciler.py +11 -2
  59. swift/container/replicator.py +22 -1
  60. swift/container/server.py +12 -1
  61. swift/container/sharder.py +36 -12
  62. swift/container/sync.py +11 -1
  63. swift/container/updater.py +11 -2
  64. swift/obj/auditor.py +18 -2
  65. swift/obj/diskfile.py +8 -6
  66. swift/obj/expirer.py +155 -36
  67. swift/obj/reconstructor.py +28 -4
  68. swift/obj/replicator.py +61 -22
  69. swift/obj/server.py +64 -36
  70. swift/obj/updater.py +11 -2
  71. swift/proxy/controllers/base.py +38 -22
  72. swift/proxy/controllers/obj.py +23 -26
  73. swift/proxy/server.py +15 -1
  74. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/AUTHORS +11 -3
  75. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/METADATA +6 -5
  76. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/RECORD +81 -107
  77. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/entry_points.txt +38 -0
  78. swift-2.34.0.dist-info/pbr.json +1 -0
  79. swift-2.33.0.data/scripts/swift-account-auditor +0 -23
  80. swift-2.33.0.data/scripts/swift-account-info +0 -52
  81. swift-2.33.0.data/scripts/swift-account-reaper +0 -23
  82. swift-2.33.0.data/scripts/swift-account-replicator +0 -34
  83. swift-2.33.0.data/scripts/swift-account-server +0 -23
  84. swift-2.33.0.data/scripts/swift-container-auditor +0 -23
  85. swift-2.33.0.data/scripts/swift-container-info +0 -59
  86. swift-2.33.0.data/scripts/swift-container-reconciler +0 -21
  87. swift-2.33.0.data/scripts/swift-container-replicator +0 -34
  88. swift-2.33.0.data/scripts/swift-container-server +0 -23
  89. swift-2.33.0.data/scripts/swift-container-sharder +0 -37
  90. swift-2.33.0.data/scripts/swift-container-sync +0 -23
  91. swift-2.33.0.data/scripts/swift-container-updater +0 -23
  92. swift-2.33.0.data/scripts/swift-dispersion-report +0 -24
  93. swift-2.33.0.data/scripts/swift-form-signature +0 -20
  94. swift-2.33.0.data/scripts/swift-init +0 -119
  95. swift-2.33.0.data/scripts/swift-object-auditor +0 -29
  96. swift-2.33.0.data/scripts/swift-object-expirer +0 -33
  97. swift-2.33.0.data/scripts/swift-object-info +0 -60
  98. swift-2.33.0.data/scripts/swift-object-reconstructor +0 -33
  99. swift-2.33.0.data/scripts/swift-object-relinker +0 -23
  100. swift-2.33.0.data/scripts/swift-object-replicator +0 -37
  101. swift-2.33.0.data/scripts/swift-object-server +0 -27
  102. swift-2.33.0.data/scripts/swift-object-updater +0 -23
  103. swift-2.33.0.data/scripts/swift-proxy-server +0 -23
  104. swift-2.33.0.data/scripts/swift-recon +0 -24
  105. swift-2.33.0.data/scripts/swift-recon-cron +0 -24
  106. swift-2.33.0.data/scripts/swift-ring-builder +0 -37
  107. swift-2.33.0.data/scripts/swift-ring-builder-analyzer +0 -22
  108. swift-2.33.0.data/scripts/swift-ring-composer +0 -22
  109. swift-2.33.0.dist-info/pbr.json +0 -1
  110. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/LICENSE +0 -0
  111. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/WHEEL +0 -0
  112. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/top_level.txt +0 -0
@@ -270,11 +270,27 @@ A GET request with the query parameters::
270
270
  will return the contents of the original manifest as it was sent by the client.
271
271
  The main purpose for both calls is solely debugging.
272
272
 
273
- When the manifest object is uploaded you are more or less guaranteed that
274
- every segment in the manifest exists and matched the specifications.
275
- However, there is nothing that prevents the user from breaking the
276
- SLO download by deleting/replacing a segment referenced in the manifest. It is
277
- left to the user to use caution in handling the segments.
273
+ A GET request to a manifest object with the query parameter::
274
+
275
+ ?part-number=<n>
276
+
277
+ will return the contents of the ``nth`` segment. Segments are indexed from 1,
278
+ so ``n`` must be an integer between 1 and the total number of segments in the
279
+ manifest. The response status will be ``206 Partial Content`` and its headers
280
+ will include: an ``X-Parts-Count`` header equal to the total number of
281
+ segments; a ``Content-Length`` header equal to the length of the specified
282
+ segment; a ``Content-Range`` header describing the byte range of the specified
283
+ part within the SLO. A HEAD request with a ``part-number`` parameter will also
284
+ return a response with status ``206 Partial Content`` and the same headers.
285
+
286
+ .. note::
287
+
288
+ When the manifest object is uploaded you are more or less guaranteed that
289
+ every segment in the manifest exists and matched the specifications.
290
+ However, there is nothing that prevents the user from breaking the SLO
291
+ download by deleting/replacing a segment referenced in the manifest. It is
292
+ left to the user to use caution in handling the segments.
293
+
278
294
 
279
295
  -----------------------
280
296
  Deleting a Large Object
@@ -353,7 +369,7 @@ from swift.common.registry import register_swift_info
353
369
  from swift.common.request_helpers import SegmentedIterable, \
354
370
  get_sys_meta_prefix, update_etag_is_at_header, resolve_etag_is_at_header, \
355
371
  get_container_update_override_key, update_ignore_range_header, \
356
- get_param
372
+ get_param, get_valid_part_num
357
373
  from swift.common.constraints import check_utf8, AUTO_CREATE_ACCOUNT_PREFIX
358
374
  from swift.common.http import HTTP_NOT_FOUND, HTTP_UNAUTHORIZED
359
375
  from swift.common.wsgi import WSGIContext, make_subrequest, make_env, \
@@ -564,6 +580,60 @@ def _annotate_segments(segments, logger=None):
564
580
  seg_dict['segment_length'] = segment_length
565
581
 
566
582
 
583
+ def calculate_byterange_for_part_num(req, segments, part_num):
584
+ """
585
+ Helper function to calculate the byterange for a part_num response.
586
+
587
+ N.B. as a side-effect of calculating the single tuple representing the
588
+ byterange required for a part_num response this function will also mutate
589
+ the request's Range header so that swob knows to return 206.
590
+
591
+ :param req: the request object
592
+ :param segments: the list of seg_dicts
593
+ :param part_num: the part number of the object to return
594
+
595
+ :returns: a tuple representing the byterange
596
+ """
597
+ start = 0
598
+ for seg in segments[:part_num - 1]:
599
+ start += seg['segment_length']
600
+ last = start + segments[part_num - 1]['segment_length']
601
+ # We need to mutate the request's Range header so that swob knows to
602
+ # handle these partial content requests correctly.
603
+ req.range = "bytes=%d-%d" % (start, last - 1)
604
+ return start, last - 1
605
+
606
+
607
+ def calculate_byteranges(req, segments, resp_attrs, part_num):
608
+ """
609
+ Calculate the byteranges based on the request, segments, and part number.
610
+
611
+ N.B. as a side-effect of calculating the single tuple representing the
612
+ byterange required for a part_num response this function will also mutate
613
+ the request's Range header so that swob knows to return 206.
614
+
615
+ :param req: the request object
616
+ :param segments: the list of seg_dicts
617
+ :param resp_attrs: the slo response attributes
618
+ :param part_num: the part number of the object to return
619
+
620
+ :returns: a list of tuples representing byteranges
621
+ """
622
+ if req.range:
623
+ byteranges = [
624
+ # For some reason, swob.Range.ranges_for_length adds 1 to the
625
+ # last byte's position.
626
+ (start, end - 1) for start, end
627
+ in req.range.ranges_for_length(resp_attrs.slo_size)]
628
+ elif part_num:
629
+ byteranges = [
630
+ calculate_byterange_for_part_num(req, segments, part_num)]
631
+ else:
632
+ byteranges = [(0, resp_attrs.slo_size - 1)]
633
+
634
+ return byteranges
635
+
636
+
567
637
  class RespAttrs(object):
568
638
  """
569
639
  Encapsulate properties of a GET or HEAD response that are pertinent to
@@ -684,6 +754,9 @@ class SloGetContext(WSGIContext):
684
754
  method='GET',
685
755
  headers={'x-auth-token': req.headers.get('x-auth-token')},
686
756
  agent='%(orig)s SLO MultipartGET', swift_source='SLO')
757
+ params_copy = dict(req.params)
758
+ params_copy.pop('part-number', None)
759
+ sub_req.params = params_copy
687
760
  sub_resp = sub_req.get_response(self.slo.app)
688
761
 
689
762
  if not sub_resp.is_success:
@@ -847,8 +920,7 @@ class SloGetContext(WSGIContext):
847
920
  # we can avoid re-fetching the object.
848
921
  return first_byte == 0 and last_byte == length - 1
849
922
 
850
- def _is_manifest_and_need_to_refetch(self, req, resp_attrs,
851
- is_manifest_get):
923
+ def _need_to_refetch_manifest(self, req, resp_attrs, is_part_num_request):
852
924
  """
853
925
  Check if the segments will be needed to service the request and update
854
926
  the segment_listing_needed attribute.
@@ -856,19 +928,11 @@ class SloGetContext(WSGIContext):
856
928
  :return: boolean indicating if we need to refetch, only if the segments
857
929
  ARE needed we MAY need to refetch them!
858
930
  """
859
- if not resp_attrs.is_slo:
860
- # Not a static large object manifest, maybe an error, regardless
861
- # no refetch needed
862
- return False
863
-
864
- if is_manifest_get:
865
- # Any manifest json object response will do
866
- return False
867
-
868
931
  if req.method == 'HEAD':
869
932
  # There may be some cases in the future where a HEAD resp on even a
870
933
  # modern manifest should refetch, e.g. lp bug #2029174
871
- self.segment_listing_needed = resp_attrs.is_legacy
934
+ self.segment_listing_needed = (resp_attrs.is_legacy or
935
+ is_part_num_request)
872
936
  # it will always be the case that a HEAD must re-fetch iff
873
937
  # segment_listing_needed
874
938
  return self.segment_listing_needed
@@ -965,22 +1029,56 @@ class SloGetContext(WSGIContext):
965
1029
  replace_headers)
966
1030
 
967
1031
  def _return_slo_response(self, req, start_response, resp_iter, resp_attrs):
968
- if self.segment_listing_needed:
969
- # consume existing resp_iter; we'll create a new one
970
- segments = self._parse_segments(resp_iter)
971
- resp_attrs.update_from_segments(segments)
972
- if req.method == 'HEAD':
973
- resp_iter = []
974
- else:
975
- resp_iter = self._build_resp_iter(req, segments, resp_attrs)
976
1032
  headers = {
977
1033
  'Etag': '"%s"' % resp_attrs.slo_etag,
978
1034
  'X-Manifest-Etag': resp_attrs.json_md5,
979
- # This isn't correct for range requests, but swob will fix it?
1035
+ # swob will fix this for a GET with Range
980
1036
  'Content-Length': str(resp_attrs.slo_size),
981
1037
  # ignore bogus content-range, make swob figure it out
982
- 'Content-Range': None
1038
+ 'Content-Range': None,
983
1039
  }
1040
+ if self.segment_listing_needed:
1041
+ # consume existing resp_iter; we'll create a new one
1042
+ segments = self._parse_segments(resp_iter)
1043
+ resp_attrs.update_from_segments(segments)
1044
+ headers['Etag'] = '"%s"' % resp_attrs.slo_etag
1045
+ headers['Content-Length'] = str(resp_attrs.slo_size)
1046
+ part_num = get_valid_part_num(req)
1047
+ if part_num:
1048
+ headers['X-Parts-Count'] = len(segments)
1049
+
1050
+ if part_num and part_num > len(segments):
1051
+ if req.method == 'HEAD':
1052
+ resp_iter = []
1053
+ headers['Content-Length'] = '0'
1054
+ else:
1055
+ body = b'The requested part number is not satisfiable'
1056
+ resp_iter = [body]
1057
+ headers['Content-Length'] = len(body)
1058
+ headers['Content-Range'] = 'bytes */%d' % resp_attrs.slo_size
1059
+ self._response_status = '416 Requested Range Not Satisfiable'
1060
+ elif part_num and req.method == 'HEAD':
1061
+ resp_iter = []
1062
+ headers['Content-Length'] = \
1063
+ segments[part_num - 1].get('segment_length')
1064
+ start, end = calculate_byterange_for_part_num(
1065
+ req, segments, part_num)
1066
+ headers['Content-Range'] = \
1067
+ 'bytes {}-{}/{}'.format(start, end,
1068
+ resp_attrs.slo_size)
1069
+ # The RFC specifies 206 in the context of Range requests, and
1070
+ # Range headers MUST be ignored for HEADs [1], so a HEAD will
1071
+ # not normally return a 206. However, a part-number HEAD
1072
+ # returns Content-Length equal to the part size, rather than
1073
+ # the whole object size, so in this case we do return 206.
1074
+ # [1] https://www.rfc-editor.org/rfc/rfc9110#name-range
1075
+ self._response_status = '206 Partial Content'
1076
+ elif req.method == 'HEAD':
1077
+ resp_iter = []
1078
+ else:
1079
+ byteranges = calculate_byteranges(
1080
+ req, segments, resp_attrs, part_num)
1081
+ resp_iter = self._build_resp_iter(req, segments, byteranges)
984
1082
  return self._return_response(req, start_response, resp_iter,
985
1083
  replace_headers=headers)
986
1084
 
@@ -1046,21 +1144,32 @@ class SloGetContext(WSGIContext):
1046
1144
  update_ignore_range_header(req, 'X-Static-Large-Object')
1047
1145
 
1048
1146
  # process original request
1147
+ orig_path_info = req.path_info
1049
1148
  resp_iter = self._app_call(req.environ)
1050
1149
  resp_attrs = RespAttrs.from_headers(self._response_headers)
1051
- # the next two calls hide a couple side-effects, sorry:
1052
- #
1053
- # 1) regardless of the return value the "need_to_refetch" check *may*
1054
- # also set self.segment_listing_needed = True (it's commented to
1055
- # help you wrap your head around that one, good luck)
1056
- # 2) if we refetch, we overwrite the current resp_iter and resp_attrs
1057
- # variables, partly because we *might* get back a NOT
1058
- # resp_attrs.is_slo response (even if we had one to start), but
1059
- # hopefully they're just the manifest resp we needed to refetch!
1060
- if self._is_manifest_and_need_to_refetch(req, resp_attrs,
1061
- is_manifest_get):
1062
- resp_attrs, resp_iter = self._refetch_manifest(
1063
- req, resp_iter, resp_attrs)
1150
+ if resp_attrs.is_slo and not is_manifest_get:
1151
+ try:
1152
+ # only validate part-number if the request is to an SLO
1153
+ part_num = get_valid_part_num(req)
1154
+ except HTTPException:
1155
+ friendly_close(resp_iter)
1156
+ raise
1157
+ # the next two calls hide a couple side effects, sorry:
1158
+ #
1159
+ # 1) regardless of the return value the "need_to_refetch" check
1160
+ # *may* also set self.segment_listing_needed = True (it's
1161
+ # commented to help you wrap your head around that one,
1162
+ # good luck)
1163
+ # 2) if we refetch, we overwrite the current resp_iter and
1164
+ # resp_attrs variables, partly because we *might* get back a NOT
1165
+ # resp_attrs.is_slo response (even if we had one to start), but
1166
+ # hopefully they're just the manifest resp we needed to refetch!
1167
+ if self._need_to_refetch_manifest(req, resp_attrs, part_num):
1168
+ # reset path in case it was modified during original request
1169
+ # (e.g. object versioning might re-write the path)
1170
+ req.path_info = orig_path_info
1171
+ resp_attrs, resp_iter = self._refetch_manifest(
1172
+ req, resp_iter, resp_attrs)
1064
1173
 
1065
1174
  if not resp_attrs.is_slo:
1066
1175
  # even if the original resp_attrs may have been SLO we may have
@@ -1115,24 +1224,16 @@ class SloGetContext(WSGIContext):
1115
1224
  raise HTTPServerError(msg)
1116
1225
  return segments
1117
1226
 
1118
- def _build_resp_iter(self, req, segments, resp_attrs):
1227
+ def _build_resp_iter(self, req, segments, byteranges):
1119
1228
  """
1120
1229
  Build a response iterable for a GET request.
1121
1230
 
1122
1231
  :param req: the request object
1123
- :param resp_attrs: the slo attributes
1232
+ :param segments: the list of seg_dicts
1233
+ :param byteranges: a list of tuples representing byteranges
1124
1234
 
1125
1235
  :returns: a segmented iterable
1126
1236
  """
1127
- if req.range:
1128
- byteranges = [
1129
- # For some reason, swob.Range.ranges_for_length adds 1 to the
1130
- # last byte's position.
1131
- (start, end - 1) for start, end
1132
- in req.range.ranges_for_length(resp_attrs.slo_size)]
1133
- else:
1134
- byteranges = [(0, resp_attrs.slo_size - 1)]
1135
-
1136
1237
  ver, account, _junk = req.split_path(3, 3, rest_with_last=True)
1137
1238
  account = wsgi_to_str(account)
1138
1239
  plain_listing_iter = self._segment_listing_iterator(
@@ -184,9 +184,11 @@ import base64
184
184
  from eventlet import Timeout
185
185
  import six
186
186
  from swift.common.memcached import MemcacheConnectionError
187
- from swift.common.swob import Response, Request, wsgi_to_str
188
- from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
189
- HTTPUnauthorized, HTTPMethodNotAllowed, HTTPServiceUnavailable
187
+ from swift.common.swob import (
188
+ Response, Request, wsgi_to_str, str_to_wsgi, wsgi_unquote,
189
+ HTTPBadRequest, HTTPForbidden, HTTPNotFound,
190
+ HTTPUnauthorized, HTTPMethodNotAllowed, HTTPServiceUnavailable,
191
+ )
190
192
 
191
193
  from swift.common.request_helpers import get_sys_meta_prefix
192
194
  from swift.common.middleware.acl import (
@@ -469,7 +471,7 @@ class TempAuth(object):
469
471
  if not s3_auth_details['check_signature'](user['key']):
470
472
  return None
471
473
  env['PATH_INFO'] = env['PATH_INFO'].replace(
472
- account_user, account_id, 1)
474
+ str_to_wsgi(account_user), wsgi_unquote(account_id), 1)
473
475
  groups = self._get_user_groups(account, account_user, account_id)
474
476
 
475
477
  return groups
@@ -255,7 +255,7 @@ This middleware understands the following configuration settings:
255
255
  incoming requests. Names may optionally end with ``*`` to
256
256
  indicate a prefix match. ``incoming_allow_headers`` is a
257
257
  list of exceptions to these removals.
258
- Default: ``x-timestamp``
258
+ Default: ``x-timestamp x-open-expired``
259
259
 
260
260
  ``incoming_allow_headers``
261
261
  A whitespace-delimited list of the headers allowed as
@@ -326,7 +326,7 @@ DISALLOWED_INCOMING_HEADERS = 'x-object-manifest x-symlink-target'
326
326
  #: delimited list of header names and names can optionally end with '*' to
327
327
  #: indicate a prefix match. DEFAULT_INCOMING_ALLOW_HEADERS is a list of
328
328
  #: exceptions to these removals.
329
- DEFAULT_INCOMING_REMOVE_HEADERS = 'x-timestamp'
329
+ DEFAULT_INCOMING_REMOVE_HEADERS = 'x-timestamp x-open-expired'
330
330
 
331
331
  #: Default headers as exceptions to DEFAULT_INCOMING_REMOVE_HEADERS. Simply a
332
332
  #: whitespace delimited list of header names and names can optionally end with
@@ -13,16 +13,13 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- from swift import gettext_ as _
17
-
18
-
19
16
  class ProfileException(Exception):
20
17
 
21
18
  def __init__(self, msg):
22
19
  self.msg = msg
23
20
 
24
21
  def __str__(self):
25
- return _('Profiling Error: %s') % self.msg
22
+ return 'Profiling Error: %s' % self.msg
26
23
 
27
24
 
28
25
  class NotFoundException(ProfileException):
@@ -19,7 +19,6 @@ import re
19
19
  import string
20
20
  import tempfile
21
21
 
22
- from swift import gettext_ as _
23
22
  from swift.common.middleware.x_profile.exceptions import PLOTLIBNotInstalled
24
23
  from swift.common.middleware.x_profile.exceptions import ODFLIBNotInstalled
25
24
  from swift.common.middleware.x_profile.exceptions import NotFoundException
@@ -307,7 +306,7 @@ class HTMLViewer(object):
307
306
  nfl_filter, download_format)
308
307
  headers.append(('Access-Control-Allow-Origin', '*'))
309
308
  else:
310
- raise MethodNotAllowed(_('method %s is not allowed.') % method)
309
+ raise MethodNotAllowed('method %s is not allowed.' % method)
311
310
  return content, headers
312
311
 
313
312
  def index_page(self, log_files=None, sort='time', limit=-1,
@@ -318,7 +317,7 @@ class HTMLViewer(object):
318
317
  try:
319
318
  stats = Stats2(*log_files)
320
319
  except (IOError, ValueError):
321
- raise DataLoadFailure(_('Can not load profile data from %s.')
320
+ raise DataLoadFailure('Can not load profile data from %s.'
322
321
  % log_files)
323
322
  if not fulldirs:
324
323
  stats.strip_dirs()
@@ -369,7 +368,7 @@ class HTMLViewer(object):
369
368
  def download(self, log_files, sort='time', limit=-1, nfl_filter='',
370
369
  output_format='default'):
371
370
  if len(log_files) == 0:
372
- raise NotFoundException(_('no log file found'))
371
+ raise NotFoundException('no log file found')
373
372
  try:
374
373
  nfl_esc = nfl_filter.replace(r'(', r'\(').replace(r')', r'\)')
375
374
  # remove the slash that is intentionally added in the URL
@@ -392,14 +391,14 @@ class HTMLViewer(object):
392
391
  except ODFLIBNotInstalled:
393
392
  raise
394
393
  except Exception as ex:
395
- raise ProfileException(_('Data download error: %s') % ex)
394
+ raise ProfileException('Data download error: %s' % ex)
396
395
 
397
396
  def plot(self, log_files, sort='time', limit=10, nfl_filter='',
398
397
  metric_selected='cc', plot_type='bar'):
399
398
  if not PLOTLIB_INSTALLED:
400
- raise PLOTLIBNotInstalled(_('python-matplotlib not installed.'))
399
+ raise PLOTLIBNotInstalled('python-matplotlib not installed.')
401
400
  if len(log_files) == 0:
402
- raise NotFoundException(_('no log file found'))
401
+ raise NotFoundException('no log file found')
403
402
  try:
404
403
  stats = Stats2(*log_files)
405
404
  stats.sort_stats(sort)
@@ -433,7 +432,7 @@ class HTMLViewer(object):
433
432
  data = profile_img.read()
434
433
  return data, [('content-type', 'image/jpg')]
435
434
  except Exception as ex:
436
- raise ProfileException(_('plotting results failed due to %s') % ex)
435
+ raise ProfileException('plotting results failed due to %s' % ex)
437
436
 
438
437
  def format_source_code(self, nfl):
439
438
  nfls = re.split('[:()]', nfl)
@@ -444,7 +443,7 @@ class HTMLViewer(object):
444
443
  lineno = 0
445
444
  # for security reason, this need to be fixed.
446
445
  if not file_path.endswith('.py'):
447
- return _('The file type are forbidden to access!')
446
+ return 'The file type are forbidden to access!'
448
447
  try:
449
448
  data = []
450
449
  i = 0
@@ -465,7 +464,7 @@ class HTMLViewer(object):
465
464
  data.append(fmt % (i, i, i, el))
466
465
  data = ''.join(data)
467
466
  except Exception:
468
- return _('Can not access the file %s.') % file_path
467
+ return 'Can not access the file %s.' % file_path
469
468
  return '<pre>%s</pre>' % data
470
469
 
471
470
  def generate_stats_html(self, stats, app_path, profile_id, *selection):
@@ -20,7 +20,6 @@ import pstats
20
20
  import tempfile
21
21
  import time
22
22
 
23
- from swift import gettext_ as _
24
23
  from swift.common.middleware.x_profile.exceptions import ODFLIBNotInstalled
25
24
 
26
25
 
@@ -125,7 +124,7 @@ class Stats2(pstats.Stats):
125
124
 
126
125
  def to_ods(self, *selection):
127
126
  if not ODFLIB_INSTALLED:
128
- raise ODFLIBNotInstalled(_('odfpy not installed.'))
127
+ raise ODFLIBNotInstalled('odfpy not installed.')
129
128
  if self.fcn_list:
130
129
  stat_list = self.fcn_list[:]
131
130
  order_text = " Ordered by: " + self.sort_type + '\n'
@@ -83,7 +83,6 @@ import eventlet.green.profile as eprofile
83
83
  import six
84
84
  from six.moves import urllib
85
85
 
86
- from swift import gettext_ as _
87
86
  from swift.common.utils import get_logger, config_true_value
88
87
  from swift.common.swob import Request
89
88
  from swift.common.middleware.x_profile.exceptions import MethodNotAllowed
@@ -227,7 +226,7 @@ class ProfileMiddleware(object):
227
226
  return '%s' % pf
228
227
  except Exception as ex:
229
228
  start_response('500 Internal Server Error', [])
230
- return _('Error on render profiling results: %s') % ex
229
+ return 'Error on render profiling results: %s' % ex
231
230
  else:
232
231
  _locals = locals()
233
232
  code = self.unwind and PROFILE_EXEC_EAGER or\
@@ -95,6 +95,39 @@ def get_param(req, name, default=None):
95
95
  return value
96
96
 
97
97
 
98
+ def get_valid_part_num(req):
99
+ """
100
+ Any non-range GET or HEAD request for a SLO object may include a
101
+ part-number parameter in query string. If the passed in request
102
+ includes a part-number parameter it will be parsed into a valid integer
103
+ and returned. If the passed in request does not include a part-number
104
+ param we will return None. If the part-number parameter is invalid for
105
+ the given request we will raise the appropriate HTTP exception
106
+
107
+ :param req: the request object
108
+
109
+ :returns: validated part-number value or None
110
+ :raises HTTPBadRequest: if request or part-number param is not valid
111
+ """
112
+ part_number_param = get_param(req, 'part-number')
113
+ if part_number_param is None:
114
+ return None
115
+ try:
116
+ part_number = int(part_number_param)
117
+ if part_number <= 0:
118
+ raise ValueError
119
+ except ValueError:
120
+ raise HTTPBadRequest('Part number must be an integer greater '
121
+ 'than 0')
122
+
123
+ if req.range:
124
+ raise HTTPBadRequest(req=req,
125
+ body='Range requests are not supported '
126
+ 'with part number queries')
127
+
128
+ return part_number
129
+
130
+
98
131
  def validate_params(req, names):
99
132
  """
100
133
  Get list of parameters from an HTTP request, validating the encoding of
@@ -960,3 +993,39 @@ def get_ip_port(node, headers):
960
993
  """
961
994
  return select_ip_port(
962
995
  node, use_replication=is_use_replication_network(headers))
996
+
997
+
998
+ def is_open_expired(app, req):
999
+ """
1000
+ Helper function to check if a request with the header 'x-open-expired'
1001
+ can access an object that has not yet been reaped by the object-expirer
1002
+ based on the allow_open_expired global config.
1003
+
1004
+ :param app: the application instance
1005
+ :param req: request object
1006
+ """
1007
+ return (config_true_value(app.allow_open_expired) and
1008
+ config_true_value(req.headers.get('x-open-expired')))
1009
+
1010
+
1011
+ def is_backend_open_expired(request):
1012
+ """
1013
+ Helper function to check if a request has either the headers
1014
+ 'x-backend-open-expired' or 'x-backend-replication' for the backend
1015
+ to access expired objects.
1016
+
1017
+ :param request: request object
1018
+ """
1019
+ x_backend_open_expired = config_true_value(request.headers.get(
1020
+ 'x-backend-open-expired', 'false'))
1021
+ x_backend_replication = config_true_value(request.headers.get(
1022
+ 'x-backend-replication', 'false'))
1023
+ return x_backend_open_expired or x_backend_replication
1024
+
1025
+
1026
+ def append_log_info(environ, log_info):
1027
+ environ.setdefault('swift.log_info', []).append(log_info)
1028
+
1029
+
1030
+ def get_log_info(environ):
1031
+ return ','.join(environ.get('swift.log_info', []))