swift 2.23.3__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.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +154 -14
- swift/cli/manage_shard_ranges.py +705 -37
- swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.3.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 +195 -128
- 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 +390 -145
- 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 -95
- 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 +51 -18
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +191 -173
- 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.3.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} +2163 -2772
- 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 +553 -535
- swift/container/auditor.py +14 -100
- swift/container/backend.py +490 -231
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +358 -165
- swift/container/sharder.py +1540 -684
- 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 +207 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +652 -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 +433 -92
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1000 -489
- swift/proxy/server.py +185 -112
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.3.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.3.data/scripts/swift-account-auditor +0 -23
- swift-2.23.3.data/scripts/swift-account-info +0 -51
- swift-2.23.3.data/scripts/swift-account-reaper +0 -23
- swift-2.23.3.data/scripts/swift-account-replicator +0 -34
- swift-2.23.3.data/scripts/swift-account-server +0 -23
- swift-2.23.3.data/scripts/swift-container-auditor +0 -23
- swift-2.23.3.data/scripts/swift-container-info +0 -55
- swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.3.data/scripts/swift-container-replicator +0 -34
- swift-2.23.3.data/scripts/swift-container-sharder +0 -37
- swift-2.23.3.data/scripts/swift-container-sync +0 -23
- swift-2.23.3.data/scripts/swift-container-updater +0 -23
- swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.3.data/scripts/swift-form-signature +0 -20
- swift-2.23.3.data/scripts/swift-init +0 -119
- swift-2.23.3.data/scripts/swift-object-auditor +0 -29
- swift-2.23.3.data/scripts/swift-object-expirer +0 -33
- swift-2.23.3.data/scripts/swift-object-info +0 -60
- swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.3.data/scripts/swift-object-relinker +0 -41
- swift-2.23.3.data/scripts/swift-object-replicator +0 -37
- swift-2.23.3.data/scripts/swift-object-server +0 -27
- swift-2.23.3.data/scripts/swift-object-updater +0 -23
- swift-2.23.3.data/scripts/swift-proxy-server +0 -23
- swift-2.23.3.data/scripts/swift-recon +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.3.data/scripts/swift-ring-composer +0 -22
- swift-2.23.3.dist-info/RECORD +0 -220
- swift-2.23.3.dist-info/pbr.json +0 -1
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
@@ -79,7 +79,7 @@ rebuilding of the composite ring.
|
|
79
79
|
|
80
80
|
The ``id`` of each component RingBuilder is therefore stored in metadata of
|
81
81
|
the composite and used to check for the component ordering when the same
|
82
|
-
composite ring is re-composed. RingBuilder ``id
|
82
|
+
composite ring is re-composed. RingBuilder ``id``\\s are normally assigned
|
83
83
|
when a RingBuilder instance is first saved. Older RingBuilder instances
|
84
84
|
loaded from file may not have an ``id`` assigned and will need to be saved
|
85
85
|
before they can be used as components of a composite ring. This can be
|
swift/common/ring/ring.py
CHANGED
@@ -14,7 +14,9 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
16
|
import array
|
17
|
-
import
|
17
|
+
import contextlib
|
18
|
+
|
19
|
+
import pickle # nosec: B403
|
18
20
|
import json
|
19
21
|
from collections import defaultdict
|
20
22
|
from gzip import GzipFile
|
@@ -22,38 +24,121 @@ from os.path import getmtime
|
|
22
24
|
import struct
|
23
25
|
from time import time
|
24
26
|
import os
|
25
|
-
from io import BufferedReader
|
26
|
-
from hashlib import md5
|
27
27
|
from itertools import chain, count
|
28
28
|
from tempfile import NamedTemporaryFile
|
29
29
|
import sys
|
30
|
-
|
31
|
-
from six.moves import range
|
30
|
+
import zlib
|
32
31
|
|
33
32
|
from swift.common.exceptions import RingLoadError
|
34
|
-
from swift.common.utils import hash_path, validate_configuration
|
33
|
+
from swift.common.utils import hash_path, validate_configuration, md5
|
35
34
|
from swift.common.ring.utils import tiers_for_dev
|
36
35
|
|
37
36
|
|
37
|
+
DEFAULT_RELOAD_TIME = 15
|
38
|
+
|
39
|
+
|
38
40
|
def calc_replica_count(replica2part2dev_id):
|
41
|
+
if not replica2part2dev_id:
|
42
|
+
return 0
|
39
43
|
base = len(replica2part2dev_id) - 1
|
40
44
|
extra = 1.0 * len(replica2part2dev_id[-1]) / len(replica2part2dev_id[0])
|
41
45
|
return base + extra
|
42
46
|
|
43
47
|
|
48
|
+
def normalize_devices(devs):
|
49
|
+
# NOTE(akscram): Replication parameters like replication_ip
|
50
|
+
# and replication_port are required for
|
51
|
+
# replication process. An old replication
|
52
|
+
# ring doesn't contain this parameters into
|
53
|
+
# device. Old-style pickled rings won't have
|
54
|
+
# region information.
|
55
|
+
for dev in devs:
|
56
|
+
if dev is None:
|
57
|
+
continue
|
58
|
+
dev.setdefault('region', 1)
|
59
|
+
if 'ip' in dev:
|
60
|
+
dev.setdefault('replication_ip', dev['ip'])
|
61
|
+
if 'port' in dev:
|
62
|
+
dev.setdefault('replication_port', dev['port'])
|
63
|
+
|
64
|
+
|
65
|
+
class RingReader(object):
|
66
|
+
chunk_size = 2 ** 16
|
67
|
+
|
68
|
+
def __init__(self, filename):
|
69
|
+
self.fp = open(filename, 'rb')
|
70
|
+
self._reset()
|
71
|
+
|
72
|
+
def _reset(self):
|
73
|
+
self._buffer = b''
|
74
|
+
self.size = 0
|
75
|
+
self.raw_size = 0
|
76
|
+
self._md5 = md5(usedforsecurity=False)
|
77
|
+
self._decomp = zlib.decompressobj(32 + zlib.MAX_WBITS)
|
78
|
+
|
79
|
+
@property
|
80
|
+
def close(self):
|
81
|
+
return self.fp.close
|
82
|
+
|
83
|
+
def seek(self, pos, ref=0):
|
84
|
+
if (pos, ref) != (0, 0):
|
85
|
+
raise NotImplementedError
|
86
|
+
self._reset()
|
87
|
+
return self.fp.seek(pos, ref)
|
88
|
+
|
89
|
+
def _buffer_chunk(self):
|
90
|
+
chunk = self.fp.read(self.chunk_size)
|
91
|
+
if not chunk:
|
92
|
+
return False
|
93
|
+
self.size += len(chunk)
|
94
|
+
self._md5.update(chunk)
|
95
|
+
chunk = self._decomp.decompress(chunk)
|
96
|
+
self.raw_size += len(chunk)
|
97
|
+
self._buffer += chunk
|
98
|
+
return True
|
99
|
+
|
100
|
+
def read(self, amount=-1):
|
101
|
+
if amount < 0:
|
102
|
+
raise IOError("don't be greedy")
|
103
|
+
|
104
|
+
while amount > len(self._buffer):
|
105
|
+
if not self._buffer_chunk():
|
106
|
+
break
|
107
|
+
|
108
|
+
result, self._buffer = self._buffer[:amount], self._buffer[amount:]
|
109
|
+
return result
|
110
|
+
|
111
|
+
def readline(self):
|
112
|
+
# apparently pickle needs this?
|
113
|
+
while b'\n' not in self._buffer:
|
114
|
+
if not self._buffer_chunk():
|
115
|
+
break
|
116
|
+
|
117
|
+
line, sep, self._buffer = self._buffer.partition(b'\n')
|
118
|
+
return line + sep
|
119
|
+
|
120
|
+
def readinto(self, buffer):
|
121
|
+
chunk = self.read(len(buffer))
|
122
|
+
buffer[:len(chunk)] = chunk
|
123
|
+
return len(chunk)
|
124
|
+
|
125
|
+
@property
|
126
|
+
def md5(self):
|
127
|
+
return self._md5.hexdigest()
|
128
|
+
|
129
|
+
|
44
130
|
class RingData(object):
|
45
131
|
"""Partitioned consistent hashing ring data (used for serialization)."""
|
46
132
|
|
47
133
|
def __init__(self, replica2part2dev_id, devs, part_shift,
|
48
|
-
next_part_power=None):
|
134
|
+
next_part_power=None, version=None):
|
135
|
+
normalize_devices(devs)
|
49
136
|
self.devs = devs
|
50
137
|
self._replica2part2dev_id = replica2part2dev_id
|
51
138
|
self._part_shift = part_shift
|
52
139
|
self.next_part_power = next_part_power
|
53
|
-
|
54
|
-
|
55
|
-
if dev is not None:
|
56
|
-
dev.setdefault("region", 1)
|
140
|
+
self.version = version
|
141
|
+
self.md5 = self.size = self.raw_size = None
|
57
142
|
|
58
143
|
@property
|
59
144
|
def replica_count(self):
|
@@ -104,27 +189,32 @@ class RingData(object):
|
|
104
189
|
:param bool metadata_only: If True, only load `devs` and `part_shift`.
|
105
190
|
:returns: A RingData instance containing the loaded data.
|
106
191
|
"""
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
192
|
+
with contextlib.closing(RingReader(filename)) as gz_file:
|
193
|
+
# See if the file is in the new format
|
194
|
+
magic = gz_file.read(4)
|
195
|
+
if magic == b'R1NG':
|
196
|
+
format_version, = struct.unpack('!H', gz_file.read(2))
|
197
|
+
if format_version == 1:
|
198
|
+
ring_data = cls.deserialize_v1(
|
199
|
+
gz_file, metadata_only=metadata_only)
|
200
|
+
else:
|
201
|
+
raise Exception('Unknown ring format version %d' %
|
202
|
+
format_version)
|
116
203
|
else:
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# Assume old-style pickled ring
|
121
|
-
gz_file.seek(0)
|
122
|
-
ring_data = pickle.load(gz_file)
|
204
|
+
# Assume old-style pickled ring
|
205
|
+
gz_file.seek(0)
|
206
|
+
ring_data = pickle.load(gz_file) # nosec: B301
|
123
207
|
|
124
|
-
if
|
208
|
+
if hasattr(ring_data, 'devs'):
|
209
|
+
# pickled RingData; make sure we've got region/replication info
|
210
|
+
normalize_devices(ring_data.devs)
|
211
|
+
else:
|
125
212
|
ring_data = RingData(ring_data['replica2part2dev_id'],
|
126
213
|
ring_data['devs'], ring_data['part_shift'],
|
127
|
-
ring_data.get('next_part_power')
|
214
|
+
ring_data.get('next_part_power'),
|
215
|
+
ring_data.get('version'))
|
216
|
+
for attr in ('md5', 'size', 'raw_size'):
|
217
|
+
setattr(ring_data, attr, getattr(gz_file, attr))
|
128
218
|
return ring_data
|
129
219
|
|
130
220
|
def serialize_v1(self, file_obj):
|
@@ -138,6 +228,9 @@ class RingData(object):
|
|
138
228
|
'replica_count': len(ring['replica2part2dev_id']),
|
139
229
|
'byteorder': sys.byteorder}
|
140
230
|
|
231
|
+
if ring['version'] is not None:
|
232
|
+
_text['version'] = ring['version']
|
233
|
+
|
141
234
|
next_part_power = ring.get('next_part_power')
|
142
235
|
if next_part_power is not None:
|
143
236
|
_text['next_part_power'] = next_part_power
|
@@ -148,7 +241,7 @@ class RingData(object):
|
|
148
241
|
file_obj.write(struct.pack('!I', json_len))
|
149
242
|
file_obj.write(json_text)
|
150
243
|
for part2dev_id in ring['replica2part2dev_id']:
|
151
|
-
|
244
|
+
part2dev_id.tofile(file_obj)
|
152
245
|
|
153
246
|
def save(self, filename, mtime=1300507380.0):
|
154
247
|
"""
|
@@ -175,7 +268,8 @@ class RingData(object):
|
|
175
268
|
return {'devs': self.devs,
|
176
269
|
'replica2part2dev_id': self._replica2part2dev_id,
|
177
270
|
'part_shift': self._part_shift,
|
178
|
-
'next_part_power': self.next_part_power
|
271
|
+
'next_part_power': self.next_part_power,
|
272
|
+
'version': self.version}
|
179
273
|
|
180
274
|
|
181
275
|
class Ring(object):
|
@@ -190,7 +284,7 @@ class Ring(object):
|
|
190
284
|
:raises RingLoadError: if the loaded ring data violates its constraint
|
191
285
|
"""
|
192
286
|
|
193
|
-
def __init__(self, serialized_path, reload_time=
|
287
|
+
def __init__(self, serialized_path, reload_time=None, ring_name=None,
|
194
288
|
validation_hook=lambda ring_data: None):
|
195
289
|
# can't use the ring unless HASH_PATH_SUFFIX is set
|
196
290
|
validate_configuration()
|
@@ -199,7 +293,8 @@ class Ring(object):
|
|
199
293
|
ring_name + '.ring.gz')
|
200
294
|
else:
|
201
295
|
self.serialized_path = os.path.join(serialized_path)
|
202
|
-
self.reload_time = reload_time
|
296
|
+
self.reload_time = (DEFAULT_RELOAD_TIME if reload_time is None
|
297
|
+
else reload_time)
|
203
298
|
self._validation_hook = validation_hook
|
204
299
|
self._reload(force=True)
|
205
300
|
|
@@ -220,25 +315,15 @@ class Ring(object):
|
|
220
315
|
|
221
316
|
self._mtime = getmtime(self.serialized_path)
|
222
317
|
self._devs = ring_data.devs
|
223
|
-
# NOTE(akscram): Replication parameters like replication_ip
|
224
|
-
# and replication_port are required for
|
225
|
-
# replication process. An old replication
|
226
|
-
# ring doesn't contain this parameters into
|
227
|
-
# device. Old-style pickled rings won't have
|
228
|
-
# region information.
|
229
|
-
for dev in self._devs:
|
230
|
-
if dev:
|
231
|
-
dev.setdefault('region', 1)
|
232
|
-
if 'ip' in dev:
|
233
|
-
dev.setdefault('replication_ip', dev['ip'])
|
234
|
-
if 'port' in dev:
|
235
|
-
dev.setdefault('replication_port', dev['port'])
|
236
|
-
|
237
318
|
self._replica2part2dev_id = ring_data._replica2part2dev_id
|
238
319
|
self._part_shift = ring_data._part_shift
|
239
320
|
self._rebuild_tier_data()
|
240
321
|
self._update_bookkeeping()
|
241
322
|
self._next_part_power = ring_data.next_part_power
|
323
|
+
self._version = ring_data.version
|
324
|
+
self._md5 = ring_data.md5
|
325
|
+
self._size = ring_data.size
|
326
|
+
self._raw_size = ring_data.raw_size
|
242
327
|
|
243
328
|
def _update_bookkeeping(self):
|
244
329
|
# Do this now, when we know the data has changed, rather than
|
@@ -257,24 +342,49 @@ class Ring(object):
|
|
257
342
|
zones = set()
|
258
343
|
ips = set()
|
259
344
|
self._num_devs = 0
|
345
|
+
self._num_assigned_devs = 0
|
346
|
+
self._num_weighted_devs = 0
|
260
347
|
for dev in self._devs:
|
261
|
-
if dev
|
348
|
+
if dev is None:
|
349
|
+
continue
|
350
|
+
self._num_devs += 1
|
351
|
+
if dev.get('weight', 0) > 0:
|
352
|
+
self._num_weighted_devs += 1
|
353
|
+
if dev['id'] in dev_ids_with_parts:
|
262
354
|
regions.add(dev['region'])
|
263
355
|
zones.add((dev['region'], dev['zone']))
|
264
356
|
ips.add((dev['region'], dev['zone'], dev['ip']))
|
265
|
-
self.
|
357
|
+
self._num_assigned_devs += 1
|
266
358
|
self._num_regions = len(regions)
|
267
359
|
self._num_zones = len(zones)
|
268
360
|
self._num_ips = len(ips)
|
269
361
|
|
270
362
|
@property
|
271
363
|
def next_part_power(self):
|
364
|
+
if time() > self._rtime:
|
365
|
+
self._reload()
|
272
366
|
return self._next_part_power
|
273
367
|
|
274
368
|
@property
|
275
369
|
def part_power(self):
|
276
370
|
return 32 - self._part_shift
|
277
371
|
|
372
|
+
@property
|
373
|
+
def version(self):
|
374
|
+
return self._version
|
375
|
+
|
376
|
+
@property
|
377
|
+
def md5(self):
|
378
|
+
return self._md5
|
379
|
+
|
380
|
+
@property
|
381
|
+
def size(self):
|
382
|
+
return self._size
|
383
|
+
|
384
|
+
@property
|
385
|
+
def raw_size(self):
|
386
|
+
return self._raw_size
|
387
|
+
|
278
388
|
def _rebuild_tier_data(self):
|
279
389
|
self.tier2devs = defaultdict(list)
|
280
390
|
for dev in self._devs:
|
@@ -301,6 +411,21 @@ class Ring(object):
|
|
301
411
|
"""Number of partitions in the ring."""
|
302
412
|
return len(self._replica2part2dev_id[0])
|
303
413
|
|
414
|
+
@property
|
415
|
+
def device_count(self):
|
416
|
+
"""Number of devices in the ring."""
|
417
|
+
return self._num_devs
|
418
|
+
|
419
|
+
@property
|
420
|
+
def weighted_device_count(self):
|
421
|
+
"""Number of devices with weight in the ring."""
|
422
|
+
return self._num_weighted_devs
|
423
|
+
|
424
|
+
@property
|
425
|
+
def assigned_device_count(self):
|
426
|
+
"""Number of devices with assignments in the ring."""
|
427
|
+
return self._num_assigned_devs
|
428
|
+
|
304
429
|
@property
|
305
430
|
def devs(self):
|
306
431
|
"""devices in the ring"""
|
@@ -416,7 +541,8 @@ class Ring(object):
|
|
416
541
|
(d['region'], d['zone'], d['ip']) for d in primary_nodes)
|
417
542
|
|
418
543
|
parts = len(self._replica2part2dev_id[0])
|
419
|
-
part_hash = md5(str(part).encode('ascii')
|
544
|
+
part_hash = md5(str(part).encode('ascii'),
|
545
|
+
usedforsecurity=False).digest()
|
420
546
|
start = struct.unpack_from('>I', part_hash)[0] >> self._part_shift
|
421
547
|
inc = int(parts / 65536) or 1
|
422
548
|
# Multiple loops for execution speed; the checks and bookkeeping get
|
@@ -490,7 +616,7 @@ class Ring(object):
|
|
490
616
|
hit_all_ips = True
|
491
617
|
break
|
492
618
|
|
493
|
-
hit_all_devs = len(used) == self.
|
619
|
+
hit_all_devs = len(used) == self._num_assigned_devs
|
494
620
|
for handoff_part in chain(range(start, parts, inc),
|
495
621
|
range(inc - ((parts - start) % inc),
|
496
622
|
start, inc)):
|
@@ -505,6 +631,6 @@ class Ring(object):
|
|
505
631
|
dev = self._devs[dev_id]
|
506
632
|
yield dict(dev, handoff_index=next(index))
|
507
633
|
used.add(dev_id)
|
508
|
-
if len(used) == self.
|
634
|
+
if len(used) == self._num_assigned_devs:
|
509
635
|
hit_all_devs = True
|
510
636
|
break
|
swift/common/ring/utils.py
CHANGED
@@ -199,7 +199,7 @@ def is_valid_hostname(hostname):
|
|
199
199
|
if hostname.endswith('.'):
|
200
200
|
# strip exactly one dot from the right, if present
|
201
201
|
hostname = hostname[:-1]
|
202
|
-
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
202
|
+
allowed = re.compile(r"(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
203
203
|
return all(allowed.match(x) for x in hostname.split("."))
|
204
204
|
|
205
205
|
|
swift/common/splice.py
CHANGED
@@ -18,8 +18,6 @@ Bindings to the `tee` and `splice` system calls
|
|
18
18
|
'''
|
19
19
|
|
20
20
|
import os
|
21
|
-
import operator
|
22
|
-
import six
|
23
21
|
import ctypes
|
24
22
|
import ctypes.util
|
25
23
|
|
@@ -82,8 +80,10 @@ class Tee(object):
|
|
82
80
|
if not self.available:
|
83
81
|
raise EnvironmentError('tee not available')
|
84
82
|
|
85
|
-
if not isinstance(flags,
|
86
|
-
c_flags =
|
83
|
+
if not isinstance(flags, int):
|
84
|
+
c_flags = 0
|
85
|
+
for flag in flags:
|
86
|
+
c_flags |= flag
|
87
87
|
else:
|
88
88
|
c_flags = flags
|
89
89
|
|
@@ -98,6 +98,7 @@ class Tee(object):
|
|
98
98
|
|
99
99
|
return self._c_tee is not None
|
100
100
|
|
101
|
+
|
101
102
|
tee = Tee()
|
102
103
|
del Tee
|
103
104
|
|
@@ -173,8 +174,10 @@ class Splice(object):
|
|
173
174
|
if not self.available:
|
174
175
|
raise EnvironmentError('splice not available')
|
175
176
|
|
176
|
-
if not isinstance(flags,
|
177
|
-
c_flags =
|
177
|
+
if not isinstance(flags, int):
|
178
|
+
c_flags = 0
|
179
|
+
for flag in flags:
|
180
|
+
c_flags |= flag
|
178
181
|
else:
|
179
182
|
c_flags = flags
|
180
183
|
|
@@ -195,5 +198,6 @@ class Splice(object):
|
|
195
198
|
|
196
199
|
return self._c_splice is not None
|
197
200
|
|
201
|
+
|
198
202
|
splice = Splice()
|
199
203
|
del Splice
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# Copyright (c) 2010-2012 OpenStack Foundation
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
""" Statsd Client """
|
17
|
+
|
18
|
+
import time
|
19
|
+
import warnings
|
20
|
+
from contextlib import closing
|
21
|
+
from random import random
|
22
|
+
|
23
|
+
from eventlet.green import socket
|
24
|
+
|
25
|
+
|
26
|
+
def get_statsd_client(conf=None, tail_prefix='', logger=None):
|
27
|
+
"""
|
28
|
+
Get an instance of StatsdClient using config settings.
|
29
|
+
|
30
|
+
**config and defaults**::
|
31
|
+
|
32
|
+
log_statsd_host = (disabled)
|
33
|
+
log_statsd_port = 8125
|
34
|
+
log_statsd_default_sample_rate = 1.0
|
35
|
+
log_statsd_sample_rate_factor = 1.0
|
36
|
+
log_statsd_metric_prefix = (empty-string)
|
37
|
+
|
38
|
+
:param conf: Configuration dict to read settings from
|
39
|
+
:param tail_prefix: tail prefix to pass to statsd client
|
40
|
+
:param logger: stdlib logger instance used by statsd client for logging
|
41
|
+
:return: an instance of ``StatsdClient``
|
42
|
+
|
43
|
+
"""
|
44
|
+
conf = conf or {}
|
45
|
+
|
46
|
+
host = conf.get('log_statsd_host')
|
47
|
+
port = int(conf.get('log_statsd_port', 8125))
|
48
|
+
base_prefix = conf.get('log_statsd_metric_prefix', '')
|
49
|
+
default_sample_rate = float(
|
50
|
+
conf.get('log_statsd_default_sample_rate', 1))
|
51
|
+
sample_rate_factor = float(
|
52
|
+
conf.get('log_statsd_sample_rate_factor', 1))
|
53
|
+
|
54
|
+
return StatsdClient(host, port, base_prefix=base_prefix,
|
55
|
+
tail_prefix=tail_prefix,
|
56
|
+
default_sample_rate=default_sample_rate,
|
57
|
+
sample_rate_factor=sample_rate_factor, logger=logger)
|
58
|
+
|
59
|
+
|
60
|
+
class StatsdClient(object):
|
61
|
+
def __init__(self, host, port, base_prefix='', tail_prefix='',
|
62
|
+
default_sample_rate=1, sample_rate_factor=1, logger=None):
|
63
|
+
self._host = host
|
64
|
+
self._port = port
|
65
|
+
self._base_prefix = base_prefix
|
66
|
+
self._default_sample_rate = default_sample_rate
|
67
|
+
self._sample_rate_factor = sample_rate_factor
|
68
|
+
self.random = random
|
69
|
+
self.logger = logger
|
70
|
+
self._set_prefix(tail_prefix)
|
71
|
+
self._sock_family = self._target = None
|
72
|
+
|
73
|
+
if self._host:
|
74
|
+
self._set_sock_family_and_target(self._host, self._port)
|
75
|
+
|
76
|
+
def _set_sock_family_and_target(self, host, port):
|
77
|
+
# Determine if host is IPv4 or IPv6
|
78
|
+
addr_info = None
|
79
|
+
try:
|
80
|
+
addr_info = socket.getaddrinfo(host, port, socket.AF_INET)
|
81
|
+
self._sock_family = socket.AF_INET
|
82
|
+
except socket.gaierror:
|
83
|
+
try:
|
84
|
+
addr_info = socket.getaddrinfo(host, port, socket.AF_INET6)
|
85
|
+
self._sock_family = socket.AF_INET6
|
86
|
+
except socket.gaierror:
|
87
|
+
# Don't keep the server from starting from what could be a
|
88
|
+
# transient DNS failure. Any hostname will get re-resolved as
|
89
|
+
# necessary in the .sendto() calls.
|
90
|
+
# However, we don't know if we're IPv4 or IPv6 in this case, so
|
91
|
+
# we assume legacy IPv4.
|
92
|
+
self._sock_family = socket.AF_INET
|
93
|
+
|
94
|
+
# NOTE: we use the original host value, not the DNS-resolved one
|
95
|
+
# because if host is a hostname, we don't want to cache the DNS
|
96
|
+
# resolution for the entire lifetime of this process. Let standard
|
97
|
+
# name resolution caching take effect. This should help operators use
|
98
|
+
# DNS trickery if they want.
|
99
|
+
if addr_info is not None:
|
100
|
+
# addr_info is a list of 5-tuples with the following structure:
|
101
|
+
# (family, socktype, proto, canonname, sockaddr)
|
102
|
+
# where sockaddr is the only thing of interest to us, and we only
|
103
|
+
# use the first result. We want to use the originally supplied
|
104
|
+
# host (see note above) and the remainder of the variable-length
|
105
|
+
# sockaddr: IPv4 has (address, port) while IPv6 has (address,
|
106
|
+
# port, flow info, scope id).
|
107
|
+
sockaddr = addr_info[0][-1]
|
108
|
+
self._target = (host,) + (sockaddr[1:])
|
109
|
+
else:
|
110
|
+
self._target = (host, port)
|
111
|
+
|
112
|
+
def _set_prefix(self, tail_prefix):
|
113
|
+
"""
|
114
|
+
Modifies the prefix that is added to metric names. The resulting prefix
|
115
|
+
is the concatenation of the component parts `base_prefix` and
|
116
|
+
`tail_prefix`. Only truthy components are included. Each included
|
117
|
+
component is followed by a period, e.g.::
|
118
|
+
|
119
|
+
<base_prefix>.<tail_prefix>.
|
120
|
+
<tail_prefix>.
|
121
|
+
<base_prefix>.
|
122
|
+
<the empty string>
|
123
|
+
|
124
|
+
Note: this method is expected to be called from the constructor only,
|
125
|
+
but exists to provide backwards compatible functionality for the
|
126
|
+
deprecated set_prefix() method.
|
127
|
+
|
128
|
+
:param tail_prefix: The new value of tail_prefix
|
129
|
+
"""
|
130
|
+
if tail_prefix and self._base_prefix:
|
131
|
+
self._prefix = '.'.join([self._base_prefix, tail_prefix, ''])
|
132
|
+
elif tail_prefix:
|
133
|
+
self._prefix = tail_prefix + '.'
|
134
|
+
elif self._base_prefix:
|
135
|
+
self._prefix = self._base_prefix + '.'
|
136
|
+
else:
|
137
|
+
self._prefix = ''
|
138
|
+
|
139
|
+
def set_prefix(self, tail_prefix):
|
140
|
+
"""
|
141
|
+
This method is deprecated; use the ``tail_prefix`` argument of the
|
142
|
+
constructor when instantiating the class instead.
|
143
|
+
"""
|
144
|
+
warnings.warn(
|
145
|
+
'set_prefix() is deprecated; use the ``tail_prefix`` argument of '
|
146
|
+
'the constructor when instantiating the class instead.',
|
147
|
+
DeprecationWarning, stacklevel=2
|
148
|
+
)
|
149
|
+
self._set_prefix(tail_prefix)
|
150
|
+
|
151
|
+
def _send(self, m_name, m_value, m_type, sample_rate):
|
152
|
+
if not self._host:
|
153
|
+
# StatsD not configured
|
154
|
+
return
|
155
|
+
|
156
|
+
if sample_rate is None:
|
157
|
+
sample_rate = self._default_sample_rate
|
158
|
+
sample_rate = sample_rate * self._sample_rate_factor
|
159
|
+
|
160
|
+
parts = ['%s%s:%s' % (self._prefix, m_name, m_value), m_type]
|
161
|
+
if sample_rate < 1:
|
162
|
+
if self.random() < sample_rate:
|
163
|
+
parts.append('@%s' % (sample_rate,))
|
164
|
+
else:
|
165
|
+
return
|
166
|
+
parts = [part.encode('utf-8') for part in parts]
|
167
|
+
# Ideally, we'd cache a sending socket in self, but that
|
168
|
+
# results in a socket getting shared by multiple green threads.
|
169
|
+
with closing(self._open_socket()) as sock:
|
170
|
+
try:
|
171
|
+
return sock.sendto(b'|'.join(parts), self._target)
|
172
|
+
except IOError as err:
|
173
|
+
if self.logger:
|
174
|
+
self.logger.warning(
|
175
|
+
'Error sending UDP message to %(target)r: %(err)s',
|
176
|
+
{'target': self._target, 'err': err})
|
177
|
+
|
178
|
+
def _open_socket(self):
|
179
|
+
return socket.socket(self._sock_family, socket.SOCK_DGRAM)
|
180
|
+
|
181
|
+
def update_stats(self, m_name, m_value, sample_rate=None):
|
182
|
+
return self._send(m_name, m_value, 'c', sample_rate)
|
183
|
+
|
184
|
+
def increment(self, metric, sample_rate=None):
|
185
|
+
return self.update_stats(metric, 1, sample_rate)
|
186
|
+
|
187
|
+
def decrement(self, metric, sample_rate=None):
|
188
|
+
return self.update_stats(metric, -1, sample_rate)
|
189
|
+
|
190
|
+
def _timing(self, metric, timing_ms, sample_rate):
|
191
|
+
# This method was added to disagregate timing metrics when testing
|
192
|
+
return self._send(metric, round(timing_ms, 4), 'ms', sample_rate)
|
193
|
+
|
194
|
+
def timing(self, metric, timing_ms, sample_rate=None):
|
195
|
+
return self._timing(metric, timing_ms, sample_rate)
|
196
|
+
|
197
|
+
def timing_since(self, metric, orig_time, sample_rate=None):
|
198
|
+
return self._timing(metric, (time.time() - orig_time) * 1000,
|
199
|
+
sample_rate)
|
200
|
+
|
201
|
+
def transfer_rate(self, metric, elapsed_time, byte_xfer, sample_rate=None):
|
202
|
+
if byte_xfer:
|
203
|
+
return self.timing(metric,
|
204
|
+
elapsed_time * 1000 / byte_xfer * 1000,
|
205
|
+
sample_rate)
|