Remove usage of global CONF

Change the API of oslo.service to accept conf object and
remove usage of global CONF object throughout the library.

List of functions/classes that accept conf as a parameter:
  1. eventlet_backdoor.py
    * initialize_if_enabled
  2. periodic_task.py
    * PeriodicTasks
  3. service.py
    * launch
    * ServiceLauncher
    * ProcessLauncher
  4. sslutils.py
    * is_enabled
    * wrap

Fixed the unit tests.

APIImpact
bp graduate-oslo-service

Change-Id: Icbd5f9e386b0eee2f7c60a65c076f53ba5f0f637
This commit is contained in:
Elena Ezhova 2015-06-08 16:59:22 +03:00
parent 8c56f24291
commit 856a653cbf
9 changed files with 126 additions and 115 deletions

View File

@ -27,14 +27,11 @@ import traceback
import eventlet.backdoor
import greenlet
from oslo_config import cfg
from oslo_service._i18n import _LI
from oslo_service import _options
CONF = cfg.CONF
CONF.register_opts(_options.eventlet_backdoor_opts)
LOG = logging.getLogger(__name__)
@ -96,7 +93,8 @@ def _listen(host, start_port, end_port, listen_func):
try_port += 1
def initialize_if_enabled():
def initialize_if_enabled(conf):
conf.register_opts(_options.eventlet_backdoor_opts)
backdoor_locals = {
'exit': _dont_use_this, # So we don't exit the entire process
'quit': _dont_use_this, # So we don't exit the entire process
@ -105,10 +103,10 @@ def initialize_if_enabled():
'pnt': _print_nativethreads,
}
if CONF.backdoor_port is None:
if conf.backdoor_port is None:
return None
start_port, end_port = _parse_port_range(str(CONF.backdoor_port))
start_port, end_port = _parse_port_range(str(conf.backdoor_port))
# NOTE(johannes): The standard sys.displayhook will print the value of
# the last expression and set it to __builtin__._, which overwrites

View File

@ -16,16 +16,12 @@ import logging
import random
import time
from oslo_config import cfg
import six
from oslo_service._i18n import _, _LE, _LI
from oslo_service import _options
CONF = cfg.CONF
CONF.register_opts(_options.periodic_opts)
LOG = logging.getLogger(__name__)
DEFAULT_INTERVAL = 60.0
@ -67,10 +63,7 @@ def periodic_task(*args, **kwargs):
# Control if run at all
f._periodic_task = True
f._periodic_external_ok = kwargs.pop('external_process_ok', False)
if f._periodic_external_ok and not CONF.run_external_periodic_tasks:
f._periodic_enabled = False
else:
f._periodic_enabled = kwargs.pop('enabled', True)
f._periodic_enabled = kwargs.pop('enabled', True)
f._periodic_name = kwargs.pop('name', f.__name__)
# Control frequency
@ -177,8 +170,10 @@ def _nearest_boundary(last_run, spacing):
@six.add_metaclass(_PeriodicTasksMeta)
class PeriodicTasks(object):
def __init__(self):
def __init__(self, conf):
super(PeriodicTasks, self).__init__()
self.conf = conf
self.conf.register_opts(_options.periodic_opts)
self._periodic_last_run = {}
for name, task in self._periodic_tasks:
self._periodic_last_run[name] = task._periodic_last_run
@ -196,6 +191,9 @@ class PeriodicTasks(object):
"""Tasks to be run at a periodic interval."""
idle_for = DEFAULT_INTERVAL
for task_name, task in self._periodic_tasks:
if (task._periodic_external_ok and not
self.conf.run_external_periodic_tasks):
continue
full_task_name = '.'.join([self.__class__.__name__, task_name])
spacing = self._periodic_spacing[task_name]

View File

@ -42,7 +42,6 @@ import time
import eventlet
from eventlet import event
from oslo_config import cfg
from oslo_service import eventlet_backdoor
from oslo_service._i18n import _LE, _LI, _LW
@ -51,9 +50,6 @@ from oslo_service import systemd
from oslo_service import threadgroup
CONF = cfg.CONF
CONF.register_opts(_options.service_opts)
LOG = logging.getLogger(__name__)
@ -137,14 +133,17 @@ class ServiceBase(object):
class Launcher(object):
"""Launch one or more services and wait for them to complete."""
def __init__(self):
def __init__(self, conf):
"""Initialize the service launcher.
:returns: None
"""
self.conf = conf
conf.register_opts(_options.service_opts)
self.services = Services()
self.backdoor_port = eventlet_backdoor.initialize_if_enabled()
self.backdoor_port = (
eventlet_backdoor.initialize_if_enabled(self.conf))
def launch_service(self, service):
"""Load and start the given service.
@ -178,7 +177,7 @@ class Launcher(object):
:returns: None
"""
cfg.CONF.reload_config_files()
self.conf.reload_config_files()
self.services.restart()
@ -189,6 +188,9 @@ class SignalExit(SystemExit):
class ServiceLauncher(Launcher):
def __init__(self, conf):
super(ServiceLauncher, self).__init__(conf)
def _handle_signal(self, signo, frame):
# Allow the process to be killed again and die from natural causes
_set_signals_handler(signal.SIG_DFL)
@ -201,9 +203,9 @@ class ServiceLauncher(Launcher):
status = None
signo = 0
if CONF.log_options:
if self.conf.log_options:
LOG.debug('Full set of CONF:')
CONF.log_opt_values(LOG, logging.DEBUG)
self.conf.log_opt_values(LOG, logging.DEBUG)
try:
if ready_callback:
@ -247,12 +249,14 @@ class ProcessLauncher(object):
for handler in cls._signal_handlers_set:
handler(*args, **kwargs)
def __init__(self, wait_interval=0.01):
def __init__(self, conf, wait_interval=0.01):
"""Constructor.
:param wait_interval: The interval to sleep for between checks
of child process exit.
"""
self.conf = conf
conf.register_opts(_options.service_opts)
self.children = {}
self.sigcaught = None
self.running = True
@ -333,7 +337,7 @@ class ProcessLauncher(object):
# Reseed random number generator
random.seed()
launcher = Launcher()
launcher = Launcher(self.conf)
launcher.launch_service(service)
return launcher
@ -421,9 +425,9 @@ class ProcessLauncher(object):
"""Loop waiting on children to die and respawning as necessary."""
systemd.notify_once()
if CONF.log_options:
if self.conf.log_options:
LOG.debug('Full set of CONF:')
CONF.log_opt_values(LOG, logging.DEBUG)
self.conf.log_opt_values(LOG, logging.DEBUG)
try:
while True:
@ -438,7 +442,7 @@ class ProcessLauncher(object):
if not _is_sighup_and_daemon(self.sigcaught):
break
cfg.CONF.reload_config_files()
self.conf.reload_config_files()
for service in set(
[wrap.service for wrap in self.children.values()]):
service.reset()
@ -545,15 +549,15 @@ class Services(object):
done.wait()
def launch(service, workers=1):
def launch(conf, service, workers=1):
if not isinstance(service, ServiceBase):
raise TypeError("Service %(service)s must be subclass of %(base)s!"
% {'service': service, 'base': ServiceBase})
if workers is None or workers == 1:
launcher = ServiceLauncher()
launcher = ServiceLauncher(conf)
launcher.launch_service(service)
else:
launcher = ProcessLauncher()
launcher = ProcessLauncher(conf)
launcher.launch_service(service, workers=workers)
return launcher

View File

@ -16,15 +16,11 @@ import copy
import os
import ssl
from oslo_config import cfg
from oslo_service._i18n import _
from oslo_service import _options
CONF = cfg.CONF
config_section = 'ssl'
CONF.register_opts(_options.ssl_opts, config_section)
def list_opts():
@ -32,10 +28,11 @@ def list_opts():
return [(config_section, copy.deepcopy(_options.ssl_opts))]
def is_enabled():
cert_file = CONF.ssl.cert_file
key_file = CONF.ssl.key_file
ca_file = CONF.ssl.ca_file
def is_enabled(conf):
conf.register_opts(_options.ssl_opts, config_section)
cert_file = conf.ssl.cert_file
key_file = conf.ssl.key_file
ca_file = conf.ssl.ca_file
use_ssl = cert_file or key_file
if cert_file and not os.path.exists(cert_file):
@ -55,16 +52,17 @@ def is_enabled():
return use_ssl
def wrap(sock):
def wrap(conf, sock):
conf.register_opts(_options.ssl_opts, config_section)
ssl_kwargs = {
'server_side': True,
'certfile': CONF.ssl.cert_file,
'keyfile': CONF.ssl.key_file,
'certfile': conf.ssl.cert_file,
'keyfile': conf.ssl.key_file,
'cert_reqs': ssl.CERT_NONE,
}
if CONF.ssl.ca_file:
ssl_kwargs['ca_certs'] = CONF.ssl.ca_file
if conf.ssl.ca_file:
ssl_kwargs['ca_certs'] = conf.ssl.ca_file
ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED
return ssl.wrap_socket(sock, **ssl_kwargs)

