swift 2.33.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.
Files changed (112) hide show
  1. swift/account/auditor.py +11 -0
  2. swift/account/reaper.py +11 -1
  3. swift/account/replicator.py +22 -0
  4. swift/account/server.py +12 -1
  5. swift-2.33.0.data/scripts/swift-account-audit → swift/cli/account_audit.py +6 -2
  6. swift-2.33.0.data/scripts/swift-config → swift/cli/config.py +1 -1
  7. swift-2.33.0.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +6 -2
  8. swift-2.33.0.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +12 -3
  9. swift-2.33.0.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +6 -2
  10. swift/cli/info.py +103 -2
  11. swift-2.33.0.data/scripts/swift-oldies → swift/cli/oldies.py +6 -3
  12. swift-2.33.0.data/scripts/swift-orphans → swift/cli/orphans.py +7 -2
  13. swift/cli/recon_cron.py +5 -5
  14. swift-2.33.0.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  15. swift/cli/relinker.py +1 -1
  16. swift/cli/ringbuilder.py +24 -0
  17. swift/common/db.py +2 -1
  18. swift/common/db_auditor.py +2 -2
  19. swift/common/db_replicator.py +6 -0
  20. swift/common/exceptions.py +12 -0
  21. swift/common/manager.py +102 -0
  22. swift/common/memcached.py +6 -13
  23. swift/common/middleware/account_quotas.py +144 -43
  24. swift/common/middleware/backend_ratelimit.py +166 -24
  25. swift/common/middleware/catch_errors.py +1 -3
  26. swift/common/middleware/cname_lookup.py +3 -5
  27. swift/common/middleware/container_sync.py +6 -10
  28. swift/common/middleware/crypto/crypto_utils.py +4 -5
  29. swift/common/middleware/crypto/decrypter.py +4 -5
  30. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  31. swift/common/middleware/proxy_logging.py +22 -16
  32. swift/common/middleware/ratelimit.py +6 -7
  33. swift/common/middleware/recon.py +6 -7
  34. swift/common/middleware/s3api/acl_handlers.py +9 -0
  35. swift/common/middleware/s3api/controllers/multi_upload.py +1 -9
  36. swift/common/middleware/s3api/controllers/obj.py +20 -1
  37. swift/common/middleware/s3api/s3api.py +2 -0
  38. swift/common/middleware/s3api/s3request.py +171 -62
  39. swift/common/middleware/s3api/s3response.py +35 -6
  40. swift/common/middleware/s3api/s3token.py +2 -2
  41. swift/common/middleware/s3api/utils.py +1 -0
  42. swift/common/middleware/slo.py +153 -52
  43. swift/common/middleware/tempauth.py +6 -4
  44. swift/common/middleware/tempurl.py +2 -2
  45. swift/common/middleware/x_profile/exceptions.py +1 -4
  46. swift/common/middleware/x_profile/html_viewer.py +9 -10
  47. swift/common/middleware/x_profile/profile_model.py +1 -2
  48. swift/common/middleware/xprofile.py +1 -2
  49. swift/common/request_helpers.py +69 -0
  50. swift/common/statsd_client.py +207 -0
  51. swift/common/utils/__init__.py +97 -1635
  52. swift/common/utils/base.py +138 -0
  53. swift/common/utils/config.py +443 -0
  54. swift/common/utils/logs.py +999 -0
  55. swift/common/wsgi.py +11 -3
  56. swift/container/auditor.py +11 -0
  57. swift/container/backend.py +10 -10
  58. swift/container/reconciler.py +11 -2
  59. swift/container/replicator.py +22 -1
  60. swift/container/server.py +12 -1
  61. swift/container/sharder.py +36 -12
  62. swift/container/sync.py +11 -1
  63. swift/container/updater.py +11 -2
  64. swift/obj/auditor.py +18 -2
  65. swift/obj/diskfile.py +8 -6
  66. swift/obj/expirer.py +155 -36
  67. swift/obj/reconstructor.py +28 -4
  68. swift/obj/replicator.py +61 -22
  69. swift/obj/server.py +64 -36
  70. swift/obj/updater.py +11 -2
  71. swift/proxy/controllers/base.py +38 -22
  72. swift/proxy/controllers/obj.py +23 -26
  73. swift/proxy/server.py +15 -1
  74. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/AUTHORS +11 -3
  75. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/METADATA +6 -5
  76. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/RECORD +81 -107
  77. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/entry_points.txt +38 -0
  78. swift-2.34.0.dist-info/pbr.json +1 -0
  79. swift-2.33.0.data/scripts/swift-account-auditor +0 -23
  80. swift-2.33.0.data/scripts/swift-account-info +0 -52
  81. swift-2.33.0.data/scripts/swift-account-reaper +0 -23
  82. swift-2.33.0.data/scripts/swift-account-replicator +0 -34
  83. swift-2.33.0.data/scripts/swift-account-server +0 -23
  84. swift-2.33.0.data/scripts/swift-container-auditor +0 -23
  85. swift-2.33.0.data/scripts/swift-container-info +0 -59
  86. swift-2.33.0.data/scripts/swift-container-reconciler +0 -21
  87. swift-2.33.0.data/scripts/swift-container-replicator +0 -34
  88. swift-2.33.0.data/scripts/swift-container-server +0 -23
  89. swift-2.33.0.data/scripts/swift-container-sharder +0 -37
  90. swift-2.33.0.data/scripts/swift-container-sync +0 -23
  91. swift-2.33.0.data/scripts/swift-container-updater +0 -23
  92. swift-2.33.0.data/scripts/swift-dispersion-report +0 -24
  93. swift-2.33.0.data/scripts/swift-form-signature +0 -20
  94. swift-2.33.0.data/scripts/swift-init +0 -119
  95. swift-2.33.0.data/scripts/swift-object-auditor +0 -29
  96. swift-2.33.0.data/scripts/swift-object-expirer +0 -33
  97. swift-2.33.0.data/scripts/swift-object-info +0 -60
  98. swift-2.33.0.data/scripts/swift-object-reconstructor +0 -33
  99. swift-2.33.0.data/scripts/swift-object-relinker +0 -23
  100. swift-2.33.0.data/scripts/swift-object-replicator +0 -37
  101. swift-2.33.0.data/scripts/swift-object-server +0 -27
  102. swift-2.33.0.data/scripts/swift-object-updater +0 -23
  103. swift-2.33.0.data/scripts/swift-proxy-server +0 -23
  104. swift-2.33.0.data/scripts/swift-recon +0 -24
  105. swift-2.33.0.data/scripts/swift-recon-cron +0 -24
  106. swift-2.33.0.data/scripts/swift-ring-builder +0 -37
  107. swift-2.33.0.data/scripts/swift-ring-builder-analyzer +0 -22
  108. swift-2.33.0.data/scripts/swift-ring-composer +0 -22
  109. swift-2.33.0.dist-info/pbr.json +0 -1
  110. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/LICENSE +0 -0
  111. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/WHEEL +0 -0
  112. {swift-2.33.0.dist-info → swift-2.34.0.dist-info}/top_level.txt +0 -0
@@ -24,9 +24,7 @@ import collections
24
24
  import errno
25
25
  import fcntl
26
26
  import grp
27
- import hashlib
28
27
  import json
29
- import operator
30
28
  import os
31
29
  import pwd
32
30
  import re
@@ -37,7 +35,7 @@ import time
37
35
  import uuid
38
36
  import functools
39
37
  import email.parser
40
- from random import random, shuffle
38
+ from random import shuffle
41
39
  from contextlib import contextmanager, closing
42
40
  import ctypes
43
41
  import ctypes.util
@@ -49,7 +47,6 @@ from tempfile import gettempdir, mkstemp, NamedTemporaryFile
49
47
  import glob
50
48
  import itertools
51
49
  import stat
52
- import datetime
53
50
 
54
51
  import eventlet
55
52
  import eventlet.debug
@@ -64,25 +61,16 @@ except ImportError:
64
61
  import pkg_resources
65
62
  from eventlet import GreenPool, sleep, Timeout
66
63
  from eventlet.event import Event
67
- from eventlet.green import socket, threading
64
+ from eventlet.green import socket
68
65
  import eventlet.hubs
69
66
  import eventlet.queue
70
- import codecs
71
- utf8_decoder = codecs.getdecoder('utf-8')
72
- utf8_encoder = codecs.getencoder('utf-8')
73
67
  import six
74
- if six.PY2:
75
- from eventlet.green import httplib as green_http_client
76
- else:
77
- from eventlet.green.http import client as green_http_client
78
- utf16_decoder = codecs.getdecoder('utf-16')
79
- utf16_encoder = codecs.getencoder('utf-16')
68
+
80
69
  from six.moves import cPickle as pickle
81
- from six.moves import configparser
82
70
  from six.moves.configparser import (ConfigParser, NoSectionError,
83
- NoOptionError, RawConfigParser)
84
- from six.moves import range, http_client
85
- from six.moves.urllib.parse import quote as _quote, unquote
71
+ NoOptionError)
72
+ from six.moves import range
73
+ from six.moves.urllib.parse import unquote
86
74
  from six.moves.urllib.parse import urlparse
87
75
  from six.moves import UserList
88
76
 
@@ -93,6 +81,53 @@ from swift.common.linkat import linkat
93
81
 
94
82
  # For backwards compatability with 3rd party middlewares
95
83
  from swift.common.registry import register_swift_info, get_swift_info # noqa
