~ubuntu-branches/ubuntu/vivid/swift/vivid-updates

« back to all changes in this revision

Viewing changes to swift/common/utils.py

  • Committer: Package Import Robot
  • Author(s): James Page, Chuck Short, James Page
  • Date: 2014-10-06 10:06:11 UTC
  • mfrom: (1.2.31)
  • Revision ID: package-import@ubuntu.com-20141006100611-wdzkkuoru7ubtlml
Tags: 2.1.0-0ubuntu1
[ Chuck Short ]
* debian/patches/fix-doc-no-network.patch: Refreshed.
* debian/control: Add python-oslosphinx as a build dependency.

[ James Page ]
* New upstream release for OpenStack Juno.
* d/copyright: Add linebreaks to fixup file-without-copyright-
  information warning.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
 
16
16
"""Miscellaneous utility functions for use with Swift."""
17
17
 
 
18
from __future__ import print_function
 
19
 
18
20
import errno
19
21
import fcntl
20
22
import grp
64
66
 
65
67
from swift import gettext_ as _
66
68
import swift.common.exceptions
67
 
from swift.common.http import is_success, is_redirection, HTTP_NOT_FOUND
 
69
from swift.common.http import is_success, is_redirection, HTTP_NOT_FOUND, \
 
70
    HTTP_PRECONDITION_FAILED, HTTP_REQUESTED_RANGE_NOT_SATISFIABLE
 
71
 
68
72
 
69
73
# logging doesn't import patched as cleanly as one would like
70
74
from logging.handlers import SysLogHandler
322
326
    :returns: a properly formated line for logging.
323
327
    """
324
328
 
325
 
    return '%s - - [%s] "%s %s" %s %s "%s" "%s" "%s" %.4f "%s"' % (
 
329
    return '%s - - [%s] "%s %s" %s %s "%s" "%s" "%s" %.4f "%s" %d' % (
326
330
        req.remote_addr,
327
331
        time.strftime('%d/%b/%Y:%H:%M:%S +0000', time.gmtime()),
328
332
        req.method, req.path, res.status.split()[0],
329
333
        res.content_length or '-', req.referer or '-',
330
334
        req.headers.get('x-trans-id', '-'),
331
 
        req.user_agent or '-', trans_time, additional_info or '-')
 
335
        req.user_agent or '-', trans_time, additional_info or '-',
 
336
        os.getpid())
332
337
 
333
338
 
334
339
def get_trans_id_time(trans_id):
629
634
        return INTERNAL_FORMAT % (self.timestamp, self.offset)
630
635
 
631
636
    def __str__(self):
632
 
        raise TypeError('You must specificy which string format is required')
 
637
        raise TypeError('You must specify which string format is required')
633
638
 
634
639
    def __float__(self):
635
640
        return self.timestamp
928
933
 
929
934
class StatsdClient(object):
930
935
    def __init__(self, host, port, base_prefix='', tail_prefix='',
931
 
                 default_sample_rate=1, sample_rate_factor=1):
 
936
                 default_sample_rate=1, sample_rate_factor=1, logger=None):
932
937
        self._host = host
933
938
        self._port = port
934
939
        self._base_prefix = base_prefix
937
942
        self._sample_rate_factor = sample_rate_factor
938
943
        self._target = (self._host, self._port)
939
944
        self.random = random
 
945
        self.logger = logger
940
946
 
941
947
    def set_prefix(self, new_prefix):
942
948
        if new_prefix and self._base_prefix:
961
967
        # Ideally, we'd cache a sending socket in self, but that
962
968
        # results in a socket getting shared by multiple green threads.
963
969
        with closing(self._open_socket()) as sock:
964
 
            return sock.sendto('|'.join(parts), self._target)
 
970
            try:
 
971
                return sock.sendto('|'.join(parts), self._target)
 
972
            except IOError as err:
 
973
                if self.logger:
 
974
                    self.logger.warn(
 
975
                        'Error sending UDP message to %r: %s',
 
976
                        self._target, err)
965
977
 
966
978
    def _open_socket(self):
967
979
        return socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
989
1001
                               sample_rate)
990
1002
 
991
1003
 
 
1004
def server_handled_successfully(status_int):
 
1005
    """
 
1006
    True for successful responses *or* error codes that are not Swift's fault,
 
1007
    False otherwise. For example, 500 is definitely the server's fault, but
 
1008
    412 is an error code (4xx are all errors) that is due to a header the
 
