swift 2.32.1__py2.py3-none-any.whl → 2.33.1__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 (95) hide show
  1. swift/account/server.py +1 -11
  2. swift/cli/info.py +28 -1
  3. swift-2.32.1.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +4 -13
  4. swift/cli/reload.py +141 -0
  5. swift/common/daemon.py +12 -2
  6. swift/common/db.py +12 -8
  7. swift/common/http_protocol.py +76 -3
  8. swift/common/manager.py +18 -5
  9. swift/common/memcached.py +18 -12
  10. swift/common/middleware/proxy_logging.py +35 -27
  11. swift/common/middleware/s3api/acl_handlers.py +1 -1
  12. swift/common/middleware/s3api/controllers/__init__.py +3 -0
  13. swift/common/middleware/s3api/controllers/acl.py +3 -2
  14. swift/common/middleware/s3api/controllers/logging.py +2 -2
  15. swift/common/middleware/s3api/controllers/multi_upload.py +30 -6
  16. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  17. swift/common/middleware/s3api/s3api.py +4 -0
  18. swift/common/middleware/s3api/s3request.py +19 -12
  19. swift/common/middleware/s3api/s3response.py +13 -2
  20. swift/common/middleware/s3api/utils.py +1 -1
  21. swift/common/middleware/slo.py +395 -298
  22. swift/common/middleware/staticweb.py +45 -14
  23. swift/common/middleware/tempurl.py +132 -91
  24. swift/common/request_helpers.py +32 -8
  25. swift/common/storage_policy.py +1 -1
  26. swift/common/swob.py +5 -2
  27. swift/common/utils/__init__.py +230 -135
  28. swift/common/utils/timestamp.py +23 -2
  29. swift/common/wsgi.py +8 -0
  30. swift/container/backend.py +126 -21
  31. swift/container/replicator.py +42 -6
  32. swift/container/server.py +264 -145
  33. swift/container/sharder.py +50 -30
  34. swift/container/updater.py +1 -0
  35. swift/obj/auditor.py +2 -1
  36. swift/obj/diskfile.py +55 -19
  37. swift/obj/expirer.py +1 -13
  38. swift/obj/mem_diskfile.py +2 -1
  39. swift/obj/mem_server.py +1 -0
  40. swift/obj/replicator.py +2 -2
  41. swift/obj/server.py +12 -23
  42. swift/obj/updater.py +1 -0
  43. swift/obj/watchers/dark_data.py +72 -34
  44. swift/proxy/controllers/account.py +3 -2
  45. swift/proxy/controllers/base.py +217 -127
  46. swift/proxy/controllers/container.py +274 -289
  47. swift/proxy/controllers/obj.py +98 -141
  48. swift/proxy/server.py +2 -12
  49. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-info +3 -0
  50. swift-2.33.1.data/scripts/swift-recon-cron +24 -0
  51. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/AUTHORS +3 -1
  52. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/METADATA +4 -3
  53. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/RECORD +94 -91
  54. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/WHEEL +1 -1
  55. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/entry_points.txt +1 -0
  56. swift-2.33.1.dist-info/pbr.json +1 -0
  57. swift-2.32.1.dist-info/pbr.json +0 -1
  58. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-audit +0 -0
  59. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-auditor +0 -0
  60. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-info +0 -0
  61. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-reaper +0 -0
  62. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-replicator +0 -0
  63. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-server +0 -0
  64. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-config +0 -0
  65. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-auditor +0 -0
  66. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-reconciler +0 -0
  67. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-replicator +0 -0
  68. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-server +0 -0
  69. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sharder +0 -0
  70. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sync +0 -0
  71. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-updater +0 -0
  72. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-populate +0 -0
  73. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-report +0 -0
  74. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-drive-audit +0 -0
  75. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-form-signature +0 -0
  76. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-get-nodes +0 -0
  77. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-init +0 -0
  78. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-auditor +0 -0
  79. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-expirer +0 -0
  80. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-info +0 -0
  81. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-reconstructor +0 -0
  82. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-relinker +0 -0
  83. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-replicator +0 -0
  84. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-server +0 -0
  85. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-updater +0 -0
  86. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-oldies +0 -0
  87. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-orphans +0 -0
  88. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-proxy-server +0 -0
  89. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-recon +0 -0
  90. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-reconciler-enqueue +0 -0
  91. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder +0 -0
  92. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder-analyzer +0 -0
  93. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-composer +0 -0
  94. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/LICENSE +0 -0
  95. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/top_level.txt +0 -0
