swift 2.32.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 (127) 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 +13 -12
  5. swift-2.32.0.data/scripts/swift-account-audit → swift/cli/account_audit.py +6 -2
  6. swift-2.32.0.data/scripts/swift-config → swift/cli/config.py +1 -1
  7. swift-2.32.0.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +6 -2
  8. swift-2.32.0.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +12 -3
  9. swift-2.32.0.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +6 -2
  10. swift/cli/info.py +131 -3
  11. swift-2.32.0.data/scripts/swift-oldies → swift/cli/oldies.py +6 -3
  12. swift-2.32.0.data/scripts/swift-orphans → swift/cli/orphans.py +7 -2
  13. swift-2.32.0.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +9 -18
  14. swift-2.32.0.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  15. swift/cli/relinker.py +1 -1
  16. swift/cli/reload.py +141 -0
  17. swift/cli/ringbuilder.py +24 -0
  18. swift/common/daemon.py +12 -2
  19. swift/common/db.py +14 -9
  20. swift/common/db_auditor.py +2 -2
  21. swift/common/db_replicator.py +6 -0
  22. swift/common/exceptions.py +12 -0
  23. swift/common/http_protocol.py +76 -3
  24. swift/common/manager.py +120 -5
  25. swift/common/memcached.py +24 -25
  26. swift/common/middleware/account_quotas.py +144 -43
  27. swift/common/middleware/backend_ratelimit.py +166 -24
  28. swift/common/middleware/catch_errors.py +1 -3
  29. swift/common/middleware/cname_lookup.py +3 -5
  30. swift/common/middleware/container_sync.py +6 -10
  31. swift/common/middleware/crypto/crypto_utils.py +4 -5
  32. swift/common/middleware/crypto/decrypter.py +4 -5
  33. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  34. swift/common/middleware/proxy_logging.py +57 -43
  35. swift/common/middleware/ratelimit.py +6 -7
  36. swift/common/middleware/recon.py +6 -7
  37. swift/common/middleware/s3api/acl_handlers.py +10 -1
  38. swift/common/middleware/s3api/controllers/__init__.py +3 -0
  39. swift/common/middleware/s3api/controllers/acl.py +3 -2
  40. swift/common/middleware/s3api/controllers/logging.py +2 -2
  41. swift/common/middleware/s3api/controllers/multi_upload.py +31 -15
  42. swift/common/middleware/s3api/controllers/obj.py +20 -1
  43. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  44. swift/common/middleware/s3api/s3api.py +6 -0
  45. swift/common/middleware/s3api/s3request.py +190 -74
  46. swift/common/middleware/s3api/s3response.py +48 -8
  47. swift/common/middleware/s3api/s3token.py +2 -2
  48. swift/common/middleware/s3api/utils.py +2 -1
  49. swift/common/middleware/slo.py +508 -310
  50. swift/common/middleware/staticweb.py +45 -14
  51. swift/common/middleware/tempauth.py +6 -4
  52. swift/common/middleware/tempurl.py +134 -93
  53. swift/common/middleware/x_profile/exceptions.py +1 -4
  54. swift/common/middleware/x_profile/html_viewer.py +9 -10
  55. swift/common/middleware/x_profile/profile_model.py +1 -2
  56. swift/common/middleware/xprofile.py +1 -2
  57. swift/common/request_helpers.py +101 -8
  58. swift/common/statsd_client.py +207 -0
  59. swift/common/storage_policy.py +1 -1
  60. swift/common/swob.py +5 -2
  61. swift/common/utils/__init__.py +331 -1774
  62. swift/common/utils/base.py +138 -0
  63. swift/common/utils/config.py +443 -0
  64. swift/common/utils/logs.py +999 -0
  65. swift/common/utils/timestamp.py +23 -2
  66. swift/common/wsgi.py +19 -3
  67. swift/container/auditor.py +11 -0
  68. swift/container/backend.py +136 -31
  69. swift/container/reconciler.py +11 -2
  70. swift/container/replicator.py +64 -7
  71. swift/container/server.py +276 -146
  72. swift/container/sharder.py +86 -42
  73. swift/container/sync.py +11 -1
  74. swift/container/updater.py +12 -2
  75. swift/obj/auditor.py +20 -3
  76. swift/obj/diskfile.py +63 -25
  77. swift/obj/expirer.py +154 -47
  78. swift/obj/mem_diskfile.py +2 -1
  79. swift/obj/mem_server.py +1 -0
  80. swift/obj/reconstructor.py +28 -4
  81. swift/obj/replicator.py +63 -24
  82. swift/obj/server.py +76 -59
  83. swift/obj/updater.py +12 -2
  84. swift/obj/watchers/dark_data.py +72 -34
  85. swift/proxy/controllers/account.py +3 -2
  86. swift/proxy/controllers/base.py +254 -148
  87. swift/proxy/controllers/container.py +274 -289
  88. swift/proxy/controllers/obj.py +120 -166
  89. swift/proxy/server.py +17 -13
  90. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/AUTHORS +14 -4
  91. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/METADATA +9 -7
  92. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/RECORD +97 -120
  93. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/entry_points.txt +39 -0
  94. swift-2.34.0.dist-info/pbr.json +1 -0
  95. swift-2.32.0.data/scripts/swift-account-auditor +0 -23
  96. swift-2.32.0.data/scripts/swift-account-info +0 -52
  97. swift-2.32.0.data/scripts/swift-account-reaper +0 -23
  98. swift-2.32.0.data/scripts/swift-account-replicator +0 -34
  99. swift-2.32.0.data/scripts/swift-account-server +0 -23
  100. swift-2.32.0.data/scripts/swift-container-auditor +0 -23
  101. swift-2.32.0.data/scripts/swift-container-info +0 -56
  102. swift-2.32.0.data/scripts/swift-container-reconciler +0 -21
  103. swift-2.32.0.data/scripts/swift-container-replicator +0 -34
  104. swift-2.32.0.data/scripts/swift-container-server +0 -23
  105. swift-2.32.0.data/scripts/swift-container-sharder +0 -37
  106. swift-2.32.0.data/scripts/swift-container-sync +0 -23
  107. swift-2.32.0.data/scripts/swift-container-updater +0 -23
  108. swift-2.32.0.data/scripts/swift-dispersion-report +0 -24
  109. swift-2.32.0.data/scripts/swift-form-signature +0 -20
  110. swift-2.32.0.data/scripts/swift-init +0 -119
  111. swift-2.32.0.data/scripts/swift-object-auditor +0 -29
  112. swift-2.32.0.data/scripts/swift-object-expirer +0 -33
  113. swift-2.32.0.data/scripts/swift-object-info +0 -60
  114. swift-2.32.0.data/scripts/swift-object-reconstructor +0 -33
  115. swift-2.32.0.data/scripts/swift-object-relinker +0 -23
  116. swift-2.32.0.data/scripts/swift-object-replicator +0 -37
  117. swift-2.32.0.data/scripts/swift-object-server +0 -27
  118. swift-2.32.0.data/scripts/swift-object-updater +0 -23
  119. swift-2.32.0.data/scripts/swift-proxy-server +0 -23
  120. swift-2.32.0.data/scripts/swift-recon +0 -24
  121. swift-2.32.0.data/scripts/swift-ring-builder +0 -37
  122. swift-2.32.0.data/scripts/swift-ring-builder-analyzer +0 -22
  123. swift-2.32.0.data/scripts/swift-ring-composer +0 -22
  124. swift-2.32.0.dist-info/pbr.json +0 -1
  125. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/LICENSE +0 -0
  126. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/WHEEL +0 -0
  127. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/top_level.txt +0 -0
