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.
- swift/__init__.py +29 -50
- swift/account/auditor.py +21 -118
- swift/account/backend.py +33 -28
- swift/account/reaper.py +37 -28
- swift/account/replicator.py +22 -0
- swift/account/server.py +60 -26
- swift/account/utils.py +28 -11
- swift-2.23.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +183 -29
- swift/cli/manage_shard_ranges.py +708 -37
- swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.2.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +807 -126
- swift/cli/reload.py +135 -0
- swift/cli/ringbuilder.py +217 -20
- swift/cli/ringcomposer.py +0 -1
- swift/cli/shard-info.py +4 -3
- swift/common/base_storage_server.py +9 -20
- swift/common/bufferedhttp.py +48 -74
- swift/common/constraints.py +20 -15
- swift/common/container_sync_realms.py +9 -11
- swift/common/daemon.py +25 -8
- swift/common/db.py +198 -127
- swift/common/db_auditor.py +168 -0
- swift/common/db_replicator.py +95 -55
- swift/common/digest.py +141 -0
- swift/common/direct_client.py +144 -33
- swift/common/error_limiter.py +93 -0
- swift/common/exceptions.py +25 -1
- swift/common/header_key_dict.py +2 -9
- swift/common/http_protocol.py +373 -0
- swift/common/internal_client.py +129 -59
- swift/common/linkat.py +3 -4
- swift/common/manager.py +284 -67
- swift/common/memcached.py +396 -147
- swift/common/middleware/__init__.py +4 -0
- swift/common/middleware/account_quotas.py +211 -46
- swift/common/middleware/acl.py +3 -8
- swift/common/middleware/backend_ratelimit.py +230 -0
- swift/common/middleware/bulk.py +22 -34
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +6 -11
- swift/common/middleware/container_quotas.py +1 -1
- swift/common/middleware/container_sync.py +39 -17
- swift/common/middleware/copy.py +12 -0
- swift/common/middleware/crossdomain.py +22 -9
- swift/common/middleware/crypto/__init__.py +2 -1
- swift/common/middleware/crypto/crypto_utils.py +11 -15
- swift/common/middleware/crypto/decrypter.py +28 -11
- swift/common/middleware/crypto/encrypter.py +12 -17
- swift/common/middleware/crypto/keymaster.py +8 -15
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/dlo.py +15 -11
- swift/common/middleware/domain_remap.py +5 -4
- swift/common/middleware/etag_quoter.py +128 -0
- swift/common/middleware/formpost.py +73 -70
- swift/common/middleware/gatekeeper.py +8 -1
- swift/common/middleware/keystoneauth.py +33 -3
- swift/common/middleware/list_endpoints.py +4 -4
- swift/common/middleware/listing_formats.py +85 -49
- swift/common/middleware/memcache.py +4 -81
- swift/common/middleware/name_check.py +3 -2
- swift/common/middleware/proxy_logging.py +160 -92
- swift/common/middleware/ratelimit.py +17 -10
- swift/common/middleware/read_only.py +6 -4
- swift/common/middleware/recon.py +59 -22
- swift/common/middleware/s3api/acl_handlers.py +25 -3
- swift/common/middleware/s3api/acl_utils.py +6 -1
- swift/common/middleware/s3api/controllers/__init__.py +6 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/bucket.py +242 -137
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
- swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
- swift/common/middleware/s3api/controllers/obj.py +112 -8
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
- swift/common/middleware/s3api/controllers/tagging.py +57 -0
- swift/common/middleware/s3api/controllers/versioning.py +36 -7
- swift/common/middleware/s3api/etree.py +22 -9
- swift/common/middleware/s3api/exception.py +0 -4
- swift/common/middleware/s3api/s3api.py +113 -41
- swift/common/middleware/s3api/s3request.py +384 -218
- swift/common/middleware/s3api/s3response.py +126 -23
- swift/common/middleware/s3api/s3token.py +16 -17
- swift/common/middleware/s3api/schema/delete.rng +1 -1
- swift/common/middleware/s3api/subresource.py +7 -10
- swift/common/middleware/s3api/utils.py +27 -10
- swift/common/middleware/slo.py +665 -358
- swift/common/middleware/staticweb.py +64 -37
- swift/common/middleware/symlink.py +52 -19
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +192 -174
- swift/common/middleware/versioned_writes/__init__.py +51 -0
- swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
- swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +18 -19
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +10 -10
- swift-2.23.2.data/scripts/swift-container-server → swift/common/recon.py +13 -8
- swift/common/registry.py +147 -0
- swift/common/request_helpers.py +324 -57
- swift/common/ring/builder.py +67 -25
- swift/common/ring/composite_builder.py +1 -1
- swift/common/ring/ring.py +177 -51
- swift/common/ring/utils.py +1 -1
- swift/common/splice.py +10 -6
- swift/common/statsd_client.py +205 -0
- swift/common/storage_policy.py +49 -44
- swift/common/swob.py +86 -102
- swift/common/{utils.py → utils/__init__.py} +2191 -2762
- swift/common/utils/base.py +131 -0
- swift/common/utils/config.py +433 -0
- swift/common/utils/ipaddrs.py +256 -0
- swift/common/utils/libc.py +345 -0
- swift/common/utils/logs.py +859 -0
- swift/common/utils/timestamp.py +412 -0
- swift/common/wsgi.py +555 -536
- swift/container/auditor.py +14 -100
- swift/container/backend.py +552 -227
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +397 -176
- swift/container/sharder.py +1580 -639
- swift/container/sync.py +94 -88
- swift/container/updater.py +53 -32
- swift/obj/auditor.py +153 -35
- swift/obj/diskfile.py +466 -217
- swift/obj/expirer.py +406 -124
- swift/obj/mem_diskfile.py +7 -4
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +523 -262
- swift/obj/replicator.py +249 -188
- swift/obj/server.py +213 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +653 -139
- swift/obj/watchers/__init__.py +0 -0
- swift/obj/watchers/dark_data.py +213 -0
- swift/proxy/controllers/account.py +11 -11
- swift/proxy/controllers/base.py +848 -604
- swift/proxy/controllers/container.py +452 -86
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1009 -490
- swift/proxy/server.py +185 -112
- swift-2.35.0.dist-info/AUTHORS +501 -0
- swift-2.35.0.dist-info/LICENSE +202 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
- swift-2.35.0.dist-info/pbr.json +1 -0
- swift/locale/de/LC_MESSAGES/swift.po +0 -1216
- swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
- swift/locale/es/LC_MESSAGES/swift.po +0 -1085
- swift/locale/fr/LC_MESSAGES/swift.po +0 -909
- swift/locale/it/LC_MESSAGES/swift.po +0 -894
- swift/locale/ja/LC_MESSAGES/swift.po +0 -965
- swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
- swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
- swift/locale/ru/LC_MESSAGES/swift.po +0 -891
- swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
- swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
- swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
- swift-2.23.2.data/scripts/swift-account-auditor +0 -23
- swift-2.23.2.data/scripts/swift-account-info +0 -51
- swift-2.23.2.data/scripts/swift-account-reaper +0 -23
- swift-2.23.2.data/scripts/swift-account-replicator +0 -34
- swift-2.23.2.data/scripts/swift-account-server +0 -23
- swift-2.23.2.data/scripts/swift-container-auditor +0 -23
- swift-2.23.2.data/scripts/swift-container-info +0 -51
- swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.2.data/scripts/swift-container-replicator +0 -34
- swift-2.23.2.data/scripts/swift-container-sharder +0 -33
- swift-2.23.2.data/scripts/swift-container-sync +0 -23
- swift-2.23.2.data/scripts/swift-container-updater +0 -23
- swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.2.data/scripts/swift-form-signature +0 -20
- swift-2.23.2.data/scripts/swift-init +0 -119
- swift-2.23.2.data/scripts/swift-object-auditor +0 -29
- swift-2.23.2.data/scripts/swift-object-expirer +0 -33
- swift-2.23.2.data/scripts/swift-object-info +0 -60
- swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.2.data/scripts/swift-object-relinker +0 -41
- swift-2.23.2.data/scripts/swift-object-replicator +0 -37
- swift-2.23.2.data/scripts/swift-object-server +0 -27
- swift-2.23.2.data/scripts/swift-object-updater +0 -23
- swift-2.23.2.data/scripts/swift-proxy-server +0 -23
- swift-2.23.2.data/scripts/swift-recon +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.2.data/scripts/swift-ring-composer +0 -22
- swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
- swift-2.23.2.dist-info/RECORD +0 -220
- swift-2.23.2.dist-info/metadata.json +0 -1
- swift-2.23.2.dist-info/pbr.json +0 -1
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
swift/container/reconciler.py
CHANGED
@@ -19,19 +19,20 @@ import itertools
|
|
19
19
|
import logging
|
20
20
|
|
21
21
|
from eventlet import GreenPile, GreenPool, Timeout
|
22
|
-
import six
|
23
22
|
|
24
23
|
from swift.common import constraints
|
25
|
-
from swift.common.daemon import Daemon
|
24
|
+
from swift.common.daemon import Daemon, run_daemon
|
26
25
|
from swift.common.direct_client import (
|
27
26
|
direct_head_container, direct_delete_container_object,
|
28
27
|
direct_put_container_object, ClientException)
|
29
28
|
from swift.common.internal_client import InternalClient, UnexpectedResponse
|
29
|
+
from swift.common.request_helpers import MISPLACED_OBJECTS_ACCOUNT, \
|
30
|
+
USE_REPLICATION_NETWORK_HEADER
|
30
31
|
from swift.common.utils import get_logger, split_path, majority_size, \
|
31
32
|
FileLikeIter, Timestamp, last_modified_date_to_timestamp, \
|
32
|
-
LRUCache, decode_timestamps
|
33
|
+
LRUCache, decode_timestamps, hash_path, parse_options
|
34
|
+
from swift.common.storage_policy import POLICIES
|
33
35
|
|
34
|
-
MISPLACED_OBJECTS_ACCOUNT = '.misplaced_objects'
|
35
36
|
MISPLACED_OBJECTS_CONTAINER_DIVISOR = 3600 # 1 hour
|
36
37
|
CONTAINER_POLICY_TTL = 30
|
37
38
|
|
@@ -85,8 +86,13 @@ def cmp_policy_info(info, remote_info):
|
|
85
86
|
return 1
|
86
87
|
elif not remote_recreated:
|
87
88
|
return -1
|
88
|
-
|
89
|
-
|
89
|
+
# both have been recreated, everything devoles to here eventually
|
90
|
+
most_recent_successful_delete = max(info['delete_timestamp'],
|
91
|
+
remote_info['delete_timestamp'])
|
92
|
+
if info['put_timestamp'] < most_recent_successful_delete:
|
93
|
+
return 1
|
94
|
+
elif remote_info['put_timestamp'] < most_recent_successful_delete:
|
95
|
+
return -1
|
90
96
|
return cmp(info['status_changed_at'], remote_info['status_changed_at'])
|
91
97
|
|
92
98
|
|
@@ -224,6 +230,7 @@ def add_to_reconciler_queue(container_ring, account, container, obj,
|
|
224
230
|
'X-Etag': obj_timestamp,
|
225
231
|
'X-Timestamp': x_timestamp,
|
226
232
|
'X-Content-Type': q_op_type,
|
233
|
+
USE_REPLICATION_NETWORK_HEADER: 'true',
|
227
234
|
}
|
228
235
|
|
229
236
|
def _check_success(*args, **kwargs):
|
@@ -264,11 +271,7 @@ def parse_raw_obj(obj_info):
|
|
264
271
|
:returns: a queue entry dict with the keys: q_policy_index, account,
|
265
272
|
container, obj, q_op, q_ts, q_record, and path
|
266
273
|
"""
|
267
|
-
|
268
|
-
raw_obj_name = obj_info['name'].encode('utf-8')
|
269
|
-
else:
|
270
|
-
raw_obj_name = obj_info['name']
|
271
|
-
|
274
|
+
raw_obj_name = obj_info['name']
|
272
275
|
policy_index, obj_name = raw_obj_name.split(':', 1)
|
273
276
|
q_policy_index = int(policy_index)
|
274
277
|
account, container, obj = split_path(obj_name, 3, 3, rest_with_last=True)
|
@@ -307,7 +310,8 @@ def direct_get_container_policy_index(container_ring, account_name,
|
|
307
310
|
"""
|
308
311
|
def _eat_client_exception(*args):
|
309
312
|
try:
|
310
|
-
return direct_head_container(*args
|
313
|
+
return direct_head_container(*args, headers={
|
314
|
+
USE_REPLICATION_NETWORK_HEADER: 'true'})
|
311
315
|
except ClientException as err:
|
312
316
|
if err.http_status == 404:
|
313
317
|
return err.http_headers
|
@@ -333,6 +337,10 @@ def direct_delete_container_entry(container_ring, account_name, container_name,
|
|
333
337
|
object listing. Does not talk to object servers; use this only when a
|
334
338
|
container entry does not actually have a corresponding object.
|
335
339
|
"""
|
340
|
+
if headers is None:
|
341
|
+
headers = {}
|
342
|
+
headers[USE_REPLICATION_NETWORK_HEADER] = 'true'
|
343
|
+
|
336
344
|
pool = GreenPool()
|
337
345
|
part, nodes = container_ring.get_nodes(account_name, container_name)
|
338
346
|
for node in nodes:
|
@@ -348,23 +356,45 @@ class ContainerReconciler(Daemon):
|
|
348
356
|
"""
|
349
357
|
Move objects that are in the wrong storage policy.
|
350
358
|
"""
|
359
|
+
log_route = 'container-reconciler'
|
351
360
|
|
352
|
-
def __init__(self, conf):
|
361
|
+
def __init__(self, conf, logger=None, swift=None):
|
353
362
|
self.conf = conf
|
354
363
|
# This option defines how long an un-processable misplaced object
|
355
364
|
# marker will be retried before it is abandoned. It is not coupled
|
356
365
|
# with the tombstone reclaim age in the consistency engine.
|
357
366
|
self.reclaim_age = int(conf.get('reclaim_age', 86400 * 7))
|
358
|
-
self.interval =
|
367
|
+
self.interval = float(conf.get('interval', 30))
|
359
368
|
conf_path = conf.get('__file__') or \
|
360
369
|
'/etc/swift/container-reconciler.conf'
|
361
|
-
self.logger = get_logger(
|
370
|
+
self.logger = logger or get_logger(
|
371
|
+
conf, log_route=self.log_route)
|
362
372
|
request_tries = int(conf.get('request_tries') or 3)
|
363
|
-
self.swift = InternalClient(
|
364
|
-
|
365
|
-
|
373
|
+
self.swift = swift or InternalClient(
|
374
|
+
conf_path,
|
375
|
+
'Swift Container Reconciler',
|
376
|
+
request_tries,
|
377
|
+
use_replication_network=True,
|
378
|
+
global_conf={'log_name': '%s-ic' % conf.get(
|
379
|
+
'log_name', self.log_route)})
|
380
|
+
self.swift_dir = conf.get('swift_dir', '/etc/swift')
|
366
381
|
self.stats = defaultdict(int)
|
367
382
|
self.last_stat_time = time.time()
|
383
|
+
self.ring_check_interval = float(conf.get('ring_check_interval', 15))
|
384
|
+
self.concurrency = int(conf.get('concurrency', 1))
|
385
|
+
if self.concurrency < 1:
|
386
|
+
raise ValueError("concurrency must be set to at least 1")
|
387
|
+
self.processes = int(self.conf.get('processes', 0))
|
388
|
+
if self.processes < 0:
|
389
|
+
raise ValueError(
|
390
|
+
'processes must be an integer greater than or equal to 0')
|
391
|
+
self.process = int(self.conf.get('process', 0))
|
392
|
+
if self.process < 0:
|
393
|
+
raise ValueError(
|
394
|
+
'process must be an integer greater than or equal to 0')
|
395
|
+
if self.processes and self.process >= self.processes:
|
396
|
+
raise ValueError(
|
397
|
+
'process must be less than processes')
|
368
398
|
|
369
399
|
def stats_log(self, metric, msg, *args, **kwargs):
|
370
400
|
"""
|
@@ -400,7 +430,7 @@ class ContainerReconciler(Daemon):
|
|
400
430
|
an object was manually re-enqued.
|
401
431
|
"""
|
402
432
|
q_path = '/%s/%s/%s' % (MISPLACED_OBJECTS_ACCOUNT, container, obj)
|
403
|
-
x_timestamp = slightly_later_timestamp(max(q_record, q_ts))
|
433
|
+
x_timestamp = slightly_later_timestamp(max(q_record, q_ts), offset=2)
|
404
434
|
self.stats_log('pop_queue', 'remove %r (%f) from the queue (%s)',
|
405
435
|
q_path, q_ts, x_timestamp)
|
406
436
|
headers = {'X-Timestamp': x_timestamp}
|
@@ -408,6 +438,13 @@ class ContainerReconciler(Daemon):
|
|
408
438
|
self.swift.container_ring, MISPLACED_OBJECTS_ACCOUNT,
|
409
439
|
container, obj, headers=headers)
|
410
440
|
|
441
|
+
def can_reconcile_policy(self, policy_index):
|
442
|
+
pol = POLICIES.get_by_index(policy_index)
|
443
|
+
if pol:
|
444
|
+
pol.load_ring(self.swift_dir, reload_time=self.ring_check_interval)
|
445
|
+
return pol.object_ring.next_part_power is None
|
446
|
+
return False
|
447
|
+
|
411
448
|
def throw_tombstones(self, account, container, obj, timestamp,
|
412
449
|
policy_index, path):
|
413
450
|
"""
|
@@ -474,15 +511,33 @@ class ContainerReconciler(Daemon):
|
|
474
511
|
container_policy_index, q_policy_index)
|
475
512
|
return True
|
476
513
|
|
514
|
+
# don't reconcile if the source or container policy_index is in the
|
515
|
+
# middle of a PPI
|
516
|
+
if not self.can_reconcile_policy(q_policy_index):
|
517
|
+
self.stats_log('ppi_skip', 'Source policy (%r) in the middle of '
|
518
|
+
'a part power increase (PPI)', q_policy_index)
|
519
|
+
return False
|
520
|
+
if not self.can_reconcile_policy(container_policy_index):
|
521
|
+
self.stats_log('ppi_skip', 'Container policy (%r) in the middle '
|
522
|
+
'of a part power increase (PPI)',
|
523
|
+
container_policy_index)
|
524
|
+
return False
|
525
|
+
|
477
526
|
# check if object exists in the destination already
|
478
527
|
self.logger.debug('checking for %r (%f) in destination '
|
479
528
|
'policy_index %s', path, q_ts,
|
480
529
|
container_policy_index)
|
481
530
|
headers = {
|
482
531
|
'X-Backend-Storage-Policy-Index': container_policy_index}
|
483
|
-
|
484
|
-
|
485
|
-
|
532
|
+
try:
|
533
|
+
dest_obj = self.swift.get_object_metadata(
|
534
|
+
account, container, obj, headers=headers,
|
535
|
+
acceptable_statuses=(2, 4))
|
536
|
+
except UnexpectedResponse:
|
537
|
+
self.stats_log('unavailable_destination', '%r (%f) unable to '
|
538
|
+
'determine the destination timestamp, if any',
|
539
|
+
path, q_ts)
|
540
|
+
return False
|
486
541
|
dest_ts = Timestamp(dest_obj.get('x-backend-timestamp', 0))
|
487
542
|
if dest_ts >= q_ts:
|
488
543
|
self.stats_log('found_object', '%r (%f) in policy_index %s '
|
@@ -563,7 +618,7 @@ class ContainerReconciler(Daemon):
|
|
563
618
|
# optimistically move any source with a timestamp >= q_ts
|
564
619
|
ts = max(Timestamp(source_ts), q_ts)
|
565
620
|
# move the object
|
566
|
-
put_timestamp = slightly_later_timestamp(ts, offset=
|
621
|
+
put_timestamp = slightly_later_timestamp(ts, offset=3)
|
567
622
|
self.stats_log('copy_attempt', '%r (%f) in policy_index %s will be '
|
568
623
|
'moved to policy_index %s (%s)', path, source_ts,
|
569
624
|
q_policy_index, container_policy_index, put_timestamp)
|
@@ -603,7 +658,7 @@ class ContainerReconciler(Daemon):
|
|
603
658
|
Issue a DELETE request against the destination to match the
|
604
659
|
misplaced DELETE against the source.
|
605
660
|
"""
|
606
|
-
delete_timestamp = slightly_later_timestamp(q_ts, offset=
|
661
|
+
delete_timestamp = slightly_later_timestamp(q_ts, offset=3)
|
607
662
|
self.stats_log('delete_attempt', '%r (%f) in policy_index %s '
|
608
663
|
'will be deleted from policy_index %s (%s)', path,
|
609
664
|
source_ts, q_policy_index, container_policy_index,
|
@@ -679,9 +734,9 @@ class ContainerReconciler(Daemon):
|
|
679
734
|
# hit most recent container first instead of waiting on the updaters
|
680
735
|
current_container = get_reconciler_container_name(time.time())
|
681
736
|
yield current_container
|
682
|
-
container_gen = self.swift.iter_containers(MISPLACED_OBJECTS_ACCOUNT)
|
683
737
|
self.logger.debug('looking for containers in %s',
|
684
738
|
MISPLACED_OBJECTS_ACCOUNT)
|
739
|
+
container_gen = self.swift.iter_containers(MISPLACED_OBJECTS_ACCOUNT)
|
685
740
|
while True:
|
686
741
|
one_page = None
|
687
742
|
try:
|
@@ -698,9 +753,6 @@ class ContainerReconciler(Daemon):
|
|
698
753
|
# reversed order since we expect older containers to be empty
|
699
754
|
for c in reversed(one_page):
|
700
755
|
container = c['name']
|
701
|
-
if six.PY2:
|
702
|
-
# encoding here is defensive
|
703
|
-
container = container.encode('utf8')
|
704
756
|
if container == current_container:
|
705
757
|
continue # we've already hit this one this pass
|
706
758
|
yield container
|
@@ -732,29 +784,57 @@ class ContainerReconciler(Daemon):
|
|
732
784
|
MISPLACED_OBJECTS_ACCOUNT, container,
|
733
785
|
acceptable_statuses=(2, 404, 409, 412))
|
734
786
|
|
787
|
+
def should_process(self, queue_item):
|
788
|
+
"""
|
789
|
+
Check if a given entry should be handled by this process.
|
790
|
+
|
791
|
+
:param container: the queue container
|
792
|
+
:param queue_item: an entry from the queue
|
793
|
+
"""
|
794
|
+
if not self.processes:
|
795
|
+
return True
|
796
|
+
hexdigest = hash_path(
|
797
|
+
queue_item['account'], queue_item['container'], queue_item['obj'])
|
798
|
+
return int(hexdigest, 16) % self.processes == self.process
|
799
|
+
|
800
|
+
def process_queue_item(self, q_container, q_entry, queue_item):
|
801
|
+
"""
|
802
|
+
Process an entry and remove from queue on success.
|
803
|
+
|
804
|
+
:param q_container: the queue container
|
805
|
+
:param q_entry: the raw_obj name from the q_container
|
806
|
+
:param queue_item: a parsed entry from the queue
|
807
|
+
"""
|
808
|
+
finished = self.reconcile_object(queue_item)
|
809
|
+
if finished:
|
810
|
+
self.pop_queue(q_container, q_entry,
|
811
|
+
queue_item['q_ts'],
|
812
|
+
queue_item['q_record'])
|
813
|
+
|
735
814
|
def reconcile(self):
|
736
815
|
"""
|
737
|
-
Main entry point for processing misplaced objects.
|
816
|
+
Main entry point for concurrent processing of misplaced objects.
|
738
817
|
|
739
|
-
Iterate over all queue entries and delegate to
|
818
|
+
Iterate over all queue entries and delegate processing to spawned
|
819
|
+
workers in the pool.
|
740
820
|
"""
|
741
821
|
self.logger.debug('pulling items from the queue')
|
822
|
+
pool = GreenPool(self.concurrency)
|
742
823
|
for container in self._iter_containers():
|
824
|
+
self.logger.debug('checking container %s', container)
|
743
825
|
for raw_obj in self._iter_objects(container):
|
744
826
|
try:
|
745
|
-
|
827
|
+
queue_item = parse_raw_obj(raw_obj)
|
746
828
|
except Exception:
|
747
829
|
self.stats_log('invalid_record',
|
748
830
|
'invalid queue record: %r', raw_obj,
|
749
831
|
level=logging.ERROR, exc_info=True)
|
750
832
|
continue
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
obj_info['q_ts'],
|
755
|
-
obj_info['q_record'])
|
833
|
+
if self.should_process(queue_item):
|
834
|
+
pool.spawn_n(self.process_queue_item,
|
835
|
+
container, raw_obj['name'], queue_item)
|
756
836
|
self.log_stats()
|
757
|
-
|
837
|
+
pool.waitall()
|
758
838
|
|
759
839
|
def run_once(self, *args, **kwargs):
|
760
840
|
"""
|
@@ -772,3 +852,12 @@ class ContainerReconciler(Daemon):
|
|
772
852
|
self.stats = defaultdict(int)
|
773
853
|
self.logger.info('sleeping between intervals (%ss)', self.interval)
|
774
854
|
time.sleep(self.interval)
|
855
|
+
|
856
|
+
|
857
|
+
def main():
|
858
|
+
conf_file, options = parse_options(once=True)
|
859
|
+
run_daemon(ContainerReconciler, conf_file, **options)
|
860
|
+
|
861
|
+
|
862
|
+
if __name__ == '__main__':
|
863
|
+
main()
|
swift/container/replicator.py
CHANGED
@@ -14,22 +14,54 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
16
|
import os
|
17
|
-
import itertools
|
18
17
|
import json
|
19
18
|
from collections import defaultdict
|
20
19
|
from eventlet import Timeout
|
20
|
+
import optparse
|
21
|
+
from random import choice
|
21
22
|
|
22
23
|
from swift.container.sync_store import ContainerSyncStore
|
23
|
-
from swift.container.backend import ContainerBroker, DATADIR, SHARDED
|
24
|
+
from swift.container.backend import ContainerBroker, DATADIR, SHARDED, \
|
25
|
+
merge_shards
|
24
26
|
from swift.container.reconciler import (
|
25
27
|
MISPLACED_OBJECTS_ACCOUNT, incorrect_policy_index,
|
26
28
|
get_reconciler_container_name, get_row_to_q_entry_translator)
|
27
29
|
from swift.common import db_replicator
|
30
|
+
from swift.common.daemon import run_daemon
|
28
31
|
from swift.common.storage_policy import POLICIES
|
29
32
|
from swift.common.swob import HTTPOk, HTTPAccepted
|
30
|
-
from swift.common.exceptions import DeviceUnavailable
|
31
33
|
from swift.common.http import is_success
|
32
|
-
from swift.common.utils import Timestamp, majority_size, get_db_files
|
34
|
+
from swift.common.utils import Timestamp, majority_size, get_db_files, \
|
35
|
+
parse_options
|
36
|
+
|
37
|
+
|
38
|
+
def check_merge_own_shard_range(shards, broker, logger, source):
|
39
|
+
"""
|
40
|
+
If broker has own_shard_range *with an epoch* then filter out an
|
41
|
+
own_shard_range *without an epoch*, and log a warning about it.
|
42
|
+
|
43
|
+
:param shards: a list of candidate ShardRanges to merge
|
44
|
+
:param broker: a ContainerBroker
|
45
|
+
:param logger: a logger
|
46
|
+
:param source: string to log as source of shards
|
47
|
+
:return: a list of ShardRanges to actually merge
|
48
|
+
"""
|
49
|
+
# work-around for https://bugs.launchpad.net/swift/+bug/1980451
|
50
|
+
own_sr = broker.get_own_shard_range()
|
51
|
+
if own_sr.epoch is None:
|
52
|
+
return shards
|
53
|
+
to_merge = []
|
54
|
+
for shard in shards:
|
55
|
+
if shard['name'] == own_sr.name and not shard['epoch']:
|
56
|
+
shard_copy = dict(shard)
|
57
|
+
new_content = merge_shards(shard_copy, dict(own_sr))
|
58
|
+
if new_content and shard_copy['epoch'] is None:
|
59
|
+
logger.warning(
|
60
|
+
'Ignoring remote osr w/o epoch, own_sr: %r, remote_sr: %r,'
|
61
|
+
' source: %s', dict(own_sr), shard, source)
|
62
|
+
continue
|
63
|
+
to_merge.append(shard)
|
64
|
+
return to_merge
|
33
65
|
|
34
66
|
|
35
67
|
class ContainerReplicator(db_replicator.Replicator):
|
@@ -139,23 +171,44 @@ class ContainerReplicator(db_replicator.Replicator):
|
|
139
171
|
with Timeout(self.node_timeout):
|
140
172
|
response = http.replicate('get_shard_ranges')
|
141
173
|
if response and is_success(response.status):
|
142
|
-
|
143
|
-
|
174
|
+
shards = json.loads(response.data.decode('ascii'))
|
175
|
+
shards = check_merge_own_shard_range(
|
176
|
+
shards, broker, self.logger, '%s%s' % (http.host, http.path))
|
177
|
+
broker.merge_shard_ranges(shards)
|
144
178
|
|
145
179
|
def find_local_handoff_for_part(self, part):
|
146
180
|
"""
|
147
|
-
|
148
|
-
|
181
|
+
Find a device in the ring that is on this node on which to place a
|
182
|
+
partition. Preference is given to a device that is a primary location
|
183
|
+
for the partition. If no such device is found then a local device with
|
184
|
+
weight is chosen, and failing that any local device.
|
149
185
|
|
186
|
+
:param part: a partition
|
150
187
|
:returns: a node entry from the ring
|
151
188
|
"""
|
152
|
-
|
153
|
-
|
189
|
+
if not self._local_device_ids:
|
190
|
+
raise RuntimeError('Cannot find local handoff; no local devices')
|
154
191
|
|
155
|
-
for node in
|
192
|
+
for node in self.ring.get_part_nodes(part):
|
156
193
|
if node['id'] in self._local_device_ids:
|
157
194
|
return node
|
158
|
-
|
195
|
+
|
196
|
+
# don't attempt to minimize handoff depth: just choose any local
|
197
|
+
# device, but start by only picking a device with a weight, just in
|
198
|
+
# case some devices are being drained...
|
199
|
+
local_devs_with_weight = [
|
200
|
+
dev for dev in self._local_device_ids.values()
|
201
|
+
if dev.get('weight', 0)]
|
202
|
+
if local_devs_with_weight:
|
203
|
+
return choice(local_devs_with_weight)
|
204
|
+
|
205
|
+
# we have to return something, so choose any local device..
|
206
|
+
node = choice(list(self._local_device_ids.values()))
|
207
|
+
self.logger.warning(
|
208
|
+
"Could not find a non-zero weight device for handoff partition "
|
209
|
+
"%d, falling back device %s" %
|
210
|
+
(part, node['device']))
|
211
|
+
return node
|
159
212
|
|
160
213
|
def get_reconciler_broker(self, timestamp):
|
161
214
|
"""
|
@@ -173,14 +226,12 @@ class ContainerReplicator(db_replicator.Replicator):
|
|
173
226
|
account = MISPLACED_OBJECTS_ACCOUNT
|
174
227
|
part = self.ring.get_part(account, container)
|
175
228
|
node = self.find_local_handoff_for_part(part)
|
176
|
-
|
177
|
-
raise DeviceUnavailable(
|
178
|
-
'No mounted devices found suitable to Handoff reconciler '
|
179
|
-
'container %s in partition %s' % (container, part))
|
180
|
-
broker = ContainerBroker.create_broker(
|
229
|
+
broker, initialized = ContainerBroker.create_broker(
|
181
230
|
os.path.join(self.root, node['device']), part, account, container,
|
182
231
|
logger=self.logger, put_timestamp=timestamp,
|
183
232
|
storage_policy_index=0)
|
233
|
+
self.logger.increment('reconciler_db_created' if initialized
|
234
|
+
else 'reconciler_db_exists')
|
184
235
|
if self.reconciler_containers is not None:
|
185
236
|
self.reconciler_containers[container] = part, broker, node['id']
|
186
237
|
return broker
|
@@ -198,8 +249,9 @@ class ContainerReplicator(db_replicator.Replicator):
|
|
198
249
|
|
199
250
|
try:
|
200
251
|
reconciler = self.get_reconciler_broker(container)
|
201
|
-
except
|
202
|
-
self.logger.
|
252
|
+
except Exception:
|
253
|
+
self.logger.exception('Failed to get reconciler broker for '
|
254
|
+
'container %s', container)
|
203
255
|
return False
|
204
256
|
self.logger.debug('Adding %d objects to the reconciler at %s',
|
205
257
|
len(item_list), reconciler.db_file)
|
@@ -377,13 +429,35 @@ class ContainerReplicatorRpc(db_replicator.ReplicatorRpc):
|
|
377
429
|
def _post_rsync_then_merge_hook(self, existing_broker, new_broker):
|
378
430
|
# Note the following hook will need to change to using a pointer and
|
379
431
|
# limit in the future.
|
380
|
-
|
381
|
-
|
432
|
+
shards = existing_broker.get_all_shard_range_data()
|
433
|
+
shards = check_merge_own_shard_range(
|
434
|
+
shards, new_broker, self.logger, 'rsync')
|
435
|
+
new_broker.merge_shard_ranges(shards)
|
382
436
|
|
383
437
|
def merge_shard_ranges(self, broker, args):
|
384
|
-
|
438
|
+
shards = check_merge_own_shard_range(
|
439
|
+
args[0], broker, self.logger, 'repl_req')
|
440
|
+
broker.merge_shard_ranges(shards)
|
385
441
|
return HTTPAccepted()
|
386
442
|
|
387
443
|
def get_shard_ranges(self, broker, args):
|
388
444
|
return HTTPOk(headers={'Content-Type': 'application/json'},
|
389
445
|
body=json.dumps(broker.get_all_shard_range_data()))
|
446
|
+
|
447
|
+
|
448
|
+
def main():
|
449
|
+
parser = optparse.OptionParser("%prog CONFIG [options]")
|
450
|
+
parser.add_option('-d', '--devices',
|
451
|
+
help=('Replicate only given devices. '
|
452
|
+
'Comma-separated list. '
|
453
|
+
'Only has effect if --once is used.'))
|
454
|
+
parser.add_option('-p', '--partitions',
|
455
|
+
help=('Replicate only given partitions. '
|
456
|
+
'Comma-separated list. '
|
457
|
+
'Only has effect if --once is used.'))
|
458
|
+
conf_file, options = parse_options(parser=parser, once=True)
|
459
|
+
run_daemon(ContainerReplicator, conf_file, **options)
|
460
|
+
|
461
|
+
|
462
|
+
if __name__ == '__main__':
|
463
|
+
main()
|