@@ -27,19 +27,28 @@ The logging format implemented below is as follows::
27
27
  start_time end_time policy_index
28
28
 
29
29
  These values are space-separated, and each is url-encoded, so that they can
30
- be separated with a simple .split()
31
-
32
- * remote_addr is the contents of the REMOTE_ADDR environment variable, while
33
- client_ip is swift's best guess at the end-user IP, extracted variously
34
- from the X-Forwarded-For header, X-Cluster-Ip header, or the REMOTE_ADDR
35
- environment variable.
36
-
37
- * source (swift.source in the WSGI environment) indicates the code
30
+ be separated with a simple ``.split()``.
31
+
32
+ * ``remote_addr`` is the contents of the REMOTE_ADDR environment variable,
33
+ while ``client_ip`` is swift's best guess at the end-user IP, extracted
34
+ variously from the X-Forwarded-For header, X-Cluster-Ip header, or the
35
+ REMOTE_ADDR environment variable.
36
+
37
+ * ``status_int`` is the integer part of the ``status`` string passed to this
38
+ middleware's start_response function, unless the WSGI environment has an item
39
+ with key ``swift.proxy_logging_status``, in which case the value of that item
40
+ is used. Other middleware's may set ``swift.proxy_logging_status`` to
41
+ override the logging of ``status_int``. In either case, the logged
42
+ ``status_int`` value is forced to 499 if a client disconnect is detected
43
+ while this middleware is handling a request, or 500 if an exception is caught
44
+ while handling a request.
45
+
46
+ * ``source`` (``swift.source`` in the WSGI environment) indicates the code
38
47
  that generated the request, such as most middleware. (See below for
39
48
  more detail.)
40
49
 
41
- * log_info (swift.log_info in the WSGI environment) is for additional
42
- information that could prove quite useful, such as any x-delete-at
50
+ * ``log_info`` (``swift.log_info`` in the WSGI environment) is for additional
51
+ information that could prove quite useful, such as any ``x-delete-at``
43
52
  value or other "behind the scenes" activity that might not
44
53
  otherwise be detectable from the plain log information. Code that
45
54
  wishes to add additional log information should use code like
@@ -62,18 +71,18 @@ For example, with staticweb, the middleware might intercept a request to
62
71
  /v1/AUTH_acc/cont/, make a subrequest to the proxy to retrieve
63
72
  /v1/AUTH_acc/cont/index.html and, in effect, respond to the client's original
64
73
  request using the 2nd request's body. In this instance the subrequest will be
65
- logged by the rightmost middleware (with a swift.source set) and the outgoing
66
- request (with body overridden) will be logged by leftmost middleware.
74
+ logged by the rightmost middleware (with a ``swift.source`` set) and the
75
+ outgoing request (with body overridden) will be logged by leftmost middleware.
67
76
 
68
77
  Requests that follow the normal pipeline (use the same wsgi environment
69
78
  throughout) will not be double logged because an environment variable
70
- (swift.proxy_access_log_made) is checked/set when a log is made.
79
+ (``swift.proxy_access_log_made``) is checked/set when a log is made.
71
80
 
72
- All middleware making subrequests should take care to set swift.source when
81
+ All middleware making subrequests should take care to set ``swift.source`` when
73
82
  needed. With the doubled proxy logs, any consumer/processor of swift's proxy