@@ -15,6 +15,7 @@
15
15
  import itertools
16
16
  import json
17
17
  import errno
18
+ from optparse import OptionParser
18
19
  import os
19
20
  from os.path import join
20
21
  import random
@@ -33,10 +34,10 @@ from swift.common.utils import (
33
34
  GreenAsyncPile, Timestamp, remove_file, node_to_string,
34
35
  load_recon_cache, parse_override_options, distribute_evenly,
35
36
  PrefixLoggerAdapter, remove_directory, config_request_node_count_value,
36
- non_negative_int)
37
+ non_negative_int, parse_options)
37
38
  from swift.common.header_key_dict import HeaderKeyDict
38
39
  from swift.common.bufferedhttp import http_connect
39
- from swift.common.daemon import Daemon
40
+ from swift.common.daemon import Daemon, run_daemon
40
41
  from swift.common.recon import RECON_OBJECT_FILE, DEFAULT_RECON_CACHE_PATH
41
42
  from swift.common.ring.utils import is_local_device
42
43
  from swift.obj.ssync_sender import Sender as ssync_sender
@@ -911,12 +912,14 @@ class ObjectReconstructor(Daemon):
911
912
  except StopIteration:
912
913
  break
913
914
  attempts_remaining -= 1
915
+ conn = None
914
916
  try:
915
917
  with Timeout(self.http_timeout):
