Commit 56596cd2 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] changelog for #1570 (forgot to push, and it caused a conflict)

parents ac686701 64b4c44c
375. [bug] jinmei, vorner
376. [bug] jinmei, vorner
The new query handling module of b10-auth did not handle type DS
query correctly: It didn't look for it in the parent zone, and
it incorrectly returned a DS from the child zone if it
......@@ -7,6 +7,14 @@
ancestor.
(Trac #1570, git 2858b2098a10a8cc2d34bf87463ace0629d3670e)
375. [func] jelte
Modules now inform the system when they are stopping. As a result, they
are removed from the 'active modules' list in bindctl, which can then
inform the user directly when it tries to send them a command or
configuration update. Previously this would result in a 'not responding'
error instead of 'not running'.
(Trac #640, git 17e78fa1bb1227340aa9815e91ed5c50d174425d)
374. [func]* stephen
Alter RRsetPtr and ConstRRsetPtr to point to AbstractRRset (instead
of RRset) to allow for specialised implementations of RRsets in
......
......@@ -679,7 +679,13 @@ class BoB:
def shutdown(self):
"""Stop the BoB instance."""
logger.info(BIND10_SHUTDOWN)
# first try using the BIND 10 request to stop
# If ccsession is still there, inform rest of the system this module
# is stopping. Since everything will be stopped shortly, this is not
# really necessary, but this is done to reflect that boss is also
# 'just' a module.
self.ccs.send_stopping()
# try using the BIND 10 request to stop
try:
self._component_configurator.shutdown()
except:
......
......@@ -36,6 +36,7 @@ import isc.bind10.socket_cache
import errno
from isc.testutils.parse_args import TestOptParser, OptsError
from isc.testutils.ccsession_mock import MockModuleCCSession
class TestProcessInfo(unittest.TestCase):
def setUp(self):
......@@ -1171,8 +1172,12 @@ class TestBossComponents(unittest.TestCase):
bob._component_configurator.shutdown = self.__nullary_hook
self.__called = False
bob.ccs = MockModuleCCSession()
self.assertFalse(bob.ccs.stopped)
bob.shutdown()
self.assertTrue(bob.ccs.stopped)
self.assertEqual([False, True], killed)
self.assertTrue(self.__called)
......
......@@ -310,12 +310,25 @@ class CommandControl():
def command_handler(self, command, args):
answer = ccsession.create_answer(0)
if command == ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE:
# The 'value' of a specification update can be either
# a specification, or None. In the first case, simply
# set it. If it is None, delete the module if it is
# known.
with self._lock:
self.modules_spec[args[0]] = args[1]
if args[1] is None:
if args[0] in self.modules_spec:
del self.modules_spec[args[0]]
else:
answer = ccsession.create_answer(1,
'No such module: ' +
args[0])
else:
self.modules_spec[args[0]] = args[1]
elif command == ccsession.COMMAND_SHUTDOWN:
#When cmdctl get 'shutdown' command from boss,
#shutdown the outer httpserver.
self._module_cc.send_stopping()
self._httpserver.shutdown()
self._serving = False
......
......@@ -345,6 +345,40 @@ class TestCommandControl(unittest.TestCase):
self.assertEqual(rcode, 0)
self.assertTrue(msg != None)
def test_command_handler_spec_update(self):
# Should not be present
self.assertFalse("foo" in self.cmdctl.modules_spec)
answer = self.cmdctl.command_handler(
ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", {} ])
rcode, msg = ccsession.parse_answer(answer)
self.assertEqual(rcode, 0)
self.assertEqual(msg, None)
# Should now be present
self.assertTrue("foo" in self.cmdctl.modules_spec)
# When sending specification 'None', it should be removed
answer = self.cmdctl.command_handler(
ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", None ])
rcode, msg = ccsession.parse_answer(answer)
self.assertEqual(rcode, 0)
self.assertEqual(msg, None)
# Should no longer be present
self.assertFalse("foo" in self.cmdctl.modules_spec)
# Don't store 'None' if it wasn't there in the first place!
answer = self.cmdctl.command_handler(
ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", None ])
rcode, msg = ccsession.parse_answer(answer)
self.assertEqual(rcode, 1)
self.assertEqual(msg, "No such module: foo")
# Should still not present
self.assertFalse("foo" in self.cmdctl.modules_spec)
def test_check_config_handler(self):
answer = self.cmdctl.config_handler({'non-exist': 123})
self._check_answer(answer, 1, 'unknown config item: non-exist')
......
......@@ -150,9 +150,10 @@ class DDNSServer:
Perform any cleanup that is necessary when shutting down the server.
Do NOT call this to initialize shutdown, use trigger_shutdown().
Currently, it does nothing, but cleanup routines are expected.
Currently, it only causes the ModuleCCSession to send a message that
this module is stopping.
'''
pass
self._cc.send_stopping()
def accept(self):
"""
......
......@@ -58,11 +58,16 @@ class MyCCSession(isc.config.ConfigData):
ddns.SPECFILE_LOCATION)
isc.config.ConfigData.__init__(self, module_spec)
self._started = False
self._stopped = False
def start(self):
'''Called by DDNSServer initialization, but not used in tests'''
self._started = True
def send_stopping(self):
'''Called by shutdown code'''
self._stopped = True
def get_socket(self):
"""
Used to get the file number for select.
......@@ -289,6 +294,7 @@ class TestDDNSServer(unittest.TestCase):
self.__select_answer = ([3], [], [])
self.ddns_server.run()
self.assertTrue(self.ddns_server._shutdown)
self.assertTrue(self.__cc_session._stopped)
self.assertIsNone(self.__select_answer)
self.assertEqual(3, self.__hook_called)
......
......@@ -184,8 +184,11 @@ class Stats:
raise StatsError("stats spec file is incorrect: "
+ ", ".join(errors))
while self.running:
self.mccs.check_command(False)
try:
while self.running:
self.mccs.check_command(False)
finally:
self.mccs.send_stopping()
def config_handler(self, new_config):
"""
......
......@@ -203,6 +203,7 @@ class StatsHttpd:
"""Closes a ModuleCCSession object"""
if self.mccs is None:
return
self.mccs.send_stopping()
logger.debug(DBG_STATHTTPD_INIT, STATHTTPD_CLOSING_CC_SESSION)
self.mccs.close()
......
......@@ -37,7 +37,10 @@ import random
import isc
import stats_httpd
import stats
from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, SignalHandler, send_command, send_shutdown
from test_utils import BaseModules, ThreadingServerManager, MyStats,\
MyStatsHttpd, SignalHandler,\
send_command, send_shutdown
from isc.testutils.ccsession_mock import MockModuleCCSession
DUMMY_DATA = {
'Boss' : {
......@@ -676,7 +679,13 @@ class TestStatsHttpd(unittest.TestCase):
def test_openclose_mccs(self):
self.stats_httpd = MyStatsHttpd(get_availaddr())
mccs = MockModuleCCSession()
self.stats_httpd.mccs = mccs
self.assertFalse(self.stats_httpd.mccs.stopped)
self.assertFalse(self.stats_httpd.mccs.closed)
self.stats_httpd.close_mccs()
self.assertTrue(mccs.stopped)
self.assertTrue(mccs.closed)
self.assertEqual(self.stats_httpd.mccs, None)
self.stats_httpd.open_mccs()
self.assertIsNotNone(self.stats_httpd.mccs)
......
......@@ -31,6 +31,7 @@ import imp
import stats
import isc.cc.session
from test_utils import BaseModules, ThreadingServerManager, MyStats, SignalHandler, send_command, send_shutdown
from isc.testutils.ccsession_mock import MockModuleCCSession
class TestUtilties(unittest.TestCase):
items = [
......@@ -201,9 +202,15 @@ class TestStats(unittest.TestCase):
self.assertEqual(send_command("status", "Stats"),
(0, "Stats is up. (PID " + str(os.getpid()) + ")"))
self.assertTrue(self.stats.running)
# Override moduleCCSession so we can check if send_stopping is called
self.stats.mccs = MockModuleCCSession()
self.assertEqual(send_shutdown("Stats"), (0, None))
self.assertFalse(self.stats.running)
self.stats_server.shutdown()
# Call server.shutdown with argument True so the thread.join() call
# blocks and we are sure the main loop has finished (and set
# mccs.stopped)
self.stats_server.shutdown(True)
self.assertTrue(self.stats.mccs.stopped)
# start with err
self.stats = stats.Stats()
......
......@@ -72,9 +72,19 @@ class ThreadingServerManager:
self.server._started.wait()
self.server._started.clear()
def shutdown(self):
def shutdown(self, blocking=False):
"""Shut down the server by calling its own shutdown() method.
Then wait for its thread to finish. If blocking is True,
the thread.join() blocks until the thread finishes. If not,
it uses a zero timeout. The latter is necessary in a number
of existing tests. We should redo this part (we should not
even need threads in most, if not all, of these threads, see
ticket #1668)"""
self.server.shutdown()
self.server._thread.join(0) # timeout is 0
if blocking:
self.server._thread.join()
else:
self.server._thread.join(0) # timeout is 0
def do_nothing(*args, **kwargs): pass
......
......@@ -20,6 +20,7 @@ import socket
import sys
import io
from isc.testutils.tsigctx_mock import MockTSIGContext
from isc.testutils.ccsession_mock import MockModuleCCSession
from isc.testutils.rrset_utils import *
from xfrin import *
import xfrin
......@@ -105,7 +106,7 @@ class XfrinTestException(Exception):
class XfrinTestTimeoutException(Exception):
pass
class MockCC():
class MockCC(MockModuleCCSession):
def get_default_value(self, identifier):
# The returned values should be identical to the spec file
# XXX: these should be retrieved from the spec file
......@@ -2052,7 +2053,9 @@ class TestXfrin(unittest.TestCase):
self.args['tsig_key'] = ''
def tearDown(self):
self.assertFalse(self.xfr._module_cc.stopped);
self.xfr.shutdown()
self.assertTrue(self.xfr._module_cc.stopped);
sys.stderr= self.stderr_backup
def _do_parse_zone_name_class(self):
......
......@@ -1224,6 +1224,7 @@ class Xfrin:
''' shutdown the xfrin process. the thread which is doing xfrin should be
terminated.
'''
self._module_cc.send_stopping()
self._shutdown_event.set()
main_thread = threading.currentThread()
for th in threading.enumerate():
......
......@@ -19,6 +19,7 @@
import unittest
import os
from isc.testutils.tsigctx_mock import MockTSIGContext
from isc.testutils.ccsession_mock import MockModuleCCSession
from isc.cc.session import *
import isc.config
from isc.dns import *
......@@ -1423,6 +1424,32 @@ class TestInitialization(unittest.TestCase):
xfrout.init_paths()
self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
class MyNotifier():
def __init__(self):
self.shutdown_called = False
def shutdown(self):
self.shutdown_called = True
class MyXfroutServer(XfroutServer):
def __init__(self):
self._cc = MockModuleCCSession()
self._shutdown_event = threading.Event()
self._notifier = MyNotifier()
self._unix_socket_server = None
# Disable the wait for threads
self._wait_for_threads = lambda : None
class TestXfroutServer(unittest.TestCase):
def setUp(self):
self.xfrout_server = MyXfroutServer()
def test_shutdown(self):
self.xfrout_server.shutdown()
self.assertTrue(self.xfrout_server._notifier.shutdown_called)
self.assertTrue(self.xfrout_server._cc.stopped)
if __name__== "__main__":
isc.log.resetUnitTestRootLogger()
unittest.main()
......@@ -969,12 +969,18 @@ class XfroutServer:
global xfrout_server
xfrout_server = None #Avoid shutdown is called twice
self._cc.send_stopping()
self._shutdown_event.set()
self._notifier.shutdown()
if self._unix_socket_server:
self._unix_socket_server.shutdown()
self._wait_for_threads()
# Wait for all threads to terminate
def _wait_for_threads(self):
# Wait for all threads to terminate. this is a call that is only used
# in shutdown(), but it has its own method, so we can test shutdown
# without involving thread operations (the test would override this
# method)
main_thread = threading.currentThread()
for th in threading.enumerate():
if th is main_thread:
......
......@@ -20,6 +20,7 @@ import unittest
import os
import tempfile
from zonemgr import *
from isc.testutils.ccsession_mock import MockModuleCCSession
ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
ZONE_NAME_CLASS1_CH = ("example.net.", "CH")
......@@ -48,10 +49,11 @@ class MySession():
def group_recvmsg(self, nonblock, seq):
return None, None
class FakeCCSession(isc.config.ConfigData):
class FakeCCSession(isc.config.ConfigData, MockModuleCCSession):
def __init__(self):
module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
ConfigData.__init__(self, module_spec)
MockModuleCCSession.__init__(self)
def get_remote_config_value(self, module_name, identifier):
if module_name == "Auth" and identifier == "database_file":
......@@ -683,6 +685,12 @@ class TestZonemgr(unittest.TestCase):
self.zonemgr._config_data_check(config_data3)
self.assertEqual(0.5, config_data3.get("refresh_jitter"))
def test_shutdown(self):
self.assertFalse(self.zonemgr._module_cc.stopped)
self.zonemgr._shutdown_event.set()
self.zonemgr.run()
self.assertTrue(self.zonemgr._module_cc.stopped)
def tearDown(self):
pass
......
......@@ -658,8 +658,11 @@ class Zonemgr:
def run(self):
self.running = True
while not self._shutdown_event.is_set():
self._module_cc.check_command(False)
try:
while not self._shutdown_event.is_set():
self._module_cc.check_command(False)
finally:
self._module_cc.send_stopping()
zonemgrd = None
......
......@@ -489,6 +489,18 @@ ModuleCCSession::ModuleCCSession(
}
ModuleCCSession::~ModuleCCSession() {
try {
sendStopping();
} catch (const std::exception& exc) {
LOG_ERROR(config_logger,
CONFIG_CCSESSION_STOPPING).arg(exc.what());
} catch (...) {
LOG_ERROR(config_logger,
CONFIG_CCSESSION_STOPPING_UNKNOWN);
}
};
void
ModuleCCSession::start() {
if (started_) {
......@@ -741,5 +753,16 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
}
}
void
ModuleCCSession::sendStopping() {
// Inform the configuration manager that this module is stopping
ConstElementPtr cmd(createCommand("stopping",
Element::fromJSON(
"{\"module_name\": \"" +
module_name_ + "\"}")));
// It's just an FYI, configmanager is not expected to respond.
session_.group_sendmsg(cmd, "ConfigManager");
}
}
}
......@@ -192,6 +192,14 @@ public:
bool handle_logging = true
);
///
/// Destructor
///
/// The destructor automatically calls sendStopping(), which sends
/// a message to the ConfigManager that this module is stopping
///
virtual ~ModuleCCSession();
/// Start receiving new commands and configuration changes asynchronously.
///
/// This method must be called only once, and only when the ModuleCCSession
......@@ -353,6 +361,7 @@ public:
private:
ModuleSpec readModuleSpecification(const std::string& filename);
void startCheck();
void sendStopping();
bool started_;
std::string module_name_;
......
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