View File

@ -0,0 +1,32 @@
# Copyright 2015 Mirantis Inc.
#
# 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.
from oslo_config import fixture as config
from oslotest import base as test_base
from oslo_service import _options
class ServiceBaseTestCase(test_base.BaseTestCase):
def setUp(self):
super(ServiceBaseTestCase, self).setUp()
self.conf_fixture = self.useFixture(config.Config())
self.conf_fixture.register_opts(_options.eventlet_backdoor_opts)
self.conf_fixture.register_opts(_options.service_opts)
self.conf_fixture.register_opts(_options.ssl_opts)
self.conf_fixture.register_opts(_options.periodic_opts)
self.conf = self.conf_fixture.conf
self.config = self.conf_fixture.config

View File

@ -25,9 +25,6 @@ from oslo_config import cfg
from oslo_service import service
CONF = cfg.CONF
POOL_SIZE = 1
@ -144,11 +141,9 @@ class ServerWrapper(object):
def run():
CONF()
eventlet.patcher.monkey_patch()
launcher = service.ProcessLauncher()
launcher = service.ProcessLauncher(cfg.CONF)
def hi_app(environ, start_response):
start_response('200 OK', [('Content-Type', 'application/json')])

View File

@ -21,19 +21,17 @@ import errno
import socket
import eventlet
from oslo_config import fixture as config
from oslotest import base as test_base
from oslotest import moxstubout
from oslo_service import eventlet_backdoor
from oslo_service.tests import base
class BackdoorPortTest(test_base.BaseTestCase):
class BackdoorPortTest(base.ServiceBaseTestCase):
def setUp(self):
super(BackdoorPortTest, self).setUp()
self.mox = self.useFixture(moxstubout.MoxStubout()).mox
self.config = self.useFixture(config.Config()).config
def common_backdoor_port_setup(self):
self.sock = self.mox.CreateMockAnything()
@ -47,7 +45,7 @@ class BackdoorPortTest(test_base.BaseTestCase):
socket.error(errno.EADDRINUSE, ''))
self.mox.ReplayAll()
self.assertRaises(socket.error,
eventlet_backdoor.initialize_if_enabled)
eventlet_backdoor.initialize_if_enabled, self.conf)
def test_backdoor_port_range(self):
self.config(backdoor_port='8800:8899')
@ -57,7 +55,7 @@ class BackdoorPortTest(test_base.BaseTestCase):
eventlet.spawn_n(eventlet.backdoor.backdoor_server, self.sock,
locals=moxstubout.mox.IsA(dict))
self.mox.ReplayAll()
port = eventlet_backdoor.initialize_if_enabled()
port = eventlet_backdoor.initialize_if_enabled(self.conf)
self.assertEqual(port, 8800)
def test_backdoor_port_range_all_inuse(self):
@ -68,9 +66,9 @@ class BackdoorPortTest(test_base.BaseTestCase):
socket.error(errno.EADDRINUSE, ''))
self.mox.ReplayAll()
self.assertRaises(socket.error,
eventlet_backdoor.initialize_if_enabled)
eventlet_backdoor.initialize_if_enabled, self.conf)
def test_backdoor_port_bad(self):
self.config(backdoor_port='abc')
self.assertRaises(eventlet_backdoor.EventletBackdoorConfigValueError,
eventlet_backdoor.initialize_if_enabled)
eventlet_backdoor.initialize_if_enabled, self.conf)

View File

