-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.py
More file actions
executable file
·133 lines (108 loc) · 4.86 KB
/
server.py
File metadata and controls
executable file
·133 lines (108 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/python3
import argparse
import errno
import logging
import os
import signal
import sys
import threading
import traceback
import typing
import yaml
from server.logger_formatter import logging_setup
from server.machine import Machine
from server.print_manager import ConsoleCursesManager
# Logger name in the main server thread
PARENT_LOGGER_NAME: str = os.path.basename(str(__file__).lower().replace(".py", ""))
# Those global variables are necessary to stop all the threads when an exception is raised
# Machine List
MACHINE_LIST: list = list()
CONSOLE_CURSES_MANAGER: typing.Optional[ConsoleCursesManager] = None
THREAD_JOIN_TIMEOUT: float = 1.0
def __end_daemon_machines():
# FIXME: This does not work when the end is before the threads are not started yet
""" General end for all machines """
logger = logging.getLogger(name=PARENT_LOGGER_NAME)
logger.info("Stopping all threads")
for machine in MACHINE_LIST:
machine.stop()
logger.info("Waiting for all threads to join")
for machine in MACHINE_LIST:
machine.join(timeout=THREAD_JOIN_TIMEOUT)
if CONSOLE_CURSES_MANAGER is not None:
CONSOLE_CURSES_MANAGER.stop()
CONSOLE_CURSES_MANAGER.join()
def __machine_thread_exception_handler(args: threading.ExceptHookArgs):
""" It handles the exception on the Machine threads
The args argument has the following attributes:
exc_type: Exception type --> DEPRECATED after Python 3.10, the value is ignored
exc_value: Exception value can be None.
exc_traceback: Exception trace-back can be None.
thread: Thread, which raised the exception, can be None. """
# FIXME: some exceptions are problematic as not all attributes are available
logger = logging.getLogger(name=PARENT_LOGGER_NAME)
exception_str = "".join(
traceback.format_exception(args.exc_type, value=args.exc_value, tb=args.exc_traceback)
)
logger.error(f"Error {exception_str} at Machine thread:{args.thread}")
# Log the thread that raises the exception
__end_daemon_machines()
sys.exit(errno.ECHILD)
def __ctrlc_handler(signum, frame):
""" Signal handler to be attached
"""
logger = logging.getLogger(name=PARENT_LOGGER_NAME)
logger.error(
f"KeyboardInterrupt detected, exiting gracefully!( at least trying :) ). signum:{signum} frame:{frame}")
logger.info("Stopping all threads")
__end_daemon_machines()
sys.exit(130)
def main():
""" Main function """
# The First thing is to guarantee that python >=3.10 is running
if sys.version_info.major < 3 or sys.version_info.minor < 10:
raise ValueError("Python 3.10 or greater required")
# Attach CTRL-C pressing to the function
signal.signal(signal.SIGINT, __ctrlc_handler)
# Argument reading
parser = argparse.ArgumentParser(description='Server to monitor radiation experiments')
parser.add_argument('-c', '--config', metavar='PATH_YAML_FILE', type=str, default="server_parameters.yaml",
help='Path to an YAML FILE that contains the server parameters. '
'Default is ./server_parameters.yaml')
parser.add_argument('--enable_curses', default=False, action="store_true", help='Enable curses display')
args = parser.parse_args()
# load yaml file
with open(args.config, 'r') as fp:
server_parameters = yaml.load(fp, Loader=yaml.SafeLoader)
server_log_file = server_parameters['server_log_file']
server_log_store_dir = server_parameters['server_log_store_dir']
server_ip = server_parameters['server_ip']
# log in the stdout
global CONSOLE_CURSES_MANAGER
if args.enable_curses is True:
CONSOLE_CURSES_MANAGER = ConsoleCursesManager(daemon=True)
CONSOLE_CURSES_MANAGER.start()
logger = logging_setup(logger_name=PARENT_LOGGER_NAME, log_file=server_log_file, enable_curses=args.enable_curses)
logger.info(f"Python version: {sys.version_info.major}.{sys.version_info.minor} machine:{server_ip}")
# If a path does not exist, create it
if os.path.isdir(server_log_store_dir) is False:
os.mkdir(server_log_store_dir)
# noinspection SpellCheckingInspection
# set the exception hook
threading.excepthook = __machine_thread_exception_handler
try:
# Start the server threads
for m in server_parameters["machines"]:
if m['enabled']:
machine = Machine(configuration_file=m["cfg_file"], server_ip=server_ip, logger_name=PARENT_LOGGER_NAME,
server_log_path=server_log_store_dir)
logger.info(f"Starting a new thread to listen at {machine}")
machine.start()
MACHINE_LIST.append(machine)
except Exception as err:
logger.exception(f"General exception:{err}")
__end_daemon_machines()
# Unknown exit
sys.exit(-1)
if __name__ == '__main__':
main()