916
- resp = http_connect(
918
+ conn = http_connect(
917
919
  node['replication_ip'], node['replication_port'],
918
920
  node['device'], job['partition'], 'REPLICATE',
919
- '', headers=headers).getresponse()
921
+ '', headers=headers)
922
+ resp = conn.getresponse()
920
923
  if resp.status == HTTP_INSUFFICIENT_STORAGE:
921
924
  self.logger.error(
922
925
  '%s responded as unmounted',
@@ -939,6 +942,9 @@ class ObjectReconstructor(Daemon):
939
942
  'from %r' % _full_path(
940
943
  node, job['partition'], '',
941
944
  job['policy']))
945
+ finally:
946
+ if conn:
947
+ conn.close()
942
948
  if remote_suffixes is None:
943
949
  raise SuffixSyncError('Unable to get remote suffix hashes')
944
950
 
@@ -1556,3 +1562,21 @@ class ObjectReconstructor(Daemon):
1556
1562
  self.logger.debug('reconstruction sleeping for %s seconds.',
1557
1563
  self.interval)
1558
1564
  sleep(self.interval)
1565
+
1566
+
1567
+ def main():
1568
+ parser = OptionParser("%prog CONFIG [options]")
1569
+ parser.add_option('-d', '--devices',
1570
+ help='Reconstruct only given devices. '
1571
+ 'Comma-separated list. '
1572
+ 'Only has effect if --once is used.')
1573
+ parser.add_option('-p', '--partitions',
1574
+ help='Reconstruct only given partitions. '
1575
+ 'Comma-separated list. '
1576
+ 'Only has effect if --once is used.')
1577
+ conf_file, options = parse_options(parser=parser, once=True)
1578
+ run_daemon(ObjectReconstructor, conf_file, **options)
1579
+
1580
+
1581
+ if __name__ == '__main__':
1582
+ main()
swift/obj/replicator.py CHANGED
@@ -14,6 +14,7 @@
14
14
  # limitations under the License.
15
15
 
16
16
  from collections import defaultdict
17
+ from optparse import OptionParser
17
18
  import os
18
19
  import errno
19
20
  from os.path import isdir, isfile, join, dirname
@@ -35,9 +36,9 @@ from swift.common.utils import whataremyips, unlink_older_than, \
35
36
  rsync_module_interpolation, mkdirs, config_true_value, \
36
37
  config_auto_int_value, storage_directory, \
37
38
  load_recon_cache, PrefixLoggerAdapter, parse_override_options, \
38
- distribute_evenly, listdir, node_to_string
39
+ distribute_evenly, listdir, node_to_string, parse_options
39
40
  from swift.common.bufferedhttp import http_connect
40
- from swift.common.daemon import Daemon
41
+ from swift.common.daemon import Daemon, run_daemon
41
42
  from swift.common.http import HTTP_OK, HTTP_INSUFFICIENT_STORAGE
42
43
  from swift.common.recon import RECON_OBJECT_FILE, DEFAULT_RECON_CACHE_PATH
43
44
  from swift.obj import ssync_sender
@@ -195,6 +196,12 @@ class ObjectReplicator(Daemon):
195
196
  'operation, please disable handoffs_first and '
196
197
  'handoff_delete before the next '
197
198
  'normal rebalance')
199
+ if all(self.load_object_ring(p).replica_count <= self.handoff_delete
200
+ for p in self.policies):
201
+ self.logger.warning('No storage policies found for which '
202
+ 'handoff_delete=%d would have an effect. '
203
+ 'Disabling.', self.handoff_delete)
204
+ self.handoff_delete = 0
198
205
  self.is_multiprocess_worker = None
199
206
  self._df_router = DiskFileRouter(conf, self.logger)
200
207
  self._child_process_reaper_queue = queue.LightQueue()
@@ -473,7 +480,10 @@ class ObjectReplicator(Daemon):
473
480
  node['replication_ip'], node['replication_port'],
474
481
  node['device'], job['partition'], 'REPLICATE',
475
482
  '/' + '-'.join(suffixes), headers=headers)
476
- conn.getresponse().read()
483
+ try:
484
+ conn.getresponse().read()
485
+ finally:
486
+ conn.close()
477
487
  return success, {}
478
488
 
479
489
  def ssync(self, node, job, suffixes, remote_check_objs=None):
@@ -493,7 +503,7 @@ class ObjectReplicator(Daemon):
493
503
  return False
494
504
  return True
495
505
 