74
- logs should look at the swift.source field, the rightmost log value, to decide
75
- if this is a middleware subrequest or not. A log processor calculating
76
- bandwidth usage will want to only sum up logs with no swift.source.
83
+ logs should look at the ``swift.source`` field, the rightmost log value, to
84
+ decide if this is a middleware subrequest or not. A log processor calculating
85
+ bandwidth usage will want to only sum up logs with no ``swift.source``.
77
86
  """
78
87
 
79
88
  import os
@@ -389,11 +398,11 @@ class ProxyLoggingMiddleware(object):
389
398
  def my_start_response(status, headers, exc_info=None):
390
399
  start_response_args[0] = (status, list(headers), exc_info)
391
400
 
392
- def status_int_for_logging(start_status, client_disconnect=False):
401
+ def status_int_for_logging():
393
402
  # log disconnected clients as '499' status code
394
- if client_disconnect or input_proxy.client_disconnect:
403
+ if input_proxy.client_disconnect:
395
404
  return 499
396
- return start_status
405
+ return env.get('swift.proxy_logging_status')
397
406
 
398
407
  def iter_response(iterable):
399
408
  iterator = reiterate(iterable)
@@ -438,21 +447,19 @@ class ProxyLoggingMiddleware(object):
438
447
  metric_name_policy + '.first-byte.timing', ttfb * 1000)
439
448
 
440
449
  bytes_sent = 0
441
- client_disconnect = False
442
- start_status = wire_status_int
443
450
  try:
444
451
  for chunk in iterator:
445
452
  bytes_sent += len(chunk)
446
453
  yield chunk
447
454
  except GeneratorExit: # generator was closed before we finished
448
- client_disconnect = True
455
+ env['swift.proxy_logging_status'] = 499
449
456
  raise
450
457
  except Exception:
451
- start_status = 500
458
+ env['swift.proxy_logging_status'] = 500
452
459
  raise
453
460
  finally:
454
- status_int = status_int_for_logging(
455
- start_status, client_disconnect)
461
+ env.setdefault('swift.proxy_logging_status', wire_status_int)
462
+ status_int = status_int_for_logging()
456
463
  self.log_request(
457
464
  req, status_int, input_proxy.bytes_received, bytes_sent,
458
465
  start_time, time.time(), resp_headers=resp_headers,
@@ -463,7 +470,8 @@ class ProxyLoggingMiddleware(object):
463
470
  iterable = self.app(env, my_start_response)
464
471
  except Exception:
465
472
  req = Request(env)
466
- status_int = status_int_for_logging(500)
473
+ env['swift.proxy_logging_status'] = 500
474
+ status_int = status_int_for_logging()
467
475
  self.log_request(
468
476
  req, status_int, input_proxy.bytes_received, 0, start_time,
469
477
  time.time())
@@ -168,7 +168,7 @@ class BaseAclHandler(object):
168
168
  elem = fromstring(body, ACL.root_tag)
169
169
  acl = ACL.from_elem(
170
170
  elem, True, self.req.conf.allow_no_owner)
171
- except(XMLSyntaxError, DocumentInvalid):
171
+ except (XMLSyntaxError, DocumentInvalid):
172
172
  raise MalformedACLError()
173
173
  except Exception as e:
174
174
  self.logger.error(e)
@@ -33,6 +33,8 @@ from swift.common.middleware.s3api.controllers.versioning import \
33
33
  VersioningController
34
34
  from swift.common.middleware.s3api.controllers.tagging import \
35
35
  TaggingController
36
+ from swift.common.middleware.s3api.controllers.object_lock import \
37
+ ObjectLockController
36
38
 
37
39
  __all__ = [
38
40
  'Controller',
@@ -50,6 +52,7 @@ __all__ = [
50
52
  'LoggingStatusController',
51
53
  'VersioningController',
52
54
  'TaggingController',
55
+ 'ObjectLockController',
53
56
 
54
57
  'UnsupportedController',
55
58
  ]
@@ -19,8 +19,9 @@ from swift.common.utils import public
19
19
 
20
20
  from swift.common.middleware.s3api.exception import ACLError
21
21
  from swift.common.middleware.s3api.controllers.base import Controller
22
- from swift.common.middleware.s3api.s3response import HTTPOk, S3NotImplemented,\
23
- MalformedACLError, UnexpectedContent, MissingSecurityHeader
22
+ from swift.common.middleware.s3api.s3response import (
23
+ HTTPOk, S3NotImplemented, MalformedACLError, UnexpectedContent,
24
+ MissingSecurityHeader)
24
25
  from swift.common.middleware.s3api.etree import Element, SubElement, tostring
25
26
  from swift.common.middleware.s3api.acl_utils import swift_acl_translate, \
26
27
  XMLNS_XSI
@@ -18,8 +18,8 @@ from swift.common.utils import public
18
18
  from swift.common.middleware.s3api.controllers.base import Controller, \
19
19
  bucket_operation
20
20
  from swift.common.middleware.s3api.etree import Element, tostring
21
- from swift.common.middleware.s3api.s3response import HTTPOk, S3NotImplemented,\
22
- NoLoggingStatusForKey
21
+ from swift.common.middleware.s3api.s3response import (
22
+ HTTPOk, S3NotImplemented, NoLoggingStatusForKey)
23
23
 
24
24
 
25
25
  class LoggingStatusController(Controller):
@@ -68,8 +68,9 @@ import time
68
68
  import six
69
69
 
70
70
  from swift.common import constraints
71
- from swift.common.swob import Range, bytes_to_wsgi, normalize_etag, wsgi_to_str
72
- from swift.common.utils import json, public, reiterate, md5
71
+ from swift.common.swob import Range, bytes_to_wsgi, normalize_etag, \
72
+ wsgi_to_str
73
+ from swift.common.utils import json, public, reiterate, md5, Timestamp
73
74
  from swift.common.db import utf8encode
74
75
  from swift.common.request_helpers import get_container_update_override_key, \
75
76
  get_param
@@ -82,7 +83,7 @@ from swift.common.middleware.s3api.s3response import InvalidArgument, \
82
83
  ErrorResponse, MalformedXML, BadDigest, KeyTooLongError, \
83
84
  InvalidPart, BucketAlreadyExists, EntityTooSmall, InvalidPartOrder, \
84
85
  InvalidRequest, HTTPOk, HTTPNoContent, NoSuchKey, NoSuchUpload, \
85
- NoSuchBucket, BucketAlreadyOwnedByYou
86
+ NoSuchBucket, BucketAlreadyOwnedByYou, ServiceUnavailable
86
87
  from swift.common.middleware.s3api.utils import unique_id, \
87
88
  MULTIUPLOAD_SUFFIX, S3Timestamp, sysmeta_header
88
89
  from swift.common.middleware.s3api.etree import Element, SubElement, \
@@ -96,6 +97,17 @@ MAX_COMPLETE_UPLOAD_BODY_SIZE = 2048 * 1024
96
97
 
97
98
 
98
99
  def _get_upload_info(req, app, upload_id):
100
+ """
101
+ Make a HEAD request for existing upload object metadata. Tries the upload
102
+ marker first, and then falls back to the manifest object.
103
+
104
+ :param req: an S3Request object.
105
+ :param app: the wsgi app.
106
+ :param upload_id: the upload id.
107
+ :returns: a tuple of (S3Response, boolean) where the boolean is True if the
108
+ response is from the upload marker and False otherwise.
109
+ :raises: NoSuchUpload if neither the marker nor the manifest were found.
110
+ """
99
111
 
100
112
  container = req.container_name + MULTIUPLOAD_SUFFIX
101
113
  obj = '%s/%s' % (req.object_name, upload_id)
@@ -106,14 +118,17 @@ def _get_upload_info(req, app, upload_id):
106
118
  # it off for now...
107
119
  copy_source = req.headers.pop('X-Amz-Copy-Source', None)
108
120
  try:
109
- return req.get_response(app, 'HEAD', container=container, obj=obj)
121
+ resp = req.get_response(app, 'HEAD', container=container, obj=obj)
122
+ return resp, True
110
123
  except NoSuchKey:
124
+ # ensure consistent path and policy are logged despite manifest HEAD
111
125
  upload_marker_path = req.environ.get('s3api.backend_path')
126
+ policy_index = req.policy_index
112
127
  try:
113
128
  resp = req.get_response(app, 'HEAD')
114
129
  if resp.sysmeta_headers.get(sysmeta_header(
115
130
  'object', 'upload-id')) == upload_id:
116
- return resp
131
+ return resp, False
117
132
  except NoSuchKey:
118
133
  pass
119
134
  finally:
@@ -121,6 +136,8 @@ def _get_upload_info(req, app, upload_id):
121
136
  # path, so put it back
122
137
  if upload_marker_path is not None:
123
138
  req.environ['s3api.backend_path'] = upload_marker_path
139
+ if policy_index is not None:
140
+ req.policy_index = policy_index
124
141
  raise NoSuchUpload(upload_id=upload_id)
125
142
  finally:
126
143
  # ...making sure to restore any copy-source before returning
@@ -651,7 +668,14 @@ class UploadController(Controller):
651
668
  Handles Complete Multipart Upload.
652
669
  """
