Hello community,

here is the log from the commit of package python-oslo.service for 
openSUSE:Factory checked in at 2016-06-02 09:38:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-oslo.service (Old)
 and      /work/SRC/openSUSE:Factory/.python-oslo.service.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-oslo.service"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-oslo.service/python-oslo.service.changes  
2016-05-04 08:19:59.000000000 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-oslo.service.new/python-oslo.service.changes 
    2016-06-02 09:38:58.000000000 +0200
@@ -1,0 +2,23 @@
+Tue May 31 13:49:31 UTC 2016 - [email protected]
+
+- update to 1.11.0
+  * Trivial: ignore openstack/common in flake8 exclude list
+- 1.10.0
+  * [Trivial] Remove executable privilege of doc/source/conf.py
+- 1.9.0
+  * Updated from global requirements
+  * Offer mutate_config_files
+  * Make _spawn_service more flexible
+  * Remove direct dependency on babel
+  * Updated from global requirements
+  * Updated from global requirements
+  * Updated from global requirements
+  * Updated from global requirements
+  * Fix argument type for _sd_notify() on python3
+  * Use a timeutils.StopWatch for cancel timing
+  * Add ability to cancel Threads and ThreadGroups
+  * Exception: message need '_' function
+  * Fix Heartbeats stop when time is changed
+  * Updated from global requirements
+
+-------------------------------------------------------------------

Old:
----
  oslo.service-1.8.0.tar.gz

New:
----
  oslo.service-1.11.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-oslo.service.spec ++++++
--- /var/tmp/diff_new_pack.zwag7s/_old  2016-06-02 09:38:59.000000000 +0200
+++ /var/tmp/diff_new_pack.zwag7s/_new  2016-06-02 09:38:59.000000000 +0200
@@ -16,16 +16,16 @@
 #
 
 
-%define version_unconverted 1.8.0
+%define version_unconverted 1.11.0
 
 Name:           python-oslo.service
-Version:        1.8.0
+Version:        1.11.0
 Release:        0
 Summary:        Oslo service library
 License:        Apache-2.0
 Group:          Development/Languages/Python
 Url:            http://launchpad.net/oslo
-Source:         
https://pypi.python.org/packages/source/o/oslo.service/oslo.service-%{version}.tar.gz
+Source:         
https://pypi.python.org/packages/90/4c/c52ea2f34c88594fbe6bfe7b2bf0c287fd953c9058aa93cae41acc66c5a9/oslo.service-%{version}.tar.gz
 BuildRequires:  openstack-suse-macros
 BuildRequires:  python-devel
 BuildRequires:  python-pbr

++++++ oslo.service-1.8.0.tar.gz -> oslo.service-1.11.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/AUTHORS 
new/oslo.service-1.11.0/AUTHORS
--- old/oslo.service-1.8.0/AUTHORS      2016-03-28 05:09:29.000000000 +0200
+++ new/oslo.service-1.11.0/AUTHORS     2016-05-25 00:10:02.000000000 +0200
@@ -1,9 +1,11 @@
 Accela Zhao <[email protected]>
 Alan Pevec <[email protected]>
+Alberto Murillo <[email protected]>
 Alessandro Pilotti <[email protected]>
 Alex Gaynor <[email protected]>
 Alex Holden <[email protected]>
 Alexander Gorodnev <[email protected]>
+Alexis Lee <[email protected]>
 Andreas Jaeger <[email protected]>
 Andreas Jaeger <[email protected]>
 Angus Salkeld <[email protected]>
@@ -53,6 +55,7 @@
 John L. Villalovos <[email protected]>
 Joshua Harlow <[email protected]>
 Joshua Harlow <[email protected]>
+Joshua Harlow <[email protected]>
 Julien Danjou <[email protected]>
 Kevin L. Mitchell <[email protected]>
 Kiall Mac Innes <[email protected]>
@@ -86,6 +89,7 @@
 Tom Cammann <[email protected]>
 Victor Sergeyev <[email protected]>
 Victor Stinner <[email protected]>
+ZhiQiang Fan <[email protected]>
 Zhongyue Luo <[email protected]>
 apporc <[email protected]>
 fujioka yuuichi <[email protected]>
@@ -98,3 +102,4 @@
 sonu.kumar <[email protected]>
 stanzgy <[email protected]>
 venkatamahesh <[email protected]>
