Commit 0ac0b460 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge remote-tracking branch 'origin/trac565'

parents 54f4650b 34be7c60
......@@ -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.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)
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)
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()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment