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,256 @@
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
+ import ctypes
17
+ import ctypes.util
18
+ import os
19
+ import platform
20
+ import re
21
+ import socket
22
+ import warnings
23
+
24
+
25
+ # Used by the parse_socket_string() function to validate IPv6 addresses
26
+ IPV6_RE = re.compile(r"^\[(?P<address>.*)\](:(?P<port>[0-9]+))?$")
27
+
28
+
29
+ def is_valid_ip(ip):
30
+ """
31
+ Return True if the provided ip is a valid IP-address
32
+ """
33
+ return is_valid_ipv4(ip) or is_valid_ipv6(ip)
34
+
35
+
36
+ def is_valid_ipv4(ip):
37
+ """
38
+ Return True if the provided ip is a valid IPv4-address
39
+ """
40
+ try:
41
+ socket.inet_pton(socket.AF_INET, ip)
42
+ except socket.error: # not a valid IPv4 address
43
+ return False
44
+ return True
45
+
46
+
47
+ def is_valid_ipv6(ip):
48
+ """
49
+ Returns True if the provided ip is a valid IPv6-address
50
+ """
51
+ try:
52
+ socket.inet_pton(socket.AF_INET6, ip)
53
+ except socket.error: # not a valid IPv6 address
54
+ return False
55
+ return True
56
+
57
+
58
+ def expand_ipv6(address):
59
+ """
60
+ Expand ipv6 address.
61
+ :param address: a string indicating valid ipv6 address
62
+ :returns: a string indicating fully expanded ipv6 address
63
+
64
+ """
65
+ packed_ip = socket.inet_pton(socket.AF_INET6, address)
66
+ return socket.inet_ntop(socket.AF_INET6, packed_ip)
67
+
68
+
69
+ libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
70
+ try:
71
+ getifaddrs = libc.getifaddrs
72
+ freeifaddrs = libc.freeifaddrs
73
+ netifaces = None # for patching
74
+ except AttributeError:
75
+ getifaddrs = None
76
+ freeifaddrs = None
77
+ try:
78
+ import netifaces
79
+ except ImportError:
80
+ raise ImportError('C function getifaddrs not available, '
81
+ 'and netifaces not installed')
82
+ else:
83
+ warnings.warn('getifaddrs is not available; falling back to the '
84
+ 'archived and no longer maintained netifaces project. '
85
+ 'This fallback will be removed in a future release; '
86
+ 'see https://bugs.launchpad.net/swift/+bug/2019233 for '
87
+ 'more information.', FutureWarning)
88
+ else:
89
+ class sockaddr_in4(ctypes.Structure):
90
+ if platform.system() == 'Linux':
91
+ _fields_ = [
92
+ ("sin_family", ctypes.c_uint16),
93
+ ("sin_port", ctypes.c_uint16),
94
+ ("sin_addr", ctypes.c_ubyte * 4),
95
+ ]
96
+ else:
97
+ # Assume BSD / OS X
98
+ _fields_ = [
99
+ ("sin_len", ctypes.c_uint8),
100
+ ("sin_family", ctypes.c_uint8),
101
+ ("sin_port", ctypes.c_uint16),
102
+ ("sin_addr", ctypes.c_ubyte * 4),
103
+ ]
104
+
105
+ class sockaddr_in6(ctypes.Structure):
106
+ if platform.system() == 'Linux':
107
+ _fields_ = [
108
+ ("sin6_family", ctypes.c_uint16),
109
+ ("sin6_port", ctypes.c_uint16),
110
+ ("sin6_flowinfo", ctypes.c_uint32),
111
+ ("sin6_addr", ctypes.c_ubyte * 16),
112
+ ]
113
+ else:
114
+ # Assume BSD / OS X
115
+ _fields_ = [
116
+ ("sin6_len", ctypes.c_uint8),
117
+ ("sin6_family", ctypes.c_uint8),
118
+ ("sin6_port", ctypes.c_uint16),
119
+ ("sin6_flowinfo", ctypes.c_uint32),
120
+ ("sin6_addr", ctypes.c_ubyte * 16),
121
+ ]
122
+
123
+ class ifaddrs(ctypes.Structure):
124
+ pass
125
+
126
+ # Have to do this a little later so we can self-reference
127
+ ifaddrs._fields_ = [
128
+ ("ifa_next", ctypes.POINTER(ifaddrs)),
129
+ ("ifa_name", ctypes.c_char_p),
130
+ ("ifa_flags", ctypes.c_int),
131
+ # Use the smaller of the two to start, can cast later
132
+ # when we *know* we're looking at INET6
133
+ ("ifa_addr", ctypes.POINTER(sockaddr_in4)),
134
+ # Don't care about the rest of the fields
135
+ ]
136
+
137
+ def errcheck(result, func, arguments):
138
+ if result != 0:
139
+ errno = ctypes.set_errno(0)
140
+ raise OSError(errno, "getifaddrs: %s" % os.strerror(errno))
141
+ return result
142
+
143
+ getifaddrs.errcheck = errcheck
144
+
145
+
146
+ def whataremyips(ring_ip=None):
147
+ """
148
+ Get "our" IP addresses ("us" being the set of services configured by
149
+ one `*.conf` file). If our REST listens on a specific address, return it.
150
+ Otherwise, if listen on '0.0.0.0' or '::' return all addresses, including
151
+ the loopback.
152
+
153
+ :param str ring_ip: Optional ring_ip/bind_ip from a config file; may be
154
+ IP address or hostname.
155
+ :returns: list of Strings of ip addresses
156
+ """
157
+ if ring_ip:
158
+ # See if bind_ip is '0.0.0.0'/'::'
159
+ try:
160
+ _, _, _, _, sockaddr = socket.getaddrinfo(
161
+ ring_ip, None, 0, socket.SOCK_STREAM, 0,
162
+ socket.AI_NUMERICHOST)[0]
163
+ if sockaddr[0] not in ('0.0.0.0', '::'):
164
+ return [ring_ip]
165
+ except socket.gaierror:
166
+ pass
167
+
168
+ addresses = []
169
+
170
+ if getifaddrs:
171
+ addrs = ctypes.POINTER(ifaddrs)()
172
+ getifaddrs(ctypes.byref(addrs))
173
+ try:
174
+ cur = addrs
175
+ while cur:
176
+ if not cur.contents.ifa_addr:
177
+ # Not all interfaces will have addresses; move on
178
+ cur = cur.contents.ifa_next
179
+ continue
180
+ sa_family = cur.contents.ifa_addr.contents.sin_family
181
+ if sa_family == socket.AF_INET:
182
+ addresses.append(
183
+ socket.inet_ntop(
184
+ socket.AF_INET,
185
+ cur.contents.ifa_addr.contents.sin_addr,
186
+ )
187
+ )
188
+ elif sa_family == socket.AF_INET6:
189
+ addr = ctypes.cast(cur.contents.ifa_addr,
190
+ ctypes.POINTER(sockaddr_in6))
191
+ addresses.append(
192
+ socket.inet_ntop(
193
+ socket.AF_INET6,
194
+ addr.contents.sin6_addr,
195
+ )
196
+ )
197
+ cur = cur.contents.ifa_next
198
+ finally:
199
+ freeifaddrs(addrs)
200
+ return addresses
201
+
202
+ # getifaddrs not available; try netifaces
203
+ for interface in netifaces.interfaces():
204
+ try:
205
+ iface_data = netifaces.ifaddresses(interface)
206
+ for family in iface_data:
207
+ if family not in (netifaces.AF_INET, netifaces.AF_INET6):
208
+ continue
209
+ for address in iface_data[family]:
210
+ addr = address['addr']
211
+
212
+ # If we have an ipv6 address remove the
213
+ # %ether_interface at the end
214
+ if family == netifaces.AF_INET6:
215
+ addr = expand_ipv6(addr.split('%')[0])
216
+ addresses.append(addr)
217
+ except ValueError:
218
+ pass
219
+ return addresses
220
+
221
+
222
+ def parse_socket_string(socket_string, default_port):
223
+ """
224
+ Given a string representing a socket, returns a tuple of (host, port).
225
+ Valid strings are DNS names, IPv4 addresses, or IPv6 addresses, with an
226
+ optional port. If an IPv6 address is specified it **must** be enclosed in
227
+ [], like *[::1]* or *[::1]:11211*. This follows the accepted prescription
228
+ for `IPv6 host literals`_.
229
+
230
+ Examples::
231
+
232
+ server.org
233
+ server.org:1337
234
+ 127.0.0.1:1337
235
+ [::1]:1337
236
+ [::1]
237
+
238
+ .. _IPv6 host literals: https://tools.ietf.org/html/rfc3986#section-3.2.2
239
+ """
240
+ port = default_port
241
+ # IPv6 addresses must be between '[]'
242
+ if socket_string.startswith('['):
243
+ match = IPV6_RE.match(socket_string)
244
+ if not match:
245
+ raise ValueError("Invalid IPv6 address: %s" % socket_string)
246
+ host = match.group('address')
247
+ port = match.group('port') or port
248
+ else:
249
+ if ':' in socket_string:
250
+ tokens = socket_string.split(':')
251
+ if len(tokens) > 2:
252
+ raise ValueError("IPv6 addresses must be between '[]'")
253
+ host, port = tokens
254
+ else:
255
+ host = socket_string
256
+ return (host, port)
@@ -0,0 +1,345 @@
1
+ # Copyright (c) 2010-2023 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
+ """Functions Swift uses to interact with libc and other low-level APIs."""
17
+
18
+ import ctypes
19
+ import ctypes.util
20
+ import fcntl
21
+ import logging
22
+ import os
23
+ import platform
24
+ import socket
25
+
26
+
27
+ # These are lazily pulled from libc elsewhere
28
+ _posix_fadvise = None
29
+ _libc_socket = None
30
+ _libc_bind = None
31
+ _libc_accept = None
32
+ # see man -s 2 setpriority
33
+ _libc_setpriority = None
34
+ # see man -s 2 syscall
35
+ _posix_syscall = None
36
+
37
+ # from /usr/src/linux-headers-*/include/uapi/linux/resource.h
38
+ PRIO_PROCESS = 0
39
+
40
+
41
+ # /usr/include/x86_64-linux-gnu/asm/unistd_64.h defines syscalls there
42
+ # are many like it, but this one is mine, see man -s 2 ioprio_set
43
+ def NR_ioprio_set():
44
+ """Give __NR_ioprio_set value for your system."""
45
+ architecture = os.uname()[4]
46
+ arch_bits = platform.architecture()[0]
47
+ # check if supported system, now support x86_64 and AArch64
48
+ if architecture == 'x86_64' and arch_bits == '64bit':
49
+ return 251
50
+ elif architecture == 'aarch64' and arch_bits == '64bit':
51
+ return 30
52
+ raise OSError("Swift doesn't support ionice priority for %s %s" %
53
+ (architecture, arch_bits))
54
+
55
+
56
+ # this syscall integer probably only works on x86_64 linux systems, you
57
+ # can check if it's correct on yours with something like this:
58
+ """
59
+ #include <stdio.h>
60
+ #include <sys/syscall.h>
61
+
62
+ int main(int argc, const char* argv[]) {
63
+ printf("%d\n", __NR_ioprio_set);
64
+ return 0;
65
+ }
66
+ """
67
+
68
+ # this is the value for "which" that says our who value will be a pid
69
+ # pulled out of /usr/src/linux-headers-*/include/linux/ioprio.h
70
+ IOPRIO_WHO_PROCESS = 1
71
+
72
+
73
+ IO_CLASS_ENUM = {
74
+ 'IOPRIO_CLASS_RT': 1,
75
+ 'IOPRIO_CLASS_BE': 2,
76
+ 'IOPRIO_CLASS_IDLE': 3,
77
+ }
78
+
79
+ # the IOPRIO_PRIO_VALUE "macro" is also pulled from
80
+ # /usr/src/linux-headers-*/include/linux/ioprio.h
81
+ IOPRIO_CLASS_SHIFT = 13
82
+
83
+
84
+ def IOPRIO_PRIO_VALUE(class_, data):
85
+ return (((class_) << IOPRIO_CLASS_SHIFT) | data)
86
+
87
+
88
+ # These constants are Linux-specific, and Python doesn't seem to know
89
+ # about them. We ask anyway just in case that ever gets fixed.
90
+ #
91
+ # The values were copied from the Linux 3.x kernel headers.
92
+ AF_ALG = getattr(socket, 'AF_ALG', 38)
93
+ F_SETPIPE_SZ = getattr(fcntl, 'F_SETPIPE_SZ', 1031)
94
+
95
+
96
+ def noop_libc_function(*args):
97
+ return 0
98
+
99
+
100
+ def load_libc_function(func_name, log_error=True,
101
+ fail_if_missing=False, errcheck=False):
102
+ """
103
+ Attempt to find the function in libc, otherwise return a no-op func.
104
+
105
+ :param func_name: name of the function to pull from libc.
106
+ :param log_error: log an error when a function can't be found
107
+ :param fail_if_missing: raise an exception when a function can't be found.
108
+ Default behavior is to return a no-op function.
109
+ :param errcheck: boolean, if true install a wrapper on the function
110
+ to check for a return values of -1 and call
111
+ ctype.get_errno and raise an OSError
112
+ """
113
+ try:
114
+ libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
115
+ func = getattr(libc, func_name)
116
+ except AttributeError:
117
+ if fail_if_missing:
118
+ raise
119
+ if log_error:
120
+ logging.warning("Unable to locate %s in libc. Leaving as a "
121
+ "no-op.", func_name)
122
+ return noop_libc_function
123
+ if errcheck:
124
+ def _errcheck(result, f, args):
125
+ if result == -1:
126
+ errcode = ctypes.get_errno()
127
+ raise OSError(errcode, os.strerror(errcode))
128
+ return result
129
+ func.errcheck = _errcheck
130
+ return func
131
+
132
+
133
+ class _LibcWrapper(object):
134
+ """
135
+ A callable object that forwards its calls to a C function from libc.
136
+
137
+ These objects are lazy. libc will not be checked until someone tries to
138
+ either call the function or check its availability.
139
+
140
+ _LibcWrapper objects have an "available" property; if true, then libc
141
+ has the function of that name. If false, then calls will fail with a
142
+ NotImplementedError.
143
+ """
144
+
145
+ def __init__(self, func_name):
146
+ self._func_name = func_name
147
+ self._func_handle = None
148
+ self._loaded = False
149
+
150
+ def _ensure_loaded(self):
151
+ if not self._loaded:
152
+ func_name = self._func_name
153
+ try:
154
+ # Keep everything in this try-block in local variables so
155
+ # that a typo in self.some_attribute_name doesn't raise a
156
+ # spurious AttributeError.
157
+ func_handle = load_libc_function(
158
+ func_name, fail_if_missing=True)
159
+ self._func_handle = func_handle
160
+ except AttributeError:
161
+ # We pass fail_if_missing=True to load_libc_function and
162
+ # then ignore the error. It's weird, but otherwise we have
163
+ # to check if self._func_handle is noop_libc_function, and
164
+ # that's even weirder.
165
+ pass
166
+ self._loaded = True
167
+
168
+ @property
169
+ def available(self):
170
+ self._ensure_loaded()
171
+ return bool(self._func_handle)
172
+
173
+ def __call__(self, *args):
174
+ if self.available:
175
+ return self._func_handle(*args)
176
+ else:
177
+ raise NotImplementedError(
178
+ "No function %r found in libc" % self._func_name)
179
+
180
+
181
+ def drop_buffer_cache(fd, offset, length):
182
+ """
183
+ Drop 'buffer' cache for the given range of the given file.
184
+
185
+ :param fd: file descriptor
186
+ :param offset: start offset
187
+ :param length: length
188
+ """
189
+ global _posix_fadvise
190
+ if _posix_fadvise is None:
191
+ _posix_fadvise = load_libc_function('posix_fadvise64')
192
+ # 4 means "POSIX_FADV_DONTNEED"
193
+ ret = _posix_fadvise(fd, ctypes.c_uint64(offset),
194
+ ctypes.c_uint64(length), 4)
195
+ if ret != 0:
196
+ logging.warning("posix_fadvise64(%(fd)s, %(offset)s, %(length)s, 4) "
197
+ "-> %(ret)s", {'fd': fd, 'offset': offset,
198
+ 'length': length, 'ret': ret})
199
+
200
+
201
+ class sockaddr_alg(ctypes.Structure):
202
+ _fields_ = [("salg_family", ctypes.c_ushort),
203
+ ("salg_type", ctypes.c_ubyte * 14),
204
+ ("salg_feat", ctypes.c_uint),
205
+ ("salg_mask", ctypes.c_uint),
206
+ ("salg_name", ctypes.c_ubyte * 64)]
207
+
208
+
209
+ _bound_md5_sockfd = None
210
+
211
+
212
+ def get_md5_socket():
213
+ """
214
+ Get an MD5 socket file descriptor. One can MD5 data with it by writing it
215
+ to the socket with os.write, then os.read the 16 bytes of the checksum out
216
+ later.
217
+
218
+ NOTE: It is the caller's responsibility to ensure that os.close() is
219
+ called on the returned file descriptor. This is a bare file descriptor,
220
+ not a Python object. It doesn't close itself.
221
+ """
222
+
223
+ # Linux's AF_ALG sockets work like this:
224
+ #
225
+ # First, initialize a socket with socket() and bind(). This tells the
226
+ # socket what algorithm to use, as well as setting up any necessary bits
227
+ # like crypto keys. Of course, MD5 doesn't need any keys, so it's just the
228
+ # algorithm name.
229
+ #
230
+ # Second, to hash some data, get a second socket by calling accept() on
231
+ # the first socket. Write data to the socket, then when finished, read the
232
+ # checksum from the socket and close it. This lets you checksum multiple
233
+ # things without repeating all the setup code each time.
234
+ #
235
+ # Since we only need to bind() one socket, we do that here and save it for
236
+ # future re-use. That way, we only use one file descriptor to get an MD5
237
+ # socket instead of two, and we also get to save some syscalls.
238
+
239
+ global _bound_md5_sockfd
240
+ global _libc_socket
241
+ global _libc_bind
242
+ global _libc_accept
243
+
244
+ if _libc_accept is None:
245
+ _libc_accept = load_libc_function('accept', fail_if_missing=True)
246
+ if _libc_socket is None:
247
+ _libc_socket = load_libc_function('socket', fail_if_missing=True)
248
+ if _libc_bind is None:
249
+ _libc_bind = load_libc_function('bind', fail_if_missing=True)
250
+
251
+ # Do this at first call rather than at import time so that we don't use a
252
+ # file descriptor on systems that aren't using any MD5 sockets.
253
+ if _bound_md5_sockfd is None:
254
+ sockaddr_setup = sockaddr_alg(
255
+ AF_ALG,
256
+ (ord('h'), ord('a'), ord('s'), ord('h'), 0),
257
+ 0, 0,
258
+ (ord('m'), ord('d'), ord('5'), 0))
259
+ hash_sockfd = _libc_socket(ctypes.c_int(AF_ALG),
260
+ ctypes.c_int(socket.SOCK_SEQPACKET),
261
+ ctypes.c_int(0))
262
+ if hash_sockfd < 0:
263
+ raise IOError(ctypes.get_errno(),
264
+ "Failed to initialize MD5 socket")
265
+
266
+ bind_result = _libc_bind(ctypes.c_int(hash_sockfd),
267
+ ctypes.pointer(sockaddr_setup),
268
+ ctypes.c_int(ctypes.sizeof(sockaddr_alg)))
269
+ if bind_result < 0:
270
+ os.close(hash_sockfd)
271
+ raise IOError(ctypes.get_errno(), "Failed to bind MD5 socket")
272
+
273
+ _bound_md5_sockfd = hash_sockfd
274
+
275
+ md5_sockfd = _libc_accept(ctypes.c_int(_bound_md5_sockfd), None, 0)
276
+ if md5_sockfd < 0:
277
+ raise IOError(ctypes.get_errno(), "Failed to accept MD5 socket")
278
+
279
+ return md5_sockfd
280
+
281
+
282
+ def modify_priority(conf, logger):
283
+ """
284
+ Modify priority by nice and ionice.
285
+ """
286
+
287
+ global _libc_setpriority
288
+ if _libc_setpriority is None:
289
+ _libc_setpriority = load_libc_function('setpriority',
290
+ errcheck=True)
291
+
292
+ def _setpriority(nice_priority):
293
+ """
294
+ setpriority for this pid
295
+
296
+ :param nice_priority: valid values are -19 to 20
297
+ """
298
+ try:
299
+ _libc_setpriority(PRIO_PROCESS, os.getpid(),
300
+ int(nice_priority))
301
+ except (ValueError, OSError):
302
+ print("WARNING: Unable to modify scheduling priority of process."
303
+ " Keeping unchanged! Check logs for more info. ")
304
+ logger.exception('Unable to modify nice priority')
305
+ else:
306
+ logger.debug('set nice priority to %s' % nice_priority)
307
+
308
+ nice_priority = conf.get('nice_priority')
309
+ if nice_priority is not None:
310
+ _setpriority(nice_priority)
311
+
312
+ global _posix_syscall
313
+ if _posix_syscall is None:
314
+ _posix_syscall = load_libc_function('syscall', errcheck=True)
315
+
316
+ def _ioprio_set(io_class, io_priority):
317
+ """
318
+ ioprio_set for this process
319
+
320
+ :param io_class: the I/O class component, can be
321
+ IOPRIO_CLASS_RT, IOPRIO_CLASS_BE,
322
+ or IOPRIO_CLASS_IDLE
323
+ :param io_priority: priority value in the I/O class
324
+ """
325
+ try:
326
+ io_class = IO_CLASS_ENUM[io_class]
327
+ io_priority = int(io_priority)
328
+ _posix_syscall(NR_ioprio_set(),
329
+ IOPRIO_WHO_PROCESS,
330
+ os.getpid(),
331
+ IOPRIO_PRIO_VALUE(io_class, io_priority))
332
+ except (KeyError, ValueError, OSError):
333
+ print("WARNING: Unable to modify I/O scheduling class "
334
+ "and priority of process. Keeping unchanged! "
335
+ "Check logs for more info.")
336
+ logger.exception("Unable to modify ionice priority")
337
+ else:
338
+ logger.debug('set ionice class %s priority %s',
339
+ io_class, io_priority)
340
+
341
+ io_class = conf.get("ionice_class")
342
+ if io_class is None:
343
+ return
344
+ io_priority = conf.get("ionice_priority", 0)
345
+ _ioprio_set(io_class, io_priority)