653
670
  upload_id = get_param(req, 'uploadId')
654
- resp = _get_upload_info(req, self.app, upload_id)
671
+ resp, is_marker = _get_upload_info(req, self.app, upload_id)
672
+ if (is_marker and
673
+ resp.sw_headers.get('X-Backend-Timestamp') >= Timestamp.now()):
674
+ # Somehow the marker was created in the future w.r.t. this thread's
675
+ # clock. The manifest PUT may succeed but the subsequent marker
676
+ # DELETE will fail, so don't attempt either.
677
+ raise ServiceUnavailable
678
+
655
679
  headers = {'Accept': 'application/json',
656
680
  sysmeta_header('object', 'upload-id'): upload_id}
657
681
  for key, val in resp.headers.items():
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2010-2023 OpenStack Foundation
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
+
16
+ from swift.common.utils import public
17
+
18
+ from swift.common.middleware.s3api.controllers.base import Controller, \
19
+ bucket_operation, S3NotImplemented
20
+ from swift.common.middleware.s3api.s3response import \
21
+ ObjectLockConfigurationNotFoundError
22
+
23
+
24
+ class ObjectLockController(Controller):
25
+ """
26
+ Handles GET object-lock request, which always returns
27
+ <ObjectLockEnabled>Disabled</ObjectLockEnabled>
28
+ """
29
+ @public
30
+ @bucket_operation
31
+ def GET(self, req):
32
+ """
33
+ Handles GET object-lock param calls.
34
+ """
35
+ raise ObjectLockConfigurationNotFoundError(req.container_name)
36
+
37
+ @public
38
+ @bucket_operation
39
+ def PUT(self, req):
40
+ """
41
+ Handles PUT object-lock param calls.
42
+ """
43
+ # Basically we don't support it, so return a 501
44
+ raise S3NotImplemented('The requested resource is not implemented')
@@ -366,6 +366,7 @@ class S3ApiMiddleware(object):
366
366
 