496
- def update_deleted(self, job):
506
+ def revert(self, job):
497
507
  """
498
508
  High-level method that replicates a single partition that doesn't
499
509
  belong on this node.
@@ -554,7 +564,8 @@ class ObjectReplicator(Daemon):
554
564
  if self.handoff_delete:
555
565
  # delete handoff if we have had handoff_delete successes
556
566
  successes_count = len([resp for resp in responses if resp])
557
- delete_handoff = successes_count >= self.handoff_delete
567
+ delete_handoff = successes_count >= min(
568
+ self.handoff_delete, len(job['nodes']))
558
569
  else:
559
570
  # delete handoff if all syncs were successful
560
571
  delete_handoff = len(responses) == len(job['nodes']) and \
@@ -608,7 +619,8 @@ class ObjectReplicator(Daemon):
608
619
  try:
609
620
  tpool.execute(shutil.rmtree, path)
610
621
  except OSError as e:
611
- if e.errno not in (errno.ENOENT, errno.ENOTEMPTY, errno.ENODATA):
622
+ if e.errno not in (errno.ENOENT, errno.ENOTEMPTY, errno.ENODATA,
623
+ errno.EUCLEAN):
612
624
  # Don't worry if there was a race to create or delete,
613
625
  # or some disk corruption that happened after the sync
614
626
  raise
@@ -675,25 +687,30 @@ class ObjectReplicator(Daemon):
675
687
  continue
676
688
  try:
677
689
  with Timeout(self.http_timeout):
678
- resp = http_connect(
690
+ conn = http_connect(
679
691
  node['replication_ip'], node['replication_port'],
680
692
  node['device'], job['partition'], 'REPLICATE',
681
- '', headers=headers).getresponse()
682
- if resp.status == HTTP_INSUFFICIENT_STORAGE:
683
- self.logger.error('%s responded as unmounted',
684
- node_str)
685
- attempts_left += 1
686
- failure_devs_info.add((node['replication_ip'],
687
- node['device']))
688
- continue
689
- if resp.status != HTTP_OK:
690
- self.logger.error(
691
- "Invalid response %(resp)s from %(remote)s",
692
- {'resp': resp.status, 'remote': node_str})
693
- failure_devs_info.add((node['replication_ip'],
694
- node['device']))
695
- continue
696
- remote_hash = pickle.loads(resp.read())
693
+ '', headers=headers)
694
+ try:
695
+ resp = conn.getresponse()
696
+ if resp.status == HTTP_INSUFFICIENT_STORAGE:
697
+ self.logger.error('%s responded as unmounted',
698
+ node_str)
699
+ attempts_left += 1
700
+ failure_devs_info.add((node['replication_ip'],
701
+ node['device']))
702
+ continue
703
+ if resp.status != HTTP_OK:
704
+ self.logger.error(
705
+ "Invalid response %(resp)s "
706
+ "from %(remote)s",
707
+ {'resp': resp.status, 'remote': node_str})
708
+ failure_devs_info.add((node['replication_ip'],
709
+ node['device']))
710
+ continue
711
+ remote_hash = pickle.loads(resp.read())
712
+ finally:
713
+ conn.close()
697
714
  del resp
698
715
  suffixes = [suffix for suffix in local_hash if
699
716
  local_hash[suffix] !=
@@ -993,7 +1010,7 @@ class ObjectReplicator(Daemon):
993
1010
  except OSError:
994
1011
  continue
995
1012
  if job['delete']:
996
- self.run_pool.spawn(self.update_deleted, job)
1013
+ self.run_pool.spawn(self.revert, job)
997
1014
  else:
998
1015
  self.run_pool.spawn(self.update, job)
999
1016
  current_nodes = None
@@ -1140,3 +1157,25 @@ class ObjectReplicator(Daemon):
1140
1157
  # This method is called after run_once using multiple workers.
1141
1158
  update = self.aggregate_recon_update()
1142
1159
  dump_recon_cache(update, self.rcache, self.logger)
1160
+
1161
+
1162
+ def main():
1163
+ parser = OptionParser("%prog CONFIG [options]")
1164
+ parser.add_option('-d', '--devices',
1165
+ help='Replicate only given devices. '
1166
+ 'Comma-separated list. '
1167
+ 'Only has effect if --once is used.')
1168
+ parser.add_option('-p', '--partitions',
1169
+ help='Replicate only given partitions. '
1170
+ 'Comma-separated list. '
1171
+ 'Only has effect if --once is used.')
1172
+ parser.add_option('-i', '--policies',
1173
+ help='Replicate only given policy indices. '
1174
+ 'Comma-separated list. '
1175
+ 'Only has effect if --once is used.')
1176
+ conf_file, options = parse_options(parser=parser, once=True)
1177
+ run_daemon(ObjectReplicator, conf_file, **options)
1178
+
1179
+
1180
+ if __name__ == '__main__':
1181
+ main()
swift/obj/server.py CHANGED
@@ -21,6 +21,7 @@ from six.moves.urllib.parse import unquote
21
21
  import json
22
22
  import os
23
23
  import multiprocessing
24
+ import sys
24
25
  import time
25
26
  import traceback
26
27
  import socket
@@ -34,7 +35,7 @@ from swift.common.utils import public, get_logger, \
34
35
  get_expirer_container, parse_mime_headers, \
35
36
  iter_multipart_mime_documents, extract_swift_bytes, safe_json_loads, \
36
37
  config_auto_int_value, split_path, get_redirect_data, \
37
- normalize_timestamp, md5
38
+ normalize_timestamp, md5, parse_options
38
39
  from swift.common.bufferedhttp import http_connect
39
40
  from swift.common.constraints import check_object_creation, \
40
41
  valid_timestamp, check_utf8, AUTO_CREATE_ACCOUNT_PREFIX
@@ -42,7 +43,7 @@ from swift.common.exceptions import ConnectionTimeout, DiskFileQuarantined, \
42
43
  DiskFileNotExist, DiskFileCollision, DiskFileNoSpace, DiskFileDeleted, \
43
44
  DiskFileDeviceUnavailable, DiskFileExpired, ChunkReadTimeout, \
44
45
  ChunkReadError, DiskFileXattrNotSupported
45
- from swift.common.request_helpers import \
46
+ from swift.common.request_helpers import resolve_ignore_range_header, \
46
47
  OBJECT_SYSMETA_CONTAINER_UPDATE_OVERRIDE_PREFIX
47
48
  from swift.obj import ssync_receiver
48
49
  from swift.common.http import is_success, HTTP_MOVED_PERMANENTLY
@@ -50,15 +51,18 @@ from swift.common.base_storage_server import BaseStorageServer
50
51
  from swift.common.header_key_dict import HeaderKeyDict
51
52
  from swift.common.request_helpers import get_name_and_placement, \
52
53
  is_user_meta, is_sys_or_user_meta, is_object_transient_sysmeta, \
53
- resolve_etag_is_at_header, is_sys_meta, validate_internal_obj
54
+ resolve_etag_is_at_header, is_sys_meta, validate_internal_obj, \
55
+ is_backend_open_expired
54
56
  from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPCreated, \
55
57
  HTTPInternalServerError, HTTPNoContent, HTTPNotFound, \
56
58
  HTTPPreconditionFailed, HTTPRequestTimeout, HTTPUnprocessableEntity, \
57
59
  HTTPClientDisconnect, HTTPMethodNotAllowed, Request, Response, \
58
60
  HTTPInsufficientStorage, HTTPForbidden, HTTPException, HTTPConflict, \
59
61
  HTTPServerError, bytes_to_wsgi, wsgi_to_bytes, wsgi_to_str, normalize_etag
62
+ from swift.common.wsgi import run_wsgi
60
63
  from swift.obj.diskfile import RESERVED_DATAFILE_META, DiskFileRouter
61
- from swift.obj.expirer import build_task_obj
64
+ from swift.obj.expirer import build_task_obj, embed_expirer_bytes_in_ctype, \
65
+ X_DELETE_TYPE
62
66
 
63
67
 
64
68
  def iter_mime_headers_and_bodies(wsgi_input, mime_boundary, read_chunk_size):
@@ -152,6 +156,7 @@ class ObjectController(BaseStorageServer):
152
156
  config_true_value(conf.get('keep_cache_private', 'false'))
153
157
  self.keep_cache_slo_manifest = \
154
158
  config_true_value(conf.get('keep_cache_slo_manifest', 'false'))
159
+ self.cooperative_period = int(conf.get("cooperative_period", 0))
155
160
 
156
161
  default_allowed_headers = '''
157
162
  content-disposition,
@@ -173,18 +178,8 @@ class ObjectController(BaseStorageServer):
173
178
  for header in extra_allowed_headers:
174
179
  if header not in RESERVED_DATAFILE_META:
175
180
  self.allowed_headers.add(header)
176
- if conf.get('auto_create_account_prefix'):
177
- self.logger.warning('Option auto_create_account_prefix is '
178
- 'deprecated. Configure '
179
- 'auto_create_account_prefix under the '
180
- 'swift-constraints section of '
181
- 'swift.conf. This option will '
182
- 'be ignored in a future release.')
183
- self.auto_create_account_prefix = \
184
- conf['auto_create_account_prefix']
185
- else:
186
- self.auto_create_account_prefix = AUTO_CREATE_ACCOUNT_PREFIX
187
181
 
182
+ self.auto_create_account_prefix = AUTO_CREATE_ACCOUNT_PREFIX
188
183
  self.expiring_objects_account = self.auto_create_account_prefix + \
189
184
  (conf.get('expiring_objects_account_name') or 'expiring_objects')
190
185
  self.expiring_objects_container_divisor = \
@@ -277,7 +272,8 @@ class ObjectController(BaseStorageServer):
277
272
 
278
273
  def async_update(self, op, account, container, obj, host, partition,
279
274
  contdevice, headers_out, objdevice, policy,
280
- logger_thread_locals=None, container_path=None):
275
+ logger_thread_locals=None, container_path=None,
276
+ db_state=None):
281
277
  """