+zwei <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/ChangeLog 
new/oslo.service-1.11.0/ChangeLog
--- old/oslo.service-1.8.0/ChangeLog    2016-03-28 05:09:29.000000000 +0200
+++ new/oslo.service-1.11.0/ChangeLog   2016-05-25 00:10:02.000000000 +0200
@@ -1,11 +1,33 @@
 CHANGES
 =======
 
-1.8.0
+1.11.0
+------
+
+* Trivial: ignore openstack/common in flake8 exclude list
+
+1.10.0
+------
+
+* [Trivial] Remove executable privilege of doc/source/conf.py
+
+1.9.0
 -----
 
 * Updated from global requirements
-* Update .gitreview for stable/mitaka
+* Offer mutate_config_files
+* Make _spawn_service more flexible
+* Remove direct dependency on babel
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Fix argument type for _sd_notify() on python3
+* Use a timeutils.StopWatch for cancel timing
+* Add ability to cancel Threads and ThreadGroups
+* Exception: message need '_' function
+* Fix Heartbeats stop when time is changed
+* Updated from global requirements
 
 1.7.0
 -----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/PKG-INFO 
new/oslo.service-1.11.0/PKG-INFO
--- old/oslo.service-1.8.0/PKG-INFO     2016-03-28 05:09:30.000000000 +0200
+++ new/oslo.service-1.11.0/PKG-INFO    2016-05-25 00:10:03.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: oslo.service
-Version: 1.8.0
+Version: 1.11.0
 Summary: oslo.service library
 Home-page: http://wiki.openstack.org/wiki/Oslo#oslo.service
 Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo.service.egg-info/PKG-INFO 
new/oslo.service-1.11.0/oslo.service.egg-info/PKG-INFO
--- old/oslo.service-1.8.0/oslo.service.egg-info/PKG-INFO       2016-03-28 
05:09:29.000000000 +0200
+++ new/oslo.service-1.11.0/oslo.service.egg-info/PKG-INFO      2016-05-25 
00:10:02.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: oslo.service
-Version: 1.8.0
+Version: 1.11.0
 Summary: oslo.service library
 Home-page: http://wiki.openstack.org/wiki/Oslo#oslo.service
 Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo.service.egg-info/pbr.json 
new/oslo.service-1.11.0/oslo.service.egg-info/pbr.json
--- old/oslo.service-1.8.0/oslo.service.egg-info/pbr.json       2016-03-28 
05:09:29.000000000 +0200
+++ new/oslo.service-1.11.0/oslo.service.egg-info/pbr.json      2016-05-25 
00:10:02.000000000 +0200
@@ -1 +1 @@
-{"git_version": "427b8ad", "is_release": true}
\ No newline at end of file
+{"is_release": true, "git_version": "43343b4"}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oslo.service-1.8.0/oslo.service.egg-info/requires.txt 
new/oslo.service-1.11.0/oslo.service.egg-info/requires.txt
--- old/oslo.service-1.8.0/oslo.service.egg-info/requires.txt   2016-03-28 
05:09:29.000000000 +0200
+++ new/oslo.service-1.11.0/oslo.service.egg-info/requires.txt  2016-05-25 
00:10:02.000000000 +0200
@@ -1,19 +1,16 @@
-Babel>=1.3
 WebOb>=1.2.3
 eventlet!=0.18.3,>=0.18.2
 greenlet>=0.3.2
 monotonic>=0.6
 oslo.utils>=3.5.0
 oslo.concurrency>=3.5.0
-oslo.config>=3.7.0
+oslo.config>=3.9.0
 oslo.log>=1.14.0
 six>=1.9.0
 oslo.i18n>=2.1.0
 PasteDeploy>=1.5.0
+Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3
 Paste
 
 [:(python_version!='2.7')]