367
367
  if 's3api.backend_path' in env and 'swift.backend_path' not in env:
368
368
  env['swift.backend_path'] = env['s3api.backend_path']
369
+
369
370
  return resp(env, start_response)
370
371
 
371
372
  def handle_request(self, req):
@@ -390,6 +391,9 @@ class S3ApiMiddleware(object):
390
391
  raise MethodNotAllowed(req.method,
391
392
  req.controller.resource_type())
392
393
 
394
+ if req.policy_index is not None:
395
+ res.headers.setdefault('X-Backend-Storage-Policy-Index',
396
+ req.policy_index)
393
397
  return res
394
398
 
395
399
  def check_pipeline(self, wsgi_conf):
@@ -26,7 +26,7 @@ from six.moves.urllib.parse import quote, unquote, parse_qsl
26
26
  import string
27
27
 
28
28
  from swift.common.utils import split_path, json, close_if_possible, md5, \
29
- streq_const_time
29
+ streq_const_time, get_policy_index
30
30
  from swift.common.registry import get_swift_info
31
31
  from swift.common import swob
32
32
  from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \
@@ -47,7 +47,7 @@ from swift.common.middleware.s3api.controllers import ServiceController, \
47
47
  LocationController, LoggingStatusController, PartController, \
48
48
  UploadController, UploadsController, VersioningController, \