@ -19,18 +19,17 @@ Unit Tests for periodic_task decorator and PeriodicTasks class.
"""
import mock
from oslo_config import fixture as config
from oslotest import base as test_base
from testtools import matchers
from oslo_service import periodic_task
from oslo_service.tests import base
class AnException(Exception):
pass
class PeriodicTasksTestCase(test_base.BaseTestCase):
class PeriodicTasksTestCase(base.ServiceBaseTestCase):
"""Test cases for PeriodicTasks."""
@mock.patch('time.time')
@ -42,8 +41,8 @@ class PeriodicTasksTestCase(test_base.BaseTestCase):
# Class inside test def to mock 'time.time' in
# the periodic task decorator
class AService(periodic_task.PeriodicTasks):
def __init__(self):
super(AService, self).__init__()
def __init__(self, conf):
super(AService, self).__init__(conf)
self.called = {'doit': 0, 'urg': 0, 'ticks': 0, 'tocks': 0}
@periodic_task.periodic_task
@ -77,7 +76,7 @@ class PeriodicTasksTestCase(test_base.BaseTestCase):
def ext2(self, context):
external_called['ext2'] += 1
serv = AService()
serv = AService(self.conf)
serv.add_periodic_task(ext1)
serv.add_periodic_task(ext2)
serv.run_periodic_tasks(None)
@ -135,15 +134,15 @@ class PeriodicTasksTestCase(test_base.BaseTestCase):
# Class inside test def to mock 'time.time' in
# the periodic task decorator
class AService(periodic_task.PeriodicTasks):
def __init__(self):
super(AService, self).__init__()
def __init__(self, conf):
super(AService, self).__init__(conf)
self.called = {'ticks': 0}
@periodic_task.periodic_task(spacing=test_spacing)
def tick(self, context):
self.called['ticks'] += 1
serv = AService()
serv = AService(self.conf)
for i in range(200):
serv.run_periodic_tasks(None)
self.assertEqual(serv.called['ticks'], int(i / test_spacing))
@ -156,8 +155,8 @@ class PeriodicTasksTestCase(test_base.BaseTestCase):
mock_time.return_value = time
class AService(periodic_task.PeriodicTasks):
def __init__(self):
super(AService, self).__init__()
def __init__(self, conf):
super(AService, self).__init__(conf)
self.called = {'urg': 0, }
@periodic_task.periodic_task
@ -165,7 +164,7 @@ class PeriodicTasksTestCase(test_base.BaseTestCase):
self.called['urg'] += 1
raise AnException('urg')
serv = AService()
serv = AService(self.conf)
now = serv._periodic_last_run['crashit']
mock_time.return_value = now + periodic_task.DEFAULT_INTERVAL
@ -175,6 +174,9 @@ class PeriodicTasksTestCase(test_base.BaseTestCase):
def test_name(self):
class AService(periodic_task.PeriodicTasks):
def __init__(self, conf):
super(AService, self).__init__(conf)
@periodic_task.periodic_task(name='better-name')
def tick(self, context):
pass
@ -187,14 +189,14 @@ class PeriodicTasksTestCase(test_base.BaseTestCase):
def foo(self, context):
pass
serv = AService()
serv = AService(self.conf)
serv.add_periodic_task(foo)
self.assertIn('better-name', serv._periodic_last_run)
self.assertIn('another-name', serv._periodic_last_run)
self.assertIn('tack', serv._periodic_last_run)
class ManagerMetaTestCase(test_base.BaseTestCase):
class ManagerMetaTestCase(base.ServiceBaseTestCase):
"""Tests for the meta class which manages the creation of periodic tasks.
"""
@ -213,7 +215,7 @@ class ManagerMetaTestCase(test_base.BaseTestCase):
def baz(self):
return 'baz'
m = Manager()
m = Manager(self.conf)
self.assertThat(m._periodic_tasks, matchers.HasLength(2))
self.assertEqual(periodic_task.DEFAULT_INTERVAL,
m._periodic_spacing['foo'])
@ -231,11 +233,10 @@ class ManagerMetaTestCase(test_base.BaseTestCase):
m._periodic_spacing['external'])
class ManagerTestCase(test_base.BaseTestCase):
class ManagerTestCase(base.ServiceBaseTestCase):
"""Tests the periodic tasks portion of the manager class."""
def setUp(self):
super(ManagerTestCase, self).setUp()
self.config = self.useFixture(config.Config()).config
def test_periodic_tasks_with_idle(self):
class Manager(periodic_task.PeriodicTasks):
@ -244,7 +245,7 @@ class ManagerTestCase(test_base.BaseTestCase):
def bar(self):
return 'bar'
m = Manager()
m = Manager(self.conf)
self.assertThat(m._periodic_tasks, matchers.HasLength(1))
self.assertEqual(200, m._periodic_spacing['bar'])
@ -259,7 +260,7 @@ class ManagerTestCase(test_base.BaseTestCase):
def bar(self):
return 'bar'
m = Manager()
m = Manager(self.conf)
idle = m.run_periodic_tasks(None)
self.assertAlmostEqual(60, idle, 1)
@ -274,7 +275,7 @@ class ManagerTestCase(test_base.BaseTestCase):
def bar(self, context):
return 'bar'
m = Manager()
m = Manager(self.conf)
# Ensure initial values are correct
self.assertEqual(1, len(m._periodic_tasks))
@ -317,7 +318,7 @@ class ManagerTestCase(test_base.BaseTestCase):
def bar(self, context):
return 'bar'
m = Manager()
m = Manager(self.conf)
# Ensure initial values are correct
self.assertEqual(1, len(m._periodic_tasks))
@ -351,7 +352,7 @@ class ManagerTestCase(test_base.BaseTestCase):
def bar(self):
return 'bar'
m = Manager()
m = Manager(self.conf)
idle = m.run_periodic_tasks(None)
self.assertAlmostEqual(60, idle, 1)
@ -364,21 +365,9 @@ class ManagerTestCase(test_base.BaseTestCase):
def bar(self):
return 'bar'
m = Manager()
m = Manager(self.conf)
self.assertThat(m._periodic_tasks, matchers.HasLength(1))
def test_external_running_elsewhere(self):
self.config(run_external_periodic_tasks=False)
class Manager(periodic_task.PeriodicTasks):
@periodic_task.periodic_task(spacing=200, external_process_ok=True)
def bar(self):
return 'bar'
m = Manager()
self.assertEqual([], m._periodic_tasks)
@mock.patch('time.time')
@mock.patch('random.random')
def test_nearest_boundary(self, mock_random, mock_time):

View File

@ -37,13 +37,13 @@ import eventlet
from eventlet import event
import mock
from mox3 import mox
from oslo_config import fixture as config
from oslotest import base as test_base
from oslotest import moxstubout
from six.moves import queue
from oslo_service import eventlet_backdoor
from oslo_service import service
from oslo_service.tests import base
LOG = logging.getLogger(__name__)
@ -72,7 +72,7 @@ class ServiceWithTimer(service.Service):
self.timer_fired = self.timer_fired + 1
class ServiceTestBase(test_base.BaseTestCase):
class ServiceTestBase(base.ServiceBaseTestCase):
"""A base class for ServiceLauncherTest and ServiceRestartTest."""
def _spawn_service(self, workers=1, *args, **kwargs):
@ -90,7 +90,7 @@ class ServiceTestBase(test_base.BaseTestCase):
status = 0
try:
serv = ServiceWithTimer()
launcher = service.launch(serv, workers=workers)
launcher = service.launch(self.conf, serv, workers=workers)
launcher.wait(*args, **kwargs)
except SystemExit as exc:
status = exc.code
@ -114,10 +114,10 @@ class ServiceTestBase(test_base.BaseTestCase):
def setUp(self):
super(ServiceTestBase, self).setUp()
self.CONF = self.useFixture(config.Config()).conf
# NOTE(markmc): ConfigOpts.log_opt_values() uses CONF.config-file
self.CONF(args=[], default_config_files=[])
self.addCleanup(self.CONF.reset)
self.conf(args=[], default_config_files=[])
self.addCleanup(self.conf.reset)
self.addCleanup(self.conf.reset)
self.addCleanup(self._reap_pid)
def _reap_pid(self):
@ -277,11 +277,10 @@ class _Service(service.Service):
super(_Service, self).stop()
class LauncherTest(test_base.BaseTestCase):
class LauncherTest(base.ServiceBaseTestCase):
def setUp(self):
super(LauncherTest, self).setUp()
self.mox = self.useFixture(moxstubout.MoxStubout()).mox
self.config = self.useFixture(config.Config()).config
def test_backdoor_port(self):
self.config(backdoor_port='1234')
@ -298,7 +297,7 @@ class LauncherTest(test_base.BaseTestCase):
self.mox.ReplayAll()
svc = service.Service()
launcher = service.launch(svc)
launcher = service.launch(self.conf, svc)
self.assertEqual(svc.backdoor_port, 1234)
launcher.stop()
@ -308,7 +307,7 @@ class LauncherTest(test_base.BaseTestCase):
self.config(backdoor_port=port)
svc = service.Service()
self.assertRaises(socket.error,
service.launch, svc)
service.launch, self.conf, svc)
sock.close()
def test_backdoor_port_range_one_inuse(self):
@ -328,7 +327,7 @@ class LauncherTest(test_base.BaseTestCase):
self.mox.ReplayAll()
svc = service.Service()
launcher = service.launch(svc)
launcher = service.launch(self.conf, svc)
self.assertEqual(svc.backdoor_port, 8801)
launcher.stop()
@ -337,13 +336,13 @@ class LauncherTest(test_base.BaseTestCase):
self.config(backdoor_port='8888:7777')
svc = service.Service()
self.assertRaises(eventlet_backdoor.EventletBackdoorConfigValueError,
service.launch, svc)
service.launch, self.conf, svc)
def test_graceful_shutdown(self):
# test that services are given a chance to clean up:
svc = _Service()
launcher = service.launch(svc)
launcher = service.launch(self.conf, svc)
# wait on 'init' so we know the service had time to start:
svc.init.wait()
@ -358,7 +357,7 @@ class LauncherTest(test_base.BaseTestCase):
@mock.patch('oslo_service.service.ServiceLauncher.launch_service')
def _test_launch_single(self, workers, mock_launch):
svc = service.Service()
service.launch(svc, workers=workers)
service.launch(self.conf, svc, workers=workers)
mock_launch.assert_called_with(svc)
def test_launch_none(self):
@ -370,22 +369,22 @@ class LauncherTest(test_base.BaseTestCase):
@mock.patch('oslo_service.service.ProcessLauncher.launch_service')
def test_multiple_worker(self, mock_launch):
svc = service.Service()
service.launch(svc, workers=3)
service.launch(self.conf, svc, workers=3)
mock_launch.assert_called_with(svc, workers=3)
def test_launch_wrong_service_base_class(self):
# check that services that do not subclass service.ServiceBase
# can not be launched.
svc = mock.Mock()
self.assertRaises(TypeError, service.launch, svc)
self.assertRaises(TypeError, service.launch, self.conf, svc)
class ProcessLauncherTest(test_base.BaseTestCase):
class ProcessLauncherTest(base.ServiceBaseTestCase):
@mock.patch("signal.signal")
def test_stop(self, signal_mock):
signal_mock.SIGTERM = 15
launcher = service.ProcessLauncher()
launcher = service.ProcessLauncher(self.conf)
self.assertTrue(launcher.running)
launcher.children = [22, 222]
@ -405,10 +404,10 @@ class ProcessLauncherTest(test_base.BaseTestCase):
new_callable=lambda: set())
def test__signal_handlers_set(self, signal_handlers_set_mock):
callables = set()
l1 = service.ProcessLauncher()
l1 = service.ProcessLauncher(self.conf)
callables.add(l1._handle_signal)
self.assertEqual(1, len(service.ProcessLauncher._signal_handlers_set))
l2 = service.ProcessLauncher()
l2 = service.ProcessLauncher(self.conf)
callables.add(l2._handle_signal)
self.assertEqual(2, len(service.ProcessLauncher._signal_handlers_set))
self.assertEqual(callables,
@ -443,7 +442,7 @@ class ProcessLauncherTest(test_base.BaseTestCase):
is_sighup_and_daemon_mock.return_value = True
respawn_children_mock.side_effect = [None,
eventlet.greenlet.GreenletExit()]
launcher = service.ProcessLauncher()
launcher = service.ProcessLauncher(self.conf)
launcher.sigcaught = 1
launcher.children = {}