swift 2.32.1__py2.py3-none-any.whl → 2.33.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swift/account/server.py +1 -11
- swift/cli/info.py +28 -1
- swift-2.32.1.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +4 -13
- swift/cli/reload.py +141 -0
- swift/common/daemon.py +12 -2
- swift/common/db.py +12 -8
- swift/common/http_protocol.py +76 -3
- swift/common/manager.py +18 -5
- swift/common/memcached.py +18 -12
- swift/common/middleware/proxy_logging.py +35 -27
- swift/common/middleware/s3api/acl_handlers.py +1 -1
- swift/common/middleware/s3api/controllers/__init__.py +3 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_upload.py +30 -6
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/s3api.py +4 -0
- swift/common/middleware/s3api/s3request.py +19 -12
- swift/common/middleware/s3api/s3response.py +13 -2
- swift/common/middleware/s3api/utils.py +1 -1
- swift/common/middleware/slo.py +395 -298
- swift/common/middleware/staticweb.py +45 -14
- swift/common/middleware/tempurl.py +132 -91
- swift/common/request_helpers.py +32 -8
- swift/common/storage_policy.py +1 -1
- swift/common/swob.py +5 -2
- swift/common/utils/__init__.py +230 -135
- swift/common/utils/timestamp.py +23 -2
- swift/common/wsgi.py +8 -0
- swift/container/backend.py +126 -21
- swift/container/replicator.py +42 -6
- swift/container/server.py +264 -145
- swift/container/sharder.py +50 -30
- swift/container/updater.py +1 -0
- swift/obj/auditor.py +2 -1
- swift/obj/diskfile.py +55 -19
- swift/obj/expirer.py +1 -13
- swift/obj/mem_diskfile.py +2 -1
- swift/obj/mem_server.py +1 -0
- swift/obj/replicator.py +2 -2
- swift/obj/server.py +12 -23
- swift/obj/updater.py +1 -0
- swift/obj/watchers/dark_data.py +72 -34
- swift/proxy/controllers/account.py +3 -2
- swift/proxy/controllers/base.py +217 -127
- swift/proxy/controllers/container.py +274 -289
- swift/proxy/controllers/obj.py +98 -141
- swift/proxy/server.py +2 -12
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-info +3 -0
- swift-2.33.1.data/scripts/swift-recon-cron +24 -0
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/AUTHORS +3 -1
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/METADATA +4 -3
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/RECORD +94 -91
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/WHEEL +1 -1
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/entry_points.txt +1 -0
- swift-2.33.1.dist-info/pbr.json +1 -0
- swift-2.32.1.dist-info/pbr.json +0 -1
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-audit +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-auditor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-info +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-reaper +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-replicator +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-config +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-auditor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-reconciler +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-replicator +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sharder +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sync +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-updater +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-populate +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-report +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-drive-audit +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-form-signature +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-get-nodes +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-init +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-auditor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-expirer +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-info +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-reconstructor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-relinker +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-replicator +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-updater +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-oldies +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-orphans +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-proxy-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-recon +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-reconciler-enqueue +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder-analyzer +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-composer +0 -0
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/LICENSE +0 -0
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/top_level.txt +0 -0
swift/account/server.py
CHANGED
@@ -85,17 +85,7 @@ class AccountController(BaseStorageServer):
|
|
85
85
|
self.replicator_rpc = ReplicatorRpc(self.root, DATADIR, AccountBroker,
|
86
86
|
self.mount_check,
|
87
87
|
logger=self.logger)
|
88
|
-
|
89
|
-
self.logger.warning('Option auto_create_account_prefix is '
|
90
|
-
'deprecated. Configure '
|
91
|
-
'auto_create_account_prefix under the '
|
92
|
-
'swift-constraints section of '
|
93
|
-
'swift.conf. This option will '
|
94
|
-
'be ignored in a future release.')
|
95
|
-
self.auto_create_account_prefix = \
|
96
|
-
conf['auto_create_account_prefix']
|
97
|
-
else:
|
98
|
-
self.auto_create_account_prefix = AUTO_CREATE_ACCOUNT_PREFIX
|
88
|
+
self.auto_create_account_prefix = AUTO_CREATE_ACCOUNT_PREFIX
|
99
89
|
|
100
90
|
swift.common.db.DB_PREALLOCATION = \
|
101
91
|
config_true_value(conf.get('db_preallocation', 'f'))
|
swift/cli/info.py
CHANGED
@@ -198,6 +198,28 @@ def print_ring_locations(ring, datadir, account, container=None, obj=None,
|
|
198
198
|
'real value is set in the config file on each storage node.')
|
199
199
|
|
200
200
|
|
201
|
+
def get_max_len_sync_item(syncs, item, title):
|
202
|
+
def map_func(element):
|
203
|
+
return str(element[item])
|
204
|
+
return max(list(map(len, map(map_func, syncs))) + [len(title)])
|
205
|
+
|
206
|
+
|
207
|
+
def print_db_syncs(incoming, syncs):
|
208
|
+
max_sync_point_len = get_max_len_sync_item(syncs, 'sync_point',
|
209
|
+
"Sync Point")
|
210
|
+
max_remote_len = get_max_len_sync_item(syncs, 'remote_id', "Remote ID")
|
211
|
+
print('%s Syncs:' % ('Incoming' if incoming else 'Outgoing'))
|
212
|
+
print(' %s\t%s\t%s' % ("Sync Point".ljust(max_sync_point_len),
|
213
|
+
"Remote ID".ljust(max_remote_len),
|
214
|
+
"Updated At"))
|
215
|
+
for sync in syncs:
|
216
|
+
print(' %s\t%s\t%s (%s)' % (
|
217
|
+
str(sync['sync_point']).ljust(max_sync_point_len),
|
218
|
+
sync['remote_id'].ljust(max_remote_len),
|
219
|
+
Timestamp(sync['updated_at']).isoformat,
|
220
|
+
sync['updated_at']))
|
221
|
+
|
222
|
+
|
201
223
|
def print_db_info_metadata(db_type, info, metadata, drop_prefixes=False,
|
202
224
|
verbose=False):
|
203
225
|
"""
|
@@ -439,7 +461,7 @@ def print_obj_metadata(metadata, drop_prefixes=False):
|
|
439
461
|
|
440
462
|
|
441
463
|
def print_info(db_type, db_file, swift_dir='/etc/swift', stale_reads_ok=False,
|
442
|
-
drop_prefixes=False, verbose=False):
|
464
|
+
drop_prefixes=False, verbose=False, sync=False):
|
443
465
|
if db_type not in ('account', 'container'):
|
444
466
|
print("Unrecognized DB type: internal error")
|
445
467
|
raise InfoSystemExit()
|
@@ -473,6 +495,11 @@ def print_info(db_type, db_file, swift_dir='/etc/swift', stale_reads_ok=False,
|
|
473
495
|
info['shard_ranges'] = sranges
|
474
496
|
print_db_info_metadata(
|
475
497
|
db_type, info, broker.metadata, drop_prefixes, verbose)
|
498
|
+
if sync:
|
499
|
+
# Print incoming / outgoing sync tables.
|
500
|
+
for incoming in (True, False):
|
501
|
+
print_db_syncs(incoming, broker.get_syncs(incoming,
|
502
|
+
include_timestamp=True))
|
476
503
|
try:
|
477
504
|
ring = Ring(swift_dir, ring_name=db_type)
|
478
505
|
except Exception:
|
@@ -1,4 +1,3 @@
|
|
1
|
-
#!python
|
2
1
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
2
|
# you may not use this file except in compliance with the License.
|
4
3
|
# You may obtain a copy of the License at
|
@@ -12,10 +11,6 @@
|
|
12
11
|
# See the License for the specific language governing permissions and
|
13
12
|
# limitations under the License.
|
14
13
|
|
15
|
-
"""
|
16
|
-
swift-recon-cron.py
|
17
|
-
"""
|
18
|
-
|
19
14
|
import os
|
20
15
|
import sys
|
21
16
|
import time
|
@@ -28,7 +23,7 @@ from swift.common.recon import RECON_OBJECT_FILE, DEFAULT_RECON_CACHE_PATH
|
|
28
23
|
from swift.obj.diskfile import ASYNCDIR_BASE
|
29
24
|
|
30
25
|
|
31
|
-
def get_async_count(device_dir
|
26
|
+
def get_async_count(device_dir):
|
32
27
|
async_count = 0
|
33
28
|
for i in os.listdir(device_dir):
|
34
29
|
device = os.path.join(device_dir, i)
|
@@ -55,7 +50,7 @@ def main():
|
|
55
50
|
except Exception:
|
56
51
|
print("Usage: %s CONF_FILE" % sys.argv[0].split('/')[-1])
|
57
52
|
print("ex: swift-recon-cron /etc/swift/object-server.conf")
|
58
|
-
|
53
|
+
return 1
|
59
54
|
conf = readconf(conf_path, 'filter:recon')
|
60
55
|
device_dir = conf.get('devices', '/srv/node')
|
61
56
|
recon_cache_path = conf.get('recon_cache_path', DEFAULT_RECON_CACHE_PATH)
|
@@ -66,7 +61,7 @@ def main():
|
|
66
61
|
logger = get_logger(conf, log_route='recon-cron')
|
67
62
|
try:
|
68
63
|
with lock_path(lock_dir):
|
69
|
-
asyncs = get_async_count(device_dir
|
64
|
+
asyncs = get_async_count(device_dir)
|
70
65
|
dump_recon_cache({
|
71
66
|
'async_pending': asyncs,
|
72
67
|
'async_pending_last': time.time(),
|
@@ -75,8 +70,4 @@ def main():
|
|
75
70
|
msg = 'Exception during recon-cron while accessing devices'
|
76
71
|
logger.exception(msg)
|
77
72
|
print('%s: %s' % (msg, err))
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
if __name__ == '__main__':
|
82
|
-
main()
|
73
|
+
return 1
|
swift/cli/reload.py
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# Copyright (c) 2022 NVIDIA
|
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
|
+
Safely reload WSGI servers while minimizing client downtime and errors by
|
18
|
+
|
19
|
+
* validating that the process is a Swift WSGI server manager,
|
20
|
+
* checking that the configuration file used is valid,
|
21
|
+
* sending the "seamless reload" signal, and
|
22
|
+
* waiting for the reload to complete.
|
23
|
+
"""
|
24
|
+
|
25
|
+
from __future__ import print_function
|
26
|
+
import argparse
|
27
|
+
import errno
|
28
|
+
import os
|
29
|
+
import os.path
|
30
|
+
import signal
|
31
|
+
import subprocess
|
32
|
+
import sys
|
33
|
+
import time
|
34
|
+
|
35
|
+
from swift.common.manager import get_child_pids
|
36
|
+
|
37
|
+
|
38
|
+
EXIT_BAD_PID = 2 # similar to argparse exiting 2 on an unknown arg
|
39
|
+
EXIT_RELOAD_FAILED = 1
|
40
|
+
EXIT_RELOAD_TIMEOUT = 128 + errno.ETIMEDOUT
|
41
|
+
|
42
|
+
|
43
|
+
def validate_manager_pid(pid):
|
44
|
+
try:
|
45
|
+
with open('/proc/%d/cmdline' % pid, 'r') as fp:
|
46
|
+
cmd = fp.read().strip('\x00').split('\x00')
|
47
|
+
sid = os.getsid(pid)
|
48
|
+
except (IOError, OSError):
|
49
|
+
print("Failed to get process information for %s" % pid,
|
50
|
+
file=sys.stderr)
|
51
|
+
exit(EXIT_BAD_PID)
|
52
|
+
|
53
|
+
scripts = [os.path.basename(c) for c in cmd
|
54
|
+
if '/bin/' in c and '/bin/python' not in c]
|
55
|
+
|
56
|
+
if len(scripts) != 1 or not scripts[0].startswith("swift-"):
|
57
|
+
print("Non-swift process: %r" % ' '.join(cmd), file=sys.stderr)
|
58
|
+
exit(EXIT_BAD_PID)
|
59
|
+
|
60
|
+
if scripts[0] not in {"swift-proxy-server", "swift-account-server",
|
61
|
+
"swift-container-server", "swift-object-server"}:
|
62
|
+
print("Process does not support config checks: %s" % scripts[0],
|
63
|
+
file=sys.stderr)
|
64
|
+
exit(EXIT_BAD_PID)
|
65
|
+
|
66
|
+
if sid != pid:
|
67
|
+
print("Process appears to be a %s worker, not a manager. "
|
68
|
+
"Did you mean %s?" % (scripts[0], sid), file=sys.stderr)
|
69
|
+
exit(EXIT_BAD_PID)
|
70
|
+
|
71
|
+
return cmd, scripts[0]
|
72
|
+
|
73
|
+
|
74
|
+
def main(args=None):
|
75
|
+
parser = argparse.ArgumentParser(__doc__)
|
76
|
+
parser.add_argument("pid", type=int,
|
77
|
+
help="server PID which should be reloaded")
|
78
|
+
wait_group = parser.add_mutually_exclusive_group()
|
79
|
+
wait_group.add_argument("-t", "--timeout", type=float, default=300.0,
|
80
|
+
help="max time to wait for reload to complete")
|
81
|
+
wait_group.add_argument("-w", "--no-wait",
|
82
|
+
action="store_false", dest="wait",
|
83
|
+
help="skip waiting for reload to complete")
|
84
|
+
parser.add_argument("-v", "--verbose", action="store_true",
|
85
|
+
help="display more information as the process reloads")
|
86
|
+
args = parser.parse_args(args)
|
87
|
+
|
88
|
+
cmd, script = validate_manager_pid(args.pid)
|
89
|
+
|
90
|
+
if args.verbose:
|
91
|
+
print("Checking config for %s" % script)
|
92
|
+
try:
|
93
|
+
subprocess.check_call(cmd + ["--test-config"])
|
94
|
+
except subprocess.CalledProcessError:
|
95
|
+
print("Failed to validate config", file=sys.stderr)
|
96
|
+
exit(EXIT_RELOAD_FAILED)
|
97
|
+
|
98
|
+
if args.wait:
|
99
|
+
try:
|
100
|
+
original_children = get_child_pids(args.pid)
|
101
|
+
children_since_reload = set()
|
102
|
+
|
103
|
+
if args.verbose:
|
104
|
+
print("Sending USR1 signal")
|
105
|
+
os.kill(args.pid, signal.SIGUSR1)
|
106
|
+
|
107
|
+
start = time.time()
|
108
|
+
while time.time() - start < args.timeout:
|
109
|
+
children = get_child_pids(args.pid)
|
110
|
+
new_children = (children - original_children
|
111
|
+
- children_since_reload)
|
112
|
+
if new_children:
|
113
|
+
if args.verbose:
|
114
|
+
print("Found new children: %s" % ", ".join(
|
115
|
+
str(pid) for pid in new_children))
|
116
|
+
children_since_reload |= new_children
|
117
|
+
if children_since_reload - children:
|
118
|
+
# At least one new child exited; presumably, it was
|
119
|
+
# the temporary child waiting to shutdown sockets
|
120
|
+
break
|
121
|
+
# We want this to be fairly low, since the temporary child
|
122
|
+
# may not hang around very long
|
123
|
+
time.sleep(0.1)
|
124
|
+
else:
|
125
|
+
print("Timed out reloading %s" % script, file=sys.stderr)
|
126
|
+
exit(EXIT_RELOAD_TIMEOUT)
|
127
|
+
|
128
|
+
except subprocess.CalledProcessError:
|
129
|
+
# This could pop during any of the calls to get_child_pids
|
130
|
+
print("Process seems to have died!", file=sys.stderr)
|
131
|
+
exit(EXIT_RELOAD_FAILED)
|
132
|
+
else: # --no-wait
|
133
|
+
if args.verbose:
|
134
|
+
print("Sending USR1 signal")
|
135
|
+
os.kill(args.pid, signal.SIGUSR1)
|
136
|
+
|
137
|
+
print("Reloaded %s" % script)
|
138
|
+
|
139
|
+
|
140
|
+
if __name__ == "__main__":
|
141
|
+
main()
|
swift/common/daemon.py
CHANGED
@@ -159,7 +159,7 @@ class DaemonStrategy(object):
|
|
159
159
|
except KeyboardInterrupt:
|
160
160
|
self.logger.notice('User quit')
|
161
161
|
finally:
|
162
|
-
self.cleanup()
|
162
|
+
self.cleanup(stopping=True)
|
163
163
|
self.running = False
|
164
164
|
|
165
165
|
def _fork(self, once, **kwargs):
|
@@ -167,6 +167,8 @@ class DaemonStrategy(object):
|
|
167
167
|
if pid == 0:
|
168
168
|
signal.signal(signal.SIGHUP, signal.SIG_DFL)
|
169
169
|
signal.signal(signal.SIGTERM, signal.SIG_DFL)
|
170
|
+
# only MAINPID should be sending notifications
|
171
|
+
os.environ.pop('NOTIFY_SOCKET', None)
|
170
172
|
|
171
173
|
self.daemon.run(once, **kwargs)
|
172
174
|
|
@@ -245,7 +247,15 @@ class DaemonStrategy(object):
|
|
245
247
|
self.daemon.post_multiprocess_run()
|
246
248
|
return 0
|
247
249
|
|
248
|
-
def cleanup(self):
|
250
|
+
def cleanup(self, stopping=False):
|
251
|
+
"""
|
252
|
+
Cleanup worker processes
|
253
|
+
|
254
|
+
:param stopping: if set, tell systemd we're stopping
|
255
|
+
"""
|
256
|
+
|
257
|
+
if stopping:
|
258
|
+
utils.systemd_notify(self.logger, "STOPPING=1")
|
249
259
|
for p in self.spawned_pids():
|
250
260
|
try:
|
251
261
|
os.kill(p, signal.SIGTERM)
|
swift/common/db.py
CHANGED
@@ -724,22 +724,26 @@ class DatabaseBroker(object):
|
|
724
724
|
return -1
|
725
725
|
return row['sync_point']
|
726
726
|
|
727
|
-
def get_syncs(self, incoming=True):
|
727
|
+
def get_syncs(self, incoming=True, include_timestamp=False):
|
728
728
|
"""
|
729
729
|
Get a serialized copy of the sync table.
|
730
730
|
|
731
731
|
:param incoming: if True, get the last incoming sync, otherwise get
|
732
732
|
the last outgoing sync
|
733
|
-
:
|
733
|
+
:param include_timestamp: If True include the updated_at timestamp
|
734
|
+
:returns: list of {'remote_id', 'sync_point'} or
|
735
|
+
{'remote_id', 'sync_point', 'updated_at'}
|
736
|
+
if include_timestamp is True.
|
734
737
|
"""
|
735
738
|
with self.get() as conn:
|
739
|
+
columns = 'remote_id, sync_point'
|
740
|
+
if include_timestamp:
|
741
|
+
columns += ', updated_at'
|
736
742
|
curs = conn.execute('''
|
737
|
-
SELECT
|
738
|
-
''' % ('incoming' if incoming else 'outgoing'))
|
739
|
-
|
740
|
-
for
|
741
|
-
result.append({'remote_id': row[0], 'sync_point': row[1]})
|
742
|
-
return result
|
743
|
+
SELECT %s FROM %s_sync
|
744
|
+
''' % (columns, 'incoming' if incoming else 'outgoing'))
|
745
|
+
curs.row_factory = dict_factory
|
746
|
+
return [r for r in curs]
|
743
747
|
|
744
748
|
def get_max_row(self, table=None):
|
745
749
|
if not table:
|
swift/common/http_protocol.py
CHANGED
@@ -16,15 +16,21 @@
|
|
16
16
|
from eventlet import wsgi, websocket
|
17
17
|
import six
|
18
18
|
|
19
|
+
from swift.common.utils import generate_trans_id
|
20
|
+
from swift.common.http import HTTP_NO_CONTENT, HTTP_RESET_CONTENT, \
|
21
|
+
HTTP_NOT_MODIFIED
|
19
22
|
|
20
23
|
if six.PY2:
|
21
24
|
from eventlet.green import httplib as http_client
|
25
|
+
from cgi import escape
|
22
26
|
else:
|
23
27
|
from eventlet.green.http import client as http_client
|
28
|
+
from html import escape
|
24
29
|
|
25
30
|
|
26
31
|
class SwiftHttpProtocol(wsgi.HttpProtocol):
|
27
32
|
default_request_version = "HTTP/1.0"
|
33
|
+
reject_bad_requests = False
|
28
34
|
|
29
35
|
def __init__(self, *args, **kwargs):
|
30
36
|
# See https://github.com/eventlet/eventlet/pull/590
|
@@ -52,7 +58,7 @@ class SwiftHttpProtocol(wsgi.HttpProtocol):
|
|
52
58
|
self.server.log.info('ERROR WSGI: ' + f, *a)
|
53
59
|
|
54
60
|
class MessageClass(wsgi.HttpProtocol.MessageClass):
|
55
|
-
|
61
|
+
"""Subclass to see when the client didn't provide a Content-Type"""
|
56
62
|
# for py2:
|
57
63
|
def parsetype(self):
|
58
64
|
if self.typeheader is None:
|
@@ -61,7 +67,7 @@ class SwiftHttpProtocol(wsgi.HttpProtocol):
|
|
61
67
|
|
62
68
|
# for py3:
|
63
69
|
def get_default_type(self):
|
64
|
-
|
70
|
+
"""If the client didn't provide a content type, leave it blank."""
|
65
71
|
return ''
|
66
72
|
|
67
73
|
def parse_request(self):
|
@@ -241,6 +247,74 @@ class SwiftHttpProtocol(wsgi.HttpProtocol):
|
|
241
247
|
self.conn_state[2] = wsgi.STATE_IDLE
|
242
248
|
return got
|
243
249
|
|
250
|
+
def send_error(self, code, message=None, explain=None):
|
251
|
+
"""Send and log an error reply, we are overriding the cpython parent
|
252
|
+
class method, so we can have logger generate txn_id's for error
|
253
|
+
response from wsgi since we are at the edge of the proxy server.
|
254
|
+
This sends an error response (so it must be called before any output
|
255
|
+
has been generated), logs the error, and finally sends a piece of HTML
|
256
|
+
explaining the error to the user.
|
257
|
+
|
258
|
+
:param code: an HTTP error code
|
259
|
+
3 digits
|
260
|
+
:param message: a simple optional 1 line reason phrase.
|
261
|
+
*( HTAB / SP / VCHAR / %x80-FF )
|
262
|
+
defaults to short entry matching the response code
|
263
|
+
:param explain: a detailed message defaults to the long entry
|
264
|
+
matching the response code.
|
265
|
+
"""
|
266
|
+
|
267
|
+
try:
|
268
|
+
shortmsg, longmsg = self.responses[code]
|
269
|
+
except KeyError:
|
270
|
+
shortmsg, longmsg = '???', '???'
|
271
|
+
if message is None:
|
272
|
+
message = shortmsg
|
273
|
+
if explain is None:
|
274
|
+
explain = longmsg
|
275
|
+
|
276
|
+
try:
|
277
|
+
# assume we have a LogAdapter
|
278
|
+
txn_id = self.server.app.logger.txn_id # just in case it was set
|
279
|
+
except AttributeError:
|
280
|
+
# turns out we don't have a LogAdapter, so go direct
|
281
|
+
txn_id = generate_trans_id('')
|
282
|
+
self.log_error("code %d, message %s, (txn: %s)", code,
|
283
|
+
message, txn_id)
|
284
|
+
else:
|
285
|
+
# we do have a LogAdapter, but likely not yet a txn_id
|
286
|
+
txn_id = txn_id or generate_trans_id('')
|
287
|
+
self.server.app.logger.txn_id = txn_id
|
288
|
+
self.log_error("code %d, message %s", code, message)
|
289
|
+
self.send_response(code, message)
|
290
|
+
self.send_header('Connection', 'close')
|
291
|
+
|
292
|
+
# Message body is omitted for cases described in:
|
293
|
+
# - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
|
294
|
+
# - RFC7231: 6.3.6. 205(Reset Content)
|
295
|
+
body = None
|
296
|
+
exclude_status = (HTTP_NO_CONTENT,
|
297
|
+
HTTP_RESET_CONTENT,
|
298
|
+
HTTP_NOT_MODIFIED)
|
299
|
+
if (code >= 200 and
|
300
|
+
code not in exclude_status):
|
301
|
+
# HTML encode to prevent Cross Site Scripting attacks
|
302
|
+
# (see bug https://bugs.python.org/issue1100201)
|
303
|
+
content = (self.error_message_format % {
|
304
|
+
'code': code,
|
305
|
+
'message': escape(message, quote=False),
|
306
|
+
'explain': escape(explain, quote=False)
|
307
|
+
})
|
308
|
+
body = content.encode('UTF-8', 'replace')
|
309
|
+
self.send_header("Content-Type", self.error_content_type)
|
310
|
+
self.send_header('Content-Length', str(len(body)))
|
311
|
+
self.send_header('X-Trans-Id', txn_id)
|
312
|
+
self.send_header('X-Openstack-Request-Id', txn_id)
|
313
|
+
self.end_headers()
|
314
|
+
|
315
|
+
if self.command != 'HEAD' and body:
|
316
|
+
self.wfile.write(body)
|
317
|
+
|
244
318
|
|
245
319
|
class SwiftHttpProxiedProtocol(SwiftHttpProtocol):
|
246
320
|
"""
|
@@ -271,7 +345,6 @@ class SwiftHttpProxiedProtocol(SwiftHttpProtocol):
|
|
271
345
|
# ourselves and our gateway proxy before processing the client
|
272
346
|
# protocol request. Hopefully the operator will know what to do!
|
273
347
|
msg = 'Invalid PROXY line %r' % connection_line
|
274
|
-
self.log_message(msg)
|
275
348
|
# Even assuming HTTP we don't even known what version of HTTP the
|
276
349
|
# client is sending? This entire endeavor seems questionable.
|
277
350
|
self.request_version = self.default_request_version
|
swift/common/manager.py
CHANGED
@@ -24,7 +24,11 @@ import subprocess
|
|
24
24
|
import re
|
25
25
|
import six
|
26
26
|
import tempfile
|
27
|
-
|
27
|
+
try:
|
28
|
+
from shutil import which
|
29
|
+
except ImportError:
|
30
|
+
# py2
|
31
|
+
from distutils.spawn import find_executable as which
|
28
32
|
|
29
33
|
from swift.common.utils import search_tree, remove_file, write_file, readconf
|
30
34
|
from swift.common.exceptions import InvalidPidFileException
|
@@ -176,6 +180,17 @@ def kill_group(pid, sig):
|
|
176
180
|
os.kill(-pid, sig)
|
177
181
|
|
178
182
|
|
183
|
+
def get_child_pids(pid):
|
184
|
+
"""
|
185
|
+
Get the current set of all child PIDs for a PID.
|
186
|
+
|
187
|
+
:param pid: process id
|
188
|
+
"""
|
189
|
+
output = subprocess.check_output(
|
190
|
+
["ps", "--ppid", str(pid), "--no-headers", "-o", "pid"])
|
191
|
+
return {int(pid) for pid in output.split()}
|
192
|
+
|
193
|
+
|
179
194
|
def format_server_name(servername):
|
180
195
|
"""
|
181
196
|
Formats server name as swift compatible server names
|
@@ -204,7 +219,7 @@ def verify_server(server):
|
|
204
219
|
if not server:
|
205
220
|
return False
|
206
221
|
_, cmd = format_server_name(server)
|
207
|
-
if
|
222
|
+
if which(cmd) is None:
|
208
223
|
return False
|
209
224
|
return True
|
210
225
|
|
@@ -696,9 +711,7 @@ class Server(object):
|
|
696
711
|
print('Removing pid file %s with invalid pid' % pid_file)
|
697
712
|
remove_file(pid_file)
|
698
713
|
continue
|
699
|
-
|
700
|
-
for pid in subprocess.check_output(ps_cmd).split():
|
701
|
-
pid = int(pid)
|
714
|
+
for pid in get_child_pids(pid):
|
702
715
|
if self._signal_pid(sig, pid, pid_file, kwargs.get('verbose')):
|
703
716
|
pids[pid] = pid_file
|
704
717
|
return pids
|
swift/common/memcached.py
CHANGED
@@ -245,6 +245,13 @@ class MemcacheRing(object):
|
|
245
245
|
def memcache_servers(self):
|
246
246
|
return list(self._client_cache.keys())
|
247
247
|
|
248
|
+
def _log_error(self, server, cmd, action, msg):
|
249
|
+
self.logger.error(
|
250
|
+
"Error %(action)s to memcached: %(server)s"
|
251
|
+
": with key_prefix %(key_prefix)s, method %(method)s: %(msg)s",
|
252
|
+
{'action': action, 'server': server, 'key_prefix': cmd.key_prefix,
|
253
|
+
'method': cmd.method, 'msg': msg})
|
254
|
+
|
248
255
|
"""
|
249
256
|
Handles exceptions.
|
250
257
|
|
@@ -367,7 +374,8 @@ class MemcacheRing(object):
|
|
367
374
|
self._exception_occurred(server, e, cmd, pool_start_time,
|
368
375
|
action='connecting', sock=sock)
|
369
376
|
if not any_yielded:
|
370
|
-
self.
|
377
|
+
self._log_error('ALL', cmd, 'connecting',
|
378
|
+
'No more memcached servers to try')
|
371
379
|
|
372
380
|
def _return_conn(self, server, fp, sock):
|
373
381
|
"""Returns a server connection to the pool."""
|
@@ -404,6 +412,14 @@ class MemcacheRing(object):
|
|
404
412
|
elif not isinstance(value, bytes):
|
405
413
|
value = str(value).encode('utf-8')
|
406
414
|
|
415
|
+
if 0 <= self.item_size_warning_threshold <= len(value):
|
416
|
+
self.logger.warning(
|
417
|
+
"Item size larger than warning threshold: "
|
418
|
+
"%d (%s) >= %d (%s)", len(value),
|
419
|
+
human_readable(len(value)),
|
420
|
+
self.item_size_warning_threshold,
|
421
|
+
human_readable(self.item_size_warning_threshold))
|
422
|
+
|
407
423
|
for (server, fp, sock) in self._get_conns(cmd):
|
408
424
|
conn_start_time = tm.time()
|
409
425
|
try:
|
@@ -414,17 +430,7 @@ class MemcacheRing(object):
|
|
414
430
|
if msg != b'STORED':
|
415
431
|
if not six.PY2:
|
416
432
|
msg = msg.decode('ascii')
|
417
|
-
|
418
|
-
"Error setting value in memcached: "
|
419
|
-
"%(server)s: %(msg)s",
|
420
|
-
{'server': server, 'msg': msg})
|
421
|
-
if 0 <= self.item_size_warning_threshold <= len(value):
|
422
|
-
self.logger.warning(
|
423
|
-
"Item size larger than warning threshold: "
|
424
|
-
"%d (%s) >= %d (%s)", len(value),
|
425
|
-
human_readable(len(value)),
|
426
|
-
self.item_size_warning_threshold,
|
427
|
-
human_readable(self.item_size_warning_threshold))
|
433
|
+
raise MemcacheConnectionError('failed set: %s' % msg)
|
428
434
|
self._return_conn(server, fp, sock)
|
429
435
|
return
|
430
436
|
except (Exception, Timeout) as e:
|