2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-02 15:05:16 +00:00

Merge remote-tracking branch 'origin/trac565'

This commit is contained in:
Michal 'vorner' Vaner
2011-02-24 13:59:10 +01:00
2 changed files with 296 additions and 79 deletions

View File

@@ -209,23 +209,68 @@ class BoB:
self.ccs = None self.ccs = None
self.cfg_start_auth = True self.cfg_start_auth = True
self.cfg_start_resolver = False self.cfg_start_resolver = False
self.started_auth_family = False
self.started_resolver_family = False
self.curproc = None self.curproc = None
self.dead_processes = {} self.dead_processes = {}
self.msgq_socket_file = msgq_socket_file self.msgq_socket_file = msgq_socket_file
self.nocache = nocache self.nocache = nocache
self.processes = {} self.processes = {}
self.expected_shutdowns = {}
self.runnable = False self.runnable = False
self.uid = setuid self.uid = setuid
self.username = username self.username = username
self.verbose = verbose self.verbose = verbose
def config_handler(self, new_config): def config_handler(self, new_config):
# If this is initial update, don't do anything now, leave it to startup
if not self.runnable:
return
# Now we declare few functions used only internally here. Besides the
# benefit of not polluting the name space, they are closures, so we
# don't need to pass some variables
def start_stop(name, started, start, stop):
if not'start_' + name in new_config:
return
if new_config['start_' + name]:
if not started:
if self.uid is not None:
sys.stderr.write("[bind10] Starting " + name + " as " +
"a user, not root. This might fail.\n")
start()
else:
stop()
# These four functions are passed to start_stop (smells like functional
# programming little bit)
def resolver_on():
self.start_resolver(self.c_channel_env)
self.started_resolver_family = True
def resolver_off():
self.stop_resolver()
self.started_resolver_family = False
def auth_on():
self.start_auth(self.c_channel_env)
self.start_xfrout(self.c_channel_env)
self.start_xfrin(self.c_channel_env)
self.start_zonemgr(self.c_channel_env)
self.started_auth_family = True
def auth_off():
self.stop_zonemgr()
self.stop_xfrin()
self.stop_xfrout()
self.stop_auth()
self.started_auth_family = False
# The real code of the config handler function follows here
if self.verbose: if self.verbose:
sys.stdout.write("[bind10] Handling new configuration: " + sys.stdout.write("[bind10] Handling new configuration: " +
str(new_config) + "\n") str(new_config) + "\n")
start_stop('resolver', self.started_resolver_family, resolver_on,
resolver_off)
start_stop('auth', self.started_auth_family, auth_on, auth_off)
answer = isc.config.ccsession.create_answer(0) answer = isc.config.ccsession.create_answer(0)
return answer return answer
# TODO
def command_handler(self, command, args): def command_handler(self, command, args):
if self.verbose: if self.verbose:
@@ -464,11 +509,12 @@ class BoB:
# XXX: we hardcode port 8080 # XXX: we hardcode port 8080
self.start_simple("b10-cmdctl", c_channel_env, 8080) self.start_simple("b10-cmdctl", c_channel_env, 8080)
def start_all_processes(self, c_channel_env): def start_all_processes(self):
""" """
Starts up all the processes. Any exception generated during the Starts up all the processes. Any exception generated during the
starting of the processes is handled by the caller. starting of the processes is handled by the caller.
""" """
c_channel_env = self.c_channel_env
self.start_msgq(c_channel_env) self.start_msgq(c_channel_env)
self.start_cfgmgr(c_channel_env) self.start_cfgmgr(c_channel_env)
self.start_ccsession(c_channel_env) self.start_ccsession(c_channel_env)
@@ -485,6 +531,7 @@ class BoB:
# ... and resolver (if selected): # ... and resolver (if selected):
if self.cfg_start_resolver: if self.cfg_start_resolver:
self.start_resolver(c_channel_env) self.start_resolver(c_channel_env)
self.started_resolver_family = True
# Everything after the main components can run as non-root. # Everything after the main components can run as non-root.
# TODO: this is only temporary - once the privileged socket creator is # TODO: this is only temporary - once the privileged socket creator is
@@ -498,6 +545,7 @@ class BoB:
self.start_xfrout(c_channel_env) self.start_xfrout(c_channel_env)
self.start_xfrin(c_channel_env) self.start_xfrin(c_channel_env)
self.start_zonemgr(c_channel_env) self.start_zonemgr(c_channel_env)
self.started_auth_family = True
# ... and finally start the remaining processes # ... and finally start the remaining processes
self.start_stats(c_channel_env) self.start_stats(c_channel_env)
@@ -528,7 +576,8 @@ class BoB:
# Start all processes. If any one fails to start, kill all started # Start all processes. If any one fails to start, kill all started
# processes and exit with an error indication. # processes and exit with an error indication.
try: try:
self.start_all_processes(c_channel_env) self.c_channel_env = c_channel_env
self.start_all_processes()
except Exception as e: except Exception as e:
self.kill_started_processes() self.kill_started_processes()
return "Unable to start " + self.curproc + ": " + str(e) return "Unable to start " + self.curproc + ": " + str(e)
@@ -550,10 +599,35 @@ class BoB:
self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr") self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
self.cc_session.group_sendmsg(cmd, "Stats", "Stats") self.cc_session.group_sendmsg(cmd, "Stats", "Stats")
def stop_process(self, process): def stop_process(self, process, recipient):
"""Stop the given process, friendly-like.""" """
# XXX nothing yet Stop the given process, friendly-like. The process is the name it has
pass (in logs, etc), the recipient is the address on msgq.
"""
if self.verbose:
sys.stdout.write("[bind10] Asking %s to terminate\n" % process)
# TODO: Some timeout to solve processes that don't want to die would
# help. We can even store it in the dict, it is used only as a set
self.expected_shutdowns[process] = 1
# Ask the process to die willingly
self.cc_session.group_sendmsg({'command': ['shutdown']}, recipient,
recipient)
# Series of stop_process wrappers
def stop_resolver(self):
self.stop_process('b10-resolver', 'Resolver')
def stop_auth(self):
self.stop_process('b10-auth', 'Auth')
def stop_xfrout(self):
self.stop_process('b10-xfrout', 'Xfrout')
def stop_xfrin(self):
self.stop_process('b10-xfrin', 'Xfrin')
def stop_zonemgr(self):
self.stop_process('b10-zonemgr', 'Zonemgr')
def shutdown(self): def shutdown(self):
"""Stop the BoB instance.""" """Stop the BoB instance."""
@@ -659,6 +733,10 @@ class BoB:
still_dead = {} still_dead = {}
now = time.time() now = time.time()
for proc_info in self.dead_processes.values(): for proc_info in self.dead_processes.values():
if proc_info.name in self.expected_shutdowns:
# We don't restart, we wanted it to die
del self.expected_shutdowns[proc_info.name]
continue
restart_time = proc_info.restart_schedule.get_restart_time(now) restart_time = proc_info.restart_schedule.get_restart_time(now)
if restart_time > now: if restart_time > now:
if (next_restart is None) or (next_restart > restart_time): if (next_restart is None) or (next_restart > restart_time):