49
49
  UnsupportedController, S3AclController, BucketController, \
50
- TaggingController
50
+ TaggingController, ObjectLockController
51
51
  from swift.common.middleware.s3api.s3response import AccessDenied, \
52
52
  InvalidArgument, InvalidDigest, BucketAlreadyOwnedByYou, \
53
53
  RequestTimeTooSkewed, S3Response, SignatureDoesNotMatch, \
@@ -74,7 +74,8 @@ ALLOWED_SUB_RESOURCES = sorted([
74
74
  'versionId', 'versioning', 'versions', 'website',
75
75
  'response-cache-control', 'response-content-disposition',
76
76
  'response-content-encoding', 'response-content-language',
77
- 'response-content-type', 'response-expires', 'cors', 'tagging', 'restore'
77
+ 'response-content-type', 'response-expires', 'cors', 'tagging', 'restore',
78
+ 'object-lock'
78
79
  ])
79
80
 
80
81
 
@@ -103,6 +104,7 @@ def _header_acl_property(resource):
103
104
  """
104
105
  Set and retrieve the acl in self.headers
105
106
  """
107
+
106
108
  def getter(self):
107
109
  return getattr(self, '_%s' % resource)
108
110
 
@@ -121,6 +123,7 @@ class HashingInput(object):
121
123
  """
122
124
  wsgi.input wrapper to verify the hash of the input as it's read.
123
125
  """
126
+
124
127
  def __init__(self, reader, content_length, hasher, expected_hex_hash):
125
128
  self._input = reader
126
129
  self._to_read = content_length
@@ -549,6 +552,7 @@ class S3Request(swob.Request):
549
552
  }
550
553
  self.account = None
551
554
  self.user_id = None
555
+ self.policy_index = None
552
556
 
553
557
  # Avoids that swift.swob.Response replaces Location header value
554
558
  # by full URL when absolute path given. See swift.swob for more detail.
@@ -919,8 +923,6 @@ class S3Request(swob.Request):
919
923
  src_resp = self.get_response(app, 'HEAD', src_bucket,
920
924
  swob.str_to_wsgi(src_obj),
921
925
  headers=headers, query=query)
922
- # we can't let this HEAD req spoil our COPY
923
- self.headers.pop('x-backend-storage-policy-index')
924
926
  if src_resp.status_int == 304: # pylint: disable-msg=E1101
925
927
  raise PreconditionFailed()
926
928
 
@@ -1051,6 +1053,8 @@ class S3Request(swob.Request):
1051
1053
  return VersioningController
1052
1054
  if 'tagging' in self.params:
1053
1055
  return TaggingController
1056
+ if 'object-lock' in self.params:
1057
+ return ObjectLockController
1054
1058
 
1055
1059
  unsupported = ('notification', 'policy', 'requestPayment', 'torrent',
1056
1060
  'website', 'cors', 'restore')
@@ -1365,11 +1369,11 @@ class S3Request(swob.Request):
1365
1369
  2, 3, True)
1366
1370
  # Update s3.backend_path from the response environ
1367
1371
  self.environ['s3api.backend_path'] = sw_resp.environ['PATH_INFO']
1368
- # Propogate backend headers back into our req headers for logging
1369
- for k, v in sw_req.headers.items():
1370
- if k.lower().startswith('x-backend-'):
1371
- self.headers.setdefault(k, v)
1372
1372
 
1373
+ # keep a record of the backend policy index so that the s3api can add
1374
+ # it to the headers of whatever response it returns, which may not
1375
+ # necessarily be this resp.
1376
+ self.policy_index = get_policy_index(sw_req.headers, sw_resp.headers)
1373
1377
  resp = S3Response.from_swift_resp(sw_resp)
1374
1378
  status = resp.status_int # pylint: disable-msg=E1101
1375
1379
 
@@ -1428,8 +1432,10 @@ class S3Request(swob.Request):
1428
1432
  raise SlowDown(status='429 Slow Down')
