swift 2.23.2__py3-none-any.whl → 2.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) 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.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +183 -29
  17. swift/cli/manage_shard_ranges.py +708 -37
  18. swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.2.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 +198 -127
  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 +396 -147
  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 -81
  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 +52 -19
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +192 -174
  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.2.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} +2191 -2762
  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 +555 -536
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +552 -227
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +397 -176
  135. swift/container/sharder.py +1580 -639
  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 +213 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +653 -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 +452 -86
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1009 -490
  156. swift/proxy/server.py +185 -112
  157. swift-2.35.0.dist-info/AUTHORS +501 -0
  158. swift-2.35.0.dist-info/LICENSE +202 -0
  159. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
  160. swift-2.35.0.dist-info/RECORD +201 -0
  161. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  162. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  163. swift-2.35.0.dist-info/pbr.json +1 -0
  164. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  165. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  166. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  167. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  168. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  169. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  170. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  171. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  172. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  173. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  174. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  175. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  176. swift-2.23.2.data/scripts/swift-account-auditor +0 -23
  177. swift-2.23.2.data/scripts/swift-account-info +0 -51
  178. swift-2.23.2.data/scripts/swift-account-reaper +0 -23
  179. swift-2.23.2.data/scripts/swift-account-replicator +0 -34
  180. swift-2.23.2.data/scripts/swift-account-server +0 -23
  181. swift-2.23.2.data/scripts/swift-container-auditor +0 -23
  182. swift-2.23.2.data/scripts/swift-container-info +0 -51
  183. swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
  184. swift-2.23.2.data/scripts/swift-container-replicator +0 -34
  185. swift-2.23.2.data/scripts/swift-container-sharder +0 -33
  186. swift-2.23.2.data/scripts/swift-container-sync +0 -23
  187. swift-2.23.2.data/scripts/swift-container-updater +0 -23
  188. swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
  189. swift-2.23.2.data/scripts/swift-form-signature +0 -20
  190. swift-2.23.2.data/scripts/swift-init +0 -119
  191. swift-2.23.2.data/scripts/swift-object-auditor +0 -29
  192. swift-2.23.2.data/scripts/swift-object-expirer +0 -33
  193. swift-2.23.2.data/scripts/swift-object-info +0 -60
  194. swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
  195. swift-2.23.2.data/scripts/swift-object-relinker +0 -41
  196. swift-2.23.2.data/scripts/swift-object-replicator +0 -37
  197. swift-2.23.2.data/scripts/swift-object-server +0 -27
  198. swift-2.23.2.data/scripts/swift-object-updater +0 -23
  199. swift-2.23.2.data/scripts/swift-proxy-server +0 -23
  200. swift-2.23.2.data/scripts/swift-recon +0 -24
  201. swift-2.23.2.data/scripts/swift-ring-builder +0 -24
  202. swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
  203. swift-2.23.2.data/scripts/swift-ring-composer +0 -22
  204. swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
  205. swift-2.23.2.dist-info/RECORD +0 -220
  206. swift-2.23.2.dist-info/metadata.json +0 -1
  207. swift-2.23.2.dist-info/pbr.json +0 -1
  208. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,859 @@
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
+
17
+ import errno
18
+ import hashlib
19
+ import logging
20
+ from logging.handlers import SysLogHandler
21
+ import os
22
+ import socket
23
+ import stat
24
+ import string
25
+ import sys
26
+ import time
27
+ import fcntl
28
+ import eventlet
29
+ import datetime
30
+
31
+ from swift.common.utils.base import md5, quote, split_path
32
+ from swift.common.utils.timestamp import UTC
33
+ from swift.common.utils.config import config_true_value
34
+ # common.utils imports a fully qualified common.exceptions so that
35
+ # common.exceptions can import common.utils with out a circular import error
36
+ # (if we only make reference to attributes of a module w/i our function/method
37
+ # bodies fully qualifed module names can have their attributes lazily
38
+ # evaluated); as the only other module with-in utils that imports exceptions:
39
+ # we do the same here
40
+ import swift.common.exceptions
41
+
42
+ from eventlet.green.http import client as green_http_client
43
+ import http.client
44
+ from eventlet.green import threading
45
+
46
+
47
+ NOTICE = 25
48
+
49
+ LOG_LINE_DEFAULT_FORMAT = '{remote_addr} - - [{time.d}/{time.b}/{time.Y}' \
50
+ ':{time.H}:{time.M}:{time.S} +0000] ' \
51
+ '"{method} {path}" {status} {content_length} ' \
52
+ '"{referer}" "{txn_id}" "{user_agent}" ' \
53
+ '{trans_time:.4f} "{additional_info}" {pid} ' \
54
+ '{policy_index}'
55
+
56
+
57
+ def logging_monkey_patch():
58
+ # explicitly patch the logging lock
59
+ logging._lock = logging.threading.RLock()
60
+ # setup notice level logging
61
+ logging.addLevelName(NOTICE, 'NOTICE')
62
+ SysLogHandler.priority_map['NOTICE'] = 'notice'
63
+ # Trying to log threads while monkey-patched can lead to deadlocks; see
64
+ # https://bugs.launchpad.net/swift/+bug/1895739
65
+ logging.logThreads = 0
66
+
67
+
68
+ class PipeMutex(object):
69
+ """
70
+ Mutex using a pipe. Works across both greenlets and real threads, even
71
+ at the same time.
72
+ """
73
+
74
+ def __init__(self):
75
+ self.rfd, self.wfd = os.pipe()
76
+
77
+ # You can't create a pipe in non-blocking mode; you must set it
78
+ # later.
79
+ rflags = fcntl.fcntl(self.rfd, fcntl.F_GETFL)
80
+ fcntl.fcntl(self.rfd, fcntl.F_SETFL, rflags | os.O_NONBLOCK)
81
+ os.write(self.wfd, b'-') # start unlocked
82
+
83
+ self.owner = None
84
+ self.recursion_depth = 0
85
+
86
+ # Usually, it's an error to have multiple greenthreads all waiting
87
+ # to read the same file descriptor. It's often a sign of inadequate
88
+ # concurrency control; for example, if you have two greenthreads
89
+ # trying to use the same memcache connection, they'll end up writing
90
+ # interleaved garbage to the socket or stealing part of each others'
91
+ # responses.
92
+ #
93
+ # In this case, we have multiple greenthreads waiting on the same
94
+ # file descriptor by design. This lets greenthreads in real thread A
95
+ # wait with greenthreads in real thread B for the same mutex.
96
+ # Therefore, we must turn off eventlet's multiple-reader detection.
97
+ #
98
+ # It would be better to turn off multiple-reader detection for only
99
+ # our calls to trampoline(), but eventlet does not support that.
100
+ eventlet.debug.hub_prevent_multiple_readers(False)
101
+
102
+ def acquire(self, blocking=True):
103
+ """
104
+ Acquire the mutex.
105
+
106
+ If called with blocking=False, returns True if the mutex was
107
+ acquired and False if it wasn't. Otherwise, blocks until the mutex
108
+ is acquired and returns True.
109
+
110
+ This lock is recursive; the same greenthread may acquire it as many
111
+ times as it wants to, though it must then release it that many times
112
+ too.
113
+ """
114
+ current_greenthread_id = id(eventlet.greenthread.getcurrent())
115
+ if self.owner == current_greenthread_id:
116
+ self.recursion_depth += 1
117
+ return True
118
+
119
+ while True:
120
+ try:
121
+ # If there is a byte available, this will read it and remove
122
+ # it from the pipe. If not, this will raise OSError with
123
+ # errno=EAGAIN.
124
+ os.read(self.rfd, 1)
125
+ self.owner = current_greenthread_id
126
+ return True
127
+ except OSError as err:
128
+ if err.errno != errno.EAGAIN:
129
+ raise
130
+
131
+ if not blocking:
132
+ return False
133
+
134
+ # Tell eventlet to suspend the current greenthread until
135
+ # self.rfd becomes readable. This will happen when someone
136
+ # else writes to self.wfd.
137
+ eventlet.hubs.trampoline(self.rfd, read=True)
138
+
139
+ def release(self):
140
+ """
141
+ Release the mutex.
142
+ """
143
+ current_greenthread_id = id(eventlet.greenthread.getcurrent())
144
+ if self.owner != current_greenthread_id:
145
+ raise RuntimeError("cannot release un-acquired lock")
146
+
147
+ if self.recursion_depth > 0:
148
+ self.recursion_depth -= 1
149
+ return
150
+
151
+ self.owner = None
152
+ os.write(self.wfd, b'X')
153
+
154
+ def close(self):
155
+ """
156
+ Close the mutex. This releases its file descriptors.
157
+
158
+ You can't use a mutex after it's been closed.
159
+ """
160
+ if self.wfd is not None:
161
+ os.close(self.rfd)
162
+ self.rfd = None
163
+ os.close(self.wfd)
164
+ self.wfd = None
165
+ self.owner = None
166
+ self.recursion_depth = 0
167
+
168
+ def __del__(self):
169
+ # We need this so we don't leak file descriptors. Otherwise, if you
170
+ # call get_swift_logger() and don't explicitly dispose of it by calling
171
+ # logger.logger.handlers[0].lock.close() [1], the pipe file
172
+ # descriptors are leaked.
173
+ #
174
+ # This only really comes up in tests. Swift processes tend to call
175
+ # get_swift_logger() once and then hang on to it until they exit,
176
+ # but the test suite calls get_swift_logger() a lot.
177
+ #
178
+ # [1] and that's a completely ridiculous thing to expect callers to
179
+ # do, so nobody does it and that's okay.
180
+ self.close()
181
+
182
+ def __enter__(self):
183
+ self.acquire()
184
+ return self
185
+
186
+ def __exit__(self, *args):
187
+ self.release()
188
+
189
+
190
+ class NoopMutex(object):
191
+ """
192
+ "Mutex" that doesn't lock anything.
193
+
194
+ We only allow our syslog logging to be configured via UDS or UDP, neither
195
+ of which have the message-interleaving trouble you'd expect from TCP or
196
+ file handlers.
197
+ """
198
+
199
+ def __init__(self):
200
+ # Usually, it's an error to have multiple greenthreads all waiting
201
+ # to write to the same file descriptor. It's often a sign of inadequate
202
+ # concurrency control; for example, if you have two greenthreads
203
+ # trying to use the same memcache connection, they'll end up writing
204
+ # interleaved garbage to the socket or stealing part of each others'
205
+ # responses.
206
+ #
207
+ # In this case, we have multiple greenthreads waiting on the same
208
+ # (logging) file descriptor by design. So, similar to the PipeMutex,
209
+ # we must turn off eventlet's multiple-waiter detection.
210
+ #
211
+ # It would be better to turn off multiple-reader detection for only
212
+ # the logging socket fd, but eventlet does not support that.
213
+ eventlet.debug.hub_prevent_multiple_readers(False)
214
+
215
+ def acquire(self, blocking=True):
216
+ pass
217
+
218
+ def release(self):
219
+ pass
220
+
221
+ def __enter__(self):
222
+ return self
223
+
224
+ def __exit__(self, *args):
225
+ pass
226
+
227
+
228
+ class ThreadSafeSysLogHandler(SysLogHandler):
229
+ def createLock(self):
230
+ if config_true_value(os.environ.get(
231
+ 'SWIFT_NOOP_LOGGING_MUTEX') or 'true'):
232
+ self.lock = NoopMutex()
233
+ else:
234
+ self.lock = PipeMutex()
235
+
236
+
237
+ # double inheritance to support property with setter
238
+ class SwiftLogAdapter(logging.LoggerAdapter, object):
239
+ """
240
+ A LogAdapter that modifies the adapted ``Logger`` instance
241
+ in the following ways:
242
+
243
+ * Performs some reformatting on calls to :meth:`exception`.
244
+ * Provides threadlocal txn_id and client_ip attributes.
245
+ * Adds the txn_id, client_ip and server attributes to the ``extras`` dict
246
+ when a message is processed.
247
+ * Adds the given prefix to the start of each log message.
248
+ * Provides a notice method for logging at NOTICE level.
249
+ """
250
+
251
+ _cls_thread_local = threading.local()
252
+
253
+ def __init__(self, logger, server, prefix=''):
254
+ logging.LoggerAdapter.__init__(self, logger, {})
255
+ self.prefix = prefix
256
+ self.server = server
257
+ self.warn = self.warning
258
+
259
+ @property
260
+ def txn_id(self):
261
+ if hasattr(self._cls_thread_local, 'txn_id'):
262
+ return self._cls_thread_local.txn_id
263
+
264
+ @txn_id.setter
265
+ def txn_id(self, value):
266
+ self._cls_thread_local.txn_id = value
267
+
268
+ @property
269
+ def client_ip(self):
270
+ if hasattr(self._cls_thread_local, 'client_ip'):
271
+ return self._cls_thread_local.client_ip
272
+
273
+ @client_ip.setter
274
+ def client_ip(self, value):
275
+ self._cls_thread_local.client_ip = value
276
+
277
+ @property
278
+ def thread_locals(self):
279
+ return (self.txn_id, self.client_ip)
280
+
281
+ @thread_locals.setter
282
+ def thread_locals(self, value):
283
+ self.txn_id, self.client_ip = value
284
+
285
+ def process(self, msg, kwargs):
286
+ """
287
+ Add extra info to message
288
+ """
289
+ kwargs['extra'] = {'server': self.server, 'txn_id': self.txn_id,
290
+ 'client_ip': self.client_ip}
291
+ msg = '%s%s' % (self.prefix, msg)
292
+ return msg, kwargs
293
+
294
+ def notice(self, msg, *args, **kwargs):
295
+ """
296
+ Convenience function for syslog priority LOG_NOTICE. The python
297
+ logging lvl is set to 25, just above info. SysLogHandler is
298
+ monkey patched to map this log lvl to the LOG_NOTICE syslog
299
+ priority.
300
+ """
301
+ self.log(NOTICE, msg, *args, **kwargs)
302
+
303
+ def _exception(self, msg, *args, **kwargs):
304
+ # We up-call to exception() where stdlib uses error() so we can get
305
+ # some of the traceback suppression from LogAdapter, below
306
+ logging.LoggerAdapter.exception(self, msg, *args, **kwargs)
307
+
308
+ def exception(self, msg, *args, **kwargs):
309
+ _junk, exc, _junk = sys.exc_info()
310
+ call = self.error
311
+ emsg = ''
312
+ if isinstance(exc, (http.client.BadStatusLine,
313
+ green_http_client.BadStatusLine)):
314
+ # Use error(); not really exceptional
315
+ emsg = repr(exc)
316
+ # Note that on py3, we've seen a RemoteDisconnected error getting
317
+ # raised, which inherits from *both* BadStatusLine and OSError;
318
+ # we want it getting caught here
319
+ elif isinstance(exc, (OSError, socket.error)):
320
+ if exc.errno in (errno.EIO, errno.ENOSPC):
321
+ emsg = str(exc)
322
+ elif exc.errno == errno.ECONNREFUSED:
323
+ emsg = 'Connection refused'
324
+ elif exc.errno == errno.ECONNRESET:
325
+ emsg = 'Connection reset'
326
+ elif exc.errno == errno.EHOSTUNREACH:
327
+ emsg = 'Host unreachable'
328
+ elif exc.errno == errno.ENETUNREACH:
329
+ emsg = 'Network unreachable'
330
+ elif exc.errno == errno.ETIMEDOUT:
331
+ emsg = 'Connection timeout'
332
+ elif exc.errno == errno.EPIPE:
333
+ emsg = 'Broken pipe'
334
+ else:
335
+ call = self._exception
336
+ elif isinstance(exc, eventlet.Timeout):
337
+ emsg = exc.__class__.__name__
338
+ detail = '%ss' % exc.seconds
339
+ if hasattr(exc, 'created_at'):
340
+ detail += ' after %0.2fs' % (time.time() - exc.created_at)
341
+ emsg += ' (%s)' % detail
342
+ if isinstance(exc, swift.common.exceptions.MessageTimeout):
343
+ if exc.msg:
344
+ emsg += ' %s' % exc.msg
345
+ else:
346
+ call = self._exception
347
+ call('%s: %s' % (msg, emsg), *args, **kwargs)
348
+
349
+
350
+ class SwiftLogFormatter(logging.Formatter):
351
+ """
352
+ Custom logging.Formatter will append txn_id to a log message if the
353
+ record has one and the message does not. Optionally it can shorten
354
+ overly long log lines.
355
+ """
356
+
357
+ def __init__(self, fmt=None, datefmt=None, max_line_length=0):
358
+ logging.Formatter.__init__(self, fmt=fmt, datefmt=datefmt)
359
+ self.max_line_length = max_line_length
360
+
361
+ def format(self, record):
362
+ if not hasattr(record, 'server'):
363
+ # Catch log messages that were not initiated by swift
364
+ # (for example, the keystone auth middleware)
365
+ record.server = record.name
366
+
367
+ # Included from Python's logging.Formatter and then altered slightly to
368
+ # replace \n with #012
369
+ record.message = record.getMessage()
370
+ if self._fmt.find('%(asctime)') >= 0:
371
+ record.asctime = self.formatTime(record, self.datefmt)
372
+ msg = (self._fmt % record.__dict__).replace('\n', '#012')
373
+ if record.exc_info:
374
+ # Cache the traceback text to avoid converting it multiple times
375
+ # (it's constant anyway)
376
+ if not record.exc_text:
377
+ record.exc_text = self.formatException(
378
+ record.exc_info).replace('\n', '#012')
379
+ if record.exc_text:
380
+ if not msg.endswith('#012'):
381
+ msg = msg + '#012'
382
+ msg = msg + record.exc_text
383
+
384
+ if (hasattr(record, 'txn_id') and record.txn_id and
385
+ record.txn_id not in msg):
386
+ msg = "%s (txn: %s)" % (msg, record.txn_id)
387
+ if (hasattr(record, 'client_ip') and record.client_ip and
388
+ record.levelno != logging.INFO and
389
+ record.client_ip not in msg):
390
+ msg = "%s (client_ip: %s)" % (msg, record.client_ip)
391
+ if self.max_line_length > 0 and len(msg) > self.max_line_length:
392
+ if self.max_line_length < 7:
393
+ msg = msg[:self.max_line_length]
394
+ else:
395
+ approxhalf = (self.max_line_length - 5) // 2
396
+ msg = msg[:approxhalf] + " ... " + msg[-approxhalf:]
397
+ return msg
398
+
399
+
400
+ class LoggerFileObject(object):
401
+
402
+ # Note: this is greenthread-local storage
403
+ _cls_thread_local = threading.local()
404
+
405
+ def __init__(self, logger, log_type='STDOUT'):
406
+ self.logger = logger
407
+ self.log_type = log_type
408
+
409
+ def write(self, value):
410
+ # We can get into a nasty situation when logs are going to syslog
411
+ # and syslog dies.
412
+ #
413
+ # It's something like this:
414
+ #
415
+ # (A) someone logs something
416
+ #
417
+ # (B) there's an exception in sending to /dev/log since syslog is
418
+ # not working
419
+ #
420
+ # (C) logging takes that exception and writes it to stderr (see
421
+ # logging.Handler.handleError)
422
+ #
423
+ # (D) stderr was replaced with a LoggerFileObject at process start,
424
+ # so the LoggerFileObject takes the provided string and tells
425
+ # its logger to log it (to syslog, naturally).
426
+ #
427
+ # Then, steps B through D repeat until we run out of stack.
428
+ if getattr(self._cls_thread_local, 'already_called_write', False):
429
+ return
430
+
431
+ self._cls_thread_local.already_called_write = True
432
+ try:
433
+ value = value.strip()
434
+ if value:
435
+ if 'Connection reset by peer' in value:
436
+ self.logger.error(
437
+ '%s: Connection reset by peer', self.log_type)
438
+ else:
439
+ self.logger.error('%(type)s: %(value)s',
440
+ {'type': self.log_type, 'value': value})
441
+ finally:
442
+ self._cls_thread_local.already_called_write = False
443
+
444
+ def writelines(self, values):
445
+ if getattr(self._cls_thread_local, 'already_called_writelines', False):
446
+ return
447
+
448
+ self._cls_thread_local.already_called_writelines = True
449
+ try:
450
+ self.logger.error('%(type)s: %(value)s',
451
+ {'type': self.log_type,
452
+ 'value': '#012'.join(values)})
453
+ finally:
454
+ self._cls_thread_local.already_called_writelines = False
455
+
456
+ def close(self):
457
+ pass
458
+
459
+ def flush(self):
460
+ pass
461
+
462
+ def __iter__(self):
463
+ return self
464
+
465
+ def __next__(self):
466
+ raise IOError(errno.EBADF, 'Bad file descriptor')
467
+
468
+ def read(self, size=-1):
469
+ raise IOError(errno.EBADF, 'Bad file descriptor')
470
+
471
+ def readline(self, size=-1):
472
+ raise IOError(errno.EBADF, 'Bad file descriptor')
473
+
474
+ def tell(self):
475
+ return 0
476
+
477
+ def xreadlines(self):
478
+ return self
479
+
480
+
481
+ class LogLevelFilter(object):
482
+ """
483
+ Drop messages for the logger based on level.
484
+
485
+ This is useful when dependencies log too much information.
486
+
487
+ :param level: All messages at or below this level are dropped
488
+ (DEBUG < INFO < WARN < ERROR < CRITICAL|FATAL)
489
+ Default: DEBUG
490
+ """
491
+
492
+ def __init__(self, level=logging.DEBUG):
493
+ self.level = level
494
+
495
+ def filter(self, record):
496
+ if record.levelno <= self.level:
497
+ return 0
498
+ return 1
499
+
500
+
501
+ def get_swift_logger(conf, name=None, log_to_console=False, log_route=None,
502
+ fmt="%(server)s: %(message)s"):
503
+ """
504
+ Get the current system logger using config settings.
505
+
506
+ **Log config and defaults**::
507
+
508
+ log_facility = LOG_LOCAL0
509
+ log_level = INFO
510
+ log_name = swift
511
+ log_max_line_length = 0
512
+ log_udp_host = (disabled)
513
+ log_udp_port = logging.handlers.SYSLOG_UDP_PORT
514
+ log_address = /dev/log
515
+
516
+ :param conf: Configuration dict to read settings from
517
+ :param name: This value is used to populate the ``server`` field in
518
+ the log format, as the default value for ``log_route``;
519
+ defaults to the ``log_name`` value in ``conf``, if it exists,
520
+ or to 'swift'.
521
+ :param log_to_console: Add handler which writes to console on stderr
522
+ :param log_route: Route for the logging, not emitted to the log, just used
523
+ to separate logging configurations; defaults to the value
524
+ of ``name`` or whatever ``name`` defaults to. This value
525
+ is used as the name attribute of the
526
+ ``logging.LogAdapter`` that is returned.
527
+ :param fmt: Override log format
528
+ :return: an instance of ``SwiftLogAdapter``
529
+ """
530
+ # note: log_name is typically specified in conf (i.e. defined by
531
+ # operators), whereas log_route is typically hard-coded in callers of
532
+ # get_swift_logger (i.e. defined by developers)
533
+ if not conf:
534
+ conf = {}
535
+ if name is None:
536
+ name = conf.get('log_name', 'swift')
537
+ if not log_route:
538
+ log_route = name
539
+ logger = logging.getLogger(log_route)
540
+ logger.propagate = False
541
+ # all new handlers will get the same formatter
542
+ formatter = SwiftLogFormatter(
543
+ fmt=fmt, max_line_length=int(conf.get('log_max_line_length', 0)))
544
+
545
+ # get_swift_logger will only ever add one SysLog Handler to a logger
546
+ if not hasattr(get_swift_logger, 'handler4logger'):
547
+ get_swift_logger.handler4logger = {}
548
+ if logger in get_swift_logger.handler4logger:
549
+ logger.removeHandler(get_swift_logger.handler4logger[logger])
550
+
551
+ # facility for this logger will be set by last call wins
552
+ facility = getattr(SysLogHandler, conf.get('log_facility', 'LOG_LOCAL0'),
553
+ SysLogHandler.LOG_LOCAL0)
554
+ udp_host = conf.get('log_udp_host')
555
+ if udp_host:
556
+ udp_port = int(conf.get('log_udp_port',
557
+ logging.handlers.SYSLOG_UDP_PORT))
558
+ handler = ThreadSafeSysLogHandler(address=(udp_host, udp_port),
559
+ facility=facility)
560
+ else:
561
+ log_address = conf.get('log_address', '/dev/log')
562
+ handler = None
563
+ try:
564
+ mode = os.stat(log_address).st_mode
565
+ if stat.S_ISSOCK(mode):
566
+ handler = ThreadSafeSysLogHandler(address=log_address,
567
+ facility=facility)
568
+ except (OSError, socket.error) as e:
569
+ # If either /dev/log isn't a UNIX socket or it does not exist at
570
+ # all then py2 would raise an error
571
+ if e.errno not in [errno.ENOTSOCK, errno.ENOENT]:
572
+ raise
573
+ if handler is None:
574
+ # fallback to default UDP
575
+ handler = ThreadSafeSysLogHandler(facility=facility)
576
+ handler.setFormatter(formatter)
577
+ logger.addHandler(handler)
578
+ get_swift_logger.handler4logger[logger] = handler
579
+
580
+ # setup console logging
581
+ if log_to_console or hasattr(get_swift_logger, 'console_handler4logger'):
582
+ # remove pre-existing console handler for this logger
583
+ if not hasattr(get_swift_logger, 'console_handler4logger'):
584
+ get_swift_logger.console_handler4logger = {}
585
+ if logger in get_swift_logger.console_handler4logger:
586
+ logger.removeHandler(
587
+ get_swift_logger.console_handler4logger[logger])
588
+
589
+ console_handler = logging.StreamHandler(sys.__stderr__)
590
+ console_handler.setFormatter(formatter)
591
+ logger.addHandler(console_handler)
592
+ get_swift_logger.console_handler4logger[logger] = console_handler
593
+
594
+ # set the level for the logger
595
+ logger.setLevel(
596
+ getattr(logging, conf.get('log_level', 'INFO').upper(), logging.INFO))
597
+
598
+ adapted_logger = SwiftLogAdapter(logger, name)
599
+ other_handlers = conf.get('log_custom_handlers', None)
600
+ if other_handlers:
601
+ log_custom_handlers = [s.strip() for s in other_handlers.split(',')
602
+ if s.strip()]
603
+ for hook in log_custom_handlers:
604
+ try:
605
+ mod, fnc = hook.rsplit('.', 1)
606
+ logger_hook = getattr(__import__(mod, fromlist=[fnc]), fnc)
607
+ logger_hook(conf, name, log_to_console, log_route, fmt,
608
+ logger, adapted_logger)
609
+ except (AttributeError, ImportError):
610
+ print('Error calling custom handler [%s]' % hook,
611
+ file=sys.stderr)
612
+ except ValueError:
613
+ print('Invalid custom handler format [%s]' % hook,
614
+ file=sys.stderr)
615
+
616
+ return adapted_logger
617
+
618
+
619
+ def get_prefixed_swift_logger(swift_logger, prefix):
620
+ """
621
+ Return a clone of the given ``swift_logger`` with a new prefix string
622
+ that replaces the prefix string of the given ``swift_logger``.
623
+
624
+ :param swift_logger: an instance of ``SwiftLogAdapter``.
625
+ :param prefix: a string prefix.
626
+ :returns: a new instance of ``SwiftLogAdapter``.
627
+ """
628
+ return SwiftLogAdapter(
629
+ swift_logger.logger, swift_logger.server, prefix=prefix)
630
+
631
+
632
+ class NullLogger(object):
633
+ """A no-op logger for eventlet wsgi."""
634
+
635
+ def write(self, *args):
636
+ # "Logs" the args to nowhere
637
+ pass
638
+
639
+ def exception(self, *args):
640
+ pass
641
+
642
+ def critical(self, *args):
643
+ pass
644
+
645
+ def error(self, *args):
646
+ pass
647
+
648
+ def warning(self, *args):
649
+ pass
650
+
651
+ def info(self, *args):
652
+ pass
653
+
654
+ def debug(self, *args):
655
+ pass
656
+
657
+ def log(self, *args):
658
+ pass
659
+
660
+
661
+ def capture_stdio(logger, **kwargs):
662
+ """
663
+ Log unhandled exceptions, close stdio, capture stdout and stderr.
664
+
665
+ param logger: Logger object to use
666
+ """
667
+ # log uncaught exceptions
668
+ sys.excepthook = lambda * exc_info: \
669
+ logger.critical('UNCAUGHT EXCEPTION', exc_info=exc_info)
670
+
671
+ # collect stdio file desc not in use for logging
672
+ stdio_files = [sys.stdin, sys.stdout, sys.stderr]
673
+ console_fds = [h.stream.fileno() for _junk, h in getattr(
674
+ get_swift_logger, 'console_handler4logger', {}).items()]
675
+ stdio_files = [f for f in stdio_files if f.fileno() not in console_fds]
676
+
677
+ with open(os.devnull, 'r+b') as nullfile:
678
+ # close stdio (excludes fds open for logging)
679
+ for f in stdio_files:
680
+ # some platforms throw an error when attempting an stdin flush
681
+ try:
682
+ f.flush()
683
+ except IOError:
684
+ pass
685
+
686
+ try:
687
+ os.dup2(nullfile.fileno(), f.fileno())
688
+ except OSError:
689
+ pass
690
+
691
+ # redirect stdio
692
+ if kwargs.pop('capture_stdout', True):
693
+ sys.stdout = LoggerFileObject(logger)
694
+ if kwargs.pop('capture_stderr', True):
695
+ sys.stderr = LoggerFileObject(logger, 'STDERR')
696
+
697
+
698
+ class StrAnonymizer(str):
699
+ """
700
+ Class that permits to get a string anonymized or simply quoted.
701
+ """
702
+
703
+ def __new__(cls, data, method, salt):
704
+ method = method.lower()
705
+ if method not in hashlib.algorithms_guaranteed:
706
+ raise ValueError('Unsupported hashing method: %r' % method)
707
+ s = str.__new__(cls, data or '')
708
+ s.method = method
709
+ s.salt = salt
710
+ return s
711
+
712
+ @property
713
+ def anonymized(self):
714
+ if not self:
715
+ return self
716
+ else:
717
+ if self.method == 'md5':
718
+ h = md5(usedforsecurity=False)
719
+ else:
720
+ h = getattr(hashlib, self.method)()
721
+ if self.salt:
722
+ h.update(self.salt.encode('latin1'))
723
+ h.update(self.encode('latin1'))
724
+ return '{%s%s}%s' % ('S' if self.salt else '', self.method.upper(),
725
+ h.hexdigest())
726
+
727
+
728
+ class StrFormatTime(object):
729
+ """
730
+ Class that permits to get formats or parts of a time.
731
+ """
732
+
733
+ def __init__(self, ts):
734
+ self.time = ts
735
+ self.time_struct = time.gmtime(ts)
736
+
737
+ def __str__(self):
738
+ return "%.9f" % self.time
739
+
740
+ def __getattr__(self, attr):
741
+ if attr not in ['a', 'A', 'b', 'B', 'c', 'd', 'H',
742
+ 'I', 'j', 'm', 'M', 'p', 'S', 'U',
743
+ 'w', 'W', 'x', 'X', 'y', 'Y', 'Z']:
744
+ raise ValueError(("The attribute %s is not a correct directive "
745
+ "for time.strftime formater.") % attr)
746
+ return datetime.datetime(*self.time_struct[:-2],
747
+ tzinfo=UTC).strftime('%' + attr)
748
+
749
+ @property
750
+ def asctime(self):
751
+ return time.asctime(self.time_struct)
752
+
753
+ @property
754
+ def datetime(self):
755
+ return time.strftime('%d/%b/%Y/%H/%M/%S', self.time_struct)
756
+
757
+ @property
758
+ def iso8601(self):
759
+ return time.strftime('%Y-%m-%dT%H:%M:%S', self.time_struct)
760
+
761
+ @property
762
+ def ms(self):
763
+ return self.__str__().split('.')[1][:3]
764
+
765
+ @property
766
+ def us(self):
767
+ return self.__str__().split('.')[1][:6]
768
+
769
+ @property
770
+ def ns(self):
771
+ return self.__str__().split('.')[1]
772
+
773
+ @property
774
+ def s(self):
775
+ return self.__str__().split('.')[0]
776
+
777
+
778
+ def get_log_line(req, res, trans_time, additional_info, fmt,
779
+ anonymization_method, anonymization_salt):
780
+ """
781
+ Make a line for logging that matches the documented log line format
782
+ for backend servers.
783
+
784
+ :param req: the request.
785
+ :param res: the response.
786
+ :param trans_time: the time the request took to complete, a float.
787
+ :param additional_info: a string to log at the end of the line
788
+
789
+ :returns: a properly formatted line for logging.
790
+ """
791
+
792
+ policy_index = get_policy_index(req.headers, res.headers)
793
+ if req.path.startswith('/'):
794
+ disk, partition, account, container, obj = split_path(req.path, 0, 5,
795
+ True)
796
+ else:
797
+ disk, partition, account, container, obj = (None, ) * 5
798
+ replacements = {
799
+ 'remote_addr': StrAnonymizer(req.remote_addr, anonymization_method,
800
+ anonymization_salt),
801
+ 'time': StrFormatTime(time.time()),
802
+ 'method': req.method,
803
+ 'path': StrAnonymizer(req.path, anonymization_method,
804
+ anonymization_salt),
805
+ 'disk': disk,
806
+ 'partition': partition,
807
+ 'account': StrAnonymizer(account, anonymization_method,
808
+ anonymization_salt),
809
+ 'container': StrAnonymizer(container, anonymization_method,
810
+ anonymization_salt),
811
+ 'object': StrAnonymizer(obj, anonymization_method,
812
+ anonymization_salt),
813
+ 'status': res.status.split()[0],
814
+ 'content_length': res.content_length,
815
+ 'referer': StrAnonymizer(req.referer, anonymization_method,
816
+ anonymization_salt),
817
+ 'txn_id': req.headers.get('x-trans-id'),
818
+ 'user_agent': StrAnonymizer(req.user_agent, anonymization_method,
819
+ anonymization_salt),
820
+ 'trans_time': trans_time,
821
+ 'additional_info': additional_info,
822
+ 'pid': os.getpid(),
823
+ 'policy_index': policy_index,
824
+ }
825
+ return LogStringFormatter(default='-').format(fmt, **replacements)
826
+
827
+
828
+ def get_policy_index(req_headers, res_headers):
829
+ """
830
+ Returns the appropriate index of the storage policy for the request from
831
+ a proxy server
832
+
833
+ :param req_headers: dict of the request headers.
834
+ :param res_headers: dict of the response headers.
835
+
836
+ :returns: string index of storage policy, or None
837
+ """
838
+ header = 'X-Backend-Storage-Policy-Index'
839
+ policy_index = res_headers.get(header, req_headers.get(header))
840
+ if isinstance(policy_index, bytes):
841
+ policy_index = policy_index.decode('ascii')
842
+ return str(policy_index) if policy_index is not None else None
843
+
844
+
845
+ class LogStringFormatter(string.Formatter):
846
+ def __init__(self, default='', quote=False):
847
+ super(LogStringFormatter, self).__init__()
848
+ self.default = default
849
+ self.quote = quote
850
+
851
+ def format_field(self, value, spec):
852
+ if not value:
853
+ return self.default
854
+ else:
855
+ log = super(LogStringFormatter, self).format_field(value, spec)
856
+ if self.quote:
857
+ return quote(log, ':/{}')
858
+ else:
859
+ return log