-Routes!=2.0,>=1.12.3
-
-[:(python_version=='2.7')]
-Routes!=2.0,!=2.1,>=1.12.3
+Routes!=2.0,!=2.3.0,>=1.12.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo_service/__init__.py 
new/oslo.service-1.11.0/oslo_service/__init__.py
--- old/oslo.service-1.8.0/oslo_service/__init__.py     2016-03-28 
05:07:08.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/__init__.py    2016-05-25 
00:08:22.000000000 +0200
@@ -0,0 +1,41 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+
+import eventlet.patcher
+import monotonic
+from oslo_log import log as logging
+
+time = eventlet.patcher.original('time')
+
+
+LOG = logging.getLogger(__name__)
+
+if hasattr(time, 'monotonic'):
+    # Use builtin monotonic clock, Python 3.3+
+    _monotonic = time.monotonic
+else:
+    _monotonic = monotonic.monotonic
+
+
+def service_hub():
+    # NOTE(dims): Add a custom impl for EVENTLET_HUB, so we can
+    # override the clock used in the eventlet hubs. The default
+    # uses time.time() and we need to use a monotonic timer
+    # to ensure that things like loopingcall work properly.
+    hub = eventlet.hubs.get_default_hub().Hub()
+    hub.clock = _monotonic
+    return hub
+
+
+os.environ['EVENTLET_HUB'] = 'oslo_service:service_hub'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo_service/eventlet_backdoor.py 
new/oslo.service-1.11.0/oslo_service/eventlet_backdoor.py
--- old/oslo.service-1.8.0/oslo_service/eventlet_backdoor.py    2016-03-28 
05:07:08.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/eventlet_backdoor.py   2016-05-25 
00:08:22.000000000 +0200
@@ -28,7 +28,7 @@
 import eventlet.backdoor
 import greenlet
 
-from oslo_service._i18n import _LI
+from oslo_service._i18n import _LI, _
 from oslo_service import _options
 
 
@@ -37,8 +37,8 @@
 
 class EventletBackdoorConfigValueError(Exception):
     def __init__(self, port_range, help_msg, ex):