282
278
  Sends or saves an async update.
283
279
 
@@ -299,6 +295,8 @@ class ObjectController(BaseStorageServer):
299
295
  to which the update should be sent. If given this path will be used
300
296
  instead of constructing a path from the ``account`` and
301
297
  ``container`` params.
298
+ :param db_state: The current database state of the container as
299
+ supplied to us by the proxy.
302
300
  """
303
301
  if logger_thread_locals:
304
302
  self.logger.thread_locals = logger_thread_locals
@@ -342,7 +340,7 @@ class ObjectController(BaseStorageServer):
342
340
  '%(ip)s:%(port)s/%(dev)s (saving for async update later)',
343
341
  {'ip': ip, 'port': port, 'dev': contdevice})
344
342
  data = {'op': op, 'account': account, 'container': container,
345
- 'obj': obj, 'headers': headers_out}
343
+ 'obj': obj, 'headers': headers_out, 'db_state': db_state}
346
344
  if redirect_data:
347
345
  self.logger.debug(
348
346
  'Update to %(path)s redirected to %(redirect)s',
@@ -376,6 +374,7 @@ class ObjectController(BaseStorageServer):
376
374
  contdevices = [d.strip() for d in
377
375
  headers_in.get('X-Container-Device', '').split(',')]
378
376
  contpartition = headers_in.get('X-Container-Partition', '')
377
+ contdbstate = headers_in.get('X-Container-Root-Db-State')
379
378
 
380
379
  if len(conthosts) != len(contdevices):
381
380
  # This shouldn't happen unless there's a bug in the proxy,
@@ -426,7 +425,7 @@ class ObjectController(BaseStorageServer):
426
425
  conthost, contpartition, contdevice, headers_out,
427
426
  objdevice, policy,
428
427
  logger_thread_locals=self.logger.thread_locals,
429
- container_path=contpath)
428
+ container_path=contpath, db_state=contdbstate)
430
429
  update_greenthreads.append(gt)
431
430
  # Wait a little bit to see if the container updates are successful.
432
431
  # If we immediately return after firing off the greenthread above, then
@@ -445,7 +444,7 @@ class ObjectController(BaseStorageServer):
445
444
  self.container_update_timeout, updates)
446
445
 
447
446
  def delete_at_update(self, op, delete_at, account, container, obj,
448
- request, objdevice, policy):
447
+ request, objdevice, policy, extra_headers=None):
449
448
  """
