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.
Files changed (206) hide show
  1. swift/__init__.py +29 -50
  2. swift/account/auditor.py +21 -118
  3. swift/account/backend.py +33 -28
  4. swift/account/reaper.py +37 -28
  5. swift/account/replicator.py +22 -0
  6. swift/account/server.py +60 -26
  7. swift/account/utils.py +28 -11
  8. swift-2.23.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +154 -14
  17. swift/cli/manage_shard_ranges.py +705 -37
  18. swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.3.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  23. swift/cli/relinker.py +807 -126
  24. swift/cli/reload.py +135 -0
  25. swift/cli/ringbuilder.py +217 -20
  26. swift/cli/ringcomposer.py +0 -1
  27. swift/cli/shard-info.py +4 -3
  28. swift/common/base_storage_server.py +9 -20
  29. swift/common/bufferedhttp.py +48 -74
  30. swift/common/constraints.py +20 -15
  31. swift/common/container_sync_realms.py +9 -11
  32. swift/common/daemon.py +25 -8
  33. swift/common/db.py +195 -128
  34. swift/common/db_auditor.py +168 -0
  35. swift/common/db_replicator.py +95 -55
  36. swift/common/digest.py +141 -0
  37. swift/common/direct_client.py +144 -33
  38. swift/common/error_limiter.py +93 -0
  39. swift/common/exceptions.py +25 -1
  40. swift/common/header_key_dict.py +2 -9
  41. swift/common/http_protocol.py +373 -0
  42. swift/common/internal_client.py +129 -59
  43. swift/common/linkat.py +3 -4
  44. swift/common/manager.py +284 -67
  45. swift/common/memcached.py +390 -145
  46. swift/common/middleware/__init__.py +4 -0
  47. swift/common/middleware/account_quotas.py +211 -46
  48. swift/common/middleware/acl.py +3 -8
  49. swift/common/middleware/backend_ratelimit.py +230 -0
  50. swift/common/middleware/bulk.py +22 -34
  51. swift/common/middleware/catch_errors.py +1 -3
  52. swift/common/middleware/cname_lookup.py +6 -11
  53. swift/common/middleware/container_quotas.py +1 -1
  54. swift/common/middleware/container_sync.py +39 -17
  55. swift/common/middleware/copy.py +12 -0
  56. swift/common/middleware/crossdomain.py +22 -9
  57. swift/common/middleware/crypto/__init__.py +2 -1
  58. swift/common/middleware/crypto/crypto_utils.py +11 -15
  59. swift/common/middleware/crypto/decrypter.py +28 -11
  60. swift/common/middleware/crypto/encrypter.py +12 -17
  61. swift/common/middleware/crypto/keymaster.py +8 -15
  62. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  63. swift/common/middleware/dlo.py +15 -11
  64. swift/common/middleware/domain_remap.py +5 -4
  65. swift/common/middleware/etag_quoter.py +128 -0
  66. swift/common/middleware/formpost.py +73 -70
  67. swift/common/middleware/gatekeeper.py +8 -1
  68. swift/common/middleware/keystoneauth.py +33 -3
  69. swift/common/middleware/list_endpoints.py +4 -4
  70. swift/common/middleware/listing_formats.py +85 -49
  71. swift/common/middleware/memcache.py +4 -95
  72. swift/common/middleware/name_check.py +3 -2
  73. swift/common/middleware/proxy_logging.py +160 -92
  74. swift/common/middleware/ratelimit.py +17 -10
  75. swift/common/middleware/read_only.py +6 -4
  76. swift/common/middleware/recon.py +59 -22
  77. swift/common/middleware/s3api/acl_handlers.py +25 -3
  78. swift/common/middleware/s3api/acl_utils.py +6 -1
  79. swift/common/middleware/s3api/controllers/__init__.py +6 -0
  80. swift/common/middleware/s3api/controllers/acl.py +3 -2
  81. swift/common/middleware/s3api/controllers/bucket.py +242 -137
  82. swift/common/middleware/s3api/controllers/logging.py +2 -2
  83. swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
  84. swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
  85. swift/common/middleware/s3api/controllers/obj.py +112 -8
  86. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  87. swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
  88. swift/common/middleware/s3api/controllers/tagging.py +57 -0
  89. swift/common/middleware/s3api/controllers/versioning.py +36 -7
  90. swift/common/middleware/s3api/etree.py +22 -9
  91. swift/common/middleware/s3api/exception.py +0 -4
  92. swift/common/middleware/s3api/s3api.py +113 -41
  93. swift/common/middleware/s3api/s3request.py +384 -218
  94. swift/common/middleware/s3api/s3response.py +126 -23
  95. swift/common/middleware/s3api/s3token.py +16 -17
  96. swift/common/middleware/s3api/schema/delete.rng +1 -1
  97. swift/common/middleware/s3api/subresource.py +7 -10
  98. swift/common/middleware/s3api/utils.py +27 -10
  99. swift/common/middleware/slo.py +665 -358
  100. swift/common/middleware/staticweb.py +64 -37
  101. swift/common/middleware/symlink.py +51 -18
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +191 -173
  104. swift/common/middleware/versioned_writes/__init__.py +51 -0
  105. swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
  106. swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
  107. swift/common/middleware/x_profile/exceptions.py +1 -4
  108. swift/common/middleware/x_profile/html_viewer.py +18 -19
  109. swift/common/middleware/x_profile/profile_model.py +1 -2
  110. swift/common/middleware/xprofile.py +10 -10
  111. swift-2.23.3.data/scripts/swift-container-server → swift/common/recon.py +13 -8
  112. swift/common/registry.py +147 -0
  113. swift/common/request_helpers.py +324 -57
  114. swift/common/ring/builder.py +67 -25
  115. swift/common/ring/composite_builder.py +1 -1
  116. swift/common/ring/ring.py +177 -51
  117. swift/common/ring/utils.py +1 -1
  118. swift/common/splice.py +10 -6
  119. swift/common/statsd_client.py +205 -0
  120. swift/common/storage_policy.py +49 -44
  121. swift/common/swob.py +86 -102
  122. swift/common/{utils.py → utils/__init__.py} +2163 -2772
  123. swift/common/utils/base.py +131 -0
  124. swift/common/utils/config.py +433 -0
  125. swift/common/utils/ipaddrs.py +256 -0
  126. swift/common/utils/libc.py +345 -0
  127. swift/common/utils/logs.py +859 -0
  128. swift/common/utils/timestamp.py +412 -0
  129. swift/common/wsgi.py +553 -535
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +490 -231
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +358 -165
  135. swift/container/sharder.py +1540 -684
  136. swift/container/sync.py +94 -88
  137. swift/container/updater.py +53 -32
  138. swift/obj/auditor.py +153 -35
  139. swift/obj/diskfile.py +466 -217
  140. swift/obj/expirer.py +406 -124
  141. swift/obj/mem_diskfile.py +7 -4
  142. swift/obj/mem_server.py +1 -0
  143. swift/obj/reconstructor.py +523 -262
  144. swift/obj/replicator.py +249 -188
  145. swift/obj/server.py +207 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +652 -139
  149. swift/obj/watchers/__init__.py +0 -0
  150. swift/obj/watchers/dark_data.py +213 -0
  151. swift/proxy/controllers/account.py +11 -11
  152. swift/proxy/controllers/base.py +848 -604
  153. swift/proxy/controllers/container.py +433 -92
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1000 -489
  156. swift/proxy/server.py +185 -112
  157. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
  158. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
  159. swift-2.35.0.dist-info/RECORD +201 -0
  160. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  161. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  162. swift-2.35.0.dist-info/pbr.json +1 -0
  163. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  164. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  165. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  166. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  167. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  168. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  169. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  170. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  171. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  172. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  173. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  174. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  175. swift-2.23.3.data/scripts/swift-account-auditor +0 -23
  176. swift-2.23.3.data/scripts/swift-account-info +0 -51
  177. swift-2.23.3.data/scripts/swift-account-reaper +0 -23
  178. swift-2.23.3.data/scripts/swift-account-replicator +0 -34
  179. swift-2.23.3.data/scripts/swift-account-server +0 -23
  180. swift-2.23.3.data/scripts/swift-container-auditor +0 -23
  181. swift-2.23.3.data/scripts/swift-container-info +0 -55
  182. swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
  183. swift-2.23.3.data/scripts/swift-container-replicator +0 -34
  184. swift-2.23.3.data/scripts/swift-container-sharder +0 -37
  185. swift-2.23.3.data/scripts/swift-container-sync +0 -23
  186. swift-2.23.3.data/scripts/swift-container-updater +0 -23
  187. swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
  188. swift-2.23.3.data/scripts/swift-form-signature +0 -20
  189. swift-2.23.3.data/scripts/swift-init +0 -119
  190. swift-2.23.3.data/scripts/swift-object-auditor +0 -29
  191. swift-2.23.3.data/scripts/swift-object-expirer +0 -33
  192. swift-2.23.3.data/scripts/swift-object-info +0 -60
  193. swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
  194. swift-2.23.3.data/scripts/swift-object-relinker +0 -41
  195. swift-2.23.3.data/scripts/swift-object-replicator +0 -37
  196. swift-2.23.3.data/scripts/swift-object-server +0 -27
  197. swift-2.23.3.data/scripts/swift-object-updater +0 -23
  198. swift-2.23.3.data/scripts/swift-proxy-server +0 -23
  199. swift-2.23.3.data/scripts/swift-recon +0 -24
  200. swift-2.23.3.data/scripts/swift-ring-builder +0 -24
  201. swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
  202. swift-2.23.3.data/scripts/swift-ring-composer +0 -22
  203. swift-2.23.3.dist-info/RECORD +0 -220
  204. swift-2.23.3.dist-info/pbr.json +0 -1
  205. {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
  206. {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``\s are normally assigned
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 six.moves.cPickle as pickle
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
- for dev in self.devs:
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
- gz_file = BufferedReader(GzipFile(filename, 'rb'))
108
-
109
- # See if the file is in the new format
110
- magic = gz_file.read(4)
111
- if magic == b'R1NG':
112
- format_version, = struct.unpack('!H', gz_file.read(2))
113
- if format_version == 1:
114
- ring_data = cls.deserialize_v1(
115
- gz_file, metadata_only=metadata_only)
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
- raise Exception('Unknown ring format version %d' %
118
- format_version)
119
- else:
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 not hasattr(ring_data, 'devs'):
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
- file_obj.write(part2dev_id.tostring())
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=15, ring_name=None,
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 and dev['id'] in dev_ids_with_parts:
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._num_devs += 1
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')).digest()
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._num_devs
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._num_devs:
634
+ if len(used) == self._num_assigned_devs:
509
635
  hit_all_devs = True
510
636
  break
@@ -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, six.integer_types):
86
- c_flags = six.moves.reduce(operator.or_, flags, 0)
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, six.integer_types):
177
- c_flags = six.moves.reduce(operator.or_, flags, 0)
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)