1009
    client sent.
 
1010
 
 
1011
    If one is tracking error rates to monitor server health, one would be
 
1012
    advised to use a function like this one, lest a client cause a flurry of
 
1013
    404s or 416s and make a spurious spike in your errors graph.
 
1014
    """
 
1015
    return (is_success(status_int) or
 
1016
            is_redirection(status_int) or
 
1017
            status_int == HTTP_NOT_FOUND or
 
1018
            status_int == HTTP_PRECONDITION_FAILED or
 
1019
            status_int == HTTP_REQUESTED_RANGE_NOT_SATISFIABLE)
 
1020
 
 
1021
 
992
1022
def timing_stats(**dec_kwargs):
993
1023
    """
994
1024
    Returns a decorator that logs timing events or errors for public methods in
1001
1031
        def _timing_stats(ctrl, *args, **kwargs):
1002
1032
            start_time = time.time()
1003
1033
            resp = func(ctrl, *args, **kwargs)
1004
 
            if is_success(resp.status_int) or \
1005
 
                    is_redirection(resp.status_int) or \
1006
 
                    resp.status_int == HTTP_NOT_FOUND:
 
1034
            if server_handled_successfully(resp.status_int):
1007
1035
                ctrl.logger.timing_since(method + '.timing',
1008
1036
                                         start_time, **dec_kwargs)
1009
1037
            else:
1311
1339
            'log_statsd_sample_rate_factor', 1))
1312
1340
        statsd_client = StatsdClient(statsd_host, statsd_port, base_prefix,
1313
1341
                                     name, default_sample_rate,
1314
 
                                     sample_rate_factor)
 
1342
                                     sample_rate_factor, logger=logger)
1315
1343
        logger.statsd_client = statsd_client
1316
1344
    else:
1317
1345
        logger.statsd_client = None
1328
1356
                logger_hook(conf, name, log_to_console, log_route, fmt,
1329
1357
                            logger, adapted_logger)
1330
1358
            except (AttributeError, ImportError):
1331
 
                print >>sys.stderr, 'Error calling custom handler [%s]' % hook
 
1359
                print(
 
1360
                    'Error calling custom handler [%s]' % hook,
 
1361
                    file=sys.stderr)
1332
1362
            except ValueError:
1333
 
                print >>sys.stderr, 'Invalid custom handler format [%s]' % hook
 
1363
                print('Invalid custom handler format [%s]' % hook, sys.stderr)
1334
1364
 
1335
1365
    # Python 2.6 has the undesirable property of keeping references to all log
1336
1366
    # handlers around forever in logging._handlers and logging._handlerList.
1469
1499
 
1470
1500
    if not args:
1471
1501
        parser.print_usage()
1472
 
        print _("Error: missing config path argument")
 
1502
        print(_("Error: missing config path argument"))
1473
1503
        sys.exit(1)
1474
1504
    config = os.path.abspath(args.pop(0))
1475
1505
    if not os.path.exists(config):
1476
1506
        parser.print_usage()
1477
 
        print _("Error: unable to locate %s") % config
 
1507
        print(_("Error: unable to locate %s") % config)
1478
1508
        sys.exit(1)
1479
1509
 
1480
1510
    extra_args = []
1577
1607
    mkdirs(directory)
1578
1608
    lockpath = '%s/.lock' % directory
1579
1609
    fd = os.open(lockpath, os.O_WRONLY | os.O_CREAT)
 
1610
    sleep_time = 0.01
 
1611
    slower_sleep_time = max(timeout * 0.01, sleep_time)
 
1612
    slowdown_at = timeout * 0.01
 
1613
    time_slept = 0
1580
1614
    try:
1581
1615
        with timeout_class(timeout, lockpath):
1582
1616
            while True:
1586
1620
                except IOError as err:
1587
1621
                    if err.errno != errno.EAGAIN:
1588
1622
                        raise
1589
 
                sleep(0.01)
 
1623
                if time_slept > slowdown_at:
 
1624
                    sleep_time = slower_sleep_time
 
1625
                sleep(sleep_time)
 
1626
                time_slept += sleep_time
1590
1627
        yield True
1591
1628
    finally:
1592
1629
        os.close(fd)
1610
1647
        mode = 'a+'
1611
1648
    else:
1612
1649
        mode = 'r+'
1613
 
    fd = os.open(filename, flags)
1614
 
    file_obj = os.fdopen(fd, mode)
