mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
Merge remote-tracking branch 'origin/trac565'
This commit is contained in:
@@ -209,23 +209,68 @@ class BoB:
|
||||
self.ccs = None
|
||||
self.cfg_start_auth = True
|
||||
self.cfg_start_resolver = False
|
||||
self.started_auth_family = False
|
||||
self.started_resolver_family = False
|
||||
self.curproc = None
|
||||
self.dead_processes = {}
|
||||
self.msgq_socket_file = msgq_socket_file
|
||||
self.nocache = nocache
|
||||
self.processes = {}
|
||||
self.expected_shutdowns = {}
|
||||
self.runnable = False
|
||||
self.uid = setuid
|
||||
self.username = username
|
||||
self.verbose = verbose
|
||||
|
||||
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:
|
||||
sys.stdout.write("[bind10] Handling new configuration: " +
|
||||
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)
|
||||
return answer
|
||||
# TODO
|
||||
|
||||
def command_handler(self, command, args):
|
||||
if self.verbose:
|
||||
@@ -464,11 +509,12 @@ class BoB:
|
||||
# XXX: we hardcode port 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
|
||||
starting of the processes is handled by the caller.
|
||||
"""
|
||||
c_channel_env = self.c_channel_env
|
||||
self.start_msgq(c_channel_env)
|
||||
self.start_cfgmgr(c_channel_env)
|
||||
self.start_ccsession(c_channel_env)
|
||||
@@ -485,6 +531,7 @@ class BoB:
|
||||
# ... and resolver (if selected):
|
||||
if self.cfg_start_resolver:
|
||||
self.start_resolver(c_channel_env)
|
||||
self.started_resolver_family = True
|
||||
|
||||
# Everything after the main components can run as non-root.
|
||||
# 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_xfrin(c_channel_env)
|
||||
self.start_zonemgr(c_channel_env)
|
||||
self.started_auth_family = True
|
||||
|
||||
# ... and finally start the remaining processes
|
||||
self.start_stats(c_channel_env)
|
||||
@@ -528,7 +576,8 @@ class BoB:
|
||||
# Start all processes. If any one fails to start, kill all started
|
||||
# processes and exit with an error indication.
|
||||
try:
|
||||
self.start_all_processes(c_channel_env)
|
||||
self.c_channel_env = c_channel_env
|
||||
self.start_all_processes()
|
||||
except Exception as e:
|
||||
self.kill_started_processes()
|
||||
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, "Stats", "Stats")
|
||||
|
||||
def stop_process(self, process):
|
||||
"""Stop the given process, friendly-like."""
|
||||
# XXX nothing yet
|
||||
pass
|
||||
def stop_process(self, process, recipient):
|
||||
"""
|
||||
Stop the given process, friendly-like. The process is the name it has
|
||||
(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):
|
||||
"""Stop the BoB instance."""
|
||||
@@ -659,6 +733,10 @@ class BoB:
|
||||
still_dead = {}
|
||||
now = time.time()
|
||||
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)
|
||||
if restart_time > now:
|
||||
if (next_restart is None) or (next_restart > restart_time):
|
||||
|
@@ -142,13 +142,13 @@ class TestBoB(unittest.TestCase):
|
||||
self.assertEqual(bob.cfg_start_auth, True)
|
||||
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
|
||||
# of the unit test, by overriding the process start methods we can check
|
||||
# that the right processes are started depending on the configuration
|
||||
# options.
|
||||
class StartAllProcessesBob(BoB):
|
||||
class StartStopCheckBob(BoB):
|
||||
def __init__(self):
|
||||
BoB.__init__(self)
|
||||
|
||||
@@ -163,6 +163,7 @@ class StartAllProcessesBob(BoB):
|
||||
self.zonemgr = False
|
||||
self.stats = False
|
||||
self.cmdctl = False
|
||||
self.c_channel_env = {}
|
||||
|
||||
def read_bind10_config(self):
|
||||
# Configuration options are set directly
|
||||
@@ -198,118 +199,256 @@ class StartAllProcessesBob(BoB):
|
||||
def start_cmdctl(self, c_channel_env):
|
||||
self.cmdctl = True
|
||||
|
||||
# Check that the start_all_processes method starts the right combination
|
||||
# of processes.
|
||||
class TestStartAllProcessesBob(unittest.TestCase):
|
||||
# We don't really use all of these stop_ methods. But it might turn out
|
||||
# someone would add some stop_ method to BoB and we want that one overriden
|
||||
# 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):
|
||||
self.assertEqual(bob.msgq, False)
|
||||
self.assertEqual(bob.cfgmgr, False)
|
||||
self.assertEqual(bob.ccsession, False)
|
||||
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, False)
|
||||
self.assertEqual(bob.cmdctl, False)
|
||||
self.check_started(bob, False, False, False)
|
||||
|
||||
def check_started_none(self, bob):
|
||||
"""
|
||||
Check that the situation is according to configuration where no servers
|
||||
should be started. Some processes still need to be running.
|
||||
"""
|
||||
self.check_started(bob, True, False, 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
|
||||
# is specified.
|
||||
def test_start_none(self):
|
||||
# Created Bob and ensure initialization correct
|
||||
bob = StartAllProcessesBob()
|
||||
# Create BoB and ensure correct initialization
|
||||
bob = StartStopCheckBob()
|
||||
self.check_preconditions(bob)
|
||||
|
||||
# Start processes and check what was started
|
||||
c_channel_env = {}
|
||||
bob.cfg_start_auth = False
|
||||
bob.cfg_start_resolver = False
|
||||
|
||||
bob.start_all_processes(c_channel_env)
|
||||
|
||||
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)
|
||||
bob.start_all_processes()
|
||||
self.check_started_none(bob)
|
||||
|
||||
# Checks the processes started when starting only the auth process
|
||||
def test_start_auth(self):
|
||||
# Created Bob and ensure initialization correct
|
||||
bob = StartAllProcessesBob()
|
||||
# Create BoB and ensure correct initialization
|
||||
bob = StartStopCheckBob()
|
||||
self.check_preconditions(bob)
|
||||
|
||||
# Start processes and check what was started
|
||||
c_channel_env = {}
|
||||
bob.cfg_start_auth = True
|
||||
bob.cfg_start_resolver = False
|
||||
|
||||
bob.start_all_processes(c_channel_env)
|
||||
bob.start_all_processes()
|
||||
|
||||
self.assertEqual(bob.msgq, True)
|
||||
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)
|
||||
self.check_started_auth(bob)
|
||||
|
||||
# Checks the processes started when starting only the resolver process
|
||||
def test_start_resolver(self):
|
||||
# Created Bob and ensure initialization correct
|
||||
bob = StartAllProcessesBob()
|
||||
# Create BoB and ensure correct initialization
|
||||
bob = StartStopCheckBob()
|
||||
self.check_preconditions(bob)
|
||||
|
||||
# Start processes and check what was started
|
||||
c_channel_env = {}
|
||||
bob.cfg_start_auth = False
|
||||
bob.cfg_start_resolver = True
|
||||
|
||||
bob.start_all_processes(c_channel_env)
|
||||
bob.start_all_processes()
|
||||
|
||||
self.assertEqual(bob.msgq, True)
|
||||
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)
|
||||
self.check_started_resolver(bob)
|
||||
|
||||
# Checks the processes started when starting both auth and resolver process
|
||||
def test_start_both(self):
|
||||
# Created Bob and ensure initialization correct
|
||||
bob = StartAllProcessesBob()
|
||||
# Create BoB and ensure correct initialization
|
||||
bob = StartStopCheckBob()
|
||||
self.check_preconditions(bob)
|
||||
|
||||
# Start processes and check what was started
|
||||
c_channel_env = {}
|
||||
bob.cfg_start_auth = True
|
||||
bob.cfg_start_resolver = True
|
||||
|
||||
bob.start_all_processes(c_channel_env)
|
||||
bob.start_all_processes()
|
||||
|
||||
self.assertEqual(bob.msgq, True)
|
||||
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)
|
||||
self.check_started_both(bob)
|
||||
|
||||
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__':
|
||||
unittest.main()
|
||||
|
Reference in New Issue
Block a user