-        msg = ('Invalid backdoor_port configuration %(range)s: %(ex)s. '
-               '%(help)s' %
+        msg = (_('Invalid backdoor_port configuration %(range)s: %(ex)s. '
+               '%(help)s') %
                {'range': port_range, 'ex': ex, 'help': help_msg})
         super(EventletBackdoorConfigValueError, self).__init__(msg)
         self.port_range = port_range
@@ -220,7 +220,7 @@
 
     where_running_thread = _initialize_if_enabled(conf)
     if not where_running_thread:
-        raise RuntimeError("Did not create backdoor at requested location")
+        raise RuntimeError(_("Did not create backdoor at requested location"))
     else:
         _where_running, thread = where_running_thread
         thread.wait()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo_service/loopingcall.py 
new/oslo.service-1.11.0/oslo_service/loopingcall.py
--- old/oslo.service-1.8.0/oslo_service/loopingcall.py  2016-03-28 
05:07:08.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/loopingcall.py 2016-05-25 
00:08:22.000000000 +0200
@@ -263,9 +263,9 @@
     """
 
     _RNG = random.SystemRandom()
-    _KIND = 'Dynamic backoff interval looping call'
-    _RUN_ONLY_ONE_MESSAGE = ("A dynamic backoff interval looping call can"
-                             " only run one function at a time")
+    _KIND = _('Dynamic backoff interval looping call')
+    _RUN_ONLY_ONE_MESSAGE = _("A dynamic backoff interval looping call can"
+                              " only run one function at a time")
 
     def __init__(self, f=None, *args, **kw):
         super(BackOffLoopingCall, self).__init__(f=f, *args, **kw)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo_service/service.py 
new/oslo.service-1.11.0/oslo_service/service.py
--- old/oslo.service-1.8.0/oslo_service/service.py      2016-03-28 
05:07:08.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/service.py     2016-05-25 
00:08:22.000000000 +0200
@@ -35,7 +35,7 @@
 
 from oslo_concurrency import lockutils
 from oslo_service import eventlet_backdoor
-from oslo_service._i18n import _LE, _LI, _LW
+from oslo_service._i18n import _LE, _LI, _LW, _
 from oslo_service import _options
 from oslo_service import systemd
 from oslo_service import threadgroup
@@ -43,6 +43,8 @@
 
 LOG = logging.getLogger(__name__)
 
+_LAUNCHER_RESTART_METHODS = ['reload', 'mutate']
+
 
 def list_opts():
     """Entry point for oslo-config-generator."""
@@ -81,7 +83,7 @@
 
 def _check_service_base(service):
     if not isinstance(service, ServiceBase):
-        raise TypeError("Service %(service)s must an instance of %(base)s!"
+        raise TypeError(_("Service %(service)s must an instance of %(base)s!")
                         % {'service': service, 'base': ServiceBase})
 
 
@@ -182,9 +184,12 @@
 class Launcher(object):
     """Launch one or more services and wait for them to complete."""
 
-    def __init__(self, conf):
+    def __init__(self, conf, restart_method='reload'):
         """Initialize the service launcher.
 
+        :param restart_method: If 'reload', calls reload_config_files on
+            SIGHUP. If 'mutate', calls mutate_config_files on SIGHUP. Other
+            values produce a ValueError.
         :returns: None
 
         """
@@ -193,15 +198,23 @@
         self.services = Services()
         self.backdoor_port = (
             eventlet_backdoor.initialize_if_enabled(self.conf))
+        self.restart_method = restart_method
+        if restart_method not in _LAUNCHER_RESTART_METHODS:
+            raise ValueError(_("Invalid restart_method: %s") % restart_method)
 
-    def launch_service(self, service):
+    def launch_service(self, service, workers=1):
         """Load and start the given service.
 
         :param service: The service you would like to start, must be an
                         instance of :class:`oslo_service.service.ServiceBase`
+        :param workers: This param makes this method compatible with
+                        ProcessLauncher.launch_service. It must be None, 1 or
+                        omitted.
         :returns: None
 
         """
+        if workers is not None and workers != 1:
+            raise ValueError(_("Launcher asked to start multiple workers"))
         _check_service_base(service)
         service.backdoor_port = self.backdoor_port
         self.services.add(service)
@@ -225,10 +238,14 @@
     def restart(self):
         """Reload config files and restart service.
 
-        :returns: None
+        :returns: The return value from reload_config_files or
+        mutate_config_files, according to the restart_method.
 
         """
-        self.conf.reload_config_files()
+        if self.restart_method == 'reload':
+            self.conf.reload_config_files()
+        elif self.restart_method == 'mutate':
+            self.conf.mutate_config_files()
         self.services.restart()
 
 
@@ -240,12 +257,14 @@
 
 class ServiceLauncher(Launcher):
     """Runs one or more service in a parent process."""
-    def __init__(self, conf):
+    def __init__(self, conf, restart_method='reload'):
         """Constructor.
 
         :param conf: an instance of ConfigOpts
+        :param restart_method: passed to super
         """
-        super(ServiceLauncher, self).__init__(conf)
+        super(ServiceLauncher, self).__init__(
+            conf, restart_method=restart_method)
         self.signal_handler = SignalHandler()
 
     def _graceful_shutdown(self, *args):
@@ -326,12 +345,15 @@
 class ProcessLauncher(object):
     """Launch a service with a given number of workers."""
 
-    def __init__(self, conf, wait_interval=0.01):
+    def __init__(self, conf, wait_interval=0.01, restart_method='reload'):
         """Constructor.
 
         :param conf: an instance of ConfigOpts
         :param wait_interval: The interval to sleep for between checks
                               of child process exit.
+        :param restart_method: If 'reload', calls reload_config_files on
+            SIGHUP. If 'mutate', calls mutate_config_files on SIGHUP. Other
+            values produce a ValueError.
         """
         self.conf = conf
         conf.register_opts(_options.service_opts)
@@ -344,6 +366,9 @@
         self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r')
         self.signal_handler = SignalHandler()
         self.handle_signal()
+        self.restart_method = restart_method
+        if restart_method not in _LAUNCHER_RESTART_METHODS:
+            raise ValueError(_("Invalid restart_method: %s") % restart_method)
 
     def handle_signal(self):
         """Add instance's signal handlers to class handlers."""
@@ -440,7 +465,7 @@
         # Reseed random number generator
         random.seed()
 
-        launcher = Launcher(self.conf)
+        launcher = Launcher(self.conf, restart_method=self.restart_method)
         launcher.launch_service(service)
         return launcher
 
@@ -555,7 +580,10 @@
                 if not _is_sighup_and_daemon(self.sigcaught):
                     break
 
-                self.conf.reload_config_files()
+                if self.restart_method == 'reload':
+                    self.conf.reload_config_files()
+                elif self.restart_method == 'mutate':
+                    self.conf.mutate_config_files()
                 for service in set(
                         [wrap.service for wrap in self.children.values()]):
                     service.reset()
@@ -685,24 +713,26 @@
             done.wait()
 
 
-def launch(conf, service, workers=1):
+def launch(conf, service, workers=1, restart_method='reload'):
     """Launch a service with a given number of workers.
 
     :param conf: an instance of ConfigOpts
     :param service: a service to launch, must be an instance of
            :class:`oslo_service.service.ServiceBase`
     :param workers: a number of processes in which a service will be running
+    :param restart_method: Passed to the constructed launcher. If 'reload', the
+        launcher will call reload_config_files on SIGHUP. If 'mutate', it will
+        call mutate_config_files on SIGHUP. Other values produce a ValueError.
     :returns: instance of a launcher that was used to launch the service
     """
 
     if workers is not None and workers <= 0:
-        raise ValueError("Number of workers should be positive!")
+        raise ValueError(_("Number of workers should be positive!"))
 
     if workers is None or workers == 1:
-        launcher = ServiceLauncher(conf)
-        launcher.launch_service(service)
+        launcher = ServiceLauncher(conf, restart_method=restart_method)
     else:
-        launcher = ProcessLauncher(conf)
-        launcher.launch_service(service, workers=workers)
+        launcher = ProcessLauncher(conf, restart_method=restart_method)
+    launcher.launch_service(service, workers=workers)
 
     return launcher
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo_service/systemd.py 
new/oslo.service-1.11.0/oslo_service/systemd.py
--- old/oslo.service-1.8.0/oslo_service/systemd.py      2016-03-28 
05:07:07.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/systemd.py     2016-05-25 
00:08:22.000000000 +0200
@@ -53,7 +53,7 @@
     For details see
     http://www.freedesktop.org/software/systemd/man/sd_notify.html
     """
-    _sd_notify(False, 'READY=1')
+    _sd_notify(False, b'READY=1')
 
 
 def notify_once():
@@ -64,7 +64,7 @@
     This method removes the NOTIFY_SOCKET environment variable to ensure
     notification is sent only once.
     """
-    _sd_notify(True, 'READY=1')
+    _sd_notify(True, b'READY=1')
 
 
 def onready(notify_socket, timeout):
@@ -86,7 +86,7 @@
             msg = sock.recv(512)
         except socket.timeout:
             return 2
-        if 'READY=1' in msg:
+        if b'READY=1' == msg:
             return 0
         else:
             return 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oslo.service-1.8.0/oslo_service/tests/test_loopingcall.py 
new/oslo.service-1.11.0/oslo_service/tests/test_loopingcall.py
--- old/oslo.service-1.8.0/oslo_service/tests/test_loopingcall.py       
2016-03-28 05:07:08.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/tests/test_loopingcall.py      
2016-05-25 00:08:22.000000000 +0200
@@ -12,12 +12,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import eventlet
 from eventlet.green import threading as greenthreading
 import mock
 from oslotest import base as test_base
 
+import oslo_service
 from oslo_service import loopingcall
 
+threading = eventlet.patcher.original('threading')
+time = eventlet.patcher.original('time')
+
 
 class LoopingCallTestCase(test_base.BaseTestCase):
 
@@ -32,6 +37,61 @@
         timer = loopingcall.FixedIntervalLoopingCall(_raise_it)
         self.assertTrue(timer.start(interval=0.5).wait())
 
+    def test_monotonic_timer(self):
+        def _raise_it():
+            clock = eventlet.hubs.get_hub().clock
+            ok = (clock == oslo_service._monotonic)
+            raise loopingcall.LoopingCallDone(ok)
+
+        timer = loopingcall.FixedIntervalLoopingCall(_raise_it)
+        self.assertTrue(timer.start(interval=0.5).wait())
+
+    def test_eventlet_clock(self):
+        # Make sure that by default the oslo_service.service_hub() kicks in,
+        # test in the main thread
+        hub = eventlet.hubs.get_hub()
+        self.assertEqual(hub.clock,
+                         oslo_service._monotonic)
+
+    def test_eventlet_use_hub_override(self):
+        ns = {}
+
+        def task():
+            try:
+                self._test_eventlet_use_hub_override()
+            except Exception as exc:
+                ns['result'] = exc
+            else:
+                ns['result'] = 'ok'
+
+        # test overriding the hub of in a new thread to not modify the hub
+        # of the main thread
+        thread = threading.Thread(target=task)
+        thread.start()
+        thread.join()
+        self.assertEqual(ns['result'], 'ok')
+
+    def _test_eventlet_use_hub_override(self):
+        # Make sure that by default the
+        # oslo_service.service_hub() kicks in
+        old_clock = eventlet.hubs.get_hub().clock
+        self.assertEqual(old_clock,
+                         oslo_service._monotonic)
+
+        # eventlet will use time.monotonic() by default, same clock than
+        # oslo.service_hub():
+        # https://github.com/eventlet/eventlet/pull/303
+        if not hasattr(time, 'monotonic'):
+            # If anyone wants to override it
+            try:
+                eventlet.hubs.use_hub('poll')
+            except Exception:
+                eventlet.hubs.use_hub('selects')
+
+            # then we get a new clock and the override works fine too!
+            clock = eventlet.hubs.get_hub().clock
+            self.assertNotEqual(old_clock, clock)
+
     def test_return_false(self):
         def _raise_it():
             raise loopingcall.LoopingCallDone(False)
@@ -132,6 +192,15 @@
 
         timer = loopingcall.DynamicLoopingCall(_raise_it)
         self.assertTrue(timer.start().wait())
+
+    def test_monotonic_timer(self):
+        def _raise_it():
+            clock = eventlet.hubs.get_hub().clock
+            ok = (clock == oslo_service._monotonic)
+            raise loopingcall.LoopingCallDone(ok)
+
+        timer = loopingcall.DynamicLoopingCall(_raise_it)
+        self.assertTrue(timer.start().wait())
 
     def test_no_double_start(self):
         wait_ev = greenthreading.Event()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oslo.service-1.8.0/oslo_service/tests/test_service.py 
new/oslo.service-1.11.0/oslo_service/tests/test_service.py
--- old/oslo.service-1.8.0/oslo_service/tests/test_service.py   2016-03-28 
05:07:07.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/tests/test_service.py  2016-05-25 
00:08:22.000000000 +0200
@@ -84,8 +84,8 @@
 
     def _spawn_service(self,
                        workers=1,
-                       service_to_launch=ServiceWithTimer,
-                       *args, **kwargs):
+                       service_maker=None,
+                       launcher_maker=None):
         self.workers = workers
         pid = os.fork()
         if pid == 0:
