mirror of
https://github.com/avatao-content/baseimage-tutorial-framework
synced 2024-11-25 11:41:33 +00:00
Separate independent classes from built-in event handlers
This commit is contained in:
parent
f6a369496d
commit
fbe60de968
@ -1,13 +1,12 @@
|
|||||||
from .tfw_server_connector import TFWServerUplinkConnector, TFWServerConnector
|
|
||||||
from .event_handler import EventHandler
|
|
||||||
from .fsm_aware_event_handler import FSMAwareEventHandler
|
|
||||||
from .directory_snapshotting_event_handler import DirectorySnapshottingEventHandler
|
from .directory_snapshotting_event_handler import DirectorySnapshottingEventHandler
|
||||||
|
from .event_handler import EventHandler
|
||||||
from .frontend_event_handler import FrontendEventHandler
|
from .frontend_event_handler import FrontendEventHandler
|
||||||
|
from .fsm_aware_event_handler import FSMAwareEventHandler
|
||||||
from .fsm_managing_event_handler import FSMManagingEventHandler
|
from .fsm_managing_event_handler import FSMManagingEventHandler
|
||||||
from .ide_event_handler import IdeEventHandler
|
from .ide_event_handler import IdeEventHandler
|
||||||
from .log_monitoring_event_handler import LogMonitoringEventHandler
|
from .log_monitoring_event_handler import LogMonitoringEventHandler
|
||||||
from .message_sender import MessageSender
|
|
||||||
from .pipe_io_event_handler import PipeIOEventHandlerBase, PipeIOEventHandler
|
from .pipe_io_event_handler import PipeIOEventHandlerBase, PipeIOEventHandler
|
||||||
from .pipe_io_event_handler import TransformerPipeIOEventHandler, CommandEventHandler
|
from .pipe_io_event_handler import TransformerPipeIOEventHandler, CommandEventHandler
|
||||||
from .process_managing_event_handler import ProcessManagingEventHandler
|
from .process_managing_event_handler import ProcessManagingEventHandler
|
||||||
from .terminal_event_handler import TerminalEventHandler
|
from .terminal_event_handler import TerminalEventHandler
|
||||||
|
from .tfw_server_connector import TFWServerUplinkConnector, TFWServerConnector
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
from abc import ABC, abstractmethod
|
|
||||||
from contextlib import suppress
|
|
||||||
|
|
||||||
from tfw.networking import Scope
|
from tfw.networking import Scope
|
||||||
|
from tfw.components import FrontendMessageStorage
|
||||||
|
|
||||||
from .message_sender import MessageSender
|
|
||||||
from .event_handler import EventHandler
|
from .event_handler import EventHandler
|
||||||
|
|
||||||
|
|
||||||
@ -22,43 +19,3 @@ class FrontendEventHandler(EventHandler):
|
|||||||
def recover_frontend(self):
|
def recover_frontend(self):
|
||||||
for message in self._frontend_message_storage.messages:
|
for message in self._frontend_message_storage.messages:
|
||||||
self.send_message(message)
|
self.send_message(message)
|
||||||
|
|
||||||
|
|
||||||
class MessageStorage(ABC):
|
|
||||||
def __init__(self):
|
|
||||||
self._messages = []
|
|
||||||
|
|
||||||
def save_message(self, message):
|
|
||||||
with suppress(KeyError, AttributeError):
|
|
||||||
if self._filter_message(message):
|
|
||||||
self._messages.extend(self._transform_message(message))
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _filter_message(self, message):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def _transform_message(self, message): # pylint: disable=no-self-use
|
|
||||||
yield message
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
self._messages.clear()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def messages(self):
|
|
||||||
yield from self._messages
|
|
||||||
|
|
||||||
|
|
||||||
class FrontendMessageStorage(MessageStorage):
|
|
||||||
def __init__(self, keys):
|
|
||||||
self._keys = keys
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def _filter_message(self, message):
|
|
||||||
key = message['key']
|
|
||||||
return key in self._keys
|
|
||||||
|
|
||||||
def _transform_message(self, message):
|
|
||||||
if message['key'] == 'queueMessages':
|
|
||||||
yield from MessageSender.generate_messages_from_queue(message)
|
|
||||||
else:
|
|
||||||
yield message
|
|
||||||
|
@ -2,6 +2,7 @@ import logging
|
|||||||
|
|
||||||
from tfw.crypto import KeyManager, sign_message, verify_message
|
from tfw.crypto import KeyManager, sign_message, verify_message
|
||||||
from tfw.networking import Scope
|
from tfw.networking import Scope
|
||||||
|
from tfw.components import FSMUpdater
|
||||||
|
|
||||||
from .event_handler import EventHandler
|
from .event_handler import EventHandler
|
||||||
|
|
||||||
@ -69,30 +70,3 @@ class FSMManagingEventHandler(EventHandler):
|
|||||||
"""
|
"""
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
class FSMUpdater:
|
|
||||||
def __init__(self, fsm):
|
|
||||||
self.fsm = fsm
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fsm_update(self):
|
|
||||||
return {
|
|
||||||
'key': 'fsm_update',
|
|
||||||
'data': self.fsm_update_data
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fsm_update_data(self):
|
|
||||||
valid_transitions = [
|
|
||||||
{'trigger': trigger}
|
|
||||||
for trigger in self.fsm.get_triggers(self.fsm.state)
|
|
||||||
]
|
|
||||||
last_fsm_event = self.fsm.event_log[-1]
|
|
||||||
last_fsm_event['timestamp'] = last_fsm_event['timestamp'].isoformat()
|
|
||||||
return {
|
|
||||||
'current_state': self.fsm.state,
|
|
||||||
'valid_transitions': valid_transitions,
|
|
||||||
'in_accepted_state': self.fsm.in_accepted_state,
|
|
||||||
'last_event': last_fsm_event
|
|
||||||
}
|
|
||||||
|
@ -1,52 +1,14 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from tfw.config import TFWENV
|
||||||
from tfw.networking import Scope
|
from tfw.networking import Scope
|
||||||
from tfw.components.inotify import InotifyObserver
|
from tfw.components import LogInotifyObserver
|
||||||
from tfw.mixins.supervisor_mixin import SupervisorLogMixin
|
|
||||||
|
|
||||||
from .event_handler import EventHandler
|
from .event_handler import EventHandler
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class LogInotifyObserver(InotifyObserver, SupervisorLogMixin):
|
|
||||||
def __init__(self, server_connector, process_name, log_tail=0):
|
|
||||||
self._prevent_log_recursion()
|
|
||||||
self._server_connector = server_connector
|
|
||||||
self._process_name = process_name
|
|
||||||
self.log_tail = log_tail
|
|
||||||
self._procinfo = None
|
|
||||||
InotifyObserver.__init__(self, self._get_logfiles())
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _prevent_log_recursion():
|
|
||||||
# This is done to prevent inotify event logs triggering themselves (infinite log recursion)
|
|
||||||
logging.getLogger('watchdog.observers.inotify_buffer').propagate = False
|
|
||||||
|
|
||||||
def _get_logfiles(self):
|
|
||||||
self._procinfo = self.supervisor.getProcessInfo(self._process_name)
|
|
||||||
return self._procinfo['stdout_logfile'], self._procinfo['stderr_logfile']
|
|
||||||
|
|
||||||
@property
|
|
||||||
def process_name(self):
|
|
||||||
return self._process_name
|
|
||||||
|
|
||||||
@process_name.setter
|
|
||||||
def process_name(self, process_name):
|
|
||||||
self._process_name = process_name
|
|
||||||
self.paths = self._get_logfiles()
|
|
||||||
|
|
||||||
def on_modified(self, event):
|
|
||||||
self._server_connector.send_message({
|
|
||||||
'key': 'processlog',
|
|
||||||
'data': {
|
|
||||||
'command': 'new_log',
|
|
||||||
'stdout': self.read_stdout(self.process_name, tail=self.log_tail),
|
|
||||||
'stderr': self.read_stderr(self.process_name, tail=self.log_tail)
|
|
||||||
}
|
|
||||||
}, Scope.BROADCAST)
|
|
||||||
|
|
||||||
|
|
||||||
class LogMonitoringEventHandler(EventHandler):
|
class LogMonitoringEventHandler(EventHandler):
|
||||||
"""
|
"""
|
||||||
Monitors the output of a supervisor process (stdout, stderr) and
|
Monitors the output of a supervisor process (stdout, stderr) and
|
||||||
@ -60,7 +22,12 @@ class LogMonitoringEventHandler(EventHandler):
|
|||||||
def __init__(self, key, process_name, log_tail=0):
|
def __init__(self, key, process_name, log_tail=0):
|
||||||
super().__init__(key, scope=Scope.WEBSOCKET)
|
super().__init__(key, scope=Scope.WEBSOCKET)
|
||||||
self.process_name = process_name
|
self.process_name = process_name
|
||||||
self._monitor = LogInotifyObserver(self.server_connector, process_name, log_tail)
|
self._monitor = LogInotifyObserver(
|
||||||
|
server_connector=self.server_connector,
|
||||||
|
supervisor_uri=TFWENV.SUPERVISOR_HTTP_URI,
|
||||||
|
process_name=process_name,
|
||||||
|
log_tail=log_tail
|
||||||
|
)
|
||||||
self._monitor.start()
|
self._monitor.start()
|
||||||
|
|
||||||
self.command_handlers = {
|
self.command_handlers = {
|
||||||
|
@ -1,27 +1,16 @@
|
|||||||
import logging
|
import logging
|
||||||
from xmlrpc.client import Fault as SupervisorFault
|
from xmlrpc.client import Fault as SupervisorFault
|
||||||
|
|
||||||
|
from tfw.config import TFWENV
|
||||||
from tfw.networking import Scope
|
from tfw.networking import Scope
|
||||||
from tfw.mixins.supervisor_mixin import SupervisorMixin, SupervisorLogMixin
|
from tfw.components import ProcessManager, LogManager
|
||||||
|
|
||||||
from .event_handler import EventHandler
|
from .event_handler import EventHandler
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ProcessManager(SupervisorMixin, SupervisorLogMixin):
|
class ProcessManagingEventHandler(EventHandler, ProcessManager, LogManager):
|
||||||
def __init__(self):
|
|
||||||
self.commands = {
|
|
||||||
'start': self.start_process,
|
|
||||||
'stop': self.stop_process,
|
|
||||||
'restart': self.restart_process
|
|
||||||
}
|
|
||||||
|
|
||||||
def __call__(self, command, process_name):
|
|
||||||
return self.commands[command](process_name)
|
|
||||||
|
|
||||||
|
|
||||||
class ProcessManagingEventHandler(EventHandler):
|
|
||||||
"""
|
"""
|
||||||
Event handler that can manage processes managed by supervisor.
|
Event handler that can manage processes managed by supervisor.
|
||||||
|
|
||||||
@ -35,23 +24,29 @@ class ProcessManagingEventHandler(EventHandler):
|
|||||||
(the names are as self-documenting as it gets)
|
(the names are as self-documenting as it gets)
|
||||||
"""
|
"""
|
||||||
def __init__(self, key, log_tail=0):
|
def __init__(self, key, log_tail=0):
|
||||||
super().__init__(key, scope=Scope.WEBSOCKET)
|
EventHandler.__init__(self, key, scope=Scope.WEBSOCKET)
|
||||||
self.processmanager = ProcessManager()
|
ProcessManager.__init__(self, TFWENV.SUPERVISOR_HTTP_URI)
|
||||||
|
LogManager.__init__(self, TFWENV.SUPERVISOR_HTTP_URI)
|
||||||
self.log_tail = log_tail
|
self.log_tail = log_tail
|
||||||
|
self.commands = {
|
||||||
|
'start': self.start_process,
|
||||||
|
'stop': self.stop_process,
|
||||||
|
'restart': self.restart_process
|
||||||
|
}
|
||||||
|
|
||||||
def handle_event(self, message):
|
def handle_event(self, message):
|
||||||
try:
|
try:
|
||||||
data = message['data']
|
data = message['data']
|
||||||
try:
|
try:
|
||||||
self.processmanager(data['command'], data['process_name'])
|
self.commands[data['command']](data['process_name'])
|
||||||
except SupervisorFault as fault:
|
except SupervisorFault as fault:
|
||||||
message['data']['error'] = fault.faultString
|
message['data']['error'] = fault.faultString
|
||||||
finally:
|
finally:
|
||||||
message['data']['stdout'] = self.processmanager.read_stdout(
|
message['data']['stdout'] = self.read_stdout(
|
||||||
data['process_name'],
|
data['process_name'],
|
||||||
self.log_tail
|
self.log_tail
|
||||||
)
|
)
|
||||||
message['data']['stderr'] = self.processmanager.read_stderr(
|
message['data']['stderr'] = self.read_stderr(
|
||||||
data['process_name'],
|
data['process_name'],
|
||||||
self.log_tail
|
self.log_tail
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from tfw.networking import Scope
|
from tfw.networking import Scope
|
||||||
from tfw.components import BashMonitor
|
from tfw.components import BashMonitor, TerminadoMiniServer
|
||||||
from tfw.components.terminado_mini_server import TerminadoMiniServer
|
|
||||||
from tfw.config import TFWENV
|
from tfw.config import TFWENV
|
||||||
from tao.config import TAOENV
|
from tao.config import TAOENV
|
||||||
|
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
from .commands_equal import CommandsEqual
|
from .commands_equal import CommandsEqual
|
||||||
from .file_manager import FileManager
|
from .file_manager import FileManager
|
||||||
from .history_monitor import HistoryMonitor, BashMonitor, GDBMonitor
|
|
||||||
from .snapshot_provider import SnapshotProvider
|
|
||||||
from .terminal_commands import TerminalCommands
|
|
||||||
from .fsm_aware import FSMAware
|
from .fsm_aware import FSMAware
|
||||||
|
from .fsm_updater import FSMUpdater
|
||||||
|
from .history_monitor import BashMonitor, GDBMonitor
|
||||||
|
from .log_inotify_observer import LogInotifyObserver
|
||||||
|
from .message_sender import MessageSender
|
||||||
|
from .message_storage import FrontendMessageStorage
|
||||||
|
from .snapshot_provider import SnapshotProvider
|
||||||
|
from .supervisor import ProcessManager, LogManager
|
||||||
|
from .terminado_mini_server import TerminadoMiniServer
|
||||||
|
from .terminal_commands import TerminalCommands
|
||||||
|
25
lib/tfw/components/fsm_updater.py
Normal file
25
lib/tfw/components/fsm_updater.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
class FSMUpdater:
|
||||||
|
def __init__(self, fsm):
|
||||||
|
self.fsm = fsm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fsm_update(self):
|
||||||
|
return {
|
||||||
|
'key': 'fsm_update',
|
||||||
|
'data': self.fsm_update_data
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fsm_update_data(self):
|
||||||
|
valid_transitions = [
|
||||||
|
{'trigger': trigger}
|
||||||
|
for trigger in self.fsm.get_triggers(self.fsm.state)
|
||||||
|
]
|
||||||
|
last_fsm_event = self.fsm.event_log[-1]
|
||||||
|
last_fsm_event['timestamp'] = last_fsm_event['timestamp'].isoformat()
|
||||||
|
return {
|
||||||
|
'current_state': self.fsm.state,
|
||||||
|
'valid_transitions': valid_transitions,
|
||||||
|
'in_accepted_state': self.fsm.in_accepted_state,
|
||||||
|
'last_event': last_fsm_event
|
||||||
|
}
|
45
lib/tfw/components/log_inotify_observer.py
Normal file
45
lib/tfw/components/log_inotify_observer.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from tfw.networking import Scope
|
||||||
|
|
||||||
|
from .inotify import InotifyObserver
|
||||||
|
from .supervisor import LogManager
|
||||||
|
|
||||||
|
|
||||||
|
class LogInotifyObserver(InotifyObserver, LogManager):
|
||||||
|
def __init__(self, server_connector, supervisor_uri, process_name, log_tail=0):
|
||||||
|
self._prevent_log_recursion()
|
||||||
|
self._server_connector = server_connector
|
||||||
|
self._process_name = process_name
|
||||||
|
self.log_tail = log_tail
|
||||||
|
self._procinfo = None
|
||||||
|
LogManager.__init__(self, supervisor_uri)
|
||||||
|
InotifyObserver.__init__(self, self._get_logfiles())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _prevent_log_recursion():
|
||||||
|
# This is done to prevent inotify event logs triggering themselves (infinite log recursion)
|
||||||
|
logging.getLogger('watchdog.observers.inotify_buffer').propagate = False
|
||||||
|
|
||||||
|
def _get_logfiles(self):
|
||||||
|
self._procinfo = self.supervisor.getProcessInfo(self._process_name)
|
||||||
|
return self._procinfo['stdout_logfile'], self._procinfo['stderr_logfile']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def process_name(self):
|
||||||
|
return self._process_name
|
||||||
|
|
||||||
|
@process_name.setter
|
||||||
|
def process_name(self, process_name):
|
||||||
|
self._process_name = process_name
|
||||||
|
self.paths = self._get_logfiles()
|
||||||
|
|
||||||
|
def on_modified(self, event):
|
||||||
|
self._server_connector.send_message({
|
||||||
|
'key': 'processlog',
|
||||||
|
'data': {
|
||||||
|
'command': 'new_log',
|
||||||
|
'stdout': self.read_stdout(self.process_name, tail=self.log_tail),
|
||||||
|
'stderr': self.read_stderr(self.process_name, tail=self.log_tail)
|
||||||
|
}
|
||||||
|
}, Scope.BROADCAST)
|
@ -1,12 +1,9 @@
|
|||||||
from .tfw_server_connector import TFWServerUplinkConnector
|
|
||||||
|
|
||||||
|
|
||||||
class MessageSender:
|
class MessageSender:
|
||||||
"""
|
"""
|
||||||
Provides mechanisms to send messages to our frontend messaging component.
|
Provides mechanisms to send messages to our frontend messaging component.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, uplink):
|
||||||
self.uplink = TFWServerUplinkConnector()
|
self.uplink = uplink
|
||||||
self.key = 'message'
|
self.key = 'message'
|
||||||
self.queue_key = 'queueMessages'
|
self.queue_key = 'queueMessages'
|
||||||
|
|
44
lib/tfw/components/message_storage.py
Normal file
44
lib/tfw/components/message_storage.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from contextlib import suppress
|
||||||
|
|
||||||
|
from .message_sender import MessageSender
|
||||||
|
|
||||||
|
|
||||||
|
class MessageStorage(ABC):
|
||||||
|
def __init__(self):
|
||||||
|
self._messages = []
|
||||||
|
|
||||||
|
def save_message(self, message):
|
||||||
|
with suppress(KeyError, AttributeError):
|
||||||
|
if self._filter_message(message):
|
||||||
|
self._messages.extend(self._transform_message(message))
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _filter_message(self, message):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _transform_message(self, message): # pylint: disable=no-self-use
|
||||||
|
yield message
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._messages.clear()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def messages(self):
|
||||||
|
yield from self._messages
|
||||||
|
|
||||||
|
|
||||||
|
class FrontendMessageStorage(MessageStorage):
|
||||||
|
def __init__(self, keys):
|
||||||
|
self._keys = keys
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def _filter_message(self, message):
|
||||||
|
key = message['key']
|
||||||
|
return key in self._keys
|
||||||
|
|
||||||
|
def _transform_message(self, message):
|
||||||
|
if message['key'] == 'queueMessages':
|
||||||
|
yield from MessageSender.generate_messages_from_queue(message)
|
||||||
|
else:
|
||||||
|
yield message
|
@ -1,20 +1,15 @@
|
|||||||
|
from os import remove
|
||||||
|
from contextlib import suppress
|
||||||
import xmlrpc.client
|
import xmlrpc.client
|
||||||
from xmlrpc.client import Fault as SupervisorFault
|
from xmlrpc.client import Fault as SupervisorFault
|
||||||
from contextlib import suppress
|
|
||||||
from os import remove
|
|
||||||
|
|
||||||
from tfw.decorators.lazy_property import lazy_property
|
|
||||||
from tfw.config import TFWENV
|
|
||||||
|
|
||||||
|
|
||||||
class SupervisorBaseMixin:
|
class SupervisorBase:
|
||||||
@lazy_property
|
def __init__(self, supervisor_uri):
|
||||||
def supervisor(self):
|
self.supervisor = xmlrpc.client.ServerProxy(supervisor_uri).supervisor
|
||||||
# pylint: disable=no-self-use
|
|
||||||
return xmlrpc.client.ServerProxy(TFWENV.SUPERVISOR_HTTP_URI).supervisor
|
|
||||||
|
|
||||||
|
|
||||||
class SupervisorMixin(SupervisorBaseMixin):
|
class ProcessManager(SupervisorBase):
|
||||||
def stop_process(self, process_name):
|
def stop_process(self, process_name):
|
||||||
with suppress(SupervisorFault):
|
with suppress(SupervisorFault):
|
||||||
self.supervisor.stopProcess(process_name)
|
self.supervisor.stopProcess(process_name)
|
||||||
@ -27,7 +22,7 @@ class SupervisorMixin(SupervisorBaseMixin):
|
|||||||
self.start_process(process_name)
|
self.start_process(process_name)
|
||||||
|
|
||||||
|
|
||||||
class SupervisorLogMixin(SupervisorBaseMixin):
|
class LogManager(SupervisorBase):
|
||||||
def read_stdout(self, process_name, tail=0):
|
def read_stdout(self, process_name, tail=0):
|
||||||
return self.supervisor.readProcessStdoutLog(process_name, -tail, 0)
|
return self.supervisor.readProcessStdoutLog(process_name, -tail, 0)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user