diff --git a/Script/CastorScript.py b/Script/CastorScript.py index e1c6dd843906ba7b8e8f657ad953688a3fa9f078..27698a8505b62a4ab348cc31c9ff2386e2277874 100644 --- a/Script/CastorScript.py +++ b/Script/CastorScript.py @@ -16,6 +16,7 @@ from cs.Threads.CopyThread import CopyThread from cs.Threads.DeleteThread import DeleteThread from cs.Threads.CheckThread import CheckThread from cs.Threads.DdmMonitoringThread import DdmMonitoringThread +from cs.Threads.InformationServiceThread import InformationServiceThread import cs.Tools.ConfigParser as ConfigParser import cs.Tools.Constants as Constants import cs.Tools.LogConfig as LogConfig @@ -55,6 +56,7 @@ DdmMonitoringQueue = Queue(config.DdmMonitoringQueueMaxSize) # created even if manager = ManagerThread(config, event, CopyQueue, DeleteQueue, ClearQueue) copy = CopyThread(config, event, dbLock, CopyQueue, DeleteQueue, ClearQueue, DdmMonitoringQueue) +info_service_thread = InformationServiceThread(config, event) delete = None if config.DeleteEnabled: @@ -163,11 +165,12 @@ def main(conf): ##### Start the threads ##### manager.start() copy.start() + info_service_thread.start() if delete is not None: delete.start() if conf.ERSenabled and check is not None: check.start() - logger.info('Manager, Copy, Delete and Check Threads started') + logger.info('Manager, InfoService, Copy, Delete and Check Threads started') if conf.DdmMonitoringEnabled: ddm_publisher.start() logger.info('DdmMonitoring publisher started') @@ -226,6 +229,8 @@ def main(conf): if conf.DdmMonitoringEnabled: ddm_publisher.DdmMonitoringExit() + info_service_thread.info_service_exit() + ##### Wait for the threads to finish and then stop ##### manager.join() copy.join() @@ -235,6 +240,9 @@ def main(conf): check.join() logger.info('Manager,Copy, Delete and Check Threads joined the main thread') + info_service_thread.join() + logger.info('InformationService Thread joined') + if conf.DdmMonitoringEnabled: ddm_publisher.join() logger.info('DdmMonitoring thread joined') diff --git a/Script/TestSuite_UnitTests.py b/Script/TestSuite_UnitTests.py index 64c6a824326fb6138de67b1a2b82a6ad1d02d428..4c090ad605caf85f8cdfacb9be0d6ba8c1c58a47 100644 --- a/Script/TestSuite_UnitTests.py +++ b/Script/TestSuite_UnitTests.py @@ -2,6 +2,7 @@ import unittest from UnitTests import BaseFileNameParser_Test from UnitTests import LogConfig_Test from UnitTests import ConfigParser_Test +from UnitTests import InformationServiceThread_Test suite = unittest.TestSuite() loader = unittest.TestLoader() @@ -9,6 +10,7 @@ loader = unittest.TestLoader() suite.addTests(loader.loadTestsFromModule(BaseFileNameParser_Test)) suite.addTests(loader.loadTestsFromModule(LogConfig_Test)) suite.addTests(loader.loadTestsFromModule(ConfigParser_Test)) +suite.addTests(loader.loadTestsFromModule(InformationServiceThread_Test)) runner = unittest.TextTestRunner(verbosity=3) print("\n==============================================================") diff --git a/Script/UnitTests/InformationServiceThread_Test.py b/Script/UnitTests/InformationServiceThread_Test.py new file mode 100644 index 0000000000000000000000000000000000000000..8cd0c8a8dd6ecada81c6c0d2c28a0ab91622b76b --- /dev/null +++ b/Script/UnitTests/InformationServiceThread_Test.py @@ -0,0 +1,45 @@ +import unittest +import mock +import threading + +if __name__ == '__main__': + import sys + from os.path import dirname, abspath, join + ## add CastorScript/Script to path so imports keeps working + SCRIPT_DIR = abspath(join(dirname(__file__), '..')) + sys.path.append(SCRIPT_DIR) + +from cs.Threads.InformationServiceThread import InformationServiceThread +from cs.Tools.ConfigParser import ConfigHolder + +# as a developer I want to publish information to the information service +class TestInformationServiceThread(unittest.TestCase): + + def setUp(self): + ## we mock the event so that the thread won't be left hanging + self.mock_event = mock.create_autospec(threading.Event()) + kwargs = {"wait.return_value": True} + self.mock_event.configure_mock(**kwargs) + + ## we mock the configuration + self.mock_cfg = mock.create_autospec(ConfigHolder) + self.mock_cfg.configure_mock(partition="initial", LogDir="", LogLevel="") + +class TestInfoServiceExit(TestInformationServiceThread): + + def test_it_should_stop_thread_execution(self): + ## we're not interested in any logging for this test + with mock.patch("cs.Threads.InformationServiceThread.enable_file_logging"): + # I run the thread + info_thread = InformationServiceThread(self.mock_cfg, self.mock_event) + info_thread.start() + # and shut it down + info_thread.info_service_exit() + info_thread.join(3.0) + + # and I see that the thread has been killed + self.assertFalse(info_thread.is_alive(), msg="InformationService Thread didn't die") + +if __name__ == "__main__": + unittest.main() + diff --git a/Script/cs/Threads/InformationServiceThread.py b/Script/cs/Threads/InformationServiceThread.py new file mode 100644 index 0000000000000000000000000000000000000000..8cf3e6610aabad4eeab9ca957d6a58dc10b2a8ba --- /dev/null +++ b/Script/cs/Threads/InformationServiceThread.py @@ -0,0 +1,39 @@ +#!/bin/env python + +import threading +from cs.Tools.utils import thread_id_string +from cs.Tools.LogConfig import enable_file_logging + +class InformationServiceThread(threading.Thread): + + + def __init__(self,conf,event): + + threading.Thread.__init__(self, name="InformationServiceThread") + + self.conf = conf + self.event = event + + self.exit_flag = 0 + + # get logger to files, (Will also log to ERS) + self.logger = enable_file_logging("InformationServiceThread","InformationServiceLog.out", conf.LogDir, conf.LogLevel) + + # end def __init__() + + + def run(self): + + self.logger.info(thread_id_string()) + + while not self.exit_flag: + + # CODE HERE + self.event.wait(5) + # end while + self.logger.info('DeleteThread exited') + # end def run() + + def info_service_exit(self): + self.exit_flag = 1 + self.logger.info('Exit signal received') \ No newline at end of file