@@ -99,8 +99,12 @@
             # os._exit() which doesn't have this problem.
             status = 0
             try:
-                serv = service_to_launch(*args, **kwargs)
-                launcher = service.launch(self.conf, serv, workers=workers)
+                serv = service_maker() if service_maker else ServiceWithTimer()
+                if launcher_maker:
+                    launcher = launcher_maker()
+                    launcher.launch_service(serv, workers=workers)
+                else:
+                    launcher = service.launch(self.conf, serv, workers=workers)
                 status = launcher.wait()
             except SystemExit as exc:
                 status = exc.code
@@ -212,7 +216,8 @@
         self.assertEqual(os.WEXITSTATUS(status), 0)
 
     def test_crashed_service(self):
-        self.pid = self._spawn_service(service_to_launch=ServiceCrashOnStart)
+        service_maker = lambda: ServiceCrashOnStart()
+        self.pid = self._spawn_service(service_maker=service_maker)
         status = self._reap_test()
         self.assertTrue(os.WIFEXITED(status))
         self.assertEqual(os.WEXITSTATUS(status), 1)
@@ -251,8 +256,8 @@
 
     def _spawn(self):
         ready_event = multiprocessing.Event()
-        self.pid = self._spawn_service(workers=1,
-                                       ready_event=ready_event)
+        service_maker = lambda: ServiceWithTimer(ready_event=ready_event)
+        self.pid = self._spawn_service(service_maker=service_maker)
         return ready_event
 
     def test_service_restart(self):
@@ -279,6 +284,58 @@
         self.assertTrue(os.WIFEXITED(status))
         self.assertEqual(os.WEXITSTATUS(status), 0)
 
+    def test_mutate_hook_service_launcher(self):
+        """Test mutate_config_files is called by ServiceLauncher on SIGHUP.
+
+        Not using _spawn_service because ServiceLauncher doesn't fork and it's
+        simplest to stay all in one process.
+        """
+        mutate = multiprocessing.Event()
+        self.conf.register_mutate_hook(lambda c, f: mutate.set())
+        launcher = service.launch(
+            self.conf, ServiceWithTimer(), restart_method='mutate')
+
+        self.assertFalse(mutate.is_set(), "Hook was called too early")
+        launcher.restart()
+        self.assertTrue(mutate.is_set(), "Hook wasn't called")
+
+    def test_mutate_hook_process_launcher(self):
+        """Test mutate_config_files is called by ProcessLauncher on SIGHUP.
+
+        Forks happen in _spawn_service and ProcessLauncher. So we get three
+        tiers of processes, the top tier being the test process. self.pid
+        refers to the middle tier, which represents our application. Both
+        service_maker and launcher_maker execute in the middle tier. The bottom
+        tier is the workers.
+
+        The behavior we want is that when the application (middle tier)
+        receives a SIGHUP, it catches that, calls mutate_config_files and
+        relaunches all the workers. This causes them to inherit the mutated
+        config.
+        """
+        mutate = multiprocessing.Event()
+        ready = multiprocessing.Event()
+
+        def service_maker():
+            self.conf.register_mutate_hook(lambda c, f: mutate.set())
+            return ServiceWithTimer(ready)
+
+        def launcher_maker():
+            return service.ProcessLauncher(self.conf, restart_method='mutate')
+
+        self.pid = self._spawn_service(1, service_maker, launcher_maker)
+
+        timeout = 5
+        ready.wait(timeout)
+        self.assertTrue(ready.is_set(), 'Service never became ready')
+        ready.clear()
+
+        self.assertFalse(mutate.is_set(), "Hook was called too early")
+        os.kill(self.pid, signal.SIGHUP)
+        ready.wait(timeout)
+        self.assertTrue(ready.is_set(), 'Service never back after SIGHUP')
+        self.assertTrue(mutate.is_set(), "Hook wasn't called")
+
 
 class _Service(service.Service):
     def __init__(self):
@@ -315,7 +372,7 @@
     def _test_launch_single(self, workers, mock_launch):
         svc = service.Service()
         service.launch(self.conf, svc, workers=workers)
-        mock_launch.assert_called_with(svc)
+        mock_launch.assert_called_with(svc, workers=workers)
 
     def test_launch_none(self):
         self._test_launch_single(None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oslo.service-1.8.0/oslo_service/tests/test_systemd.py 
new/oslo.service-1.11.0/oslo_service/tests/test_systemd.py
--- old/oslo.service-1.8.0/oslo_service/tests/test_systemd.py   2016-03-28 
05:07:07.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/tests/test_systemd.py  2016-05-25 
00:08:22.000000000 +0200
@@ -45,7 +45,7 @@
                 self.closed = True
 
             def sendall(fs, data):
-                if data == 'READY=1':
+                if data == b'READY=1':
                     self.ready = True
 
         with mock.patch.object(socket, 'socket', new=FakeSocket):
@@ -67,7 +67,7 @@
 
     @mock.patch("socket.socket")
     def test_onready(self, sock_mock):
-        recv_results = ['READY=1', '', socket.timeout]
+        recv_results = [b'READY=1', '', socket.timeout]
         expected_results = [0, 1, 2]
         for recv, expected in zip(recv_results, expected_results):
             if recv == socket.timeout:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oslo.service-1.8.0/oslo_service/tests/test_threadgroup.py 
new/oslo.service-1.11.0/oslo_service/tests/test_threadgroup.py
--- old/oslo.service-1.8.0/oslo_service/tests/test_threadgroup.py       
2016-03-28 05:07:08.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/tests/test_threadgroup.py      
2016-05-25 00:08:22.000000000 +0200
@@ -93,6 +93,35 @@
         self.assertEqual(0, len(self.tg.threads))
         self.assertTrue(end_time - start_time >= 1)
 
+    def test_cancel_early(self):
+
+        def foo(*args, **kwargs):
+            time.sleep(1)
+        self.tg.add_thread(foo, 'arg', kwarg='kwarg')
+        self.tg.cancel()
+
+        self.assertEqual(0, len(self.tg.threads))
+
+    def test_cancel_late(self):
+
+        def foo(*args, **kwargs):
+            time.sleep(0.3)
+        self.tg.add_thread(foo, 'arg', kwarg='kwarg')
+        time.sleep(0)
+        self.tg.cancel()
+
+        self.assertEqual(1, len(self.tg.threads))
+
+    def test_cancel_timeout(self):
+
+        def foo(*args, **kwargs):
+            time.sleep(0.3)
+        self.tg.add_thread(foo, 'arg', kwarg='kwarg')
+        time.sleep(0)
+        self.tg.cancel(timeout=0.2, wait_time=0.1)
+
+        self.assertEqual(0, len(self.tg.threads))
+
     def test_stop_timers(self):
 
         def foo(*args, **kwargs):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/oslo_service/threadgroup.py 
new/oslo.service-1.11.0/oslo_service/threadgroup.py
--- old/oslo.service-1.8.0/oslo_service/threadgroup.py  2016-03-28 
05:07:08.000000000 +0200
+++ new/oslo.service-1.11.0/oslo_service/threadgroup.py 2016-05-25 
00:08:22.000000000 +0200
@@ -11,6 +11,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+
 import logging
 import threading
 
@@ -19,7 +20,7 @@
 
 from oslo_service._i18n import _LE
 from oslo_service import loopingcall
-
+from oslo_utils import timeutils
 
 LOG = logging.getLogger(__name__)
 
@@ -58,6 +59,9 @@
     def link(self, func, *args, **kwargs):
         self.thread.link(func, *args, **kwargs)
 
+    def cancel(self, *throw_args):
+        self.thread.cancel(*throw_args)
+
 
 class ThreadGroup(object):
     """The point of the ThreadGroup class is to:
@@ -154,3 +158,31 @@
         self._perform_action_on_threads(
             lambda x: x.wait(),
             lambda x: LOG.exception(_LE('Error waiting on thread.')))
+
+    def _any_threads_alive(self):
+        current = threading.current_thread()
+        for x in self.threads[:]:
+            if x.ident == current.ident:
+                # Don't check current thread.
+                continue
+            if not x.thread.dead:
+                return True
+        return False
+
+    def cancel(self, *throw_args, **kwargs):
+        self._perform_action_on_threads(
+            lambda x: x.cancel(*throw_args),
+            lambda x: LOG.exception(_LE('Error canceling thread.')))
+
+        timeout = kwargs.get('timeout', None)
+        if timeout is None:
+            return
+        wait_time = kwargs.get('wait_time', 1)
+        watch = timeutils.StopWatch(duration=timeout)
+        watch.start()
+        while self._any_threads_alive():
+            if not watch.expired():
+                eventlet.sleep(wait_time)
+                continue
+            LOG.debug("Cancel timeout reached, stopping threads.")
+            self.stop()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/requirements.txt 
new/oslo.service-1.11.0/requirements.txt
--- old/oslo.service-1.8.0/requirements.txt     2016-03-28 05:07:07.000000000 
+0200
+++ new/oslo.service-1.11.0/requirements.txt    2016-05-25 00:08:24.000000000 
+0200
@@ -2,18 +2,17 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 
-Babel>=1.3 # BSD
 WebOb>=1.2.3 # MIT
 eventlet!=0.18.3,>=0.18.2 # MIT
 greenlet>=0.3.2 # MIT
 monotonic>=0.6 # Apache-2.0
 oslo.utils>=3.5.0 # Apache-2.0
 oslo.concurrency>=3.5.0 # Apache-2.0
-oslo.config>=3.7.0 # Apache-2.0
+oslo.config>=3.9.0 # Apache-2.0
 oslo.log>=1.14.0 # Apache-2.0
 six>=1.9.0 # MIT
 oslo.i18n>=2.1.0 # Apache-2.0
 PasteDeploy>=1.5.0 # MIT
-Routes!=2.0,!=2.1,>=1.12.3;python_version=='2.7' # MIT
-Routes!=2.0,>=1.12.3;python_version!='2.7' # MIT
+Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3;python_version=='2.7' # MIT
+Routes!=2.0,!=2.3.0,>=1.12.3;python_version!='2.7' # MIT
 Paste # MIT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/setup.cfg 
new/oslo.service-1.11.0/setup.cfg
--- old/oslo.service-1.8.0/setup.cfg    2016-03-28 05:09:30.000000000 +0200
+++ new/oslo.service-1.11.0/setup.cfg   2016-05-25 00:10:03.000000000 +0200
@@ -59,7 +59,7 @@
 universal = true
 
 [egg_info]
-tag_svn_revision = 0
 tag_build = 
 tag_date = 0
+tag_svn_revision = 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/test-requirements.txt 
new/oslo.service-1.11.0/test-requirements.txt
--- old/oslo.service-1.8.0/test-requirements.txt        2016-03-28 
05:07:07.000000000 +0200
+++ new/oslo.service-1.11.0/test-requirements.txt       2016-05-25 
00:08:22.000000000 +0200
@@ -2,7 +2,7 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 
-fixtures>=1.3.1 # Apache-2.0/BSD
+fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
 hacking<0.11,>=0.10.0
 mock>=1.2 # BSD
 oslotest>=1.10.0 # Apache-2.0
@@ -15,4 +15,4 @@
 coverage>=3.6 # Apache-2.0
 
 # Bandit security code scanner
-bandit>=0.17.3 # Apache-2.0
+bandit>=1.0.1 # Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.service-1.8.0/tox.ini 
new/oslo.service-1.11.0/tox.ini
--- old/oslo.service-1.8.0/tox.ini      2016-03-28 05:07:07.000000000 +0200
+++ new/oslo.service-1.11.0/tox.ini     2016-05-25 00:08:22.000000000 +0200
@@ -32,7 +32,7 @@
 
 show-source = True
 ignore = E123,E125
-exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
+exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
 
 [hacking]
 import_exceptions = oslo_service._i18n


Reply via email to