84
+
85
+ from .base import ( # noqa
86
+ md5, get_valid_utf8_str, quote, split_path)
87
+ from swift.common.utils.logs import ( # noqa
88
+ SysLogHandler, # t.u.helpers.setup_servers monkey patch is sketch
89
+ logging_monkey_patch,
90
+ get_logger,
91
+ PrefixLoggerAdapter,
92
+ LogLevelFilter,
93
+ NullLogger,
94
+ capture_stdio,
95
+ SwiftLogFormatter,
96
+ SwiftLoggerAdapter,
97
+ LogAdapter,
98
+ LoggerFileObject,
99
+ PipeMutex,
100
+ NoopMutex,
101
+ ThreadSafeSysLogHandler,
102
+ StrAnonymizer,
103
+ get_log_line,
104
+ StrFormatTime,
105
+ LogStringFormatter,
106
+ get_policy_index,
107
+ LOG_LINE_DEFAULT_FORMAT,
108
+ NOTICE,
109
+ )
110
+ from swift.common.utils.config import ( # noqa
111
+ TRUE_VALUES,
112
+ NicerInterpolation,
113
+ config_true_value,
114
+ append_underscore,
115
+ non_negative_float,
116
+ non_negative_int,
117
+ config_positive_int_value,
118
+ config_float_value,
119
+ config_auto_int_value,
120
+ config_percent_value,
121
+ config_request_node_count_value,
122
+ config_fallocate_value,
123
+ config_read_prefixed_options,
124
+ config_read_reseller_options,
125
+ parse_prefixed_conf,
126
+ affinity_locality_predicate,
127
+ affinity_key_function,
128
+ readconf,
129
+ read_conf_dir,
130
+ )
96
131
  from swift.common.utils.libc import ( # noqa
97
132
  F_SETPIPE_SZ,
98
133
  load_libc_function,
@@ -124,11 +159,9 @@ from swift.common.utils.ipaddrs import ( # noqa
124
159
  parse_socket_string,
125
160
  whataremyips,
126
161
  )
127
- from logging.handlers import SysLogHandler
162
+ from swift.common.statsd_client import StatsdClient # noqa
128
163
  import logging
129
164
 
130
- NOTICE = 25
131
-
132
165
  # These are lazily pulled from libc elsewhere
133
166
  _sys_fallocate = None
134
167
 
@@ -164,12 +197,6 @@ RESERVED_STR = u'\x00'
164
197
  RESERVED = '\x00'
165
198
 
166
199
 
167
- LOG_LINE_DEFAULT_FORMAT = '{remote_addr} - - [{time.d}/{time.b}/{time.Y}' \
168
- ':{time.H}:{time.M}:{time.S} +0000] ' \
169
- '"{method} {path}" {status} {content_length} ' \
170
- '"{referer}" "{txn_id}" "{user_agent}" ' \
171
- '{trans_time:.4f} "{additional_info}" {pid} ' \
172
- '{policy_index}'
173
200
  DEFAULT_LOCK_TIMEOUT = 10
174
201
  # this is coupled with object-server.conf's network_chunk_size; if someone is
175
202
  # running that unreasonably small they may find this number inefficient, but in
@@ -280,199 +307,6 @@ def backward(f, blocksize=4096):
280
307
  yield last_row
281
308
 
282
309
 
283
- # Used when reading config values
284
- TRUE_VALUES = {'true', '1', 'yes', 'on', 't', 'y'}
285
-
286
-
287
- def non_negative_float(value):
288
- """
289
- Check that the value casts to a float and is non-negative.
290
-
291
- :param value: value to check
292
- :raises ValueError: if the value cannot be cast to a float or is negative.
293
- :return: a float
294
- """
295
- try:
296
- value = float(value)
297
- if value < 0:
298
- raise ValueError
299
- except (TypeError, ValueError):
300
- raise ValueError('Value must be a non-negative float number, not "%s".'
301
- % value)
302
- return value
303
-
304
-
305
- def non_negative_int(value):
306
- """
307
- Check that the value casts to an int and is a whole number.
308
-
309
- :param value: value to check
310
- :raises ValueError: if the value cannot be cast to an int or does not
311
- represent a whole number.
312
- :return: an int
313
- """
314
- int_value = int(value)
315
- if int_value != non_negative_float(value):
316
- raise ValueError
317
- return int_value
318
-
319
-
320
- def config_true_value(value):
321
- """
322
- Returns True if the value is either True or a string in TRUE_VALUES.
323
- Returns False otherwise.
324
- """
325
- return value is True or \
326
- (isinstance(value, six.string_types) and value.lower() in TRUE_VALUES)
327
-
328
-
329
- def config_positive_int_value(value):
330
- """
331
- Returns positive int value if it can be cast by int() and it's an
332
- integer > 0. (not including zero) Raises ValueError otherwise.
333
- """
334
- try:
335
- result = int(value)
336
- if result < 1:
337
- raise ValueError()
338
- except (TypeError, ValueError):
339
- raise ValueError(
340
- 'Config option must be an positive int number, not "%s".' % value)
341
- return result
342
-
343
-
344
- def config_float_value(value, minimum=None, maximum=None):
345
- try:
346
- val = float(value)
347
- if minimum is not None and val < minimum:
348
- raise ValueError()
349
- if maximum is not None and val > maximum:
350
- raise ValueError()
351
- return val
352
- except (TypeError, ValueError):
353
- min_ = ', greater than %s' % minimum if minimum is not None else ''
354
- max_ = ', less than %s' % maximum if maximum is not None else ''
355
- raise ValueError('Config option must be a number%s%s, not "%s".' %
356
- (min_, max_, value))
357
-
358
-
359
- def config_auto_int_value(value, default):
360
- """
361
- Returns default if value is None or 'auto'.
362
- Returns value as an int or raises ValueError otherwise.
363
- """
364
- if value is None or \
365
- (isinstance(value, six.string_types) and value.lower() == 'auto'):
366
- return default
367
- try:
368
- value = int(value)
369
- except (TypeError, ValueError):
370
- raise ValueError('Config option must be an integer or the '
371
- 'string "auto", not "%s".' % value)
372
- return value
373
-
374
-
375
- def config_percent_value(value):
376
- try:
377
- return config_float_value(value, 0, 100) / 100.0
378
- except ValueError as err:
379
- raise ValueError("%s: %s" % (str(err), value))
380
-
381
-
382
- def config_request_node_count_value(value):
383
- try:
384
- value_parts = value.lower().split()
385
- rnc_value = int(value_parts[0])
386
- except (ValueError, AttributeError):
387
- pass
388
- else:
389
- if len(value_parts) == 1:
390
- return lambda replicas: rnc_value
391
- elif (len(value_parts) == 3 and
392
- value_parts[1] == '*' and
393
- value_parts[2] == 'replicas'):
394
- return lambda replicas: rnc_value * replicas
395
- raise ValueError(
396
- 'Invalid request_node_count value: %r' % value)
397
-
398
-
399
- def append_underscore(prefix):
400
- if prefix and not prefix.endswith('_'):
401
- prefix += '_'
402
- return prefix
403
-
404
-
405
- def config_read_reseller_options(conf, defaults):
406
- """
407
- Read reseller_prefix option and associated options from configuration
408
-
409
- Reads the reseller_prefix option, then reads options that may be
410
- associated with a specific reseller prefix. Reads options such that an
411
- option without a prefix applies to all reseller prefixes unless an option
412
- has an explicit prefix.
413
-
414
- :param conf: the configuration
415
- :param defaults: a dict of default values. The key is the option
416
- name. The value is either an array of strings or a string
417
- :return: tuple of an array of reseller prefixes and a dict of option values
418
- """
419
- reseller_prefix_opt = conf.get('reseller_prefix', 'AUTH').split(',')
420
- reseller_prefixes = []
421
- for prefix in [pre.strip() for pre in reseller_prefix_opt if pre.strip()]:
422
- if prefix == "''":
423
- prefix = ''
424
- prefix = append_underscore(prefix)
425
- if prefix not in reseller_prefixes:
426
- reseller_prefixes.append(prefix)
427
- if len(reseller_prefixes) == 0:
428
- reseller_prefixes.append('')
429
-
430
- # Get prefix-using config options
431
- associated_options = {}
432
- for prefix in reseller_prefixes:
433
- associated_options[prefix] = dict(defaults)
434
- associated_options[prefix].update(
435
- config_read_prefixed_options(conf, '', defaults))
436
- prefix_name = prefix if prefix != '' else "''"
437
- associated_options[prefix].update(
438
- config_read_prefixed_options(conf, prefix_name, defaults))
439
- return reseller_prefixes, associated_options
440
-
441
-
442
- def config_read_prefixed_options(conf, prefix_name, defaults):
443
- """
444
- Read prefixed options from configuration
445
-
446
- :param conf: the configuration
447
- :param prefix_name: the prefix (including, if needed, an underscore)
448
- :param defaults: a dict of default values. The dict supplies the
449
- option name and type (string or comma separated string)
450
- :return: a dict containing the options
451
- """
452
- params = {}
453
- for option_name in defaults.keys():
454
- value = conf.get('%s%s' % (prefix_name, option_name))
455
- if value:
456
- if isinstance(defaults.get(option_name), list):
457
- params[option_name] = []
458
- for role in value.lower().split(','):
459
- params[option_name].append(role.strip())
460
- else:
461
- params[option_name] = value.strip()
462
- return params
463
-
464
-
465
- def logging_monkey_patch():
466
- # explicitly patch the logging lock
467
- logging._lock = logging.threading.RLock()
468
- # setup notice level logging
469
- logging.addLevelName(NOTICE, 'NOTICE')
470
- SysLogHandler.priority_map['NOTICE'] = 'notice'
471
- # Trying to log threads while monkey-patched can lead to deadlocks; see
472
- # https://bugs.launchpad.net/swift/+bug/1895739
473
- logging.logThreads = 0
474
-
475
-
476
310
  def eventlet_monkey_patch():
477
311
  """
478
312
  Install the appropriate Eventlet monkey patches.
@@ -505,171 +339,6 @@ def generate_trans_id(trans_id_suffix):
505
339
  uuid.uuid4().hex[:21], int(time.time()), quote(trans_id_suffix))
506
340
 
507
341
 
508
- def get_policy_index(req_headers, res_headers):
509
- """
510
- Returns the appropriate index of the storage policy for the request from
511
- a proxy server
512
-
513
- :param req_headers: dict of the request headers.
514
- :param res_headers: dict of the response headers.
515
-
516
- :returns: string index of storage policy, or None
517
- """
518
- header = 'X-Backend-Storage-Policy-Index'
519
- policy_index = res_headers.get(header, req_headers.get(header))
520
- if isinstance(policy_index, six.binary_type) and not six.PY2:
521
- policy_index = policy_index.decode('ascii')
522
- return str(policy_index) if policy_index is not None else None
523
-
524
-
525
- class LogStringFormatter(string.Formatter):
526
- def __init__(self, default='', quote=False):
527
- super(LogStringFormatter, self).__init__()
528
- self.default = default
529
- self.quote = quote
530
-
531
- def format_field(self, value, spec):
532
- if not value:
533
- return self.default
534
- else:
535
- log = super(LogStringFormatter, self).format_field(value, spec)
536
- if self.quote:
537
- return quote(log, ':/{}')
538
- else:
539
- return log
540
-
541
-
542
- class StrAnonymizer(str):
543
- """
544
- Class that permits to get a string anonymized or simply quoted.
545
- """
546
-
547
- def __new__(cls, data, method, salt):
548
- method = method.lower()
549
- if method not in (hashlib.algorithms if six.PY2 else
550
- hashlib.algorithms_guaranteed):
551
- raise ValueError('Unsupported hashing method: %r' % method)
552
- s = str.__new__(cls, data or '')
553
- s.method = method
554
- s.salt = salt
555
- return s
556
-
557
- @property
558
- def anonymized(self):
559
- if not self:
560
- return self
561
- else:
562
- if self.method == 'md5':
563
- h = md5(usedforsecurity=False)
564
- else:
565
- h = getattr(hashlib, self.method)()
566
- if self.salt:
567
- h.update(six.b(self.salt))
568
- h.update(six.b(self))
569
- return '{%s%s}%s' % ('S' if self.salt else '', self.method.upper(),
570
- h.hexdigest())
571
-
572
-
573
- class StrFormatTime(object):
574
- """
575
- Class that permits to get formats or parts of a time.
576
- """
577
-
578
- def __init__(self, ts):
579
- self.time = ts
580
- self.time_struct = time.gmtime(ts)
581
-
582
- def __str__(self):
583
- return "%.9f" % self.time
584
-
585
- def __getattr__(self, attr):
586
- if attr not in ['a', 'A', 'b', 'B', 'c', 'd', 'H',
587
- 'I', 'j', 'm', 'M', 'p', 'S', 'U',
588
- 'w', 'W', 'x', 'X', 'y', 'Y', 'Z']:
589
- raise ValueError(("The attribute %s is not a correct directive "
590
- "for time.strftime formater.") % attr)
591
- return datetime.datetime(*self.time_struct[:-2],
592
- tzinfo=UTC).strftime('%' + attr)
593
-
594
- @property
595
- def asctime(self):
596
- return time.asctime(self.time_struct)
597
-
598
- @property
599
- def datetime(self):
600
- return time.strftime('%d/%b/%Y/%H/%M/%S', self.time_struct)
601
-
602
- @property
603
- def iso8601(self):
604
- return time.strftime('%Y-%m-%dT%H:%M:%S', self.time_struct)
605
-
606
- @property
607
- def ms(self):
608
- return self.__str__().split('.')[1][:3]
609
-
610
- @property
611
- def us(self):
612
- return self.__str__().split('.')[1][:6]
613
-
614
- @property
615
- def ns(self):
616
- return self.__str__().split('.')[1]
617
-
618
- @property
619
- def s(self):
620
- return self.__str__().split('.')[0]
621
-
622
-
623
- def get_log_line(req, res, trans_time, additional_info, fmt,
624
- anonymization_method, anonymization_salt):
625
- """
626
- Make a line for logging that matches the documented log line format
627
- for backend servers.
628
-
629
- :param req: the request.
630
- :param res: the response.
631
- :param trans_time: the time the request took to complete, a float.
632
- :param additional_info: a string to log at the end of the line
633
-
634
- :returns: a properly formatted line for logging.
635
- """
636
-
637
- policy_index = get_policy_index(req.headers, res.headers)
638
- if req.path.startswith('/'):
639
- disk, partition, account, container, obj = split_path(req.path, 0, 5,
640
- True)
641
- else:
642
- disk, partition, account, container, obj = (None, ) * 5
643
- replacements = {
644
- 'remote_addr': StrAnonymizer(req.remote_addr, anonymization_method,
645
- anonymization_salt),
646
- 'time': StrFormatTime(time.time()),
647
- 'method': req.method,
648
- 'path': StrAnonymizer(req.path, anonymization_method,
649
- anonymization_salt),
650
- 'disk': disk,
651
- 'partition': partition,
652
- 'account': StrAnonymizer(account, anonymization_method,
653
- anonymization_salt),
654
- 'container': StrAnonymizer(container, anonymization_method,
655
- anonymization_salt),
656
- 'object': StrAnonymizer(obj, anonymization_method,
657
- anonymization_salt),
658
- 'status': res.status.split()[0],
659
- 'content_length': res.content_length,
660
- 'referer': StrAnonymizer(req.referer, anonymization_method,
661
- anonymization_salt),
662
- 'txn_id': req.headers.get('x-trans-id'),
663
- 'user_agent': StrAnonymizer(req.user_agent, anonymization_method,
664
- anonymization_salt),
665
- 'trans_time': trans_time,
666
- 'additional_info': additional_info,
667
- 'pid': os.getpid(),
668
- 'policy_index': policy_index,
669
- }
670
- return LogStringFormatter(default='-').format(fmt, **replacements)
671
-
672
-
673
342
  def get_trans_id_time(trans_id):
674
343
  if len(trans_id) >= 34 and \
675
344
  trans_id.startswith('tx') and trans_id[23] == '-':
@@ -680,25 +349,6 @@ def get_trans_id_time(trans_id):
680
349
  return None
681
350
 
682
351
 
683
- def config_fallocate_value(reserve_value):
684
- """
685
- Returns fallocate reserve_value as an int or float.
686
- Returns is_percent as a boolean.
687
- Returns a ValueError on invalid fallocate value.
688
- """
689
- try:
690
- if str(reserve_value[-1:]) == '%':
691
- reserve_value = float(reserve_value[:-1])
692
- is_percent = True
693
- else:
694
- reserve_value = int(reserve_value)
695
- is_percent = False
696
- except ValueError:
697
- raise ValueError('Error: %s is an invalid value for fallocate'
698
- '_reserve.' % reserve_value)
699
- return reserve_value, is_percent
700
-
701
-
702
352
  class FileLikeIter(object):
703
353
 
704
354
  def __init__(self, iterable):
@@ -1130,53 +780,6 @@ def link_fd_to_path(fd, target_path, dirs_created=0, retries=2, fsync=True):
1130
780
  dirpath = os.path.dirname(dirpath)
1131
781
 
1132
782
 
1133
- def split_path(path, minsegs=1, maxsegs=None, rest_with_last=False):
1134
- """
1135
- Validate and split the given HTTP request path.
1136
-
1137
- **Examples**::
1138
-
1139
- ['a'] = split_path('/a')
1140
- ['a', None] = split_path('/a', 1, 2)
1141
- ['a', 'c'] = split_path('/a/c', 1, 2)
1142
- ['a', 'c', 'o/r'] = split_path('/a/c/o/r', 1, 3, True)
1143
-
1144
- :param path: HTTP Request path to be split
1145
- :param minsegs: Minimum number of segments to be extracted
1146
- :param maxsegs: Maximum number of segments to be extracted
1147
- :param rest_with_last: If True, trailing data will be returned as part
1148
- of last segment. If False, and there is
1149
- trailing data, raises ValueError.
1150
- :returns: list of segments with a length of maxsegs (non-existent
1151
- segments will return as None)
1152
- :raises ValueError: if given an invalid path
1153
- """
1154
- if not maxsegs:
1155
- maxsegs = minsegs
1156
- if minsegs > maxsegs:
1157
- raise ValueError('minsegs > maxsegs: %d > %d' % (minsegs, maxsegs))
1158
- if rest_with_last:
1159
- segs = path.split('/', maxsegs)
1160
- minsegs += 1
1161
- maxsegs += 1
1162
- count = len(segs)
1163
- if (segs[0] or count < minsegs or count > maxsegs or
1164
- '' in segs[1:minsegs]):
1165
- raise ValueError('Invalid path: %s' % quote(path))
1166
- else:
1167
- minsegs += 1
1168
- maxsegs += 1
1169
- segs = path.split('/', maxsegs)
1170
- count = len(segs)
1171
- if (segs[0] or count < minsegs or count > maxsegs + 1 or
1172
- '' in segs[1:minsegs] or
1173
- (count == maxsegs + 1 and segs[maxsegs])):
1174
- raise ValueError('Invalid path: %s' % quote(path))
1175
- segs = segs[1:maxsegs]
1176
- segs.extend([None] * (maxsegs - 1 - len(segs)))
1177
- return segs
1178
-
1179
-
1180
783
  def validate_device_partition(device, partition):
1181
784
  """
1182
785
  Validate that a device and a partition are valid and won't lead to
@@ -1251,260 +854,6 @@ class GreenthreadSafeIterator(object):
1251
854
  __next__ = next
1252
855
 
1253
856
 
1254
- class NullLogger(object):
1255
- """A no-op logger for eventlet wsgi."""
1256
-
1257
- def write(self, *args):
1258
- # "Logs" the args to nowhere
1259
- pass
1260
-
1261
- def exception(self, *args):
1262
- pass
1263
-
1264
- def critical(self, *args):
1265
- pass
1266
-
1267
- def error(self, *args):
1268
- pass
1269
-
1270
- def warning(self, *args):
1271
- pass
1272
-
1273
- def info(self, *args):
1274
- pass
1275
-
1276
- def debug(self, *args):
1277
- pass
1278
-
1279
- def log(self, *args):
1280
- pass
1281
-
1282
-
1283
- class LoggerFileObject(object):
1284
-
1285
- # Note: this is greenthread-local storage
1286
- _cls_thread_local = threading.local()
1287
-
1288
- def __init__(self, logger, log_type='STDOUT'):
1289
- self.logger = logger
1290
- self.log_type = log_type
1291
-
1292
- def write(self, value):
1293
- # We can get into a nasty situation when logs are going to syslog
1294
- # and syslog dies.
1295
- #
1296
- # It's something like this:
1297
- #
1298
- # (A) someone logs something
1299
- #
1300
- # (B) there's an exception in sending to /dev/log since syslog is
1301
- # not working
1302
- #
1303
- # (C) logging takes that exception and writes it to stderr (see
1304
- # logging.Handler.handleError)
1305
- #
1306
- # (D) stderr was replaced with a LoggerFileObject at process start,
1307
- # so the LoggerFileObject takes the provided string and tells
1308
- # its logger to log it (to syslog, naturally).
1309
- #
1310
- # Then, steps B through D repeat until we run out of stack.
1311
- if getattr(self._cls_thread_local, 'already_called_write', False):
1312
- return
1313
-
1314
- self._cls_thread_local.already_called_write = True
1315
- try:
1316
- value = value.strip()
1317
- if value:
1318
- if 'Connection reset by peer' in value:
1319
- self.logger.error(
1320
- '%s: Connection reset by peer', self.log_type)
1321
- else:
1322
- self.logger.error('%(type)s: %(value)s',
1323
- {'type': self.log_type, 'value': value})
1324
- finally:
1325
- self._cls_thread_local.already_called_write = False
1326
-
1327
- def writelines(self, values):
1328
- if getattr(self._cls_thread_local, 'already_called_writelines', False):
1329
- return
1330
-
1331
- self._cls_thread_local.already_called_writelines = True
1332
- try:
1333
- self.logger.error('%(type)s: %(value)s',
1334
- {'type': self.log_type,
1335
- 'value': '#012'.join(values)})
1336
- finally:
1337
- self._cls_thread_local.already_called_writelines = False
1338
-
1339
- def close(self):
1340
- pass
1341
-
1342
- def flush(self):
1343
- pass
1344
-
1345
- def __iter__(self):
1346
- return self
1347
-
1348
- def next(self):
1349
- raise IOError(errno.EBADF, 'Bad file descriptor')
1350
- __next__ = next
1351
-
1352
- def read(self, size=-1):
1353
- raise IOError(errno.EBADF, 'Bad file descriptor')
1354
-
1355
- def readline(self, size=-1):
1356
- raise IOError(errno.EBADF, 'Bad file descriptor')
1357
-
1358
- def tell(self):
1359
- return 0
1360
-
1361
- def xreadlines(self):
1362
- return self
1363
-
1364
-
1365
- class StatsdClient(object):
1366
- def __init__(self, host, port, base_prefix='', tail_prefix='',
1367
- default_sample_rate=1, sample_rate_factor=1, logger=None):
1368
- self._host = host
1369
- self._port = port
1370
- self._base_prefix = base_prefix
1371
- self._set_prefix(tail_prefix)
1372
- self._default_sample_rate = default_sample_rate
1373
- self._sample_rate_factor = sample_rate_factor
1374
- self.random = random
1375
- self.logger = logger
1376
-
1377
- # Determine if host is IPv4 or IPv6
1378
- addr_info, self._sock_family = self._determine_sock_family(host, port)
1379
-
1380
- # NOTE: we use the original host value, not the DNS-resolved one
1381
- # because if host is a hostname, we don't want to cache the DNS
1382
- # resolution for the entire lifetime of this process. Let standard
1383
- # name resolution caching take effect. This should help operators use
1384
- # DNS trickery if they want.
1385
- if addr_info is not None:
1386
- # addr_info is a list of 5-tuples with the following structure:
1387
- # (family, socktype, proto, canonname, sockaddr)
1388
- # where sockaddr is the only thing of interest to us, and we only
1389
- # use the first result. We want to use the originally supplied
1390
- # host (see note above) and the remainder of the variable-length
1391
- # sockaddr: IPv4 has (address, port) while IPv6 has (address,
1392
- # port, flow info, scope id).
1393
- sockaddr = addr_info[0][-1]
1394
- self._target = (host,) + (sockaddr[1:])
1395
- else:
1396
- self._target = (host, port)
1397
-
1398
- def _determine_sock_family(self, host, port):
1399
- addr_info = sock_family = None
1400
- try:
1401
- addr_info = socket.getaddrinfo(host, port, socket.AF_INET)
1402
- sock_family = socket.AF_INET
1403
- except socket.gaierror:
1404
- try:
1405
- addr_info = socket.getaddrinfo(host, port, socket.AF_INET6)
1406
- sock_family = socket.AF_INET6
1407
- except socket.gaierror:
1408
- # Don't keep the server from starting from what could be a
1409
- # transient DNS failure. Any hostname will get re-resolved as
1410
- # necessary in the .sendto() calls.
1411
- # However, we don't know if we're IPv4 or IPv6 in this case, so
1412
- # we assume legacy IPv4.
1413
- sock_family = socket.AF_INET
1414
- return addr_info, sock_family
1415
-
1416
- def _set_prefix(self, tail_prefix):
1417
- """
1418
- Modifies the prefix that is added to metric names. The resulting prefix
1419
- is the concatenation of the component parts `base_prefix` and
1420
- `tail_prefix`. Only truthy components are included. Each included
1421
- component is followed by a period, e.g.::
1422
-
1423
- <base_prefix>.<tail_prefix>.
1424
- <tail_prefix>.
1425
- <base_prefix>.
1426
- <the empty string>
1427
-
1428
- Note: this method is expected to be called from the constructor only,
1429
- but exists to provide backwards compatible functionality for the
1430
- deprecated set_prefix() method.
1431
-
1432
- :param tail_prefix: The new value of tail_prefix
1433
- """
1434
- if tail_prefix and self._base_prefix:
1435
- self._prefix = '.'.join([self._base_prefix, tail_prefix, ''])
1436
- elif tail_prefix:
1437
- self._prefix = tail_prefix + '.'
1438
- elif self._base_prefix:
1439
- self._prefix = self._base_prefix + '.'
1440
- else:
1441
- self._prefix = ''
1442
-
1443
- def set_prefix(self, tail_prefix):
1444
- """
1445
- This method is deprecated; use the ``tail_prefix`` argument of the
1446
- constructor when instantiating the class instead.
1447
- """
1448
- warnings.warn(
1449
- 'set_prefix() is deprecated; use the ``tail_prefix`` argument of '
1450
- 'the constructor when instantiating the class instead.',
1451
- DeprecationWarning, stacklevel=2
1452
- )
1453
- self._set_prefix(tail_prefix)
1454
-
1455
- def _send(self, m_name, m_value, m_type, sample_rate):
1456
- if sample_rate is None:
1457
- sample_rate = self._default_sample_rate
1458
- sample_rate = sample_rate * self._sample_rate_factor
1459
- parts = ['%s%s:%s' % (self._prefix, m_name, m_value), m_type]
1460
- if sample_rate < 1:
1461
- if self.random() < sample_rate:
1462
- parts.append('@%s' % (sample_rate,))
1463
- else:
1464
- return
1465
- if six.PY3:
1466
- parts = [part.encode('utf-8') for part in parts]
1467
- # Ideally, we'd cache a sending socket in self, but that
1468
- # results in a socket getting shared by multiple green threads.
1469
- with closing(self._open_socket()) as sock:
1470
- try:
1471
- return sock.sendto(b'|'.join(parts), self._target)
1472
- except IOError as err:
1473
- if self.logger:
1474
- self.logger.warning(
1475
- 'Error sending UDP message to %(target)r: %(err)s',
1476
- {'target': self._target, 'err': err})
1477
-
1478
- def _open_socket(self):
1479
- return socket.socket(self._sock_family, socket.SOCK_DGRAM)
1480
-
1481
- def update_stats(self, m_name, m_value, sample_rate=None):
1482
- return self._send(m_name, m_value, 'c', sample_rate)
1483
-
1484
- def increment(self, metric, sample_rate=None):
1485
- return self.update_stats(metric, 1, sample_rate)
1486
-
1487
- def decrement(self, metric, sample_rate=None):
1488
- return self.update_stats(metric, -1, sample_rate)
1489
-
1490
- def _timing(self, metric, timing_ms, sample_rate):
1491
- # This method was added to disagregate timing metrics when testing
1492
- return self._send(metric, round(timing_ms, 4), 'ms', sample_rate)
1493
-
1494
- def timing(self, metric, timing_ms, sample_rate=None):
1495
- return self._timing(metric, timing_ms, sample_rate)
1496
-
1497
- def timing_since(self, metric, orig_time, sample_rate=None):
1498
- return self._timing(metric, (time.time() - orig_time) * 1000,
1499
- sample_rate)
1500
-
1501
- def transfer_rate(self, metric, elapsed_time, byte_xfer, sample_rate=None):
1502
- if byte_xfer:
1503
- return self.timing(metric,
1504
- elapsed_time * 1000 / byte_xfer * 1000,
1505
- sample_rate)
1506
-
1507
-
1508
857
  def timing_stats(**dec_kwargs):
1509
858
  """
1510
859
  Returns a decorator that logs timing events or errors for public methods in
@@ -1557,467 +906,6 @@ def memcached_timing_stats(**dec_kwargs):
1557
906
  return decorating_func
1558
907
 
1559
908
 
1560
- class SwiftLoggerAdapter(logging.LoggerAdapter):
1561
- """
1562
- A logging.LoggerAdapter subclass that also passes through StatsD method
1563
- calls.
1564
-
1565
- Like logging.LoggerAdapter, you have to subclass this and override the
1566
- process() method to accomplish anything useful.
1567
- """
1568
-
1569
- @property
1570
- def name(self):
1571
- # py3 does this for us already; add it for py2
1572
- return self.logger.name
1573
-
1574
- def update_stats(self, *a, **kw):
1575
- return self.logger.update_stats(*a, **kw)
1576
-
1577
- def increment(self, *a, **kw):
1578
- return self.logger.increment(*a, **kw)
1579
-
1580
- def decrement(self, *a, **kw):
1581
- return self.logger.decrement(*a, **kw)
1582
-
1583
- def timing(self, *a, **kw):
1584
- return self.logger.timing(*a, **kw)
1585
-
1586
- def timing_since(self, *a, **kw):
1587
- return self.logger.timing_since(*a, **kw)
1588
-
1589
- def transfer_rate(self, *a, **kw):
1590
- return self.logger.transfer_rate(*a, **kw)
1591
-
1592
- @property
1593
- def thread_locals(self):
1594
- return self.logger.thread_locals
1595
-
1596
- @thread_locals.setter
1597
- def thread_locals(self, thread_locals):
1598
- self.logger.thread_locals = thread_locals
1599
-
1600
- def exception(self, msg, *a, **kw):
1601
- # We up-call to exception() where stdlib uses error() so we can get
1602
- # some of the traceback suppression from LogAdapter, below
1603
- self.logger.exception(msg, *a, **kw)
1604
-
1605
-
1606
- class PrefixLoggerAdapter(SwiftLoggerAdapter):
1607
- """
1608
- Adds an optional prefix to all its log messages. When the prefix has not
1609
- been set, messages are unchanged.
1610
- """
1611
-
1612
- def set_prefix(self, prefix):
1613
- self.extra['prefix'] = prefix
1614
-
1615
- def exception(self, msg, *a, **kw):
1616
- if 'prefix' in self.extra:
1617
- msg = self.extra['prefix'] + msg
1618
- super(PrefixLoggerAdapter, self).exception(msg, *a, **kw)
1619
-
1620
- def process(self, msg, kwargs):
1621
- msg, kwargs = super(PrefixLoggerAdapter, self).process(msg, kwargs)
1622
- if 'prefix' in self.extra:
1623
- msg = self.extra['prefix'] + msg
1624
- return (msg, kwargs)
1625
-
1626
-
1627
- # double inheritance to support property with setter
1628
- class LogAdapter(logging.LoggerAdapter, object):
1629
- """
1630
- A Logger like object which performs some reformatting on calls to
1631
- :meth:`exception`. Can be used to store a threadlocal transaction id and
1632
- client ip.
1633
- """
1634
-
1635
- _cls_thread_local = threading.local()
1636
-
1637
- def __init__(self, logger, server):
1638
- logging.LoggerAdapter.__init__(self, logger, {})
1639
- self.server = server
1640
- self.warn = self.warning
1641
-
1642
- # There are a few properties needed for py35; see
1643
- # - https://bugs.python.org/issue31457
1644
- # - https://github.com/python/cpython/commit/1bbd482
1645
- # - https://github.com/python/cpython/commit/0b6a118
1646
- # - https://github.com/python/cpython/commit/ce9e625
1647
- def _log(self, level, msg, args, exc_info=None, extra=None,
1648
- stack_info=False):
1649
- """
1650
- Low-level log implementation, proxied to allow nested logger adapters.
1651
- """
1652
- return self.logger._log(
1653
- level,
1654
- msg,
1655
- args,
1656
- exc_info=exc_info,
1657
- extra=extra,
1658
- stack_info=stack_info,
1659
- )
1660
-
1661
- @property
1662
- def manager(self):
1663
- return self.logger.manager
1664
-
1665
- @manager.setter
1666
- def manager(self, value):
1667
- self.logger.manager = value
1668
-
1669
- @property
1670
- def name(self):
1671
- return self.logger.name
1672
-
1673
- @property
1674
- def txn_id(self):
1675
- if hasattr(self._cls_thread_local, 'txn_id'):
1676
- return self._cls_thread_local.txn_id
1677
-
1678
- @txn_id.setter
1679
- def txn_id(self, value):
1680
- self._cls_thread_local.txn_id = value
1681
-
1682
- @property
1683
- def client_ip(self):
1684
- if hasattr(self._cls_thread_local, 'client_ip'):
1685
- return self._cls_thread_local.client_ip
1686
-
1687
- @client_ip.setter
1688
- def client_ip(self, value):
1689
- self._cls_thread_local.client_ip = value
1690
-
1691
- @property
1692
- def thread_locals(self):
1693
- return (self.txn_id, self.client_ip)
1694
-
1695
- @thread_locals.setter
1696
- def thread_locals(self, value):
1697
- self.txn_id, self.client_ip = value
1698
-
1699
- def getEffectiveLevel(self):
1700
- return self.logger.getEffectiveLevel()
1701
-
1702
- def process(self, msg, kwargs):
1703
- """
1704
- Add extra info to message
1705
- """
1706
- kwargs['extra'] = {'server': self.server, 'txn_id': self.txn_id,
1707
- 'client_ip': self.client_ip}
1708
- return msg, kwargs
1709
-
1710
- def notice(self, msg, *args, **kwargs):
1711
- """
1712
- Convenience function for syslog priority LOG_NOTICE. The python
1713
- logging lvl is set to 25, just above info. SysLogHandler is
1714
- monkey patched to map this log lvl to the LOG_NOTICE syslog
1715
- priority.
1716
- """
1717
- self.log(NOTICE, msg, *args, **kwargs)
1718
-
1719
- def _exception(self, msg, *args, **kwargs):
1720
- logging.LoggerAdapter.exception(self, msg, *args, **kwargs)
1721
-
1722
- def exception(self, msg, *args, **kwargs):
1723
- _junk, exc, _junk = sys.exc_info()
1724
- call = self.error
1725
- emsg = ''
1726
- if isinstance(exc, (http_client.BadStatusLine,
1727
- green_http_client.BadStatusLine)):
1728
- # Use error(); not really exceptional
1729
- emsg = repr(exc)
1730
- # Note that on py3, we've seen a RemoteDisconnected error getting
1731
- # raised, which inherits from *both* BadStatusLine and OSError;
1732
- # we want it getting caught here
1733
- elif isinstance(exc, (OSError, socket.error)):
1734
- if exc.errno in (errno.EIO, errno.ENOSPC):
1735
- emsg = str(exc)
1736
- elif exc.errno == errno.ECONNREFUSED:
1737
- emsg = 'Connection refused'
1738
- elif exc.errno == errno.ECONNRESET:
1739
- emsg = 'Connection reset'
1740
- elif exc.errno == errno.EHOSTUNREACH:
1741
- emsg = 'Host unreachable'
1742
- elif exc.errno == errno.ENETUNREACH:
1743
- emsg = 'Network unreachable'
1744
- elif exc.errno == errno.ETIMEDOUT:
1745
- emsg = 'Connection timeout'
1746
- elif exc.errno == errno.EPIPE:
1747
- emsg = 'Broken pipe'
1748
- else:
1749
- call = self._exception
1750
- elif isinstance(exc, eventlet.Timeout):
1751
- emsg = exc.__class__.__name__
1752
- detail = '%ss' % exc.seconds
1753
- if hasattr(exc, 'created_at'):
1754
- detail += ' after %0.2fs' % (time.time() - exc.created_at)
1755
- emsg += ' (%s)' % detail
1756
- if isinstance(exc, swift.common.exceptions.MessageTimeout):
1757
- if exc.msg:
1758
- emsg += ' %s' % exc.msg
1759
- else:
1760
- call = self._exception
1761
- call('%s: %s' % (msg, emsg), *args, **kwargs)
1762
-
1763
- def set_statsd_prefix(self, prefix):
1764
- """
1765
- This method is deprecated. Callers should use the
1766
- ``statsd_tail_prefix`` argument of ``get_logger`` when instantiating a
1767
- logger.
1768
-
1769
- The StatsD client prefix defaults to the "name" of the logger. This
1770
- method may override that default with a specific value. Currently used
1771
- in the proxy-server to differentiate the Account, Container, and Object
1772
- controllers.
1773
- """
1774
- warnings.warn(
1775
- 'set_statsd_prefix() is deprecated; use the '
1776
- '``statsd_tail_prefix`` argument to ``get_logger`` instead.',
1777
- DeprecationWarning, stacklevel=2
1778
- )
1779
- if self.logger.statsd_client:
1780
- self.logger.statsd_client._set_prefix(prefix)
1781
-
1782
- def statsd_delegate(statsd_func_name):
1783
- """
1784
- Factory to create methods which delegate to methods on
1785
- self.logger.statsd_client (an instance of StatsdClient). The
1786
- created methods conditionally delegate to a method whose name is given
1787
- in 'statsd_func_name'. The created delegate methods are a no-op when
1788
- StatsD logging is not configured.
1789
-
1790
- :param statsd_func_name: the name of a method on StatsdClient.
1791
- """
1792
- func = getattr(StatsdClient, statsd_func_name)
1793
-
1794
- @functools.wraps(func)
1795
- def wrapped(self, *a, **kw):
1796
- if getattr(self.logger, 'statsd_client'):
1797
- func = getattr(self.logger.statsd_client, statsd_func_name)
1798
- return func(*a, **kw)
1799
- return wrapped
1800
-
1801
- update_stats = statsd_delegate('update_stats')
1802
- increment = statsd_delegate('increment')
1803
- decrement = statsd_delegate('decrement')
1804
- timing = statsd_delegate('timing')
1805
- timing_since = statsd_delegate('timing_since')
1806
- transfer_rate = statsd_delegate('transfer_rate')
1807
-
1808
-
1809
- class SwiftLogFormatter(logging.Formatter):
1810
- """
1811
- Custom logging.Formatter will append txn_id to a log message if the
1812
- record has one and the message does not. Optionally it can shorten
1813
- overly long log lines.
1814
- """
1815
-
1816
- def __init__(self, fmt=None, datefmt=None, max_line_length=0):
1817
- logging.Formatter.__init__(self, fmt=fmt, datefmt=datefmt)
1818
- self.max_line_length = max_line_length
1819
-
1820
- def format(self, record):
1821
- if not hasattr(record, 'server'):
1822
- # Catch log messages that were not initiated by swift
1823
- # (for example, the keystone auth middleware)
1824
- record.server = record.name
1825
-
1826
- # Included from Python's logging.Formatter and then altered slightly to
1827
- # replace \n with #012
1828
- record.message = record.getMessage()
1829
- if self._fmt.find('%(asctime)') >= 0:
1830
- record.asctime = self.formatTime(record, self.datefmt)
1831
- msg = (self._fmt % record.__dict__).replace('\n', '#012')
1832
- if record.exc_info:
1833
- # Cache the traceback text to avoid converting it multiple times
1834
- # (it's constant anyway)
1835
- if not record.exc_text:
1836
- record.exc_text = self.formatException(
1837
- record.exc_info).replace('\n', '#012')
1838
- if record.exc_text:
1839
- if not msg.endswith('#012'):
1840
- msg = msg + '#012'
1841
- msg = msg + record.exc_text
1842
-
1843
- if (hasattr(record, 'txn_id') and record.txn_id and
1844
- record.txn_id not in msg):
1845
- msg = "%s (txn: %s)" % (msg, record.txn_id)
1846
- if (hasattr(record, 'client_ip') and record.client_ip and
1847
- record.levelno != logging.INFO and
1848
- record.client_ip not in msg):
1849
- msg = "%s (client_ip: %s)" % (msg, record.client_ip)
1850
- if self.max_line_length > 0 and len(msg) > self.max_line_length:
1851
- if self.max_line_length < 7:
1852
- msg = msg[:self.max_line_length]
1853
- else:
1854
- approxhalf = (self.max_line_length - 5) // 2
1855
- msg = msg[:approxhalf] + " ... " + msg[-approxhalf:]
1856
- return msg
1857
-
1858
-
1859
- class LogLevelFilter(object):
1860
- """
1861
- Drop messages for the logger based on level.
1862
-
1863
- This is useful when dependencies log too much information.
1864
-
1865
- :param level: All messages at or below this level are dropped
1866
- (DEBUG < INFO < WARN < ERROR < CRITICAL|FATAL)
1867
- Default: DEBUG
1868
- """
1869
-
1870
- def __init__(self, level=logging.DEBUG):
1871
- self.level = level
1872
-
1873
- def filter(self, record):
1874
- if record.levelno <= self.level:
1875
- return 0
1876
- return 1
1877
-
1878
-
1879
- def get_logger(conf, name=None, log_to_console=False, log_route=None,
1880
- fmt="%(server)s: %(message)s", statsd_tail_prefix=None):
1881
- """
1882
- Get the current system logger using config settings.
1883
-
1884
- **Log config and defaults**::
1885
-
1886
- log_facility = LOG_LOCAL0
1887
- log_level = INFO
1888
- log_name = swift
1889
- log_max_line_length = 0
1890
- log_udp_host = (disabled)
1891
- log_udp_port = logging.handlers.SYSLOG_UDP_PORT
1892
- log_address = /dev/log
1893
- log_statsd_host = (disabled)
1894
- log_statsd_port = 8125
1895
- log_statsd_default_sample_rate = 1.0
1896
- log_statsd_sample_rate_factor = 1.0
1897
- log_statsd_metric_prefix = (empty-string)
1898
-
1899
- :param conf: Configuration dict to read settings from
1900
- :param name: This value is used to populate the ``server`` field in the log
1901
- format, as the prefix for statsd messages, and as the default
1902
- value for ``log_route``; defaults to the ``log_name`` value in
1903
- ``conf``, if it exists, or to 'swift'.
1904
- :param log_to_console: Add handler which writes to console on stderr
1905
- :param log_route: Route for the logging, not emitted to the log, just used
1906
- to separate logging configurations; defaults to the value
1907
- of ``name`` or whatever ``name`` defaults to. This value
1908
- is used as the name attribute of the
1909
- ``logging.LogAdapter`` that is returned.
1910
- :param fmt: Override log format
1911
- :param statsd_tail_prefix: tail prefix to pass to statsd client; if None
1912
- then the tail prefix defaults to the value of ``name``.
1913
- :return: an instance of ``LogAdapter``
1914
- """
1915
- # note: log_name is typically specified in conf (i.e. defined by
1916
- # operators), whereas log_route is typically hard-coded in callers of
1917
- # get_logger (i.e. defined by developers)
1918
- if not conf:
1919
- conf = {}
1920
- if name is None:
1921
- name = conf.get('log_name', 'swift')
1922
- if not log_route:
1923
- log_route = name
1924
- logger = logging.getLogger(log_route)
1925
- logger.propagate = False
1926
- # all new handlers will get the same formatter
1927
- formatter = SwiftLogFormatter(
1928
- fmt=fmt, max_line_length=int(conf.get('log_max_line_length', 0)))
1929
-
1930
- # get_logger will only ever add one SysLog Handler to a logger
1931
- if not hasattr(get_logger, 'handler4logger'):
1932
- get_logger.handler4logger = {}
1933
- if logger in get_logger.handler4logger:
1934
- logger.removeHandler(get_logger.handler4logger[logger])
1935
-
1936
- # facility for this logger will be set by last call wins
1937
- facility = getattr(SysLogHandler, conf.get('log_facility', 'LOG_LOCAL0'),
1938
- SysLogHandler.LOG_LOCAL0)
1939
- udp_host = conf.get('log_udp_host')
1940
- if udp_host:
1941
- udp_port = int(conf.get('log_udp_port',
1942
- logging.handlers.SYSLOG_UDP_PORT))
1943
- handler = ThreadSafeSysLogHandler(address=(udp_host, udp_port),
1944
- facility=facility)
1945
- else:
1946
- log_address = conf.get('log_address', '/dev/log')
1947
- handler = None
1948
- try:
1949
- mode = os.stat(log_address).st_mode
1950
- if stat.S_ISSOCK(mode):
1951
- handler = ThreadSafeSysLogHandler(address=log_address,
1952
- facility=facility)
1953
- except (OSError, socket.error) as e:
1954
- # If either /dev/log isn't a UNIX socket or it does not exist at
1955
- # all then py2 would raise an error
1956
- if e.errno not in [errno.ENOTSOCK, errno.ENOENT]:
1957
- raise
1958
- if handler is None:
1959
- # fallback to default UDP
1960
- handler = ThreadSafeSysLogHandler(facility=facility)
1961
- handler.setFormatter(formatter)
1962
- logger.addHandler(handler)
1963
- get_logger.handler4logger[logger] = handler
1964
-
1965
- # setup console logging
1966
- if log_to_console or hasattr(get_logger, 'console_handler4logger'):
1967
- # remove pre-existing console handler for this logger
1968
- if not hasattr(get_logger, 'console_handler4logger'):
1969
- get_logger.console_handler4logger = {}
1970
- if logger in get_logger.console_handler4logger:
1971
- logger.removeHandler(get_logger.console_handler4logger[logger])
1972
-
1973
- console_handler = logging.StreamHandler(sys.__stderr__)
1974
- console_handler.setFormatter(formatter)
1975
- logger.addHandler(console_handler)
1976
- get_logger.console_handler4logger[logger] = console_handler
1977
-
1978
- # set the level for the logger
1979
- logger.setLevel(
1980
- getattr(logging, conf.get('log_level', 'INFO').upper(), logging.INFO))
1981
-
1982
- # Setup logger with a StatsD client if so configured
1983
- statsd_host = conf.get('log_statsd_host')
1984
- if statsd_host:
1985
- statsd_port = int(conf.get('log_statsd_port', 8125))
1986
- base_prefix = conf.get('log_statsd_metric_prefix', '')
1987
- default_sample_rate = float(conf.get(
1988
- 'log_statsd_default_sample_rate', 1))
1989
- sample_rate_factor = float(conf.get(
1990
- 'log_statsd_sample_rate_factor', 1))
1991
- if statsd_tail_prefix is None:
1992
- statsd_tail_prefix = name
1993
- statsd_client = StatsdClient(statsd_host, statsd_port, base_prefix,
1994
- statsd_tail_prefix, default_sample_rate,
1995
- sample_rate_factor, logger=logger)
1996
- logger.statsd_client = statsd_client
1997
- else:
1998
- logger.statsd_client = None
1999
-
2000
- adapted_logger = LogAdapter(logger, name)
2001
- other_handlers = conf.get('log_custom_handlers', None)
2002
- if other_handlers:
2003
- log_custom_handlers = [s.strip() for s in other_handlers.split(',')
2004
- if s.strip()]
2005
- for hook in log_custom_handlers:
2006
- try:
2007
- mod, fnc = hook.rsplit('.', 1)
2008
- logger_hook = getattr(__import__(mod, fromlist=[fnc]), fnc)
2009
- logger_hook(conf, name, log_to_console, log_route, fmt,
2010
- logger, adapted_logger)
2011
- except (AttributeError, ImportError):
2012
- print('Error calling custom handler [%s]' % hook,
2013
- file=sys.stderr)
2014
- except ValueError:
2015
- print('Invalid custom handler format [%s]' % hook,
2016
- file=sys.stderr)
2017
-
2018
- return adapted_logger
2019
-
2020
-
2021
909
  def get_hub():
2022
910
  """
2023
911
  Checks whether poll is available and falls back
@@ -2095,43 +983,6 @@ def clean_up_daemon_hygiene():
2095
983
  os.umask(0o22) # ensure files are created with the correct privileges
2096
984
 
2097
985
 
2098
- def capture_stdio(logger, **kwargs):
2099
- """
2100
- Log unhandled exceptions, close stdio, capture stdout and stderr.
2101
-
2102
- param logger: Logger object to use
2103
- """
2104
- # log uncaught exceptions
2105
- sys.excepthook = lambda * exc_info: \
2106
- logger.critical('UNCAUGHT EXCEPTION', exc_info=exc_info)
2107
-
2108
- # collect stdio file desc not in use for logging
2109
- stdio_files = [sys.stdin, sys.stdout, sys.stderr]
2110
- console_fds = [h.stream.fileno() for _junk, h in getattr(
2111
- get_logger, 'console_handler4logger', {}).items()]
2112
- stdio_files = [f for f in stdio_files if f.fileno() not in console_fds]
2113
-
2114
- with open(os.devnull, 'r+b') as nullfile:
2115
- # close stdio (excludes fds open for logging)
2116
- for f in stdio_files:
2117
- # some platforms throw an error when attempting an stdin flush
2118
- try:
2119
- f.flush()
2120
- except IOError:
2121
- pass
2122
-
2123
- try:
2124
- os.dup2(nullfile.fileno(), f.fileno())
2125
- except OSError:
2126
- pass
2127
-
2128
- # redirect stdio
2129
- if kwargs.pop('capture_stdout', True):
2130
- sys.stdout = LoggerFileObject(logger)
2131
- if kwargs.pop('capture_stderr', True):
2132
- sys.stderr = LoggerFileObject(logger, 'STDERR')
2133
-
2134
-
2135
986
  def parse_options(parser=None, once=False, test_config=False, test_args=None):
2136
987
  """Parse standard swift server/daemon options with optparse.OptionParser.
2137
988
 
@@ -2518,119 +1369,6 @@ def cache_from_env(env, allow_none=False):
2518
1369
  return item_from_env(env, 'swift.cache', allow_none)
2519
1370
 
2520
1371
 
2521
- def read_conf_dir(parser, conf_dir):
2522
- conf_files = []
2523
- for f in os.listdir(conf_dir):
2524
- if f.endswith('.conf') and not f.startswith('.'):
2525
- conf_files.append(os.path.join(conf_dir, f))
2526
- return parser.read(sorted(conf_files))
2527
-
2528
-
2529
- if six.PY2:
2530
- NicerInterpolation = None # just don't cause ImportErrors over in wsgi.py
2531
- else:
2532
- class NicerInterpolation(configparser.BasicInterpolation):
2533
- def before_get(self, parser, section, option, value, defaults):
2534
- if '%(' not in value:
2535
- return value
2536
- return super(NicerInterpolation, self).before_get(
2537
- parser, section, option, value, defaults)
2538
-
2539
-
2540
- def readconf(conf_path, section_name=None, log_name=None, defaults=None,
2541
- raw=False):
2542
- """
2543
- Read config file(s) and return config items as a dict
2544
-
2545
- :param conf_path: path to config file/directory, or a file-like object
2546
- (hasattr readline)
2547
- :param section_name: config section to read (will return all sections if
2548
- not defined)
2549
- :param log_name: name to be used with logging (will use section_name if
2550
- not defined)
2551
- :param defaults: dict of default values to pre-populate the config with
2552
- :returns: dict of config items
2553
- :raises ValueError: if section_name does not exist
2554
- :raises IOError: if reading the file failed
2555
- """
2556
- if defaults is None:
2557
- defaults = {}
2558
- if raw:
2559
- c = RawConfigParser(defaults)
2560
- else:
2561
- if six.PY2:
2562
- c = ConfigParser(defaults)
2563
- else:
2564
- # In general, we haven't really thought much about interpolation
2565
- # in configs. Python's default ConfigParser has always supported
2566
- # it, though, so *we* got it "for free". Unfortunatley, since we
2567
- # "supported" interpolation, we have to assume there are
2568
- # deployments in the wild that use it, and try not to break them.
2569
- # So, do what we can to mimic the py2 behavior of passing through
2570
- # values like "1%" (which we want to support for
2571
- # fallocate_reserve).
2572
- c = ConfigParser(defaults, interpolation=NicerInterpolation())
2573
- c.optionxform = str # Don't lower-case keys
2574
-
2575
- if hasattr(conf_path, 'readline'):
2576
- if hasattr(conf_path, 'seek'):
2577
- conf_path.seek(0)
2578
- if six.PY2:
2579
- c.readfp(conf_path)
2580
- else:
2581
- c.read_file(conf_path)
2582
- else:
2583
- if os.path.isdir(conf_path):
2584
- # read all configs in directory
2585
- success = read_conf_dir(c, conf_path)
2586
- else:
2587
- success = c.read(conf_path)
2588
- if not success:
2589
- raise IOError("Unable to read config from %s" %
2590
- conf_path)
2591
- if section_name:
2592
- if c.has_section(section_name):
2593
- conf = dict(c.items(section_name))
2594
- else:
2595
- raise ValueError(
2596
- "Unable to find %(section)s config section in %(conf)s" %
2597
- {'section': section_name, 'conf': conf_path})
2598
- if "log_name" not in conf:
2599
- if log_name is not None:
2600
- conf['log_name'] = log_name
2601
- else:
2602
- conf['log_name'] = section_name
2603
- else:
2604
- conf = {}
2605
- for s in c.sections():
2606
- conf.update({s: dict(c.items(s))})
2607
- if 'log_name' not in conf:
2608
- conf['log_name'] = log_name
2609
- conf['__file__'] = conf_path
2610
- return conf
2611
-
2612
-
2613
- def parse_prefixed_conf(conf_file, prefix):
2614
- """
2615
- Search the config file for any common-prefix sections and load those
2616
- sections to a dict mapping the after-prefix reference to options.
2617
-
2618
- :param conf_file: the file name of the config to parse
2619
- :param prefix: the common prefix of the sections
2620
- :return: a dict mapping policy reference -> dict of policy options
2621
- :raises ValueError: if a policy config section has an invalid name
2622
- """
2623
-
2624
- ret_config = {}
2625
- all_conf = readconf(conf_file)
2626
- for section, options in all_conf.items():
2627
- if not section.startswith(prefix):
2628
- continue
2629
- target_ref = section[len(prefix):]
2630
- ret_config[target_ref] = options
2631
- return ret_config
2632
-
2633
-
2634
1372
  def write_pickle(obj, dest, tmp=None, pickle_protocol=0):
2635
1373
  """
2636
1374
  Ensure that a pickle file gets written to disk. The file
@@ -2920,13 +1658,19 @@ class AbstractRateLimiter(object):
2920
1658
  running_time < (current time - rate_buffer ms) to allow an initial
2921
1659
  burst.
2922
1660
  """
2923
- self.max_rate = max_rate
2924
- self.rate_buffer_ms = rate_buffer * self.clock_accuracy
1661
+ self.set_max_rate(max_rate)
1662
+ self.set_rate_buffer(rate_buffer)
2925
1663
  self.burst_after_idle = burst_after_idle
2926
1664
  self.running_time = running_time
1665
+
1666
+ def set_max_rate(self, max_rate):
1667
+ self.max_rate = max_rate
2927
1668
  self.time_per_incr = (self.clock_accuracy / self.max_rate
2928
1669
  if self.max_rate else 0)
2929
1670
 
1671
+ def set_rate_buffer(self, rate_buffer):
1672
+ self.rate_buffer_ms = rate_buffer * self.clock_accuracy
1673
+
2930
1674
  def _sleep(self, seconds):
2931
1675
  # subclasses should override to implement a sleep
2932
1676
  raise NotImplementedError
@@ -3255,112 +1999,6 @@ def validate_sync_to(value, allowed_sync_hosts, realms_conf):
3255
1999
  return (None, value, None, None)
3256
2000
 
3257
2001
 
3258
- def affinity_key_function(affinity_str):
3259
- """Turns an affinity config value into a function suitable for passing to
3260
- sort(). After doing so, the array will be sorted with respect to the given
3261
- ordering.
3262
-
3263
- For example, if affinity_str is "r1=1, r2z7=2, r2z8=2", then the array
3264
- will be sorted with all nodes from region 1 (r1=1) first, then all the
3265
- nodes from region 2 zones 7 and 8 (r2z7=2 and r2z8=2), then everything
3266
- else.
3267
-
3268
- Note that the order of the pieces of affinity_str is irrelevant; the
3269
- priority values are what comes after the equals sign.
3270
-
3271
- If affinity_str is empty or all whitespace, then the resulting function
3272
- will not alter the ordering of the nodes.
3273
-
3274
- :param affinity_str: affinity config value, e.g. "r1z2=3"
3275
- or "r1=1, r2z1=2, r2z2=2"
3276
- :returns: single-argument function
3277
- :raises ValueError: if argument invalid
3278
- """
3279
- affinity_str = affinity_str.strip()
3280
-
3281
- if not affinity_str:
3282
- return lambda x: 0
3283
-
3284
- priority_matchers = []
3285
- pieces = [s.strip() for s in affinity_str.split(',')]
3286
- for piece in pieces:
3287
- # matches r<number>=<number> or r<number>z<number>=<number>
3288
- match = re.match(r"r(\d+)(?:z(\d+))?=(\d+)$", piece)
3289
- if match:
3290
- region, zone, priority = match.groups()
3291
- region = int(region)
3292
- priority = int(priority)
3293
- zone = int(zone) if zone else None
3294
-
3295
- matcher = {'region': region, 'priority': priority}
3296
- if zone is not None:
3297
- matcher['zone'] = zone
3298
- priority_matchers.append(matcher)
3299
- else:
3300
- raise ValueError("Invalid affinity value: %r" % affinity_str)
3301
-
3302
- priority_matchers.sort(key=operator.itemgetter('priority'))
3303
-
3304
- def keyfn(ring_node):
3305
- for matcher in priority_matchers:
3306
- if (matcher['region'] == ring_node['region']
3307
- and ('zone' not in matcher
3308
- or matcher['zone'] == ring_node['zone'])):
3309
- return matcher['priority']
3310
- return 4294967296 # 2^32, i.e. "a big number"
3311
- return keyfn
3312
-
3313
-
3314
- def affinity_locality_predicate(write_affinity_str):
3315
- """
3316
- Turns a write-affinity config value into a predicate function for nodes.
3317
- The returned value will be a 1-arg function that takes a node dictionary
3318
- and returns a true value if it is "local" and a false value otherwise. The
3319
- definition of "local" comes from the affinity_str argument passed in here.
3320
-
3321
- For example, if affinity_str is "r1, r2z2", then only nodes where region=1
3322
- or where (region=2 and zone=2) are considered local.
3323
-
3324
- If affinity_str is empty or all whitespace, then the resulting function
3325
- will consider everything local
3326
-
3327
- :param write_affinity_str: affinity config value, e.g. "r1z2"
3328
- or "r1, r2z1, r2z2"
3329
- :returns: single-argument function, or None if affinity_str is empty
3330
- :raises ValueError: if argument invalid
3331
- """
3332
- affinity_str = write_affinity_str.strip()
3333
-
3334
- if not affinity_str:
3335
- return None
3336
-
3337
- matchers = []
3338
- pieces = [s.strip() for s in affinity_str.split(',')]
3339
- for piece in pieces:
3340
- # matches r<number> or r<number>z<number>
3341
- match = re.match(r"r(\d+)(?:z(\d+))?$", piece)
3342
- if match:
3343
- region, zone = match.groups()
3344
- region = int(region)
3345
- zone = int(zone) if zone else None
3346
-
3347
- matcher = {'region': region}
3348
- if zone is not None:
3349
- matcher['zone'] = zone
3350
- matchers.append(matcher)
3351
- else:
3352
- raise ValueError("Invalid write-affinity value: %r" % affinity_str)
3353
-
3354
- def is_local(ring_node):
3355
- for matcher in matchers:
3356
- if (matcher['region'] == ring_node['region']
3357
- and ('zone' not in matcher
3358
- or matcher['zone'] == ring_node['zone'])):
3359
- return True
3360
- return False
3361
- return is_local
3362
-
3363
-
3364
2002
  def get_remote_client(req):
3365
2003
  # remote host for zeus
3366
2004
  client = req.headers.get('x-cluster-client-ip')
@@ -3616,30 +2254,6 @@ def rsync_module_interpolation(template, device):
3616
2254
  return module
3617
2255
 
3618
2256
 
3619
- def get_valid_utf8_str(str_or_unicode):
3620
- """
3621
- Get valid parts of utf-8 str from str, unicode and even invalid utf-8 str
3622
-
3623
- :param str_or_unicode: a string or an unicode which can be invalid utf-8
3624
- """
3625
- if six.PY2:
3626
- if isinstance(str_or_unicode, six.text_type):
3627
- (str_or_unicode, _len) = utf8_encoder(str_or_unicode, 'replace')
3628
- (valid_unicode_str, _len) = utf8_decoder(str_or_unicode, 'replace')
3629
- else:
3630
- # Apparently under py3 we need to go to utf-16 to collapse surrogates?
3631
- if isinstance(str_or_unicode, six.binary_type):
3632
- try:
3633
- (str_or_unicode, _len) = utf8_decoder(str_or_unicode,
3634
- 'surrogatepass')
3635
- except UnicodeDecodeError:
3636
- (str_or_unicode, _len) = utf8_decoder(str_or_unicode,
3637
- 'replace')
3638
- (str_or_unicode, _len) = utf16_encoder(str_or_unicode, 'surrogatepass')
3639
- (valid_unicode_str, _len) = utf16_decoder(str_or_unicode, 'replace')
3640
- return valid_unicode_str.encode('utf-8')
3641
-
3642
-
3643
2257
  class Everything(object):
3644
2258
  """
3645
2259
  A container that contains everything. If "e" is an instance of
@@ -3720,9 +2334,36 @@ class ClosingIterator(object):
3720
2334
  if not self.closed:
3721
2335
  for wrapped in self.closeables:
3722
2336
  close_if_possible(wrapped)
2337
+ # clear it out so they get GC'ed
2338
+ self.closeables = []
2339
+ self.wrapped_iter = iter([])
3723
2340
  self.closed = True
3724
2341
 
3725
2342
 
2343
+ class ClosingMapper(ClosingIterator):
2344
+ """
2345
+ A closing iterator that yields the result of ``function`` as it is applied
2346
+ to each item of ``iterable``.
2347
+
2348
+ Note that while this behaves similarly to the built-in ``map`` function,
2349
+ ``other_closeables`` does not have the same semantic as the ``iterables``
2350
+ argument of ``map``.
2351
+
2352
+ :param function: a function that will be called with each item of
2353
+ ``iterable`` before yielding its result.
2354
+ :param iterable: iterator to wrap.
2355
+ :param other_closeables: other resources to attempt to close.
2356
+ """
2357
+ __slots__ = ('func',)
2358
+
2359
+ def __init__(self, function, iterable, other_closeables=None):
2360
+ self.func = function
2361
+ super(ClosingMapper, self).__init__(iterable, other_closeables)
2362
+
2363
+ def _get_next_item(self):
2364
+ return self.func(super(ClosingMapper, self)._get_next_item())
2365
+
2366
+
3726
2367
  class CloseableChain(ClosingIterator):
3727
2368
  """
3728
2369
  Like itertools.chain, but with a close method that will attempt to invoke
@@ -4172,16 +2813,6 @@ def clean_content_type(value):
4172
2813
  return value
4173
2814
 
4174
2815
 
4175
- def quote(value, safe='/'):
4176
- """
4177
- Patched version of urllib.quote that encodes utf-8 strings before quoting
4178
- """
4179
- quoted = _quote(get_valid_utf8_str(value), safe)
4180
- if isinstance(value, six.binary_type):
4181
- quoted = quoted.encode('utf-8')
4182
- return quoted
4183
-
4184
-
4185
2816
  def get_expirer_container(x_delete_at, expirer_divisor, acc, cont, obj):
4186
2817
  """
4187
2818
  Returns an expiring object container name for given X-Delete-At and
@@ -4564,27 +3195,6 @@ def parse_content_disposition(header):
4564
3195
  return header, attributes
4565
3196
 
4566
3197
 
4567
- try:
4568
- _test_md5 = hashlib.md5(usedforsecurity=False) # nosec
4569
-
4570
- def md5(string=b'', usedforsecurity=True):
4571
- """Return an md5 hashlib object using usedforsecurity parameter
4572
-
4573
- For python distributions that support the usedforsecurity keyword
4574
- parameter, this passes the parameter through as expected.
4575
- See https://bugs.python.org/issue9216
4576
- """
4577
- return hashlib.md5(string, usedforsecurity=usedforsecurity) # nosec
4578
- except TypeError:
4579
- def md5(string=b'', usedforsecurity=True):
4580
- """Return an md5 hashlib object without usedforsecurity parameter
4581
-
4582
- For python distributions that do not yet support this keyword
4583
- parameter, we drop the parameter
4584
- """
4585
- return hashlib.md5(string) # nosec
4586
-
4587
-
4588
3198
  class NamespaceOuterBound(object):
4589
3199
  """
4590
3200
  A custom singleton type to be subclassed for the outer bounds of
@@ -5975,162 +4585,6 @@ def load_pkg_resource(group, uri):
5975
4585
  return entry_points[0].load()
5976
4586
 
5977
4587
 
5978
- class PipeMutex(object):
5979
- """
5980
- Mutex using a pipe. Works across both greenlets and real threads, even
5981
- at the same time.
5982
- """
5983
-
5984
- def __init__(self):
5985
- self.rfd, self.wfd = os.pipe()
5986
-
5987
- # You can't create a pipe in non-blocking mode; you must set it
5988
- # later.
5989
- rflags = fcntl.fcntl(self.rfd, fcntl.F_GETFL)
5990
- fcntl.fcntl(self.rfd, fcntl.F_SETFL, rflags | os.O_NONBLOCK)
5991
- os.write(self.wfd, b'-') # start unlocked
5992
-
5993
- self.owner = None
5994
- self.recursion_depth = 0
5995
-
5996
- # Usually, it's an error to have multiple greenthreads all waiting
5997
- # to read the same file descriptor. It's often a sign of inadequate
5998
- # concurrency control; for example, if you have two greenthreads
5999
- # trying to use the same memcache connection, they'll end up writing
6000
- # interleaved garbage to the socket or stealing part of each others'
6001
- # responses.
6002
- #
6003
- # In this case, we have multiple greenthreads waiting on the same
6004
- # file descriptor by design. This lets greenthreads in real thread A
6005
- # wait with greenthreads in real thread B for the same mutex.
6006
- # Therefore, we must turn off eventlet's multiple-reader detection.
6007
- #
6008
- # It would be better to turn off multiple-reader detection for only
6009
- # our calls to trampoline(), but eventlet does not support that.
6010
- eventlet.debug.hub_prevent_multiple_readers(False)
6011
-
6012
- def acquire(self, blocking=True):
6013
- """
6014
- Acquire the mutex.
6015
-
6016
- If called with blocking=False, returns True if the mutex was
6017
- acquired and False if it wasn't. Otherwise, blocks until the mutex
6018
- is acquired and returns True.
6019
-
6020
- This lock is recursive; the same greenthread may acquire it as many
6021
- times as it wants to, though it must then release it that many times
6022
- too.
6023
- """
6024
- current_greenthread_id = id(eventlet.greenthread.getcurrent())
6025
- if self.owner == current_greenthread_id:
6026
- self.recursion_depth += 1
6027
- return True
6028
-
6029
- while True:
6030
- try:
6031
- # If there is a byte available, this will read it and remove
6032
- # it from the pipe. If not, this will raise OSError with
6033
- # errno=EAGAIN.
6034
- os.read(self.rfd, 1)
6035
- self.owner = current_greenthread_id
6036
- return True
6037
- except OSError as err:
6038
- if err.errno != errno.EAGAIN:
6039
- raise
6040
-
6041
- if not blocking:
6042
- return False
6043
-
6044
- # Tell eventlet to suspend the current greenthread until
6045
- # self.rfd becomes readable. This will happen when someone
6046
- # else writes to self.wfd.
6047
- eventlet.hubs.trampoline(self.rfd, read=True)
6048
-
6049
- def release(self):
6050
- """
6051
- Release the mutex.
6052
- """
6053
- current_greenthread_id = id(eventlet.greenthread.getcurrent())
6054
- if self.owner != current_greenthread_id:
6055
- raise RuntimeError("cannot release un-acquired lock")
6056
-
6057
- if self.recursion_depth > 0:
6058
- self.recursion_depth -= 1
6059
- return
6060
-
6061
- self.owner = None
6062
- os.write(self.wfd, b'X')
6063
-
6064
- def close(self):
6065
- """
6066
- Close the mutex. This releases its file descriptors.
6067
-
6068
- You can't use a mutex after it's been closed.
6069
- """
6070
- if self.wfd is not None:
6071
- os.close(self.rfd)
6072
- self.rfd = None
6073
- os.close(self.wfd)
6074
- self.wfd = None
6075
- self.owner = None
6076
- self.recursion_depth = 0
6077
-
6078
- def __del__(self):
6079
- # We need this so we don't leak file descriptors. Otherwise, if you
6080
- # call get_logger() and don't explicitly dispose of it by calling
6081
- # logger.logger.handlers[0].lock.close() [1], the pipe file
6082
- # descriptors are leaked.
6083
- #
6084
- # This only really comes up in tests. Swift processes tend to call
6085
- # get_logger() once and then hang on to it until they exit, but the
6086
- # test suite calls get_logger() a lot.
6087
- #
6088
- # [1] and that's a completely ridiculous thing to expect callers to
6089
- # do, so nobody does it and that's okay.
6090
- self.close()
6091
-
6092
-
6093
- class NoopMutex(object):
6094
- """
6095
- "Mutex" that doesn't lock anything.
6096
-
6097
- We only allow our syslog logging to be configured via UDS or UDP, neither
6098
- of which have the message-interleaving trouble you'd expect from TCP or
6099
- file handlers.
6100
- """
6101
-
6102
- def __init__(self):
6103
- # Usually, it's an error to have multiple greenthreads all waiting
6104
- # to write to the same file descriptor. It's often a sign of inadequate
6105
- # concurrency control; for example, if you have two greenthreads
6106
- # trying to use the same memcache connection, they'll end up writing
6107
- # interleaved garbage to the socket or stealing part of each others'
6108
- # responses.
6109
- #
6110
- # In this case, we have multiple greenthreads waiting on the same
6111
- # (logging) file descriptor by design. So, similar to the PipeMutex,
6112
- # we must turn off eventlet's multiple-waiter detection.
6113
- #
6114
- # It would be better to turn off multiple-reader detection for only
6115
- # the logging socket fd, but eventlet does not support that.
6116
- eventlet.debug.hub_prevent_multiple_readers(False)
6117
-
6118
- def acquire(self, blocking=True):
6119
- pass
6120
-
6121
- def release(self):
6122
- pass
6123
-
6124
-
6125
- class ThreadSafeSysLogHandler(SysLogHandler):
6126
- def createLock(self):
6127
- if config_true_value(os.environ.get(
6128
- 'SWIFT_NOOP_LOGGING_MUTEX') or 'true'):
6129
- self.lock = NoopMutex()
6130
- else:
6131
- self.lock = PipeMutex()
6132
-
6133
-
6134
4588
  def round_robin_iter(its):
6135
4589
  """
6136
4590
  Takes a list of iterators, yield an element from each in a round-robin
@@ -6432,6 +4886,14 @@ class Watchdog(object):
6432
4886
  if self._run_gth is None:
6433
4887
  self._run_gth = eventlet.spawn(self.run)
6434
4888
 
4889
+ def kill(self):
4890
+ """
4891
+ Stop the watchdog greenthread.
4892
+ """
4893
+ if self._run_gth is not None:
4894
+ self._run_gth.kill()
4895
+ self._run_gth = None
4896
+
6435
4897
  def run(self):
6436
4898
  while True:
6437
4899
  self._run()