450
449
  Update the expiring objects container when objects are updated.
451
450
 
@@ -457,6 +456,7 @@ class ObjectController(BaseStorageServer):
457
456
  :param request: the original request driving the update
458
457
  :param objdevice: device name that the object is in
459
458
  :param policy: the BaseStoragePolicy instance (used for tmp dir)
459
+ :param extra_headers: dict of additional headers for the update
460
460
  """
461
461
  if config_true_value(
462
462
  request.headers.get('x-backend-replication', 'f')):
@@ -502,8 +502,10 @@ class ObjectController(BaseStorageServer):
502
502
  if not updates:
503
503
  updates = [(None, None)]
504
504
  headers_out['x-size'] = '0'
505
- headers_out['x-content-type'] = 'text/plain'
505
+ headers_out['x-content-type'] = X_DELETE_TYPE
506
506
  headers_out['x-etag'] = 'd41d8cd98f00b204e9800998ecf8427e'
507
+ if extra_headers:
508
+ headers_out.update(extra_headers)
507
509
  else:
508
510
  if not config_true_value(
509
511
  request.headers.get(
@@ -628,6 +630,24 @@ class ObjectController(BaseStorageServer):
628
630
  override = key.lower().replace(override_prefix, 'x-')
629
631
  update_headers[override] = val
630
632
 
633
+ def _conditional_delete_at_update(self, request, device, account,
634
+ container, obj, policy, metadata,
635
+ orig_delete_at, new_delete_at):
636
+ if new_delete_at:
637
+ extra_headers = {
638
+ 'x-content-type': embed_expirer_bytes_in_ctype(
639
+ X_DELETE_TYPE, metadata),
640
+ 'x-content-type-timestamp':
641
+ metadata.get('X-Timestamp'),
642
+ }
643
+ self.delete_at_update(
644
+ 'PUT', new_delete_at, account, container, obj, request,
645
+ device, policy, extra_headers)
646
+ if orig_delete_at and orig_delete_at != new_delete_at:
647
+ self.delete_at_update(
648
+ 'DELETE', orig_delete_at, account, container, obj,
649
+ request, device, policy)
650
+
631
651
  @public
632
652
  @timing_stats()
633
653
  def POST(self, request):
@@ -644,8 +664,7 @@ class ObjectController(BaseStorageServer):
644
664
  try:
645
665
  disk_file = self.get_diskfile(
646
666
  device, partition, account, container, obj,
647
- policy=policy, open_expired=config_true_value(
648
- request.headers.get('x-backend-replication', 'false')),
667
+ policy=policy, open_expired=is_backend_open_expired(request),
649
668
  next_part_power=next_part_power)
650
669
  except DiskFileDeviceUnavailable:
651
670
  return HTTPInsufficientStorage(drive=device, request=request)
@@ -684,15 +703,11 @@ class ObjectController(BaseStorageServer):
684
703
  wsgi_to_bytes(header_key).title())
685
704
  metadata[header_caps] = request.headers[header_key]
686
705
  orig_delete_at = int(orig_metadata.get('X-Delete-At') or 0)
687
- if orig_delete_at != new_delete_at:
688
- if new_delete_at:
689
- self.delete_at_update(
690
- 'PUT', new_delete_at, account, container, obj, request,
691
- device, policy)
692
- if orig_delete_at:
693
- self.delete_at_update('DELETE', orig_delete_at, account,
694
- container, obj, request, device,
695
- policy)
706
+ disk_file_metadata = disk_file.get_datafile_metadata()
707
+ self._conditional_delete_at_update(
708
+ request, device, account, container, obj, policy,
709
+ disk_file_metadata, orig_delete_at, new_delete_at
710
+ )
696
711
  else:
697
712
  # preserve existing metadata, only content-type may be updated
698
713
  metadata = dict(disk_file.get_metafile_metadata())
@@ -1002,15 +1017,10 @@ class ObjectController(BaseStorageServer):
1002
1017
  orig_metadata, footers_metadata, metadata):
1003
1018
  orig_delete_at = int(orig_metadata.get('X-Delete-At') or 0)
1004
1019
  new_delete_at = int(request.headers.get('X-Delete-At') or 0)
1005
- if orig_delete_at != new_delete_at:
1006
- if new_delete_at:
1007
- self.delete_at_update(
1008
- 'PUT', new_delete_at, account, container, obj, request,
1009
- device, policy)
1010
- if orig_delete_at:
1011
- self.delete_at_update(
1012
- 'DELETE', orig_delete_at, account, container, obj,
1013
- request, device, policy)
1020
+
1021
+ self._conditional_delete_at_update(request, device, account, container,
1022
+ obj, policy, metadata,
1023
+ orig_delete_at, new_delete_at)
1014
1024
 
1015
1025
  update_headers = HeaderKeyDict({
1016
1026
  'x-size': metadata['Content-Length'],
@@ -1083,21 +1093,13 @@ class ObjectController(BaseStorageServer):
1083
1093
  disk_file = self.get_diskfile(
1084
1094
  device, partition, account, container, obj,
1085
1095
  policy=policy, frag_prefs=frag_prefs,
1086
- open_expired=config_true_value(
1087
- request.headers.get('x-backend-replication', 'false')))
1096
+ open_expired=is_backend_open_expired(request))
1088
1097
  except DiskFileDeviceUnavailable:
1089
1098
  return HTTPInsufficientStorage(drive=device, request=request)
1090
1099
  try:
1091
1100
  with disk_file.open(current_time=req_timestamp):
1092
1101
  metadata = disk_file.get_metadata()
1093
- ignore_range_headers = set(
1094
- h.strip().lower()
1095
- for h in request.headers.get(
1096
- 'X-Backend-Ignore-Range-If-Metadata-Present',
1097
- '').split(','))
1098
- if ignore_range_headers.intersection(
1099
- h.lower() for h in metadata):
1100
- request.headers.pop('Range', None)
1102
+ resolve_ignore_range_header(request, metadata)
1101
1103
  obj_size = int(metadata['Content-Length'])
1102
1104
  file_x_ts = Timestamp(metadata['X-Timestamp'])
1103
1105
  keep_cache = (
@@ -1114,10 +1116,15 @@ class ObjectController(BaseStorageServer):
1114
1116
  )
1115
1117
  )
1116
1118
  conditional_etag = resolve_etag_is_at_header(request, metadata)
1119
+ app_iter = disk_file.reader(
1120
+ keep_cache=keep_cache,
1121
+ cooperative_period=self.cooperative_period,
1122
+ )
1117
1123
  response = Response(
1118
- app_iter=disk_file.reader(keep_cache=keep_cache),
1119
- request=request, conditional_response=True,
1120
- conditional_etag=conditional_etag)
1124
+ app_iter=app_iter, request=request,
1125
+ conditional_response=True,
1126
+ conditional_etag=conditional_etag,
1127
+ )
1121
1128
  response.headers['Content-Type'] = metadata.get(
1122
1129
  'Content-Type', 'application/octet-stream')
1123
1130
  for key, value in metadata.items():
@@ -1168,8 +1175,7 @@ class ObjectController(BaseStorageServer):
1168
1175
  disk_file = self.get_diskfile(
1169
1176
  device, partition, account, container, obj,
1170
1177
  policy=policy, frag_prefs=frag_prefs,
1171
- open_expired=config_true_value(
1172
- request.headers.get('x-backend-replication', 'false')))
1178
+ open_expired=is_backend_open_expired(request))
1173
1179
  except DiskFileDeviceUnavailable:
1174
1180
  return HTTPInsufficientStorage(drive=device, request=request)
1175
1181
  try:
@@ -1275,10 +1281,10 @@ class ObjectController(BaseStorageServer):
1275
1281
  else:
1276
1282
  # differentiate success from no object at all
1277
1283
  response_class = HTTPNoContent
1278
- if orig_delete_at:
1279
- self.delete_at_update('DELETE', orig_delete_at, account,
1280
- container, obj, request, device,
1281
- policy)
1284
+ self._conditional_delete_at_update(
1285
+ request, device, account, container, obj, policy, {},
1286
+ orig_delete_at, 0
1287
+ )
1282
1288
  if orig_timestamp < req_timestamp:
1283
1289
  try:
1284
1290
  disk_file.delete(req_timestamp)
@@ -1454,3 +1460,14 @@ def app_factory(global_conf, **local_conf):
1454
1460
  conf = global_conf.copy()
1455
1461
  conf.update(local_conf)
1456
1462
  return ObjectController(conf)
1463
+
1464
+
1465
+ def main():
1466
+ conf_file, options = parse_options(test_config=True)
1467
+ sys.exit(run_wsgi(conf_file, 'object-server',
1468
+ global_conf_callback=global_conf_callback,
1469
+ **options))
1470
+
1471
+
1472
+ if __name__ == '__main__':
1473
+ main()
swift/obj/updater.py CHANGED
@@ -34,8 +34,8 @@ from swift.common.utils import get_logger, renamer, write_pickle, \
34
34
  dump_recon_cache, config_true_value, RateLimitedIterator, split_path, \
35
35
  eventlet_monkey_patch, get_redirect_data, ContextPool, hash_path, \
36
36
  non_negative_float, config_positive_int_value, non_negative_int, \
37
- EventletRateLimiter, node_to_string
38
- from swift.common.daemon import Daemon
37
+ EventletRateLimiter, node_to_string, parse_options
38
+ from swift.common.daemon import Daemon, run_daemon
39
39
  from swift.common.header_key_dict import HeaderKeyDict
40
40
  from swift.common.storage_policy import split_policy_string, PolicyError
41
41
  from swift.common.recon import RECON_OBJECT_FILE, DEFAULT_RECON_CACHE_PATH
@@ -381,6 +381,7 @@ class ObjectUpdater(Daemon):
381
381
  pids.append(pid)
382
382
  else:
383
383
  signal.signal(signal.SIGTERM, signal.SIG_DFL)
384
+ os.environ.pop('NOTIFY_SOCKET', None)
384
385
  eventlet_monkey_patch()
385
386
  self.stats.reset()
386
387
  forkbegin = time.time()
@@ -757,3 +758,12 @@ class ObjectUpdater(Daemon):
757
758
  self.logger.timing('updater.timing.status.%s' % status,
758
759
  elapsed * 1000)
759
760
  return HTTP_INTERNAL_SERVER_ERROR, node['id'], redirect
761
+
762
+
763
+ def main():
764
+ conf_file, options = parse_options(once=True)
765
+ run_daemon(ObjectUpdater, conf_file, **options)
766
+
767
+
768
+ if __name__ == '__main__':
769
+ main()