1615
 
    try:
1616
 
        with swift.common.exceptions.LockTimeout(timeout, filename):
1617
 
            while True:
1618
 
                try:
1619
 
                    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1620
 
                    break
1621
 
                except IOError as err:
1622
 
                    if err.errno != errno.EAGAIN:
1623
 
                        raise
1624
 
                sleep(0.01)
1625
 
        yield file_obj
1626
 
    finally:
 
1650
    while True:
 
1651
        fd = os.open(filename, flags)
 
1652
        file_obj = os.fdopen(fd, mode)
1627
1653
        try:
 
1654
            with swift.common.exceptions.LockTimeout(timeout, filename):
 
1655
                while True:
 
1656
                    try:
 
1657
                        fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
 
1658
                        break
 
1659
                    except IOError as err:
 
1660
                        if err.errno != errno.EAGAIN:
 
1661
                            raise
 
1662
                    sleep(0.01)
 
1663
            try:
 
1664
                if os.stat(filename).st_ino != os.fstat(fd).st_ino:
 
1665
                    continue
 
1666
            except OSError as err:
 
1667
                if err.errno == errno.ENOENT:
 
1668
                    continue
 
1669
                raise
 
1670
            yield file_obj
 
1671
            if unlink:
 
1672
                os.unlink(filename)
 
1673
            break
 
1674
        finally:
1628
1675
            file_obj.close()
1629
 
        except UnboundLocalError:
1630
 
            pass  # may have not actually opened the file
1631
 
        if unlink:
1632
 
            os.unlink(filename)
1633
1676
 
1634
1677
 
1635
1678
def lock_parent_directory(filename, timeout=10):
1758
1801
        else:
1759
1802
            success = c.read(conf_path)
1760
1803
        if not success:
1761
 
            print _("Unable to read config from %s") % conf_path
 
1804
            print(_("Unable to read config from %s") % conf_path)
1762
1805
            sys.exit(1)
1763
1806
    if section_name:
1764
1807
        if c.has_section(section_name):
1765
1808
            conf = dict(c.items(section_name))
1766
1809
        else:
1767
 
            print _("Unable to find %s config section in %s") % \
1768
 
                (section_name, conf_path)
 
1810
            print(_("Unable to find %s config section in %s") %
 
1811
                  (section_name, conf_path))
1769
1812
            sys.exit(1)
1770
1813
        if "log_name" not in conf:
1771
1814
            if log_name is not None:
1896
1939
    for device in device_dir:
1897
1940
        if mount_check and not ismount(os.path.join(devices, device)):
1898
1941
            if logger:
1899
 
                logger.debug(
 
1942
                logger.warning(
1900
1943
                    _('Skipping %s as it is not mounted'), device)
1901
1944
            continue
1902
1945
        datadir_path = os.path.join(devices, device, datadir)
1903
 
        partitions = listdir(datadir_path)
 
1946
        try:
 
1947
            partitions = listdir(datadir_path)
 
1948
        except OSError as e:
 
1949
            if logger:
 
1950
                logger.warning('Skipping %s because %s', datadir_path, e)
 
1951
            continue
1904
1952
        for partition in partitions:
1905
1953
            part_path = os.path.join(datadir_path, partition)
1906
1954
            try:
2284
2332
    """
2285
2333
    Function that will check if item is a dict, and if so put it under
2286
2334
    cache_entry[key].  We use nested recon cache entries when the object
2287
 
    auditor runs in 'once' mode with a specified subset of devices.
 
2335
    auditor runs in parallel or else in 'once' mode with a specified
 
2336
    subset of devices.
2288
2337
    """
2289
2338
    if isinstance(item, dict):
2290
2339
        if key not in cache_entry or key in cache_entry and not \
2918
2967
    Patched version of urllib.quote that encodes utf-8 strings before quoting
2919
2968
    """
2920
2969
    return _quote(get_valid_utf8_str(value), safe)
 
2970
 
 
2971
 
 
2972
def get_expirer_container(x_delete_at, expirer_divisor, acc, cont, obj):
 
2973
    """
 
2974
    Returns a expiring object container name for given X-Delete-At and
 
2975
    a/c/o.
 
2976
    """
 
2977
    shard_int = int(hash_path(acc, cont, obj), 16) % 100
 
2978
    return normalize_delete_at_timestamp(
 
2979
        int(x_delete_at) / expirer_divisor * expirer_divisor - shard_int)