1429
1433
  raise SlowDown()
1430
1434
  if resp.status_int == HTTP_CONFLICT:
1431
- # TODO: validate that this actually came up out of SLO
1432
- raise BrokenMPU()
1435
+ if self.method == 'GET':
1436
+ raise BrokenMPU()
1437
+ else:
1438
+ raise ServiceUnavailable()
1433
1439
 
1434
1440
  raise InternalError('unexpected status code %d' % status)
1435
1441
 
@@ -1497,7 +1503,7 @@ class S3Request(swob.Request):
1497
1503
 
1498
1504
  _, self.account, _ = split_path(sw_resp.environ['PATH_INFO'],
1499
1505
  2, 3, True)
1500
- sw_req = self.to_swift_req(app, self.container_name, None)
1506
+ sw_req = self.to_swift_req('TEST', self.container_name, None)
1501
1507
  info = get_container_info(sw_req.environ, app, swift_source='S3')
1502
1508
  if is_success(info['status']):
1503
1509
  return info
@@ -1536,6 +1542,7 @@ class S3AclRequest(S3Request):
1536
1542
  """
1537
1543
  S3Acl request object.
1538
1544
  """
1545
+
1539
1546
  def __init__(self, env, app=None, conf=None):
1540
1547
  super(S3AclRequest, self).__init__(env, app, conf)
1541
1548
  self.authenticate(app)
@@ -64,7 +64,7 @@ def translate_swift_to_s3(key, val):
64
64
 
65
65
  if _key.startswith('x-object-meta-'):
66
66
  return translate_meta_key(_key), val
67
- elif _key in ('content-length', 'content-type',
67
+ elif _key in ('accept-ranges', 'content-length', 'content-type',
68
68
  'content-range', 'content-encoding',
69
69
  'content-disposition', 'content-language',
70
70
  'etag', 'last-modified', 'x-robots-tag',
@@ -111,6 +111,7 @@ class S3Response(S3ResponseBase, swob.Response):
111
111
  headers instead of Swift's HeaderKeyDict. This also translates Swift
112
112
  specific headers to S3 headers.
113
113
  """
114
+
114
115
  def __init__(self, *args, **kwargs):
115
116
  swob.Response.__init__(self, *args, **kwargs)
116
117
 
@@ -239,7 +240,7 @@ class ErrorResponse(S3ResponseBase, swob.HTTPException):
239
240
  self.info = kwargs.copy()
240
241
  for reserved_key in ('headers', 'body'):
241
242
  if self.info.get(reserved_key):
242
- del(self.info[reserved_key])
243
+ del (self.info[reserved_key])
243
244
 
244
245
  swob.HTTPException.__init__(
245
246
  self, status=kwargs.pop('status', self._status),
@@ -613,6 +614,16 @@ class NoSuchKey(ErrorResponse):
613
614
  ErrorResponse.__init__(self, msg, key=key, *args, **kwargs)
614
615
 
615
616
 
617
+ class ObjectLockConfigurationNotFoundError(ErrorResponse):
618
+ _status = '404 Not found'
619
+ _msg = 'Object Lock configuration does not exist for this bucket'
620
+
621
+ def __init__(self, bucket, msg=None, *args, **kwargs):
622
+ if not bucket:
623
+ raise InternalError()
624
+ ErrorResponse.__init__(self, msg, bucket_name=bucket, *args, **kwargs)
625
+
626
+
616
627
  class NoSuchLifecycleConfiguration(ErrorResponse):
617
628
  _status = '404 Not Found'
618
629
  _msg = 'The lifecycle configuration does not exist. .'
@@ -113,7 +113,7 @@ class S3Timestamp(utils.Timestamp):
113
113
 
114
114
  @property
115
115
  def s3xmlformat(self):
116
- dt = datetime.datetime.utcfromtimestamp(self.ceil())
116
+ dt = datetime.datetime.fromtimestamp(self.ceil(), utils.UTC)
117
117
  return dt.strftime(self.S3_XML_FORMAT)
118
118
 
119
119
  @classmethod