View File

@@ -142,13 +142,13 @@ class TestBoB(unittest.TestCase):
self.assertEqual(bob.cfg_start_auth, True) self.assertEqual(bob.cfg_start_auth, True)
self.assertEqual(bob.cfg_start_resolver, False) self.assertEqual(bob.cfg_start_resolver, False)
# Class for testing the Bob.start_all_processes() method call. # Class for testing the BoB start/stop components routines.
# #
# Although testing that external processes start is outside the scope # Although testing that external processes start is outside the scope
# of the unit test, by overriding the process start methods we can check # of the unit test, by overriding the process start methods we can check
# that the right processes are started depending on the configuration # that the right processes are started depending on the configuration
# options. # options.
class StartAllProcessesBob(BoB): class StartStopCheckBob(BoB):
def __init__(self): def __init__(self):
BoB.__init__(self) BoB.__init__(self)
@@ -163,6 +163,7 @@ class StartAllProcessesBob(BoB):
self.zonemgr = False self.zonemgr = False
self.stats = False self.stats = False
self.cmdctl = False self.cmdctl = False
self.c_channel_env = {}
def read_bind10_config(self): def read_bind10_config(self):
# Configuration options are set directly # Configuration options are set directly
@@ -198,118 +199,256 @@ class StartAllProcessesBob(BoB):
def start_cmdctl(self, c_channel_env): def start_cmdctl(self, c_channel_env):
self.cmdctl = True self.cmdctl = True
# Check that the start_all_processes method starts the right combination # We don't really use all of these stop_ methods. But it might turn out
# of processes. # someone would add some stop_ method to BoB and we want that one overriden
class TestStartAllProcessesBob(unittest.TestCase): # in case he forgets to update the tests.
def stop_msgq(self):
self.msgq = False
def stop_cfgmgr(self):
self.cfgmgr = False
def stop_ccsession(self):
self.ccsession = False
def stop_auth(self):
self.auth = False
def stop_resolver(self):
self.resolver = False
def stop_xfrout(self):
self.xfrout = False
def stop_xfrin(self):
self.xfrin = False
def stop_zonemgr(self):
self.zonemgr = False
def stop_stats(self):
self.stats = False
def stop_cmdctl(self):
self.cmdctl = False
class TestStartStopProcessesBob(unittest.TestCase):
"""
Check that the start_all_processes method starts the right combination
of processes and that the right processes are started and stopped
according to changes in configuration.
"""
def check_started(self, bob, core, auth, resolver):
"""
Check that the right sets of services are started. The ones that
should be running are specified by the core, auth and resolver parameters
(they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
and -zonemgr).
"""
self.assertEqual(bob.msgq, core)
self.assertEqual(bob.cfgmgr, core)
self.assertEqual(bob.ccsession, core)
self.assertEqual(bob.auth, auth)
self.assertEqual(bob.resolver, resolver)
self.assertEqual(bob.xfrout, auth)
self.assertEqual(bob.xfrin, auth)
self.assertEqual(bob.zonemgr, auth)
self.assertEqual(bob.stats, core)
self.assertEqual(bob.cmdctl, core)
def check_preconditions(self, bob): def check_preconditions(self, bob):
self.assertEqual(bob.msgq, False) self.check_started(bob, False, False, False)
self.assertEqual(bob.cfgmgr, False)
self.assertEqual(bob.ccsession, False) def check_started_none(self, bob):
self.assertEqual(bob.auth, False) """
self.assertEqual(bob.resolver, False) Check that the situation is according to configuration where no servers
self.assertEqual(bob.xfrout, False) should be started. Some processes still need to be running.
self.assertEqual(bob.xfrin, False) """
self.assertEqual(bob.zonemgr, False) self.check_started(bob, True, False, False)
self.assertEqual(bob.stats, False)
self.assertEqual(bob.cmdctl, False) def check_started_both(self, bob):
"""
Check the situation is according to configuration where both servers
(auth and resolver) are enabled.
"""
self.check_started(bob, True, True, True)
def check_started_auth(self, bob):
"""
Check the set of processes needed to run auth only is started.
"""
self.check_started(bob, True, True, False)
def check_started_resolver(self, bob):
"""
Check the set of processes needed to run resolver only is started.
"""
self.check_started(bob, True, False, True)
# Checks the processes started when starting neither auth nor resolver # Checks the processes started when starting neither auth nor resolver
# is specified. # is specified.
def test_start_none(self): def test_start_none(self):
# Created Bob and ensure initialization correct # Create BoB and ensure correct initialization
bob = StartAllProcessesBob() bob = StartStopCheckBob()
self.check_preconditions(bob) self.check_preconditions(bob)
# Start processes and check what was started # Start processes and check what was started
c_channel_env = {}
bob.cfg_start_auth = False bob.cfg_start_auth = False
bob.cfg_start_resolver = False bob.cfg_start_resolver = False
bob.start_all_processes(c_channel_env) bob.start_all_processes()
self.check_started_none(bob)
self.assertEqual(bob.msgq, True)
self.assertEqual(bob.cfgmgr, True)
self.assertEqual(bob.ccsession, True)
self.assertEqual(bob.auth, False)
self.assertEqual(bob.resolver, False)
self.assertEqual(bob.xfrout, False)
self.assertEqual(bob.xfrin, False)
self.assertEqual(bob.zonemgr, False)
self.assertEqual(bob.stats, True)
self.assertEqual(bob.cmdctl, True)
# Checks the processes started when starting only the auth process # Checks the processes started when starting only the auth process
def test_start_auth(self): def test_start_auth(self):
# Created Bob and ensure initialization correct # Create BoB and ensure correct initialization
bob = StartAllProcessesBob() bob = StartStopCheckBob()
self.check_preconditions(bob) self.check_preconditions(bob)
# Start processes and check what was started # Start processes and check what was started
c_channel_env = {}
bob.cfg_start_auth = True bob.cfg_start_auth = True
bob.cfg_start_resolver = False bob.cfg_start_resolver = False
bob.start_all_processes(c_channel_env) bob.start_all_processes()
self.assertEqual(bob.msgq, True) self.check_started_auth(bob)
self.assertEqual(bob.cfgmgr, True)
self.assertEqual(bob.ccsession, True)
self.assertEqual(bob.auth, True)
self.assertEqual(bob.resolver, False)
self.assertEqual(bob.xfrout, True)
self.assertEqual(bob.xfrin, True)
self.assertEqual(bob.zonemgr, True)
self.assertEqual(bob.stats, True)
self.assertEqual(bob.cmdctl, True)
# Checks the processes started when starting only the resolver process # Checks the processes started when starting only the resolver process
def test_start_resolver(self): def test_start_resolver(self):
# Created Bob and ensure initialization correct # Create BoB and ensure correct initialization
bob = StartAllProcessesBob() bob = StartStopCheckBob()
self.check_preconditions(bob) self.check_preconditions(bob)
# Start processes and check what was started # Start processes and check what was started
c_channel_env = {}
bob.cfg_start_auth = False bob.cfg_start_auth = False
bob.cfg_start_resolver = True bob.cfg_start_resolver = True
bob.start_all_processes(c_channel_env) bob.start_all_processes()
self.assertEqual(bob.msgq, True) self.check_started_resolver(bob)
self.assertEqual(bob.cfgmgr, True)
self.assertEqual(bob.ccsession, True)
self.assertEqual(bob.auth, False)
self.assertEqual(bob.resolver, True)
self.assertEqual(bob.xfrout, False)
self.assertEqual(bob.xfrin, False)
self.assertEqual(bob.zonemgr, False)
self.assertEqual(bob.stats, True)
self.assertEqual(bob.cmdctl, True)
# Checks the processes started when starting both auth and resolver process # Checks the processes started when starting both auth and resolver process
def test_start_both(self): def test_start_both(self):
# Created Bob and ensure initialization correct # Create BoB and ensure correct initialization
bob = StartAllProcessesBob() bob = StartStopCheckBob()
self.check_preconditions(bob) self.check_preconditions(bob)
# Start processes and check what was started # Start processes and check what was started
c_channel_env = {}
bob.cfg_start_auth = True bob.cfg_start_auth = True
bob.cfg_start_resolver = True bob.cfg_start_resolver = True
bob.start_all_processes(c_channel_env) bob.start_all_processes()
self.assertEqual(bob.msgq, True) self.check_started_both(bob)
self.assertEqual(bob.cfgmgr, True)
self.assertEqual(bob.ccsession, True)
self.assertEqual(bob.auth, True)
self.assertEqual(bob.resolver, True)
self.assertEqual(bob.xfrout, True)
self.assertEqual(bob.xfrin, True)
self.assertEqual(bob.zonemgr, True)
self.assertEqual(bob.stats, True)
self.assertEqual(bob.cmdctl, True)
def test_config_start(self):
"""
Test that the configuration starts and stops processes according
to configuration changes.
"""
# Create BoB and ensure correct initialization
bob = StartStopCheckBob()
self.check_preconditions(bob)
# Start processes (nothing much should be started, as in
# test_start_none)
bob.cfg_start_auth = False
bob.cfg_start_resolver = False
bob.start_all_processes()
bob.runnable = True
self.check_started_none(bob)
# Enable both at once
bob.config_handler({'start_auth': True, 'start_resolver': True})
self.check_started_both(bob)
# Not touched by empty change
bob.config_handler({})
self.check_started_both(bob)
# Not touched by change to the same configuration
bob.config_handler({'start_auth': True, 'start_resolver': True})
self.check_started_both(bob)
# Turn them both off again
bob.config_handler({'start_auth': False, 'start_resolver': False})
self.check_started_none(bob)
# Not touched by empty change
bob.config_handler({})
self.check_started_none(bob)
# Not touched by change to the same configuration
bob.config_handler({'start_auth': False, 'start_resolver': False})
self.check_started_none(bob)
# Start and stop auth separately
bob.config_handler({'start_auth': True})
self.check_started_auth(bob)
bob.config_handler({'start_auth': False})
self.check_started_none(bob)
# Start and stop resolver separately
bob.config_handler({'start_resolver': True})
self.check_started_resolver(bob)
bob.config_handler({'start_resolver': False})
self.check_started_none(bob)
# Alternate
bob.config_handler({'start_auth': True})
self.check_started_auth(bob)
bob.config_handler({'start_auth': False, 'start_resolver': True})
self.check_started_resolver(bob)
bob.config_handler({'start_auth': True, 'start_resolver': False})
self.check_started_auth(bob)
def test_config_start_once(self):
"""
Tests that a process is started only once.
"""
# Create BoB and ensure correct initialization
bob = StartStopCheckBob()
self.check_preconditions(bob)
# Start processes (both)
bob.cfg_start_auth = True
bob.cfg_start_resolver = True
bob.start_all_processes()
bob.runnable = True
self.check_started_both(bob)
bob.start_auth = lambda: self.fail("Started auth again")
bob.start_xfrout = lambda: self.fail("Started xfrout again")
bob.start_xfrin = lambda: self.fail("Started xfrin again")
bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
bob.start_resolver = lambda: self.fail("Started resolver again")
# Send again we want to start them. Should not do it, as they are.
bob.config_handler({'start_auth': True})
bob.config_handler({'start_resolver': True})
def test_config_not_started_early(self):
"""
Test that processes are not started by the config handler before
startup.
"""
bob = StartStopCheckBob()
self.check_preconditions(bob)
bob.start_auth = lambda: self.fail("Started auth again")
bob.start_xfrout = lambda: self.fail("Started xfrout again")
bob.start_xfrin = lambda: self.fail("Started xfrin again")
bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
bob.start_resolver = lambda: self.fail("Started resolver again")
bob.config_handler({'start_auth': True, 'start_resolver': True})
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()