swift 2.32.0__py2.py3-none-any.whl → 2.34.0__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/auditor.py +11 -0
- swift/account/reaper.py +11 -1
- swift/account/replicator.py +22 -0
- swift/account/server.py +13 -12
- swift-2.32.0.data/scripts/swift-account-audit → swift/cli/account_audit.py +6 -2
- swift-2.32.0.data/scripts/swift-config → swift/cli/config.py +1 -1
- swift-2.32.0.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +6 -2
- swift-2.32.0.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +12 -3
- swift-2.32.0.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +6 -2
- swift/cli/info.py +131 -3
- swift-2.32.0.data/scripts/swift-oldies → swift/cli/oldies.py +6 -3
- swift-2.32.0.data/scripts/swift-orphans → swift/cli/orphans.py +7 -2
- swift-2.32.0.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +9 -18
- swift-2.32.0.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +1 -1
- swift/cli/reload.py +141 -0
- swift/cli/ringbuilder.py +24 -0
- swift/common/daemon.py +12 -2
- swift/common/db.py +14 -9
- swift/common/db_auditor.py +2 -2
- swift/common/db_replicator.py +6 -0
- swift/common/exceptions.py +12 -0
- swift/common/http_protocol.py +76 -3
- swift/common/manager.py +120 -5
- swift/common/memcached.py +24 -25
- swift/common/middleware/account_quotas.py +144 -43
- swift/common/middleware/backend_ratelimit.py +166 -24
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +3 -5
- swift/common/middleware/container_sync.py +6 -10
- swift/common/middleware/crypto/crypto_utils.py +4 -5
- swift/common/middleware/crypto/decrypter.py +4 -5
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/proxy_logging.py +57 -43
- swift/common/middleware/ratelimit.py +6 -7
- swift/common/middleware/recon.py +6 -7
- swift/common/middleware/s3api/acl_handlers.py +10 -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 +31 -15
- swift/common/middleware/s3api/controllers/obj.py +20 -1
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/s3api.py +6 -0
- swift/common/middleware/s3api/s3request.py +190 -74
- swift/common/middleware/s3api/s3response.py +48 -8
- swift/common/middleware/s3api/s3token.py +2 -2
- swift/common/middleware/s3api/utils.py +2 -1
- swift/common/middleware/slo.py +508 -310
- swift/common/middleware/staticweb.py +45 -14
- swift/common/middleware/tempauth.py +6 -4
- swift/common/middleware/tempurl.py +134 -93
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +9 -10
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +1 -2
- swift/common/request_helpers.py +101 -8
- swift/common/statsd_client.py +207 -0
- swift/common/storage_policy.py +1 -1
- swift/common/swob.py +5 -2
- swift/common/utils/__init__.py +331 -1774
- swift/common/utils/base.py +138 -0
- swift/common/utils/config.py +443 -0
- swift/common/utils/logs.py +999 -0
- swift/common/utils/timestamp.py +23 -2
- swift/common/wsgi.py +19 -3
- swift/container/auditor.py +11 -0
- swift/container/backend.py +136 -31
- swift/container/reconciler.py +11 -2
- swift/container/replicator.py +64 -7
- swift/container/server.py +276 -146
- swift/container/sharder.py +86 -42
- swift/container/sync.py +11 -1
- swift/container/updater.py +12 -2
- swift/obj/auditor.py +20 -3
- swift/obj/diskfile.py +63 -25
- swift/obj/expirer.py +154 -47
- swift/obj/mem_diskfile.py +2 -1
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +28 -4
- swift/obj/replicator.py +63 -24
- swift/obj/server.py +76 -59
- swift/obj/updater.py +12 -2
- swift/obj/watchers/dark_data.py +72 -34
- swift/proxy/controllers/account.py +3 -2
- swift/proxy/controllers/base.py +254 -148
- swift/proxy/controllers/container.py +274 -289
- swift/proxy/controllers/obj.py +120 -166
- swift/proxy/server.py +17 -13
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/AUTHORS +14 -4
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/METADATA +9 -7
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/RECORD +97 -120
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/entry_points.txt +39 -0
- swift-2.34.0.dist-info/pbr.json +1 -0
- swift-2.32.0.data/scripts/swift-account-auditor +0 -23
- swift-2.32.0.data/scripts/swift-account-info +0 -52
- swift-2.32.0.data/scripts/swift-account-reaper +0 -23
- swift-2.32.0.data/scripts/swift-account-replicator +0 -34
- swift-2.32.0.data/scripts/swift-account-server +0 -23
- swift-2.32.0.data/scripts/swift-container-auditor +0 -23
- swift-2.32.0.data/scripts/swift-container-info +0 -56
- swift-2.32.0.data/scripts/swift-container-reconciler +0 -21
- swift-2.32.0.data/scripts/swift-container-replicator +0 -34
- swift-2.32.0.data/scripts/swift-container-server +0 -23
- swift-2.32.0.data/scripts/swift-container-sharder +0 -37
- swift-2.32.0.data/scripts/swift-container-sync +0 -23
- swift-2.32.0.data/scripts/swift-container-updater +0 -23
- swift-2.32.0.data/scripts/swift-dispersion-report +0 -24
- swift-2.32.0.data/scripts/swift-form-signature +0 -20
- swift-2.32.0.data/scripts/swift-init +0 -119
- swift-2.32.0.data/scripts/swift-object-auditor +0 -29
- swift-2.32.0.data/scripts/swift-object-expirer +0 -33
- swift-2.32.0.data/scripts/swift-object-info +0 -60
- swift-2.32.0.data/scripts/swift-object-reconstructor +0 -33
- swift-2.32.0.data/scripts/swift-object-relinker +0 -23
- swift-2.32.0.data/scripts/swift-object-replicator +0 -37
- swift-2.32.0.data/scripts/swift-object-server +0 -27
- swift-2.32.0.data/scripts/swift-object-updater +0 -23
- swift-2.32.0.data/scripts/swift-proxy-server +0 -23
- swift-2.32.0.data/scripts/swift-recon +0 -24
- swift-2.32.0.data/scripts/swift-ring-builder +0 -37
- swift-2.32.0.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.32.0.data/scripts/swift-ring-composer +0 -22
- swift-2.32.0.dist-info/pbr.json +0 -1
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/LICENSE +0 -0
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/WHEEL +0 -0
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/top_level.txt +0 -0
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/cli/ringbuilder.py
CHANGED
@@ -22,9 +22,11 @@ from itertools import islice
|
|
22
22
|
from operator import itemgetter
|
23
23
|
from os import mkdir
|
24
24
|
from os.path import basename, abspath, dirname, exists, join as pathjoin
|
25
|
+
import sys
|
25
26
|
from sys import argv as sys_argv, exit, stderr, stdout
|
26
27
|
from textwrap import wrap
|
27
28
|
from time import time
|
29
|
+
import traceback
|
28
30
|
from datetime import timedelta
|
29
31
|
import optparse
|
30
32
|
import math
|
@@ -1698,3 +1700,25 @@ def main(arguments=None):
|
|
1698
1700
|
exit(2)
|
1699
1701
|
else:
|
1700
1702
|
getattr(Commands, command, Commands.unknown)()
|
1703
|
+
|
1704
|
+
|
1705
|
+
def error_handling_main():
|
1706
|
+
# We exit code 1 on WARNING statuses, 2 on ERROR. This means we need
|
1707
|
+
# to handle any uncaught exceptions by printing the usual backtrace,
|
1708
|
+
# but then exiting 2 (not 1 as is usual for a python
|
1709
|
+
# exception).
|
1710
|
+
|
1711
|
+
# We *don't* want to do this in main(), however, because we don't want to
|
1712
|
+
# pollute the test environment or cause a bunch of test churn to mock out
|
1713
|
+
# sys.excepthook
|
1714
|
+
|
1715
|
+
def exit_with_status_two(tp, val, tb):
|
1716
|
+
traceback.print_exception(tp, val, tb)
|
1717
|
+
exit(2)
|
1718
|
+
|
1719
|
+
sys.excepthook = exit_with_status_two
|
1720
|
+
main()
|
1721
|
+
|
1722
|
+
|
1723
|
+
if __name__ == '__main__':
|
1724
|
+
error_handling_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
@@ -138,7 +138,8 @@ class GreenDBConnection(sqlite3.Connection):
|
|
138
138
|
timeout = BROKER_TIMEOUT
|
139
139
|
self.timeout = timeout
|
140
140
|
self.db_file = database
|
141
|
-
super(GreenDBConnection, self).__init__(
|
141
|
+
super(GreenDBConnection, self).__init__(
|
142
|
+
database, timeout=0, *args, **kwargs)
|
142
143
|
|
143
144
|
def cursor(self, cls=None):
|
144
145
|
if cls is None:
|
@@ -724,22 +725,26 @@ class DatabaseBroker(object):
|
|
724
725
|
return -1
|
725
726
|
return row['sync_point']
|
726
727
|
|
727
|
-
def get_syncs(self, incoming=True):
|
728
|
+
def get_syncs(self, incoming=True, include_timestamp=False):
|
728
729
|
"""
|
729
730
|
Get a serialized copy of the sync table.
|
730
731
|
|
731
732
|
:param incoming: if True, get the last incoming sync, otherwise get
|
732
733
|
the last outgoing sync
|
733
|
-
:
|
734
|
+
:param include_timestamp: If True include the updated_at timestamp
|
735
|
+
:returns: list of {'remote_id', 'sync_point'} or
|
736
|
+
{'remote_id', 'sync_point', 'updated_at'}
|
737
|
+
if include_timestamp is True.
|
734
738
|
"""
|
735
739
|
with self.get() as conn:
|
740
|
+
columns = 'remote_id, sync_point'
|
741
|
+
if include_timestamp:
|
742
|
+
columns += ', updated_at'
|
736
743
|
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
|
744
|
+
SELECT %s FROM %s_sync
|
745
|
+
''' % (columns, 'incoming' if incoming else 'outgoing'))
|
746
|
+
curs.row_factory = dict_factory
|
747
|
+
return [r for r in curs]
|
743
748
|
|
744
749
|
def get_max_row(self, table=None):
|
745
750
|
if not table:
|
swift/common/db_auditor.py
CHANGED
@@ -96,7 +96,7 @@ class DatabaseAuditor(Daemon):
|
|
96
96
|
time.sleep(random() * self.interval)
|
97
97
|
while True:
|
98
98
|
self.logger.info(
|
99
|
-
'Begin
|
99
|
+
'Begin %s audit pass.', self.server_type)
|
100
100
|
begin = time.time()
|
101
101
|
try:
|
102
102
|
reported = self._one_audit_pass(reported)
|
@@ -116,7 +116,7 @@ class DatabaseAuditor(Daemon):
|
|
116
116
|
def run_once(self, *args, **kwargs):
|
117
117
|
"""Run the database audit once."""
|
118
118
|
self.logger.info(
|
119
|
-
'Begin
|
119
|
+
'Begin %s audit "once" mode', self.server_type)
|
120
120
|
begin = reported = time.time()
|
121
121
|
self._one_audit_pass(reported)
|
122
122
|
elapsed = time.time() - begin
|
swift/common/db_replicator.py
CHANGED
@@ -240,6 +240,12 @@ class Replicator(Daemon):
|
|
240
240
|
self.handoffs_only = config_true_value(conf.get('handoffs_only', 'no'))
|
241
241
|
self.handoff_delete = config_auto_int_value(
|
242
242
|
conf.get('handoff_delete', 'auto'), 0)
|
243
|
+
if self.handoff_delete >= self.ring.replica_count:
|
244
|
+
self.logger.warning(
|
245
|
+
'handoff_delete=%d is too high to have an effect on a ring '
|
246
|
+
'with replica count %d. Disabling.',
|
247
|
+
self.handoff_delete, self.ring.replica_count)
|
248
|
+
self.handoff_delete = 0
|
243
249
|
|
244
250
|
def _zero_stats(self):
|
245
251
|
"""Zero out the stats."""
|
swift/common/exceptions.py
CHANGED
@@ -243,6 +243,18 @@ class QuarantineRequest(SwiftException):
|
|
243
243
|
pass
|
244
244
|
|
245
245
|
|
246
|
+
class MemcacheConnectionError(Exception):
|
247
|
+
pass
|
248
|
+
|
249
|
+
|
250
|
+
class MemcacheIncrNotFoundError(MemcacheConnectionError):
|
251
|
+
pass
|
252
|
+
|
253
|
+
|
254
|
+
class MemcachePoolTimeout(Timeout):
|
255
|
+
pass
|
256
|
+
|
257
|
+
|
246
258
|
class ClientException(Exception):
|
247
259
|
|
248
260
|
def __init__(self, msg, http_scheme='', http_host='', http_port='',
|
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
@@ -13,9 +13,11 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
+
|
16
17
|
from __future__ import print_function
|
17
18
|
import functools
|
18
19
|
import errno
|
20
|
+
from optparse import OptionParser
|
19
21
|
import os
|
20
22
|
import resource
|
21
23
|
import signal
|
@@ -23,8 +25,13 @@ import time
|
|
23
25
|
import subprocess
|
24
26
|
import re
|
25
27
|
import six
|
28
|
+
import sys
|
26
29
|
import tempfile
|
27
|
-
|
30
|
+
try:
|
31
|
+
from shutil import which
|
32
|
+
except ImportError:
|
33
|
+
# py2
|
34
|
+
from distutils.spawn import find_executable as which
|
28
35
|
|
29
36
|
from swift.common.utils import search_tree, remove_file, write_file, readconf
|
30
37
|
from swift.common.exceptions import InvalidPidFileException
|
@@ -176,6 +183,17 @@ def kill_group(pid, sig):
|
|
176
183
|
os.kill(-pid, sig)
|
177
184
|
|
178
185
|
|
186
|
+
def get_child_pids(pid):
|
187
|
+
"""
|
188
|
+
Get the current set of all child PIDs for a PID.
|
189
|
+
|
190
|
+
:param pid: process id
|
191
|
+
"""
|
192
|
+
output = subprocess.check_output(
|
193
|
+
["ps", "--ppid", str(pid), "--no-headers", "-o", "pid"])
|
194
|
+
return {int(pid) for pid in output.split()}
|
195
|
+
|
196
|
+
|
179
197
|
def format_server_name(servername):
|
180
198
|
"""
|
181
199
|
Formats server name as swift compatible server names
|
@@ -204,7 +222,7 @@ def verify_server(server):
|
|
204
222
|
if not server:
|
205
223
|
return False
|
206
224
|
_, cmd = format_server_name(server)
|
207
|
-
if
|
225
|
+
if which(cmd) is None:
|
208
226
|
return False
|
209
227
|
return True
|
210
228
|
|
@@ -696,9 +714,7 @@ class Server(object):
|
|
696
714
|
print('Removing pid file %s with invalid pid' % pid_file)
|
697
715
|
remove_file(pid_file)
|
698
716
|
continue
|
699
|
-
|
700
|
-
for pid in subprocess.check_output(ps_cmd).split():
|
701
|
-
pid = int(pid)
|
717
|
+
for pid in get_child_pids(pid):
|
702
718
|
if self._signal_pid(sig, pid, pid_file, kwargs.get('verbose')):
|
703
719
|
pids[pid] = pid_file
|
704
720
|
return pids
|
@@ -923,3 +939,102 @@ class Server(object):
|
|
923
939
|
|
924
940
|
"""
|
925
941
|
return self.kill_running_pids(**kwargs)
|
942
|
+
|
943
|
+
|
944
|
+
USAGE = \
|
945
|
+
"""%prog <server>[.<config>] [<server>[.<config>] ...] <command> [options]
|
946
|
+
|
947
|
+
where:
|
948
|
+
<server> is the name of a swift service e.g. proxy-server.
|
949
|
+
The '-server' part of the name may be omitted.
|
950
|
+
'all', 'main' and 'rest' are reserved words that represent a
|
951
|
+
group of services.
|
952
|
+
all: Expands to all swift daemons.
|
953
|
+
main: Expands to main swift daemons.
|
954
|
+
(proxy, container, account, object)
|
955
|
+
rest: Expands to all remaining background daemons (beyond
|
956
|
+
"main").
|
957
|
+
(updater, replicator, auditor, etc)
|
958
|
+
<config> is an explicit configuration filename without the
|
959
|
+
.conf extension. If <config> is specified then <server> should
|
960
|
+
refer to a directory containing the configuration file, e.g.:
|
961
|
+
|
962
|
+
swift-init object.1 start
|
963
|
+
|
964
|
+
will start an object-server using the configuration file
|
965
|
+
/etc/swift/object-server/1.conf
|
966
|
+
<command> is a command from the list below.
|
967
|
+
|
968
|
+
Commands:
|
969
|
+
""" + '\n'.join(["%16s: %s" % x for x in Manager.list_commands()])
|
970
|
+
|
971
|
+
|
972
|
+
def main():
|
973
|
+
parser = OptionParser(USAGE)
|
974
|
+
parser.add_option('-v', '--verbose', action="store_true",
|
975
|
+
default=False, help="display verbose output")
|
976
|
+
parser.add_option('-w', '--no-wait', action="store_false", dest="wait",
|
977
|
+
default=True, help="won't wait for server to start "
|
978
|
+
"before returning")
|
979
|
+
parser.add_option('-o', '--once', action="store_true",
|
980
|
+
default=False, help="only run one pass of daemon")
|
981
|
+
# this is a negative option, default is options.daemon = True
|
982
|
+
parser.add_option('-n', '--no-daemon', action="store_false", dest="daemon",
|
983
|
+
default=True, help="start server interactively")
|
984
|
+
parser.add_option('-g', '--graceful', action="store_true",
|
985
|
+
default=False, help="send SIGHUP to supporting servers")
|
986
|
+
parser.add_option('-c', '--config-num', metavar="N", type="int",
|
987
|
+
dest="number", default=0,
|
988
|
+
help="send command to the Nth server only")
|
989
|
+
parser.add_option('-k', '--kill-wait', metavar="N", type="int",
|
990
|
+
dest="kill_wait", default=KILL_WAIT,
|
991
|
+
help="wait N seconds for processes to die (default 15)")
|
992
|
+
parser.add_option('-r', '--run-dir', type="str",
|
993
|
+
dest="run_dir", default=RUN_DIR,
|
994
|
+
help="alternative directory to store running pid files "
|
995
|
+
"default: %s" % RUN_DIR)
|
996
|
+
# Changing behaviour if missing config
|
997
|
+
parser.add_option('--strict', dest='strict', action='store_true',
|
998
|
+
help="Return non-zero status code if some config is "
|
999
|
+
"missing. Default mode if all servers are "
|
1000
|
+
"explicitly named.")
|
1001
|
+
# a negative option for strict
|
1002
|
+
parser.add_option('--non-strict', dest='strict', action='store_false',
|
1003
|
+
help="Return zero status code even if some config is "
|
1004
|
+
"missing. Default mode if any server is a glob or "
|
1005
|
+
"one of aliases `all`, `main` or `rest`.")
|
1006
|
+
# SIGKILL daemon after kill_wait period
|
1007
|
+
parser.add_option('--kill-after-timeout', dest='kill_after_timeout',
|
1008
|
+
action='store_true',
|
1009
|
+
help="Kill daemon and all children after kill-wait "
|
1010
|
+
"period.")
|
1011
|
+
|
1012
|
+
options, args = parser.parse_args()
|
1013
|
+
|
1014
|
+
if len(args) < 2:
|
1015
|
+
parser.print_help()
|
1016
|
+
print('ERROR: specify server(s) and command')
|
1017
|
+
return 1
|
1018
|
+
|
1019
|
+
command = args[-1]
|
1020
|
+
servers = args[:-1]
|
1021
|
+
|
1022
|
+
# this is just a silly swap for me cause I always try to "start main"
|
1023
|
+
commands = dict(Manager.list_commands()).keys()
|
1024
|
+
if command not in commands and servers[0] in commands:
|
1025
|
+
servers.append(command)
|
1026
|
+
command = servers.pop(0)
|
1027
|
+
|
1028
|
+
manager = Manager(servers, run_dir=options.run_dir)
|
1029
|
+
try:
|
1030
|
+
status = manager.run_command(command, **options.__dict__)
|
1031
|
+
except UnknownCommandError:
|
1032
|
+
parser.print_help()
|
1033
|
+
print('ERROR: unknown command, %s' % command)
|
1034
|
+
status = 1
|
1035
|
+
|
1036
|
+
return 1 if status else 0
|
1037
|
+
|
1038
|
+
|
1039
|
+
if __name__ == "__main__":
|
1040
|
+
sys.exit(main())
|