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.
Files changed (95) hide show
  1. swift/account/server.py +1 -11
  2. swift/cli/info.py +28 -1
  3. swift-2.32.1.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +4 -13
  4. swift/cli/reload.py +141 -0
  5. swift/common/daemon.py +12 -2
  6. swift/common/db.py +12 -8
  7. swift/common/http_protocol.py +76 -3
  8. swift/common/manager.py +18 -5
  9. swift/common/memcached.py +18 -12
  10. swift/common/middleware/proxy_logging.py +35 -27
  11. swift/common/middleware/s3api/acl_handlers.py +1 -1
  12. swift/common/middleware/s3api/controllers/__init__.py +3 -0
  13. swift/common/middleware/s3api/controllers/acl.py +3 -2
  14. swift/common/middleware/s3api/controllers/logging.py +2 -2
  15. swift/common/middleware/s3api/controllers/multi_upload.py +30 -6
  16. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  17. swift/common/middleware/s3api/s3api.py +4 -0
  18. swift/common/middleware/s3api/s3request.py +19 -12
  19. swift/common/middleware/s3api/s3response.py +13 -2
  20. swift/common/middleware/s3api/utils.py +1 -1
  21. swift/common/middleware/slo.py +395 -298
  22. swift/common/middleware/staticweb.py +45 -14
  23. swift/common/middleware/tempurl.py +132 -91
  24. swift/common/request_helpers.py +32 -8
  25. swift/common/storage_policy.py +1 -1
  26. swift/common/swob.py +5 -2
  27. swift/common/utils/__init__.py +230 -135
  28. swift/common/utils/timestamp.py +23 -2
  29. swift/common/wsgi.py +8 -0
  30. swift/container/backend.py +126 -21
  31. swift/container/replicator.py +42 -6
  32. swift/container/server.py +264 -145
  33. swift/container/sharder.py +50 -30
  34. swift/container/updater.py +1 -0
  35. swift/obj/auditor.py +2 -1
  36. swift/obj/diskfile.py +55 -19
  37. swift/obj/expirer.py +1 -13
  38. swift/obj/mem_diskfile.py +2 -1
  39. swift/obj/mem_server.py +1 -0
  40. swift/obj/replicator.py +2 -2
  41. swift/obj/server.py +12 -23
  42. swift/obj/updater.py +1 -0
  43. swift/obj/watchers/dark_data.py +72 -34
  44. swift/proxy/controllers/account.py +3 -2
  45. swift/proxy/controllers/base.py +217 -127
  46. swift/proxy/controllers/container.py +274 -289
  47. swift/proxy/controllers/obj.py +98 -141
  48. swift/proxy/server.py +2 -12
  49. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-info +3 -0
  50. swift-2.33.1.data/scripts/swift-recon-cron +24 -0
  51. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/AUTHORS +3 -1
  52. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/METADATA +4 -3
  53. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/RECORD +94 -91
  54. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/WHEEL +1 -1
  55. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/entry_points.txt +1 -0
  56. swift-2.33.1.dist-info/pbr.json +1 -0
  57. swift-2.32.1.dist-info/pbr.json +0 -1
  58. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-audit +0 -0
  59. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-auditor +0 -0
  60. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-info +0 -0
  61. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-reaper +0 -0
  62. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-replicator +0 -0
  63. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-server +0 -0
  64. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-config +0 -0
  65. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-auditor +0 -0
  66. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-reconciler +0 -0
  67. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-replicator +0 -0
  68. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-server +0 -0
  69. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sharder +0 -0
  70. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sync +0 -0
  71. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-updater +0 -0
  72. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-populate +0 -0
  73. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-report +0 -0
  74. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-drive-audit +0 -0
  75. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-form-signature +0 -0
  76. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-get-nodes +0 -0
  77. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-init +0 -0
  78. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-auditor +0 -0
  79. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-expirer +0 -0
  80. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-info +0 -0
  81. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-reconstructor +0 -0
  82. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-relinker +0 -0
  83. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-replicator +0 -0
  84. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-server +0 -0
  85. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-updater +0 -0
  86. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-oldies +0 -0
  87. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-orphans +0 -0
  88. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-proxy-server +0 -0
  89. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-recon +0 -0
  90. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-reconciler-enqueue +0 -0
  91. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder +0 -0
  92. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder-analyzer +0 -0
  93. {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-composer +0 -0
  94. {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/LICENSE +0 -0
  95. {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
- if conf.get('auto_create_account_prefix'):
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, logger):
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
- sys.exit(1)
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, logger)
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
- sys.exit(1)
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
- :returns: list of {'remote_id', 'sync_point'}
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 remote_id, sync_point FROM %s_sync
738
- ''' % ('incoming' if incoming else 'outgoing'))
739
- result = []
740
- for row in curs:
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:
@@ -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
- '''Subclass to see when the client didn't provide a Content-Type'''
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
- '''If the client didn't provide a content type, leave it blank.'''
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
- from distutils.spawn import find_executable
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 find_executable(cmd) is None:
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
- ps_cmd = ['ps', '--ppid', str(pid), '--no-headers', '-o', 'pid']
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.logger.error('All memcached servers error-limited')
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
- self.logger.error(
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: