Merge tag '1.10.0' into debian/liberty

oslo.concurrency 1.10.0 release
This commit is contained in:
James Page
2015-06-03 10:06:50 +01:00
19 changed files with 420 additions and 310 deletions

View File

@@ -2,7 +2,15 @@
oslo.concurrency
==================
Oslo concurrency library has utilities for safely running multi-thread,
.. image:: https://pypip.in/version/oslo.concurrency/badge.svg
:target: https://pypi.python.org/pypi/oslo.concurrency/
:alt: Latest Version
.. image:: https://pypip.in/download/oslo.concurrency/badge.svg?period=month
:target: https://pypi.python.org/pypi/oslo.concurrency/
:alt: Downloads
The oslo.concurrency library has utilities for safely running multi-thread,
multi-process applications using locking mechanisms and for running
external processes.

View File

@@ -1,16 +1,14 @@
.. oslo.concurrency documentation master file, created by
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
============================================
Welcome to oslo.concurrency's documentation!
============================================
==================
oslo.concurrency
==================
The `oslo`_ concurrency library has utilities for safely running multi-thread,
multi-process applications using locking mechanisms and for running
external processes.
.. toctree::
:maxdepth: 1
readme
installation
usage
contributing
@@ -30,3 +28,5 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. _oslo: https://wiki.openstack.org/wiki/Oslo

View File

@@ -1 +0,0 @@
.. include:: ../../README.rst

View File

@@ -2,7 +2,6 @@
# The list of modules to copy from oslo-incubator.git
module = fileutils
script = tools/run_cross_tests.sh
# The base module to hold the copy of openstack.common
base=oslo.concurrency
base=oslo_concurrency

View File

@@ -0,0 +1,30 @@
# Translations template for oslo.concurrency.
# Copyright (C) 2015 ORGANIZATION
# This file is distributed under the same license as the oslo.concurrency
# project.
#
# Translators:
# Andi Chandler <andi@gowling.com>, 2014-2015
msgid ""
msgstr ""
"Project-Id-Version: oslo.concurrency\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-05-28 06:05+0000\n"
"PO-Revision-Date: 2015-03-24 01:20+0000\n"
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
"Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/"
"osloconcurrency/language/en_GB/)\n"
"Language: en_GB\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#, python-format
msgid "Could not close the acquired file handle `%s`"
msgstr "Could not close the acquired file handle `%s`"
#, python-format
msgid "Could not unlock the acquired lock `%s`"
msgstr "Could not unlock the acquired lock `%s`"

View File

@@ -21,14 +21,10 @@ msgstr ""
"Generated-By: Babel 1.3\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: oslo/concurrency/lockutils.py:84
#: oslo/concurrency/openstack/common/lockutils.py:82
#, python-format
msgid "Created lock path: %s"
msgstr "Created lock path: %s"
#: oslo/concurrency/lockutils.py:201
#: oslo/concurrency/openstack/common/lockutils.py:251
#, python-format
msgid "Failed to remove file %(file)s"
msgstr "Failed to remove file %(file)s"

View File

@@ -1,79 +1,25 @@
# English (United Kingdom) translations for oslo.concurrency.
# Copyright (C) 2014 ORGANIZATION
# Copyright (C) 2015 ORGANIZATION
# This file is distributed under the same license as the oslo.concurrency
# project.
#
# Translators:
# Andi Chandler <andi@gowling.com>, 2014
# Andi Chandler <andi@gowling.com>, 2014-2015
msgid ""
msgstr ""
"Project-Id-Version: oslo.concurrency\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2014-10-29 06:10+0000\n"
"PO-Revision-Date: 2014-10-28 20:48+0000\n"
"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
"Language-Team: English (United Kingdom) "
"(http://www.transifex.com/projects/p/osloconcurrency/language/en_GB/)\n"
"POT-Creation-Date: 2015-05-28 06:05+0000\n"
"PO-Revision-Date: 2015-03-24 01:19+0000\n"
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
"Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/"
"osloconcurrency/language/en_GB/)\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
#: oslo/concurrency/lockutils.py:120
#, python-format
msgid "Unable to acquire lock on `%(filename)s` due to %(exception)s"
msgstr "Unable to acquire lock on `%(filename)s` due to %(exception)s"
#: oslo/concurrency/lockutils.py:134
msgid "Unable to release an unacquired lock"
msgstr ""
#: oslo/concurrency/lockutils.py:385
msgid ""
"Calling lockutils directly is no longer supported. Please use the "
"lockutils-wrapper console script instead."
msgstr ""
#: oslo/concurrency/processutils.py:69
msgid "Unexpected error while running command."
msgstr "Unexpected error while running command."
#: oslo/concurrency/processutils.py:72
#, python-format
msgid ""
"%(description)s\n"
"Command: %(cmd)s\n"
"Exit code: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
msgstr ""
"%(description)s\n"
"Command: %(cmd)s\n"
"Exit code: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
#: oslo/concurrency/processutils.py:171
#, python-format
msgid "Got unknown keyword args: %r"
msgstr "Got unknown keyword args: %r"
#: oslo/concurrency/processutils.py:174
#, python-format
msgid "Got invalid arg log_errors: %r"
msgstr ""
#: oslo/concurrency/processutils.py:180
msgid "Command requested root, but did not specify a root helper."
msgstr "Command requested root, but did not specify a root helper."
#: oslo/concurrency/processutils.py:191
#, python-format
msgid "Running cmd (subprocess): %s"
msgstr "Running cmd (subprocess): %s"
#: oslo/concurrency/processutils.py:233
#, python-format
msgid ""
"%(desc)r\n"
@@ -82,30 +28,78 @@ msgid ""
"stdout: %(stdout)r\n"
"stderr: %(stderr)r"
msgstr ""
"%(desc)r\n"
"command: %(cmd)r\n"
"exit code: %(code)r\n"
"stdout: %(stdout)r\n"
"stderr: %(stderr)r"
#, python-format
msgid ""
"%(description)s\n"
"Command: %(cmd)s\n"
"Exit code: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
msgstr ""
"%(description)s\n"
"Command: %(cmd)s\n"
"Exit code: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
#, python-format
msgid "%r failed. Not Retrying."
msgstr "%r failed. Not Retrying."
#, python-format
msgid "%r failed. Retrying."
msgstr "%r failed. Retrying."
msgid ""
"Calling lockutils directly is no longer supported. Please use the lockutils-"
"wrapper console script instead."
msgstr ""
"Calling lockutils directly is no longer supported. Please use the lockutils-"
"wrapper console script instead."
msgid "Command requested root, but did not specify a root helper."
msgstr "Command requested root, but did not specify a root helper."
msgid "Environment not supported over SSH"
msgstr "Environment not supported over SSH"
#: oslo/concurrency/processutils.py:242
#, python-format
msgid ""
"Got an OSError\n"
"command: %(cmd)r\n"
"errno: %(errno)r"
msgstr ""
"Got an OSError\n"
"command: %(cmd)r\n"
"errno: %(errno)r"
#: oslo/concurrency/processutils.py:248
#, python-format
msgid "%r failed. Not Retrying."
msgstr ""
msgid "Got invalid arg log_errors: %r"
msgstr "Got invalid arg log_errors: %r"
#: oslo/concurrency/processutils.py:252
#, python-format
msgid "%r failed. Retrying."
msgstr "%r failed. Retrying."
msgid "Got unknown keyword args: %r"
msgstr "Got unknown keyword args: %r"
#: oslo/concurrency/processutils.py:299
msgid "Environment not supported over SSH"
msgstr "Environment not supported over SSH"
#, python-format
msgid "Running cmd (subprocess): %s"
msgstr "Running cmd (subprocess): %s"
#, python-format
msgid "Unable to acquire lock on `%(filename)s` due to %(exception)s"
msgstr "Unable to acquire lock on `%(filename)s` due to %(exception)s"
msgid "Unable to release an unacquired lock"
msgstr "Unable to release an unacquired lock"
msgid "Unexpected error while running command."
msgstr "Unexpected error while running command."
#: oslo/concurrency/processutils.py:303
msgid "process_input not supported over SSH"
msgstr "process_input not supported over SSH"

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: oslo.concurrency\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-03-09 06:08+0000\n"
"POT-Creation-Date: 2015-05-28 06:05+0000\n"
"PO-Revision-Date: 2015-03-08 16:34+0000\n"
"Last-Translator: Maxime COQUEREL <max.coquerel@gmail.com>\n"
"Language-Team: French (http://www.transifex.com/projects/p/osloconcurrency/"
@@ -21,12 +21,10 @@ msgstr ""
"Generated-By: Babel 1.3\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: oslo_concurrency/lockutils.py:217
#, python-format
msgid "Could not unlock the acquired lock `%s`"
msgstr "Impossible de libérer le verrou acquis %s"
#: oslo_concurrency/lockutils.py:223
#, python-format
msgid "Could not close the acquired file handle `%s`"
msgstr "Impossible de libérer le descripteur de fichier %s"
#, python-format
msgid "Could not unlock the acquired lock `%s`"
msgstr "Impossible de libérer le verrou acquis %s"

View File

@@ -21,12 +21,10 @@ msgstr ""
"Generated-By: Babel 1.3\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: oslo_concurrency/lockutils.py:181
#, python-format
msgid "Created lock path: %s"
msgstr "Chemin de verrou créé %s"
#: oslo_concurrency/lockutils.py:332
#, python-format
msgid "Failed to remove file %(file)s"
msgstr "Échec lors de la suppression du fichier %(file)s"

View File

@@ -9,75 +9,17 @@ msgid ""
msgstr ""
"Project-Id-Version: oslo.concurrency\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-03-09 06:08+0000\n"
"POT-Creation-Date: 2015-05-28 06:05+0000\n"
"PO-Revision-Date: 2015-03-08 16:41+0000\n"
"Last-Translator: Maxime COQUEREL <max.coquerel@gmail.com>\n"
"Language-Team: French "
"(http://www.transifex.com/projects/p/osloconcurrency/language/fr/)\n"
"Language-Team: French (http://www.transifex.com/projects/p/osloconcurrency/"
"language/fr/)\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
#: oslo_concurrency/lockutils.py:111
#, python-format
msgid "Unable to acquire lock on `%(filename)s` due to %(exception)s"
msgstr ""
"Impossible d'acquérir le verrou sur `%(filename)s` à cause de "
"%(exception)s"
#: oslo_concurrency/lockutils.py:208
msgid "Unable to release an unacquired lock"
msgstr "Impossible de libérer le verrou acquis %s"
#: oslo_concurrency/lockutils.py:671
msgid ""
"Calling lockutils directly is no longer supported. Please use the "
"lockutils-wrapper console script instead."
msgstr ""
"Lockutils appelant directement n'est plus pris en charge. Merci "
"d'utiliser le script de la console lockutils -wrapper à la place."
#: oslo_concurrency/processutils.py:69
msgid "Unexpected error while running command."
msgstr "Erreur inattendue lors de lexécution de la commande."
#: oslo_concurrency/processutils.py:72
#, python-format
msgid ""
"%(description)s\n"
"Command: %(cmd)s\n"
"Exit code: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
msgstr ""
"%(description)s\n"
"Commande: %(cmd)s\n"
"Code de sortie: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
#: oslo_concurrency/processutils.py:174
#, python-format
msgid "Got unknown keyword args: %r"
msgstr "Ags, mot clé inconnu: %r"
#: oslo_concurrency/processutils.py:177
#, python-format
msgid "Got invalid arg log_errors: %r"
msgstr "Argument reçu non valide log_errors: %r"
#: oslo_concurrency/processutils.py:183
msgid "Command requested root, but did not specify a root helper."
msgstr "La commande exigeait root, mais n'indiquait pas comment obtenir root."
#: oslo_concurrency/processutils.py:199
#, python-format
msgid "Running cmd (subprocess): %s"
msgstr "Exécution de la commande (sous-processus): %s"
#: oslo_concurrency/processutils.py:242
#, python-format
msgid ""
"%(desc)r\n"
@@ -92,7 +34,41 @@ msgstr ""
"stdout: %(stdout)r\n"
"stderr: %(stderr)r"
#: oslo_concurrency/processutils.py:251
#, python-format
msgid ""
"%(description)s\n"
"Command: %(cmd)s\n"
"Exit code: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
msgstr ""
"%(description)s\n"
"Commande: %(cmd)s\n"
"Code de sortie: %(exit_code)s\n"
"Stdout: %(stdout)r\n"
"Stderr: %(stderr)r"
#, python-format
msgid "%r failed. Not Retrying."
msgstr "Echec de %r. Nouvelle tentative."
#, python-format
msgid "%r failed. Retrying."
msgstr "Echec de %r. Nouvelle tentative."
msgid ""
"Calling lockutils directly is no longer supported. Please use the lockutils-"
"wrapper console script instead."
msgstr ""
"Lockutils appelant directement n'est plus pris en charge. Merci d'utiliser "
"le script de la console lockutils -wrapper à la place."
msgid "Command requested root, but did not specify a root helper."
msgstr "La commande exigeait root, mais n'indiquait pas comment obtenir root."
msgid "Environment not supported over SSH"
msgstr "Environnement non prise en charge sur SSH"
#, python-format
msgid ""
"Got an OSError\n"
@@ -103,21 +79,28 @@ msgstr ""
"commande: %(cmd)r\n"
"errno: %(errno)r"
#: oslo_concurrency/processutils.py:257
#, python-format
msgid "%r failed. Not Retrying."
msgstr "Echec de %r. Nouvelle tentative."
msgid "Got invalid arg log_errors: %r"
msgstr "Argument reçu non valide log_errors: %r"
#: oslo_concurrency/processutils.py:261
#, python-format
msgid "%r failed. Retrying."
msgstr "Echec de %r. Nouvelle tentative."
msgid "Got unknown keyword args: %r"
msgstr "Ags, mot clé inconnu: %r"
#: oslo_concurrency/processutils.py:308
msgid "Environment not supported over SSH"
msgstr "Environnement non prise en charge sur SSH"
#, python-format
msgid "Running cmd (subprocess): %s"
msgstr "Exécution de la commande (sous-processus): %s"
#, python-format
msgid "Unable to acquire lock on `%(filename)s` due to %(exception)s"
msgstr ""
"Impossible d'acquérir le verrou sur `%(filename)s` à cause de %(exception)s"
msgid "Unable to release an unacquired lock"
msgstr "Impossible de libérer le verrou acquis %s"
msgid "Unexpected error while running command."
msgstr "Erreur inattendue lors de lexécution de la commande."
#: oslo_concurrency/processutils.py:312
msgid "process_input not supported over SSH"
msgstr "process_input non pris en charge sur SSH"

View File

@@ -17,6 +17,7 @@ import contextlib
import errno
import logging
import os
import stat
import tempfile
from oslo_utils import excutils
@@ -24,15 +25,17 @@ from oslo_utils import excutils
LOG = logging.getLogger(__name__)
_FILE_CACHE = {}
DEFAULT_MODE = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO
def ensure_tree(path):
def ensure_tree(path, mode=DEFAULT_MODE):
"""Create a directory (and any ancestor directories required)
:param path: Directory to create
:param mode: Directory creation permissions
"""
try:
os.makedirs(path)
os.makedirs(path, mode)
except OSError as exc:
if exc.errno == errno.EEXIST:
if not os.path.isdir(path):
@@ -58,7 +61,7 @@ def read_cached_file(filename, force_reload=False):
cache_info = _FILE_CACHE.setdefault(filename, {})
if not cache_info or mtime > cache_info.get('mtime', 0):
LOG.debug("Reloading cached file %s" % filename)
LOG.debug("Reloading cached file %s", filename)
with open(filename) as fap:
cache_info['data'] = fap.read()
cache_info['mtime'] = mtime

View File

@@ -144,6 +144,9 @@ def execute(*cmd, **kwargs):
last attempt, and LOG_ALL_ERRORS requires
logging on each occurence of an error.
:type log_errors: integer.
:param binary: On Python 3, return stdout and stderr as bytes if
binary is True, as Unicode otherwise.
:type binary: boolean
:returns: (stdout, stderr) from process execution
:raises: :class:`UnknownArgumentError` on
receiving unknown arguments
@@ -163,6 +166,7 @@ def execute(*cmd, **kwargs):
shell = kwargs.pop('shell', False)
loglevel = kwargs.pop('loglevel', logging.DEBUG)
log_errors = kwargs.pop('log_errors', None)
binary = kwargs.pop('binary', False)
if isinstance(check_exit_code, bool):
ignore_exit_code = not check_exit_code
@@ -225,13 +229,24 @@ def execute(*cmd, **kwargs):
(sanitized_cmd, _returncode, end_time))
if not ignore_exit_code and _returncode not in check_exit_code:
(stdout, stderr) = result
if six.PY3:
stdout = os.fsdecode(stdout)
stderr = os.fsdecode(stderr)
sanitized_stdout = strutils.mask_password(stdout)
sanitized_stderr = strutils.mask_password(stderr)
raise ProcessExecutionError(exit_code=_returncode,
stdout=sanitized_stdout,
stderr=sanitized_stderr,
cmd=sanitized_cmd)
return result
if six.PY3 and not binary and result is not None:
(stdout, stderr) = result
# Decode from the locale using using the surrogateescape error
# handler (decoding cannot fail)
stdout = os.fsdecode(stdout)
stderr = os.fsdecode(stderr)
return (stdout, stderr)
else:
return result
except (ProcessExecutionError, OSError) as err:
# if we want to always log the errors or if this is
@@ -301,7 +316,8 @@ def trycmd(*args, **kwargs):
def ssh_execute(ssh, cmd, process_input=None,
addl_env=None, check_exit_code=True):
addl_env=None, check_exit_code=True,
binary=False):
sanitized_cmd = strutils.mask_password(cmd)
LOG.debug('Running cmd (SSH): %s', sanitized_cmd)
if addl_env:
@@ -317,24 +333,46 @@ def ssh_execute(ssh, cmd, process_input=None,
# NOTE(justinsb): This seems suspicious...
# ...other SSH clients have buffering issues with this approach
stdout = stdout_stream.read()
sanitized_stdout = strutils.mask_password(stdout)
stderr = stderr_stream.read()
sanitized_stderr = strutils.mask_password(stderr)
stdin_stream.close()
exit_status = channel.recv_exit_status()
if six.PY3:
# Decode from the locale using using the surrogateescape error handler
# (decoding cannot fail). Decode even if binary is True because
# mask_password() requires Unicode on Python 3
stdout = os.fsdecode(stdout)
stderr = os.fsdecode(stderr)
stdout = strutils.mask_password(stdout)
stderr = strutils.mask_password(stderr)
# exit_status == -1 if no exit code was returned
if exit_status != -1:
LOG.debug('Result was %s' % exit_status)
if check_exit_code and exit_status != 0:
raise ProcessExecutionError(exit_code=exit_status,
stdout=sanitized_stdout,
stderr=sanitized_stderr,
stdout=stdout,
stderr=stderr,
cmd=sanitized_cmd)
return (sanitized_stdout, sanitized_stderr)
if binary:
if six.PY2:
# On Python 2, stdout is a bytes string if mask_password() failed
# to decode it, or an Unicode string otherwise. Encode to the
# default encoding (ASCII) because mask_password() decodes from
# the same encoding.
if isinstance(stdout, unicode):
stdout = stdout.encode()
if isinstance(stderr, unicode):
stderr = stderr.encode()
else:
# fsencode() is the reverse operation of fsdecode()
stdout = os.fsencode(stdout)
stderr = os.fsencode(stderr)
return (stdout, stderr)
def get_worker_count():

View File

@@ -20,6 +20,7 @@ import logging
import multiprocessing
import os
import stat
import sys
import tempfile
import fixtures
@@ -29,6 +30,8 @@ import six
from oslo_concurrency import processutils
from oslotest import mockpatch
PROCESS_EXECUTION_ERROR_LOGGING_TEST = """#!/bin/bash
exit 41"""
@@ -40,6 +43,9 @@ echo onstdout --password='"secret"'
echo onstderr --password='"secret"' 1>&2
exit 38"""
# This byte sequence is undecodable from most encoding
UNDECODABLE_BYTES = b'[a\x80\xe9\xff]'
class UtilsTest(test_base.BaseTestCase):
# NOTE(jkoelker) Moar tests from nova need to be ported. But they
@@ -173,7 +179,7 @@ exit 1
out, err = processutils.execute('/usr/bin/env',
'sh', '-c', 'pwd',
cwd=tmpdir)
self.assertIn(six.b(tmpdir), out)
self.assertIn(tmpdir, out)
def test_check_exit_code_list(self):
processutils.execute('/usr/bin/env', 'sh', '-c', 'exit 101',
@@ -301,6 +307,19 @@ grep foo
env_vars = {'SUPER_UNIQUE_VAR': 'The answer is 42'}
out, err = processutils.execute('/usr/bin/env', env_variables=env_vars)
self.assertIsInstance(out, str)
self.assertIsInstance(err, str)
self.assertIn('SUPER_UNIQUE_VAR=The answer is 42', out)
def test_binary(self):
env_vars = {'SUPER_UNIQUE_VAR': 'The answer is 42'}
out, err = processutils.execute('/usr/bin/env',
env_variables=env_vars,
binary=True)
self.assertIsInstance(out, bytes)
self.assertIsInstance(err, bytes)
self.assertIn(b'SUPER_UNIQUE_VAR=The answer is 42', out)
@@ -321,6 +340,8 @@ grep foo
'something')
self.assertEqual(38, err.exit_code)
self.assertIsInstance(err.stdout, six.text_type)
self.assertIsInstance(err.stderr, six.text_type)
self.assertIn('onstdout --password="***"', err.stdout)
self.assertIn('onstderr --password="***"', err.stderr)
self.assertEqual(err.cmd, ' '.join([tmpfilename,
@@ -328,6 +349,72 @@ grep foo
'something']))
self.assertNotIn('secret', str(err))
def execute_undecodable_bytes(self, out_bytes, err_bytes,
exitcode=0, binary=False):
if six.PY3:
code = ';'.join(('import sys',
'sys.stdout.buffer.write(%a)' % out_bytes,
'sys.stdout.flush()',
'sys.stderr.buffer.write(%a)' % err_bytes,
'sys.stderr.flush()',
'sys.exit(%s)' % exitcode))
else:
code = ';'.join(('import sys',
'sys.stdout.write(%r)' % out_bytes,
'sys.stdout.flush()',
'sys.stderr.write(%r)' % err_bytes,
'sys.stderr.flush()',
'sys.exit(%s)' % exitcode))
return processutils.execute(sys.executable, '-c', code, binary=binary)
def check_undecodable_bytes(self, binary):
out_bytes = b'out: ' + UNDECODABLE_BYTES
err_bytes = b'err: ' + UNDECODABLE_BYTES
out, err = self.execute_undecodable_bytes(out_bytes, err_bytes,
binary=binary)
if six.PY3 and not binary:
self.assertEqual(out, os.fsdecode(out_bytes))
self.assertEqual(err, os.fsdecode(err_bytes))
else:
self.assertEqual(out, out_bytes)
self.assertEqual(err, err_bytes)
def test_undecodable_bytes(self):
self.check_undecodable_bytes(False)
def test_binary_undecodable_bytes(self):
self.check_undecodable_bytes(True)
def check_undecodable_bytes_error(self, binary):
out_bytes = b'out: password="secret1" ' + UNDECODABLE_BYTES
err_bytes = b'err: password="secret2" ' + UNDECODABLE_BYTES
exc = self.assertRaises(processutils.ProcessExecutionError,
self.execute_undecodable_bytes,
out_bytes, err_bytes, exitcode=1,
binary=binary)
out = exc.stdout
err = exc.stderr
out_bytes = b'out: password="***" ' + UNDECODABLE_BYTES
err_bytes = b'err: password="***" ' + UNDECODABLE_BYTES
if six.PY3:
# On Python 3, stdout and stderr attributes of
# ProcessExecutionError must always be Unicode
self.assertEqual(out, os.fsdecode(out_bytes))
self.assertEqual(err, os.fsdecode(err_bytes))
else:
# On Python 2, stdout and stderr attributes of
# ProcessExecutionError must always be bytes
self.assertEqual(out, out_bytes)
self.assertEqual(err, err_bytes)
def test_undecodable_bytes_error(self):
self.check_undecodable_bytes_error(False)
def test_binary_undecodable_bytes_error(self):
self.check_undecodable_bytes_error(True)
class ProcessExecutionErrorLoggingTest(test_base.BaseTestCase):
def setUp(self):
@@ -433,21 +520,23 @@ class FakeSshChannel(object):
return self.rc
class FakeSshStream(six.StringIO):
class FakeSshStream(six.BytesIO):
def setup_channel(self, rc):
self.channel = FakeSshChannel(rc)
class FakeSshConnection(object):
def __init__(self, rc):
def __init__(self, rc, out=b'stdout', err=b'stderr'):
self.rc = rc
self.out = out
self.err = err
def exec_command(self, cmd):
stdout = FakeSshStream('stdout')
stdout = FakeSshStream(self.out)
stdout.setup_channel(self.rc)
return (six.StringIO(),
return (six.BytesIO(),
stdout,
six.StringIO('stderr'))
six.BytesIO(self.err))
class SshExecuteTestCase(test_base.BaseTestCase):
@@ -462,9 +551,70 @@ class SshExecuteTestCase(test_base.BaseTestCase):
None, 'ls', process_input='important')
def test_works(self):
o, e = processutils.ssh_execute(FakeSshConnection(0), 'ls')
self.assertEqual('stdout', o)
self.assertEqual('stderr', e)
out, err = processutils.ssh_execute(FakeSshConnection(0), 'ls')
self.assertEqual('stdout', out)
self.assertEqual('stderr', err)
self.assertIsInstance(out, six.text_type)
self.assertIsInstance(err, six.text_type)
def test_binary(self):
o, e = processutils.ssh_execute(FakeSshConnection(0), 'ls',
binary=True)
self.assertEqual(b'stdout', o)
self.assertEqual(b'stderr', e)
self.assertIsInstance(o, bytes)
self.assertIsInstance(e, bytes)
def check_undecodable_bytes(self, binary):
out_bytes = b'out: ' + UNDECODABLE_BYTES
err_bytes = b'err: ' + UNDECODABLE_BYTES
conn = FakeSshConnection(0, out=out_bytes, err=err_bytes)
out, err = processutils.ssh_execute(conn, 'ls', binary=binary)
if six.PY3 and not binary:
self.assertEqual(out, os.fsdecode(out_bytes))
self.assertEqual(err, os.fsdecode(err_bytes))
else:
self.assertEqual(out, out_bytes)
self.assertEqual(err, err_bytes)
def test_undecodable_bytes(self):
self.check_undecodable_bytes(False)
def test_binary_undecodable_bytes(self):
self.check_undecodable_bytes(True)
def check_undecodable_bytes_error(self, binary):
out_bytes = b'out: password="secret1" ' + UNDECODABLE_BYTES
err_bytes = b'err: password="secret2" ' + UNDECODABLE_BYTES
conn = FakeSshConnection(1, out=out_bytes, err=err_bytes)
out_bytes = b'out: password="***" ' + UNDECODABLE_BYTES
err_bytes = b'err: password="***" ' + UNDECODABLE_BYTES
exc = self.assertRaises(processutils.ProcessExecutionError,
processutils.ssh_execute,
conn, 'ls',
binary=binary, check_exit_code=True)
out = exc.stdout
err = exc.stderr
if six.PY3:
# On Python 3, stdout and stderr attributes of
# ProcessExecutionError must always be Unicode
self.assertEqual(out, os.fsdecode(out_bytes))
self.assertEqual(err, os.fsdecode(err_bytes))
else:
# On Python 2, stdout and stderr attributes of
# ProcessExecutionError must always be bytes
self.assertEqual(out, out_bytes)
self.assertEqual(err, err_bytes)
def test_undecodable_bytes_error(self):
self.check_undecodable_bytes_error(False)
def test_binary_undecodable_bytes_error(self):
self.check_undecodable_bytes_error(True)
def test_fails(self):
self.assertRaises(processutils.ProcessExecutionError,
@@ -472,13 +622,13 @@ class SshExecuteTestCase(test_base.BaseTestCase):
def _test_compromising_ssh(self, rc, check):
fixture = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG))
fake_stdin = six.StringIO()
fake_stdin = six.BytesIO()
fake_stdout = mock.Mock()
fake_stdout.channel.recv_exit_status.return_value = rc
fake_stdout.read.return_value = 'password="secret"'
fake_stdout.read.return_value = b'password="secret"'
fake_stderr = six.StringIO('password="foobar"')
fake_stderr = six.BytesIO(b'password="foobar"')
command = 'ls --password="bar"'

View File

@@ -2,13 +2,12 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr>=0.6,!=0.7,<1.0
pbr>=0.11,<2.0
Babel>=1.3
iso8601>=0.1.9
fixtures>=0.3.14
oslo.config>=1.9.0 # Apache-2.0
oslo.i18n>=1.3.0 # Apache-2.0
oslo.utils>=1.2.0 # Apache-2.0
oslo.config>=1.11.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0
oslo.utils>=1.4.0 # Apache-2.0
posix_ipc
six>=1.9.0
retrying>=1.2.3,!=1.3.0 # Apache-2.0

View File

@@ -1,6 +1,6 @@
[metadata]
name = oslo.concurrency
summary = oslo.concurrency library
summary = Oslo Concurrency library
description-file =
README.rst
author = OpenStack
@@ -17,7 +17,7 @@ classifier =
Programming Language :: Python :: 2.7
Programming Language :: Python :: 2.6
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
[files]
packages =

View File

@@ -3,12 +3,13 @@
# process, which may cause wedges in the gate later.
hacking>=0.10.0,<0.11
oslotest>=1.2.0 # Apache-2.0
oslotest>=1.5.1 # Apache-2.0
coverage>=3.6
futures>=2.1.6
futures>=3.0
fixtures>=0.3.14
# These are needed for docs generation
oslosphinx>=2.2.0 # Apache-2.0
oslosphinx>=2.5.0 # Apache-2.0
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
eventlet>=0.16.1
eventlet>=0.17.3

View File

@@ -295,17 +295,22 @@ grep foo
env_vars = {'SUPER_UNIQUE_VAR': 'The answer is 42'}
out, err = processutils.execute('/usr/bin/env', env_variables=env_vars)
self.assertEqual(type(out), str)
self.assertEqual(type(err), str)
self.assertIn(b'SUPER_UNIQUE_VAR=The answer is 42', out)
self.assertIn('SUPER_UNIQUE_VAR=The answer is 42', out)
def test_as_root(self):
out, err = processutils.execute('a', 'b', 'c', run_as_root=True,
root_helper='echo')
# For the following two tests: processutils.execute() does not
# prepend the root_helper if we are already running with root privs,
# so add it as the first argument to be certain.
out, err = processutils.execute('echo', 'a', 'b', 'c',
run_as_root=True, root_helper='echo')
self.assertIn('a b c', six.text_type(out))
def test_as_root_via_shell(self):
out, err = processutils.execute('a b c', run_as_root=True,
out, err = processutils.execute('echo a b c', run_as_root=True,
root_helper='echo', shell=True)
self.assertIn('a b c', six.text_type(out))
@@ -327,6 +332,8 @@ grep foo
'something')
self.assertEqual(38, err.exit_code)
self.assertEqual(type(err.stdout), six.text_type)
self.assertEqual(type(err.stderr), six.text_type)
self.assertIn('onstdout --password="***"', err.stdout)
self.assertIn('onstderr --password="***"', err.stderr)
self.assertEqual(err.cmd, ' '.join([tmpfilename,
@@ -439,7 +446,7 @@ class FakeSshChannel(object):
return self.rc
class FakeSshStream(six.StringIO):
class FakeSshStream(six.BytesIO):
def setup_channel(self, rc):
self.channel = FakeSshChannel(rc)
@@ -449,11 +456,11 @@ class FakeSshConnection(object):
self.rc = rc
def exec_command(self, cmd):
stdout = FakeSshStream('stdout')
stdout = FakeSshStream(b'stdout')
stdout.setup_channel(self.rc)
return (six.StringIO(),
return (six.BytesIO(),
stdout,
six.StringIO('stderr'))
six.BytesIO(b'stderr'))
class SshExecuteTestCase(test_base.BaseTestCase):
@@ -471,6 +478,8 @@ class SshExecuteTestCase(test_base.BaseTestCase):
o, e = processutils.ssh_execute(FakeSshConnection(0), 'ls')
self.assertEqual('stdout', o)
self.assertEqual('stderr', e)
self.assertEqual(type(o), six.text_type)
self.assertEqual(type(e), six.text_type)
def test_fails(self):
self.assertRaises(processutils.ProcessExecutionError,
@@ -478,13 +487,13 @@ class SshExecuteTestCase(test_base.BaseTestCase):
def _test_compromising_ssh(self, rc, check):
fixture = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG))
fake_stdin = six.StringIO()
fake_stdin = six.BytesIO()
fake_stdout = mock.Mock()
fake_stdout.channel.recv_exit_status.return_value = rc
fake_stdout.read.return_value = 'password="secret"'
fake_stdout.read.return_value = b'password="secret"'
fake_stderr = six.StringIO('password="foobar"')
fake_stderr = six.BytesIO(b'password="foobar"')
command = 'ls --password="bar"'

View File

@@ -1,91 +0,0 @@
#!/bin/bash
#
# Run cross-project tests
#
# Usage:
#
# run_cross_tests.sh project_dir venv
# Fail the build if any command fails
set -e
project_dir="$1"
venv="$2"
if [ -z "$project_dir" -o -z "$venv" ]
then
cat - <<EOF
ERROR: Missing argument(s)
Usage:
$0 PROJECT_DIR VIRTUAL_ENV
Example, run the python 2.7 tests for python-neutronclient:
$0 /opt/stack/python-neutronclient py27
EOF
exit 1
fi
# Set up the virtualenv without running the tests
(cd $project_dir && tox --notest -e $venv)
tox_envbin=$project_dir/.tox/$venv/bin
our_name=$(python setup.py --name)
# Replace the pip-installed package with the version in our source
# tree. Look to see if we are already installed before trying to
# uninstall ourselves, to avoid failures from packages that do not use us
# yet.
if $tox_envbin/pip freeze | grep -q $our_name
then
$tox_envbin/pip uninstall -y $our_name
fi
$tox_envbin/pip install -U .
# Run the tests
(cd $project_dir && tox -e $venv)
result=$?
# The below checks are modified from
# openstack-infra/config/modules/jenkins/files/slave_scripts/run-unittests.sh.
# They expect to be run in the project being tested.
cd $project_dir
echo "Begin pip freeze output from test virtualenv:"
echo "======================================================================"
.tox/$venv/bin/pip freeze
echo "======================================================================"
# We only want to run the next check if the tool is installed, so look
# for it before continuing.
if [ -f /usr/local/jenkins/slave_scripts/subunit2html.py -a -d ".testrepository" ] ; then
if [ -f ".testrepository/0.2" ] ; then
cp .testrepository/0.2 ./subunit_log.txt
elif [ -f ".testrepository/0" ] ; then
.tox/$venv/bin/subunit-1to2 < .testrepository/0 > ./subunit_log.txt
fi
.tox/$venv/bin/python /usr/local/jenkins/slave_scripts/subunit2html.py ./subunit_log.txt testr_results.html
gzip -9 ./subunit_log.txt
gzip -9 ./testr_results.html
export PYTHON=.tox/$venv/bin/python
set -e
rancount=$(.tox/$venv/bin/testr last | sed -ne 's/Ran \([0-9]\+\).*tests in.*/\1/p')
if [ "$rancount" -eq "0" ] ; then
echo
echo "Zero tests were run. At least one test should have been run."
echo "Failing this test as a result"
echo
exit 1
fi
fi
# If we make it this far, report status based on the tests that were
# run.
exit $result

View File

@@ -19,10 +19,6 @@ commands =
lockutils-wrapper python setup.py testr --slowest --testr-args='{posargs}'
env TEST_EVENTLET=1 lockutils-wrapper python setup.py testr --slowest --testr-args='{posargs}'
[testenv:py33]
commands =
lockutils-wrapper python setup.py testr --slowest --testr-args='{posargs}'
[testenv:py34]
commands =
lockutils-wrapper python setup.py testr --slowest --testr-args='{posargs}'