From 201f61d1f43ca5e044797d38dc6985941b09f744 Mon Sep 17 00:00:00 2001 From: David Trattnig <david.trattnig@o94.at> Date: Mon, 27 Apr 2020 16:37:30 +0200 Subject: [PATCH] Improved startup and shutdown. --- .vscode/launch.json | 27 ++++-- configuration/sample.engine.ini | 6 +- configuration/supervisor/aura-engine-api.conf | 2 +- configuration/supervisor/aura-engine-lqs.conf | 14 --- ...aura-engine-core.conf => aura-engine.conf} | 2 +- configuration/systemd/aura-engine-api.service | 3 +- .../systemd/aura-engine-liquidsoap.service | 13 --- ...ngine-core.service => aura-engine.service} | 0 engine-api.py | 2 +- engine-core.py | 86 ++++++++++++++++--- guru.py | 2 +- modules/communication/redis/adapter.py | 16 +++- modules/core/startup.py | 2 +- modules/scheduling/scheduler.py | 5 +- run.sh | 42 +++++---- 15 files changed, 150 insertions(+), 72 deletions(-) delete mode 100644 configuration/supervisor/aura-engine-lqs.conf rename configuration/supervisor/{aura-engine-core.conf => aura-engine.conf} (95%) delete mode 100644 configuration/systemd/aura-engine-liquidsoap.service rename configuration/systemd/{aura-engine-core.service => aura-engine.service} (100%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 127cf15f..56ec3c0f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,14 +5,34 @@ "version": "0.2.0", "configurations": [ { - "name": "Aura Engine - Run", + "name": "Start Aura Engine", "type": "python", "request": "launch", "program": "${workspaceFolder}/engine-core.py", "console": "integratedTerminal" }, { - "name": "Aura Engine API - Run", + "name": "Start Engine Core Only", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/engine-core.py", + "args": [ + "--without-lqs" + ], + "console": "integratedTerminal" + }, + { + "name": "Start Engine LQS Only", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/engine-core.py", + "args": [ + "--lqs-only" + ], + "console": "integratedTerminal" + }, + { + "name": "Start Engine API", "type": "python", "request": "launch", "stopOnEntry": false, @@ -23,9 +43,6 @@ }, "args": [ "run" - ], - "debugOptions": [ - "RedirectOutput" ] }, { diff --git a/configuration/sample.engine.ini b/configuration/sample.engine.ini index f387a475..565b8d6a 100644 --- a/configuration/sample.engine.ini +++ b/configuration/sample.engine.ini @@ -75,7 +75,11 @@ fade_out_time="2.5" # all these settings from here to the bottom require a restart of the liquidsoap server -# Liquidsoap execution delay; Crucial to keep things in sync +[lqs] +liquidsoap_path="/home/david/.opam/4.08.0/bin/liquidsoap" +liquidsoap_working_dir="modules/liquidsoap/" + +# Liquidsoap execution delay in seconds; Crucial to keep things in sync lqs_delay_offset=1 [user] diff --git a/configuration/supervisor/aura-engine-api.conf b/configuration/supervisor/aura-engine-api.conf index 49c38844..fb2a8944 100644 --- a/configuration/supervisor/aura-engine-api.conf +++ b/configuration/supervisor/aura-engine-api.conf @@ -2,7 +2,7 @@ [program:aura-engine-api] user = engineuser directory = /opt/aura/engine -command = /opt/aura/engine/run.sh api-prod +command = /opt/aura/engine/run.sh api priority = 999 autostart = true diff --git a/configuration/supervisor/aura-engine-lqs.conf b/configuration/supervisor/aura-engine-lqs.conf deleted file mode 100644 index e793be36..00000000 --- a/configuration/supervisor/aura-engine-lqs.conf +++ /dev/null @@ -1,14 +0,0 @@ -;/etc/supervisor/conf.d/aura-engine-lqs.conf -[program:aura-engine-lqs] -user = engineuser -directory = /opt/aura/engine -command = /opt/aura/engine/run.sh lqs - -priority = 666 -autostart = false -autorestart = false -stopsignal = TERM - -redirect_stderr = true -stdout_logfile = /var/log/aura/engine-lqs-stdout.log -stderr_logfile = /var/log/aura/engine-lqs-error.log \ No newline at end of file diff --git a/configuration/supervisor/aura-engine-core.conf b/configuration/supervisor/aura-engine.conf similarity index 95% rename from configuration/supervisor/aura-engine-core.conf rename to configuration/supervisor/aura-engine.conf index c1a8e353..b8c09109 100644 --- a/configuration/supervisor/aura-engine-core.conf +++ b/configuration/supervisor/aura-engine.conf @@ -4,7 +4,7 @@ user = engineuser directory = /opt/aura/engine command = /opt/aura/engine/run.sh engine -priority = 333 +priority = 666 autostart = true autorestart = true stopsignal = TERM diff --git a/configuration/systemd/aura-engine-api.service b/configuration/systemd/aura-engine-api.service index 3a65a601..4659be79 100644 --- a/configuration/systemd/aura-engine-api.service +++ b/configuration/systemd/aura-engine-api.service @@ -6,8 +6,7 @@ After=network.target Type=simple User=engineuser WorkingDirectory=/opt/Code/aura/engine -ExecStart=/opt/aura/engine/run.sh api-prod -ExecStop=/opt/aura/engine/guru.py --shutdown --quiet +ExecStart=/opt/aura/engine/run.sh api Restart=always [Install] diff --git a/configuration/systemd/aura-engine-liquidsoap.service b/configuration/systemd/aura-engine-liquidsoap.service deleted file mode 100644 index a0b445ae..00000000 --- a/configuration/systemd/aura-engine-liquidsoap.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Aura Engine - Liquidsoap Server -After=network.target aura-engine.service -Wants=aura-engine.service - -[Service] -Type=simple -User=engineuser -ExecStart=/usr/bin/liquidsoap /opt/aura/engine/modules/liquidsoap/engine.liq -Restart=always - -[Install] -WantedBy=multi-user.target diff --git a/configuration/systemd/aura-engine-core.service b/configuration/systemd/aura-engine.service similarity index 100% rename from configuration/systemd/aura-engine-core.service rename to configuration/systemd/aura-engine.service diff --git a/engine-api.py b/engine-api.py index c24ab335..fc025a0d 100644 --- a/engine-api.py +++ b/engine-api.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.7 +#!/usr/bin/env python3.7 # # Aura Engine diff --git a/engine-core.py b/engine-core.py index b327db38..a8c45d2f 100755 --- a/engine-core.py +++ b/engine-core.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.7 +#!/usr/bin/env python3.7 # # Aura Engine @@ -67,6 +67,8 @@ class AuraEngine: controller = None scheduler = None lqs = None + lqs_startup = None + def __init__(self): @@ -74,18 +76,23 @@ class AuraEngine: Initializes Engine Core. """ self.config = config - AuraLogger(self.config) - self.logger = logging.getLogger("AuraEngine") - def startup(self): + + def startup(self, lqs_startup): """ Starts Engine Core. """ + AuraLogger(self.config) + self.logger = logging.getLogger("AuraEngine") + from modules.scheduling.scheduler import AuraScheduler from modules.core.engine import SoundSystem from modules.communication.redis.adapter import ServerRedisAdapter + # If Liquidsoap should be started automatically + self.lqs_startup = lqs_startup + # Check if the database has to be re-created if self.config.get("recreate_db") is not None: AuraScheduler(self.config, None, None) @@ -109,19 +116,66 @@ class AuraEngine: Called when the engine is initialized, before the Liquidsoap connection is established." """ self.logger.info(SimpleUtil.green("Engine Core initialized - Waiting for Liquidsoap connection ...")) - cwd = os.getcwd() - cwd += "/modules/liquidsoap/" - command = "/home/david/.opam/4.08.0/bin/liquidsoap" - self.lqs = subprocess.Popen([command, "engine.liq"], cwd=cwd, stdout=subprocess.PIPE, shell=False) + if self.lqs_startup: + self.start_lqs(False, False) + else: + self.logger.info(SimpleUtil.yellow("Please note, Liquidsoap needs to be started manually.")) + + + + def start_lqs(self, debug_output, verbose_output): + """ + Starts Liquidsoap. + """ + lqs_path = self.config.get("liquidsoap_path") + lqs_cwd = os.getcwd() + "/" + self.config.get("liquidsoap_working_dir") + lqs_output = "" + lqs_output = self.get_debug_flags(debug_output, verbose_output) + + self.lqs = subprocess.Popen([lqs_path, lqs_output, "engine.liq"], \ + cwd=lqs_cwd, \ + stdout=subprocess.PIPE, \ + shell=False) + + + + def get_lqs_cmd(self, debug_output, verbose_output): + """ + Returns a shell command string to start Liquidsoap + """ + lqs_path = self.config.get("liquidsoap_path") + lqs_cwd = os.getcwd() + "/" + self.config.get("liquidsoap_working_dir") + lqs_output = self.get_debug_flags(debug_output, verbose_output) + return "(cd %s && %s %s ./engine.liq)" % (lqs_cwd, lqs_path, lqs_output) + + + + def get_debug_flags(self, debug_output, verbose_output): + """ + Build Liquidssoap debug parameters. + """ + output = "" + if debug_output: + output += "--debug " + if verbose_output: + output += "--verbose " + return output def exit_gracefully(self, signum, frame): """ - Shutdown of the engine. Also terminates the liquidsoap thread. + Shutdown of the engine. Also terminates the Liquidsoap thread. """ - self.lqs.terminate() + if self.lqs: + self.lqs.terminate() + self.logger.info("Terminated Liquidsoap") + + if self.messenger: + self.messenger.terminate() + self.logger.info("Gracefully terminated Aura Engine!" + str(self.lqs)) + sys.exit(0) @@ -131,15 +185,25 @@ class AuraEngine: if __name__ == "__main__": + engine = AuraEngine() + lqs_startup = True + lqs_cmd = False signal.signal(signal.SIGINT, engine.exit_gracefully) signal.signal(signal.SIGTERM, engine.exit_gracefully) if len(sys.argv) >= 2: + if "--without-lqs" in sys.argv: + lqs_startup = False + if "--get-lqs-command" in sys.argv: + lqs_cmd = True if "--use-test-data" in sys.argv: engine.config.set("use_test_data", True) if "--recreate-database" in sys.argv: engine.config.set("recreate_db", True) - engine.startup() + if lqs_cmd: + print(engine.get_lqs_cmd(True, True)) + else: + engine.startup(lqs_startup) diff --git a/guru.py b/guru.py index e0130a90..240769ba 100755 --- a/guru.py +++ b/guru.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.7 +#!/usr/bin/env python3.7 # # engine diff --git a/modules/communication/redis/adapter.py b/modules/communication/redis/adapter.py index 48db04bb..acba10b6 100644 --- a/modules/communication/redis/adapter.py +++ b/modules/communication/redis/adapter.py @@ -148,10 +148,7 @@ class ServerRedisAdapter(threading.Thread, RedisMessenger): self.execute(RedisChannel.FNP_REPLY.value, self.scheduler.get_act_programme_as_string) elif item["data"] == "shutdown": - self.shutdown_event.set() - self.scheduler.stop() - self.pubsub.close() - self.logger.info("shutdown event received. Bye bye...") + self.terminate() elif item["data"] == "init_player": self.execute(RedisChannel.IP_REPLY.value, self.soundsystem.init_player) @@ -259,6 +256,17 @@ class ServerRedisAdapter(threading.Thread, RedisMessenger): self.logger.warning("cannot send message via REDIS: "+str(message)) + + def terminate(self): + """ + Called when thread is stopped or a signal to terminate is received. + """ + self.shutdown_event.set() + self.scheduler.terminate() + self.pubsub.close() + self.logger.info("Shutdown event received. Bye bye ...") + + # ------------------------------------------------------------------------------------------ # class ClientRedisAdapter(RedisMessenger): diff --git a/modules/core/startup.py b/modules/core/startup.py index 66c7bde6..6ba23638 100644 --- a/modules/core/startup.py +++ b/modules/core/startup.py @@ -64,7 +64,7 @@ class StartupThread(threading.Thread): except NoActiveScheduleException as e: self.logger.info("Nothing scheduled at startup time. Please check if there are follow-up schedules.") except Exception as e: - self.logger.error("Error while initializing the soundsystem: " + str(e)) + self.logger.error(SimpleUtil.red("Error while initializing the sound-system: " + str(e))) diff --git a/modules/scheduling/scheduler.py b/modules/scheduling/scheduler.py index 406cdf91..68af909b 100644 --- a/modules/scheduling/scheduler.py +++ b/modules/scheduling/scheduler.py @@ -798,11 +798,12 @@ class AuraScheduler(ExceptionLogger, threading.Thread): - def stop(self): + def terminate(self): """ - Called when thread is stopped. + Called when thread is stopped or a signal to terminate is received. """ self.exit_event.set() + self.logger.info("Shutting down scheduler ...") # ------------------------------------------------------------------------------------------ # diff --git a/run.sh b/run.sh index fb7682f4..86ceeb69 100755 --- a/run.sh +++ b/run.sh @@ -1,35 +1,47 @@ -#/usr/bin/bash +#!/bin/bash mode="engine" -debug="--debug" -#debug="--debug --verbose" -if [ -n "$1" ]; then - if [[ $1 =~ ^(engine|lqs|api|api-prod)$ ]]; then - mode=$1 - fi +if [[ $* =~ ^(engine|core|lqs|api-dev|api)$ ]]; then + mode=$1 fi echo "[ Run mode=$mode ]" -if [ $mode == "engine" ]; then - /usr/bin/python3.7 aura.py + +### Runs Engine Core & Liquidsoap ### + +if [[ $mode == "engine" ]]; then + /usr/bin/env python3.7 engine-core.py +fi + +### Runs Engine Core only ### + +if [[ $mode == "core" ]]; then + /usr/bin/env python3.7 engine-core.py --without-lqs fi -if [ $mode == "lqs" ]; then - (cd modules/liquidsoap/ && liquidsoap $debug ./engine.liq) +### Runs Liquidsoap only ### + +if [[ $mode == "lqs" ]]; then + lqs=$(/usr/bin/env python3.7 engine-core.py --get-lqs-command) + eval "$lqs" fi -if [ $mode == "api" ]; then +### Runs the API Server (Development) ### + +if [[ $mode == "api-dev" ]]; then echo "Building Web Applications" sh ./script/build-web.sh echo "Starting API Server" - /usr/bin/python3.7 api.py + /usr/bin/env python3.7 engine-api.py fi -if [ $mode == "api-prod" ]; then +### Runs the API Server (Production) ### + +if [[ $mode == "api" ]]; then echo "Building Web Applications" sh ./script/build-web.sh echo "Starting API Server" - gunicorn -c configuration/gunicorn.conf.py api:app + gunicorn -c configuration/gunicorn.conf.py engine-api:app fi \ No newline at end of file -- GitLab