BIND 10 trac213-incremental, updated. 5166d1a65421c3e8515dbcb0d5fcb44c7f400035 [213] Have an abstract BaseComponent
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Nov 2 13:34:42 UTC 2011
The branch, trac213-incremental has been updated
via 5166d1a65421c3e8515dbcb0d5fcb44c7f400035 (commit)
from 3a25578a01620918cd722e430b61c0fe91177e0a (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 5166d1a65421c3e8515dbcb0d5fcb44c7f400035
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Wed Nov 2 14:30:48 2011 +0100
[213] Have an abstract BaseComponent
-----------------------------------------------------------------------
Summary of changes:
src/lib/python/isc/bind10/component.py | 306 +++++++++++++--------
src/lib/python/isc/bind10/special_component.py | 34 ++-
src/lib/python/isc/bind10/tests/component_test.py | 13 +-
3 files changed, 212 insertions(+), 141 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py
index 6763ffc..8eefcba 100644
--- a/src/lib/python/isc/bind10/component.py
+++ b/src/lib/python/isc/bind10/component.py
@@ -44,17 +44,12 @@ STATE_DEAD = 'dead'
STATE_STOPPED = 'stopped'
STATE_RUNNING = 'running'
-class Component:
+class BaseComponent:
"""
- This represents a single component. It has some defaults of behaviour,
- which should be reasonable for majority of ordinary components, but
- it might be inherited and modified for special-purpose components,
- like the core modules with different ways of starting up. Another
- way to tweak only the start of the component (eg. by providing some
- command line parameters) is to set _start_func function from within
- inherited class.
-
- The methods are marked if it is expected for them to be overridden.
+ This represents a single component. This one is an abstract base class.
+ There are some methods which should be left untouched, but there are
+ others which define the interface only and should be overriden in
+ concrete implementations.
The component is in one of the three states:
- Stopped - it is either not started yet or it was explicitly stopped.
@@ -88,12 +83,11 @@ class Component:
that is already shutting down, impossible to stop, etc. We need to add more
states in future to handle it properly.
"""
- def __init__(self, process, boss, kind, address=None, params=None):
+ def __init__(self, boss, kind):
"""
Creates the component in not running mode.
The parameters are:
- - `process` is the name of the process to start.
- `boss` the boss object to plug into. The component needs to plug
into it to know when it failed, etc.
- `kind` is the kind of component. It may be one of:
@@ -109,37 +103,42 @@ class Component:
* 'dispensable' means the component should be running, but if it
doesn't start or crashes for some reason, the system simply tries
to restart it and keeps running.
- - `address` is the address on message bus. It is used to ask it to
- shut down at the end. If you specialize the class for a component
- that is shut down differently, it might be None.
- - `params` is a list of parameters to pass to the process when it
- starts. It is currently unused and this support is left out for
- now.
+
+ Note that the __init__ method of child class should have these
+ parameters:
+
+ __init__(self, process, boss, kind, address=None, params=None)
+
+ The extra parameters are:
+ - `process` - which program should be started.
+ - `address` - the address on message buss, used to talk to the
+ component.
+ - `params` - parameters to the program.
+
+ The methods you should not override are:
+ - start
+ - stop
+ - failed
+ - running
+
+ You should override:
+ - _start_internal
+ - _stop_internal
+ - _failed_internal (if you like, the empty default might be suitable)
+ - name
+ - pid
+ - kill
"""
if kind not in ['core', 'needed', 'dispensable']:
raise ValueError('Component kind can not be ' + kind)
self.__state = STATE_STOPPED
- # These should be read-only
self._kind = kind
self._boss = boss
- self._process = process
- # This can be overwritten/set by the child classes
- self._start_func = None
- self._address = address
- self._params = params
- # These should be considered private. It is protected to
- # allow tests in and for really rare ocassions, but a care
- # should be taken to understand the Component code.
- #
- # It should not be accessed when the component wasn't run
- # yet.
- self._procinfo = None
def start(self):
"""
- Start the component for the first time or restart it. If you need to
- modify the way a component is started, do not replace this method,
- but _start_internal. This one does some more bookkeeping around.
+ Start the component for the first time or restart it. It runs
+ _start_internal to actually start the component.
If you try to start an already running component, it raises ValueError.
"""
@@ -157,49 +156,10 @@ class Component:
self.failed(None)
raise
- def _start_internal(self):
- """
- This method does the actual starting of a process. If you need to
- change the way the component is started, replace this method.
-
- You can change the "core" of this function by setting self._start_func
- to a function without parameters. Such function should start the
- process and return the procinfo object describing the running process.
-
- If you don't provide the _start_func, the usual startup by calling
- boss.start_simple is performed.
-
- If you override the method completely, you should consider overriding
- pid, _stop_internal (and possibly _failed_internal and name) and kill
- as well. You should also register any processes started within boss.
- (In fact, you could set the _procinfo variable and use the provided
- ones, but then you are OK with providing _start_func anyway).
-
- The ability to override this method presents some flexibility. It
- allows processes started in a strange way, as well as components that
- have no processes at all or components with multiple processes (in case
- of multiple processes, care should be taken to make their
- started/stopped state in sync and all the processes that can fail
- should be registered).
- """
- # This one is not tested. For one, it starts a real process
- # which is out of scope of unit tests, for another, it just
- # delegates the starting to other function in boss (if a derived
- # class does not provide an override function), which is tested
- # by use.
- if self._start_func is not None:
- procinfo = self._start_func()
- else:
- # TODO Handle params, etc
- procinfo = self._boss.start_simple(self._process)
- self._procinfo = procinfo
- self._boss.register_process(self.pid(), self)
-
def stop(self):
"""
- Stop the component. If you need to modify the way a component is
- stopped, do not replace this method, but _stop_internal. This one
- does some more bookkeeping.
+ Stop the component. It calls _stop_internal to do the actual
+ stopping.
If you try to stop a component that is not running, it raises
ValueError.
@@ -212,24 +172,6 @@ class Component:
self.__state = STATE_STOPPED
self._stop_internal()
- def _stop_internal(self):
- """
- This is the method that does the actual stopping of a component.
- You can replace this method if you want a different way to do it.
-
- If you're overriding this one, you probably want to replace the
- _start_internal, kill and pid methods (and maybe _failed_internal and
- name as well).
-
- Also, note that it is a bad idea to raise exceptions from here.
- Under such circumstance, the component will be considered stopped,
- and the exception propagated, but we can't be sure it really is
- dead.
- """
- self._boss.stop_process(self._process, self._address)
- # TODO Some way to wait for the process that doesn't want to
- # terminate and kill it would prove nice (or add it to boss somewhere?)
-
def failed(self, exit_code):
"""
Notify the component it crashed. This will be called from boss object.
@@ -244,6 +186,8 @@ class Component:
Otherwise the component will try to restart.
The exit code is used for logging. It might be None.
+
+ It calles _failed_internal internally.
"""
logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(),
exit_code if exit_code is not None else "unknown")
@@ -264,6 +208,47 @@ class Component:
logger.warn(BIND10_COMPONENT_RESTART, self.name())
self.start()
+ def running(self):
+ """
+ Informs if the component is currently running. It assumes the failed
+ is called whenever the component really fails and there might be some
+ time in between actual failure and the call, so this might be
+ inaccurate (it corresponds to the thing the object thinks is true, not
+ to the real "external" state).
+
+ It is not expected for this method to be overriden.
+ """
+ return self.__state == STATE_RUNNING
+
+ def _start_internal(self):
+ """
+ This method does the actual starting of a process. You need to override
+ this method to do the actual starting.
+
+ The ability to override this method presents some flexibility. It
+ allows processes started in a strange way, as well as components that
+ have no processes at all or components with multiple processes (in case
+ of multiple processes, care should be taken to make their
+ started/stopped state in sync and all the processes that can fail
+ should be registered).
+
+ You should register all the processes created by calling
+ self._boss.register_process.
+ """
+ pass
+
+ def _stop_internal(self):
+ """
+ This is the method that does the actual stopping of a component.
+ You need to provide it in a concrete implementation.
+
+ Also, note that it is a bad idea to raise exceptions from here.
+ Under such circumstance, the component will be considered stopped,
+ and the exception propagated, but we can't be sure it really is
+ dead.
+ """
+ pass
+
def _failed_internal(self):
"""
This method is called from failed. You can replace it if you need
@@ -275,52 +260,129 @@ class Component:
"""
pass
- def running(self):
+ def name(self):
"""
- Informs if the component is currently running. It assumes the failed
- is called whenever the component really fails and there might be some
- time in between actual failure and the call.
+ Provides human readable name of the component, for logging and similar
+ purposes.
- It is not expected for this method to be overriden.
+ You need to provide this method in a concrete implementation.
"""
- return self.__state == STATE_RUNNING
+ pass
- def name(self):
+ def pid(self):
"""
- Returns human-readable name of the component. This is usually the
- name of the executable, but it might be something different in a
- derived class (in case it is overriden).
+ Provides a PID of a process, if the component is real running process.
+ This may return None in cases when there's no process involved with the
+ component or in case the component is not started yet.
+
+ However, it is expected the component preserves the pid after it was
+ stopped, to ensure we can log it when we ask it to be killed (in case
+ the process refused to stop willingly).
+
+ You need to provide this method in a concrete implementation.
"""
- return self._process
+ pass
- def pid(self):
+ def kill(self, forcefull=False):
"""
- Provides a PID of a process, if the component is real running process.
- This implementation expects it to be a real process, but derived class
- may return None in case the component is something else.
+ Kills the component.
- This returns None in case it is not yet running.
+ If forcefull is true, it should do it in more direct and aggressive way
+ (for example by using SIGKILL or some equivalent). If it is false, more
+ peaceful way should be used (SIGTERM or equivalent).
- You probably want to override this method if you're providing custom
- _start_internal.
+ You need to provide this method in a concrete implementation.
+ """
+ pass
- Note that some components preserve the pid after a call to stop or
- failed. This is because the components need to preserve it in order
- to be able to kill the process if it failed to stop properly. Therefore
- you should not rely on the pid being None if the component is stopped.
+class Component(BaseComponent):
+ """
+ The most common implementation of a component. It can be used either
+ directly, and it will just start the process without anything special,
+ or slightly customised by passing a start_func hook to the __init__
+ to change the way it starts.
+
+ If such customisation isn't enough, you should inherit BaseComponent
+ directly. It is not recommended to override methods of this class
+ on one-by-one basis.
+ """
+ def __init__(self, process, boss, kind, address=None, params=None,
+ start_func=None):
"""
- return self._procinfo.pid if self._procinfo is not None else None
+ Creates the component in not running mode.
- def kill(self, forcefull=False):
+ The parameters are:
+ - `process` is the name of the process to start.
+ - `boss` the boss object to plug into. The component needs to plug
+ into it to know when it failed, etc.
+ - `kind` is the kind of component. It may be one of:
+ * 'core' means the system can't run without it and it can't be
+ safely restarted. If it does not start, the system is brought
+ down. If it crashes, the system is turned off as well (with
+ non-zero exit status).
+ * 'needed' means the system is able to restart the component,
+ but it is vital part of the service (like auth server). If
+ it fails to start or crashes in less than 10s after the first
+ startup, the system is brought down. If it crashes later on,
+ it is restarted.
+ * 'dispensable' means the component should be running, but if it
+ doesn't start or crashes for some reason, the system simply tries
+ to restart it and keeps running.
+ - `address` is the address on message bus. It is used to ask it to
+ shut down at the end. If you specialize the class for a component
+ that is shut down differently, it might be None.
+ - `params` is a list of parameters to pass to the process when it
+ starts. It is currently unused and this support is left out for
+ now.
+ - `start_func` is a function called when it is started. It is supposed
+ to start up the process and return a ProcInfo object describing it.
+ There's a sensible default if not provided, which just launches
+ the program without any special care.
"""
- The component should be forcefully killed. This does not change the
- internal state, it just kills the external process and expects a
- failure to be reported when the process really dies.
+ BaseComponent.__init__(self, boss, kind)
+ self._process = process
+ self._start_func = start_func
+ self._address = address
+ self._params = params
+ self._procinfo = None
- If it isn't running, it does nothing.
+ def _start_internal(self):
+ """
+ You can change the "core" of this function by setting self._start_func
+ to a function without parameters. Such function should start the
+ process and return the procinfo object describing the running process.
- If the forcefull is true, it uses SIGKILL instead of SIGTERM.
+ If you don't provide the _start_func, the usual startup by calling
+ boss.start_simple is performed.
"""
+ # This one is not tested. For one, it starts a real process
+ # which is out of scope of unit tests, for another, it just
+ # delegates the starting to other function in boss (if a derived
+ # class does not provide an override function), which is tested
+ # by use.
+ if self._start_func is not None:
+ procinfo = self._start_func()
+ else:
+ # TODO Handle params, etc
+ procinfo = self._boss.start_simple(self._process)
+ self._procinfo = procinfo
+ self._boss.register_process(self.pid(), self)
+
+ def _stop_internal(self):
+ self._boss.stop_process(self._process, self._address)
+ # TODO Some way to wait for the process that doesn't want to
+ # terminate and kill it would prove nice (or add it to boss somewhere?)
+
+ def name(self):
+ """
+ Returns the name, derived from the process name.
+ """
+ return self._process
+
+ def pid(self):
+ return self._procinfo.pid if self._procinfo is not None else None
+
+ def kill(self, forcefull=False):
if self._procinfo is not None:
if forcefull:
self._procinfo.process.kill()
diff --git a/src/lib/python/isc/bind10/special_component.py b/src/lib/python/isc/bind10/special_component.py
index d44928a..d05eeee 100644
--- a/src/lib/python/isc/bind10/special_component.py
+++ b/src/lib/python/isc/bind10/special_component.py
@@ -13,18 +13,18 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-from isc.bind10.component import Component
+from isc.bind10.component import Component, BaseComponent
import isc.bind10.sockcreator
from bind10_config import LIBEXECDIR
import os
-class SockCreator(Component):
+class SockCreator(BaseComponent):
"""
The socket creator component. Will start and stop the socket creator
accordingly.
"""
def __init__(self, process, boss, kind, address=None, params=None):
- Component.__init__(self, process, boss, kind)
+ BaseComponent.__init__(self, boss, kind)
self.__creator = None
def _start_internal(self):
@@ -37,6 +37,9 @@ class SockCreator(Component):
self.__creator.terminate()
self.__creator = None
+ def name(self):
+ return "Socket creator"
+
def pid(self):
"""
Pid of the socket creator. It is provided differently from a usual
@@ -55,36 +58,37 @@ class Msgq(Component):
and we leave the boss kill it by signal.
"""
def __init__(self, process, boss, kind, address=None, params=None):
- Component.__init__(self, process, boss, kind)
- self._start_func = boss.start_msgq
+ Component.__init__(self, process, boss, kind, None, None,
+ boss.start_msgq)
def _stop_internal(self):
pass # Wait for the boss to actually kill it. There's no stop command.
+ # This is a hackish way, though
class CfgMgr(Component):
def __init__(self, process, boss, kind, address=None, params=None):
- Component.__init__(self, process, boss, kind, 'ConfigManager')
- self._start_func = boss.start_cfgmgr
+ Component.__init__(self, process, boss, kind, 'ConfigManager',
+ None, boss.start_cfgmgr)
class Auth(Component):
def __init__(self, process, boss, kind, address=None, params=None):
- Component.__init__(self, process, boss, kind, 'Auth')
- self._start_func = boss.start_auth
+ Component.__init__(self, process, boss, kind, 'Auth', None,
+ boss.start_auth)
class Resolver(Component):
def __init__(self, process, boss, kind, address=None, params=None):
- Component.__init__(self, process, boss, kind, 'Resolver')
- self._start_func = boss.start_resolver
+ Component.__init__(self, process, boss, kind, 'Resolver', None,
+ boss.start_resolver)
class CmdCtl(Component):
def __init__(self, process, boss, kind, address=None, params=None):
- Component.__init__(self, process, boss, kind, 'Cmdctl')
- self._start_func = boss.start_cmdctl
+ Component.__init__(self, process, boss, kind, 'Cmdctl', None,
+ boss.start_cmdctl)
class XfrIn(Component):
def __init__(self, process, boss, kind, address=None, params=None):
- Component.__init__(self, process, boss, kind, 'Xfrin')
- self._start_func = boss.start_xfrin
+ Component.__init__(self, process, boss, kind, 'Xfrin', None,
+ boss.start_xfrin)
def get_specials():
"""
diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py
index d884b57..c959983 100644
--- a/src/lib/python/isc/bind10/tests/component_test.py
+++ b/src/lib/python/isc/bind10/tests/component_test.py
@@ -22,7 +22,7 @@ import unittest
import isc.log
import time
import copy
-from isc.bind10.component import Component, Configurator
+from isc.bind10.component import Component, Configurator, BaseComponent
import isc.bind10.special_component
class TestError(Exception):
@@ -439,7 +439,7 @@ class ComponentTests(BossUtils, unittest.TestCase):
component.kill()
component.kill(True)
-class TestComponent(Component):
+class TestComponent(BaseComponent):
"""
A test component. It does not start any processes or so, it just logs
information about what happens.
@@ -451,11 +451,13 @@ class TestComponent(Component):
The process is used as a name for the logging.
"""
- Component.__init__(self, name, owner, kind, address, params)
+ BaseComponent.__init__(self, owner, kind)
self.__owner = owner
self.__name = name
self.log('init')
self.log(kind)
+ self._address = address
+ self._params = params
def log(self, event):
"""
@@ -476,10 +478,13 @@ class TestComponent(Component):
def kill(self, forcefull=False):
self.log('killed')
-class FailComponent(Component):
+class FailComponent(BaseComponent):
"""
A mock component that fails whenever it is started.
"""
+ def __init__(self, name, boss, kind, address=None, params=None):
+ BaseComponent.__init__(self, boss, kind)
+
def _start_internal(self):
raise TestError("test error")
More information about the bind10-changes
mailing list