|
@@ -0,0 +1,293 @@
|
|
|
+"""
|
|
|
+Utilities and mock modules for unittests of statistics modules
|
|
|
+
|
|
|
+"""
|
|
|
+import os
|
|
|
+import io
|
|
|
+import time
|
|
|
+import sys
|
|
|
+import threading
|
|
|
+import tempfile
|
|
|
+
|
|
|
+import msgq
|
|
|
+import isc.config.cfgmgr
|
|
|
+import stats
|
|
|
+import stats_httpd
|
|
|
+
|
|
|
+# TODO: consider appropriate timeout seconds
|
|
|
+TIMEOUT_SEC = 0.01
|
|
|
+
|
|
|
+def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC*2):
|
|
|
+ if not session:
|
|
|
+ cc_session = isc.cc.Session()
|
|
|
+ else:
|
|
|
+ cc_session = session
|
|
|
+ orig_timeout = cc_session.get_timeout()
|
|
|
+ cc_session.set_timeout(timeout * 1000)
|
|
|
+ command = isc.config.ccsession.create_command(command_name, params)
|
|
|
+ seq = cc_session.group_sendmsg(command, module_name)
|
|
|
+ try:
|
|
|
+ (answer, env) = cc_session.group_recvmsg(nonblock, seq)
|
|
|
+ if answer:
|
|
|
+ return isc.config.ccsession.parse_answer(answer)
|
|
|
+ except isc.cc.SessionTimeout:
|
|
|
+ pass
|
|
|
+ finally:
|
|
|
+ if not session:
|
|
|
+ cc_session.close()
|
|
|
+ else:
|
|
|
+ cc_session.set_timeout(orig_timeout)
|
|
|
+
|
|
|
+def send_shutdown(module_name):
|
|
|
+ return send_command("shutdown", module_name)
|
|
|
+
|
|
|
+class ThreadingServerManager:
|
|
|
+ def __init__(self, server_class, verbose):
|
|
|
+ self.server_class = server_class
|
|
|
+ self.server_class_name = server_class.__name__
|
|
|
+ self.verbose = verbose
|
|
|
+ self.server = self.server_class(self.verbose)
|
|
|
+ self.server._thread = threading.Thread(
|
|
|
+ name=self.server_class_name, target=self.server.run)
|
|
|
+ self.server._thread.daemon = True
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self.server._thread.start()
|
|
|
+ self.server._started.wait()
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ self.server.shutdown()
|
|
|
+ self.server._thread.join(TIMEOUT_SEC)
|
|
|
+
|
|
|
+class MockMsgq:
|
|
|
+ def __init__(self, verbose):
|
|
|
+ self.verbose = verbose
|
|
|
+ self._started = threading.Event()
|
|
|
+ self.msgq = msgq.MsgQ(None, verbose)
|
|
|
+ result = self.msgq.setup()
|
|
|
+ if result:
|
|
|
+ sys.exit("Error on Msgq startup: %s" % result)
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self._started.set()
|
|
|
+ try:
|
|
|
+ self.msgq.run()
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+ finally:
|
|
|
+ self.shutdown()
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ self.msgq.shutdown()
|
|
|
+
|
|
|
+class MockCfgmgr:
|
|
|
+ def __init__(self, verbose):
|
|
|
+ self._started = threading.Event()
|
|
|
+ self.cfgmgr = isc.config.cfgmgr.ConfigManager(
|
|
|
+ os.environ['CONFIG_TESTDATA_PATH'], "b10-config.db")
|
|
|
+ self.cfgmgr.read_config()
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self._started.set()
|
|
|
+ try:
|
|
|
+ self.cfgmgr.run()
|
|
|
+ finally:
|
|
|
+ self.shutdown()
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ self.cfgmgr.running = False
|
|
|
+
|
|
|
+class MockBoss:
|
|
|
+ spec_str = """\
|
|
|
+{
|
|
|
+ "module_spec": {
|
|
|
+ "module_name": "Boss",
|
|
|
+ "module_description": "Mock Master process",
|
|
|
+ "config_data": [],
|
|
|
+ "commands": [
|
|
|
+ {
|
|
|
+ "command_name": "sendstats",
|
|
|
+ "command_description": "Send data to a statistics module at once",
|
|
|
+ "command_args": []
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "statistics": [
|
|
|
+ {
|
|
|
+ "item_name": "boot_time",
|
|
|
+ "item_type": "string",
|
|
|
+ "item_optional": false,
|
|
|
+ "item_default": "1970-01-01T00:00:00Z",
|
|
|
+ "item_title": "Boot time",
|
|
|
+ "item_description": "A date time when bind10 process starts initially",
|
|
|
+ "item_format": "date-time"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+"""
|
|
|
+ _BASETIME = (2011, 6, 22, 8, 14, 8, 2, 173, 0)
|
|
|
+
|
|
|
+ def __init__(self, verbose):
|
|
|
+ self.verbose = verbose
|
|
|
+ self._started = threading.Event()
|
|
|
+ self.running = False
|
|
|
+ self.spec_file = io.StringIO(self.spec_str)
|
|
|
+ # create ModuleCCSession object
|
|
|
+ self.mccs = isc.config.ModuleCCSession(
|
|
|
+ self.spec_file,
|
|
|
+ self.config_handler,
|
|
|
+ self.command_handler)
|
|
|
+ self.spec_file.close()
|
|
|
+ self.cc_session = self.mccs._session
|
|
|
+ self.got_command_name = ''
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self.mccs.start()
|
|
|
+ self.running = True
|
|
|
+ self._started.set()
|
|
|
+ while self.running:
|
|
|
+ self.mccs.check_command(False)
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ self.running = False
|
|
|
+
|
|
|
+ def config_handler(self, new_config):
|
|
|
+ return isc.config.create_answer(0)
|
|
|
+
|
|
|
+ def command_handler(self, command, *args, **kwargs):
|
|
|
+ self.got_command_name = command
|
|
|
+ if command == 'sendstats':
|
|
|
+ params = { "owner": "Boss",
|
|
|
+ "data": {
|
|
|
+ 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return send_command("set", "Stats", params=params, session=self.cc_session)
|
|
|
+ return isc.config.create_answer(1, "Unknown Command")
|
|
|
+
|
|
|
+class MockAuth:
|
|
|
+ spec_str = """\
|
|
|
+{
|
|
|
+ "module_spec": {
|
|
|
+ "module_name": "Auth",
|
|
|
+ "module_description": "Mock Authoritative service",
|
|
|
+ "config_data": [],
|
|
|
+ "commands": [
|
|
|
+ {
|
|
|
+ "command_name": "sendstats",
|
|
|
+ "command_description": "Send data to a statistics module at once",
|
|
|
+ "command_args": []
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "statistics": [
|
|
|
+ {
|
|
|
+ "item_name": "queries.tcp",
|
|
|
+ "item_type": "integer",
|
|
|
+ "item_optional": false,
|
|
|
+ "item_default": 0,
|
|
|
+ "item_title": "Queries TCP ",
|
|
|
+ "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "item_name": "queries.udp",
|
|
|
+ "item_type": "integer",
|
|
|
+ "item_optional": false,
|
|
|
+ "item_default": 0,
|
|
|
+ "item_title": "Queries UDP",
|
|
|
+ "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+"""
|
|
|
+ def __init__(self, verbose):
|
|
|
+ self.verbose = verbose
|
|
|
+ self._started = threading.Event()
|
|
|
+ self.running = False
|
|
|
+ self.spec_file = io.StringIO(self.spec_str)
|
|
|
+ # create ModuleCCSession object
|
|
|
+ self.mccs = isc.config.ModuleCCSession(
|
|
|
+ self.spec_file,
|
|
|
+ self.config_handler,
|
|
|
+ self.command_handler)
|
|
|
+ self.spec_file.close()
|
|
|
+ self.cc_session = self.mccs._session
|
|
|
+ self.got_command_name = ''
|
|
|
+ self.queries_tcp = 3
|
|
|
+ self.queries_udp = 2
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self.mccs.start()
|
|
|
+ self.running = True
|
|
|
+ self._started.set()
|
|
|
+ while self.running:
|
|
|
+ self.mccs.check_command(False)
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ self.running = False
|
|
|
+
|
|
|
+ def config_handler(self, new_config):
|
|
|
+ return isc.config.create_answer(0)
|
|
|
+
|
|
|
+ def command_handler(self, command, *args, **kwargs):
|
|
|
+ self.got_command_name = command
|
|
|
+ if command == 'sendstats':
|
|
|
+ params = { "owner": "Auth",
|
|
|
+ "data": { 'queries.tcp': self.queries_tcp,
|
|
|
+ 'queries.udp': self.queries_udp } }
|
|
|
+ return send_command("set", "Stats", params=params, session=self.cc_session)
|
|
|
+ return isc.config.create_answer(1, "Unknown Command")
|
|
|
+
|
|
|
+class MyStats(stats.Stats):
|
|
|
+ def __init__(self, verbose):
|
|
|
+ self._started = threading.Event()
|
|
|
+ stats.Stats.__init__(self, verbose)
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self._started.set()
|
|
|
+ stats.Stats.start(self)
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ send_shutdown("Stats")
|
|
|
+
|
|
|
+class MyStatsHttpd(stats_httpd.StatsHttpd):
|
|
|
+ def __init__(self, verbose):
|
|
|
+ self._started = threading.Event()
|
|
|
+ stats_httpd.StatsHttpd.__init__(self, verbose)
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self._started.set()
|
|
|
+ stats_httpd.StatsHttpd.start(self)
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ send_shutdown("StatsHttpd")
|
|
|
+
|
|
|
+class BaseModules:
|
|
|
+ def __init__(self, verbose):
|
|
|
+ self.verbose = verbose
|
|
|
+ self.class_name = BaseModules.__name__
|
|
|
+
|
|
|
+ # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables
|
|
|
+ os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='unix_socket.')
|
|
|
+ # MockMsgq
|
|
|
+ self.msgq = ThreadingServerManager(MockMsgq, self.verbose)
|
|
|
+ self.msgq.run()
|
|
|
+ # MockCfgmgr
|
|
|
+ self.cfgmgr = ThreadingServerManager(MockCfgmgr, self.verbose)
|
|
|
+ self.cfgmgr.run()
|
|
|
+ # MockBoss
|
|
|
+ self.boss = ThreadingServerManager(MockBoss, self.verbose)
|
|
|
+ self.boss.run()
|
|
|
+ # MockAuth
|
|
|
+ self.auth = ThreadingServerManager(MockAuth, self.verbose)
|
|
|
+ self.auth.run()
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ # MockAuth
|
|
|
+ self.auth.shutdown()
|
|
|
+ # MockBoss
|
|
|
+ self.boss.shutdown()
|
|
|
+ # MockCfgmgr
|
|
|
+ self.cfgmgr.shutdown()
|
|
|
+ # MockMsgq
|
|
|
+ self.msgq.shutdown()
|