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.
- swift/account/auditor.py +11 -0
- swift/account/reaper.py +11 -1
- swift/account/replicator.py +22 -0
- swift/account/server.py +13 -12
- swift-2.32.0.data/scripts/swift-account-audit → swift/cli/account_audit.py +6 -2
- swift-2.32.0.data/scripts/swift-config → swift/cli/config.py +1 -1
- swift-2.32.0.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +6 -2
- swift-2.32.0.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +12 -3
- swift-2.32.0.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +6 -2
- swift/cli/info.py +131 -3
- swift-2.32.0.data/scripts/swift-oldies → swift/cli/oldies.py +6 -3
- swift-2.32.0.data/scripts/swift-orphans → swift/cli/orphans.py +7 -2
- swift-2.32.0.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +9 -18
- swift-2.32.0.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +1 -1
- swift/cli/reload.py +141 -0
- swift/cli/ringbuilder.py +24 -0
- swift/common/daemon.py +12 -2
- swift/common/db.py +14 -9
- swift/common/db_auditor.py +2 -2
- swift/common/db_replicator.py +6 -0
- swift/common/exceptions.py +12 -0
- swift/common/http_protocol.py +76 -3
- swift/common/manager.py +120 -5
- swift/common/memcached.py +24 -25
- swift/common/middleware/account_quotas.py +144 -43
- swift/common/middleware/backend_ratelimit.py +166 -24
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +3 -5
- swift/common/middleware/container_sync.py +6 -10
- swift/common/middleware/crypto/crypto_utils.py +4 -5
- swift/common/middleware/crypto/decrypter.py +4 -5
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/proxy_logging.py +57 -43
- swift/common/middleware/ratelimit.py +6 -7
- swift/common/middleware/recon.py +6 -7
- swift/common/middleware/s3api/acl_handlers.py +10 -1
- swift/common/middleware/s3api/controllers/__init__.py +3 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_upload.py +31 -15
- swift/common/middleware/s3api/controllers/obj.py +20 -1
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/s3api.py +6 -0
- swift/common/middleware/s3api/s3request.py +190 -74
- swift/common/middleware/s3api/s3response.py +48 -8
- swift/common/middleware/s3api/s3token.py +2 -2
- swift/common/middleware/s3api/utils.py +2 -1
- swift/common/middleware/slo.py +508 -310
- swift/common/middleware/staticweb.py +45 -14
- swift/common/middleware/tempauth.py +6 -4
- swift/common/middleware/tempurl.py +134 -93
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +9 -10
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +1 -2
- swift/common/request_helpers.py +101 -8
- swift/common/statsd_client.py +207 -0
- swift/common/storage_policy.py +1 -1
- swift/common/swob.py +5 -2
- swift/common/utils/__init__.py +331 -1774
- swift/common/utils/base.py +138 -0
- swift/common/utils/config.py +443 -0
- swift/common/utils/logs.py +999 -0
- swift/common/utils/timestamp.py +23 -2
- swift/common/wsgi.py +19 -3
- swift/container/auditor.py +11 -0
- swift/container/backend.py +136 -31
- swift/container/reconciler.py +11 -2
- swift/container/replicator.py +64 -7
- swift/container/server.py +276 -146
- swift/container/sharder.py +86 -42
- swift/container/sync.py +11 -1
- swift/container/updater.py +12 -2
- swift/obj/auditor.py +20 -3
- swift/obj/diskfile.py +63 -25
- swift/obj/expirer.py +154 -47
- swift/obj/mem_diskfile.py +2 -1
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +28 -4
- swift/obj/replicator.py +63 -24
- swift/obj/server.py +76 -59
- swift/obj/updater.py +12 -2
- swift/obj/watchers/dark_data.py +72 -34
- swift/proxy/controllers/account.py +3 -2
- swift/proxy/controllers/base.py +254 -148
- swift/proxy/controllers/container.py +274 -289
- swift/proxy/controllers/obj.py +120 -166
- swift/proxy/server.py +17 -13
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/AUTHORS +14 -4
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/METADATA +9 -7
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/RECORD +97 -120
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/entry_points.txt +39 -0
- swift-2.34.0.dist-info/pbr.json +1 -0
- swift-2.32.0.data/scripts/swift-account-auditor +0 -23
- swift-2.32.0.data/scripts/swift-account-info +0 -52
- swift-2.32.0.data/scripts/swift-account-reaper +0 -23
- swift-2.32.0.data/scripts/swift-account-replicator +0 -34
- swift-2.32.0.data/scripts/swift-account-server +0 -23
- swift-2.32.0.data/scripts/swift-container-auditor +0 -23
- swift-2.32.0.data/scripts/swift-container-info +0 -56
- swift-2.32.0.data/scripts/swift-container-reconciler +0 -21
- swift-2.32.0.data/scripts/swift-container-replicator +0 -34
- swift-2.32.0.data/scripts/swift-container-server +0 -23
- swift-2.32.0.data/scripts/swift-container-sharder +0 -37
- swift-2.32.0.data/scripts/swift-container-sync +0 -23
- swift-2.32.0.data/scripts/swift-container-updater +0 -23
- swift-2.32.0.data/scripts/swift-dispersion-report +0 -24
- swift-2.32.0.data/scripts/swift-form-signature +0 -20
- swift-2.32.0.data/scripts/swift-init +0 -119
- swift-2.32.0.data/scripts/swift-object-auditor +0 -29
- swift-2.32.0.data/scripts/swift-object-expirer +0 -33
- swift-2.32.0.data/scripts/swift-object-info +0 -60
- swift-2.32.0.data/scripts/swift-object-reconstructor +0 -33
- swift-2.32.0.data/scripts/swift-object-relinker +0 -23
- swift-2.32.0.data/scripts/swift-object-replicator +0 -37
- swift-2.32.0.data/scripts/swift-object-server +0 -27
- swift-2.32.0.data/scripts/swift-object-updater +0 -23
- swift-2.32.0.data/scripts/swift-proxy-server +0 -23
- swift-2.32.0.data/scripts/swift-recon +0 -24
- swift-2.32.0.data/scripts/swift-ring-builder +0 -37
- swift-2.32.0.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.32.0.data/scripts/swift-ring-composer +0 -22
- swift-2.32.0.dist-info/pbr.json +0 -1
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/LICENSE +0 -0
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/WHEEL +0 -0
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/top_level.txt +0 -0
swift/obj/reconstructor.py
CHANGED
@@ -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
|
-
|
918
|
+
conn = http_connect(
|
917
919
|
node['replication_ip'], node['replication_port'],
|
918
920
|
node['device'], job['partition'], 'REPLICATE',
|
919
|
-
'', headers=headers)
|
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
|
-
|
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
|
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 >=
|
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
|
-
|
690
|
+
conn = http_connect(
|
679
691
|
node['replication_ip'], node['replication_port'],
|
680
692
|
node['device'], job['partition'], 'REPLICATE',
|
681
|
-
'', headers=headers)
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
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.
|
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'] =
|
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=
|
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
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
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
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
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=
|
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
|
-
|
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=
|
1119
|
-
|
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=
|
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
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
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()
|