Add external-runner option to the launcher to allow for replacing the native evennia runner with an external process manager (like Linux' start-stop-daemon). Note that without a replacement, this option will make evennia un-reloadable.

This commit is contained in:
Griatch 2016-12-08 23:24:14 +01:00
parent ad13594a46
commit 293c3b077d
2 changed files with 29 additions and 7 deletions

View file

@ -1084,7 +1084,7 @@ def run_menu():
return return
def server_operation(mode, service, interactive, profiler, logserver=False): def server_operation(mode, service, interactive, profiler, logserver=False, doexit=False):
""" """
Handle argument options given on the command line. Handle argument options given on the command line.
@ -1095,6 +1095,10 @@ def server_operation(mode, service, interactive, profiler, logserver=False):
profiler (bool): Run the service under the profiler. profiler (bool): Run the service under the profiler.
logserver (bool, optional): Log Server data to logfile logserver (bool, optional): Log Server data to logfile
specified by settings.SERVER_LOG_FILE. specified by settings.SERVER_LOG_FILE.
doexit (bool, optional): If True, immediately exit the runner after
starting the relevant processes. If the runner exits, Evennia
cannot be reloaded. This is meant to be used with an external
process manager like Linux' start-stop-daemon.
""" """
@ -1136,6 +1140,8 @@ def server_operation(mode, service, interactive, profiler, logserver=False):
cmdstr.append('--logserver') cmdstr.append('--logserver')
django.core.management.call_command( django.core.management.call_command(
'collectstatic', verbosity=1, interactive=False) 'collectstatic', verbosity=1, interactive=False)
if doexit:
cmdstr.append('--doexit')
cmdstr.extend([ cmdstr.extend([
GAMEDIR, TWISTED_BINARY, SERVER_LOGFILE, GAMEDIR, TWISTED_BINARY, SERVER_LOGFILE,
PORTAL_LOGFILE, HTTP_LOGFILE]) PORTAL_LOGFILE, HTTP_LOGFILE])
@ -1242,6 +1248,10 @@ def main():
'--initsettings', action='store_true', dest="initsettings", '--initsettings', action='store_true', dest="initsettings",
default=False, default=False,
help="Create a new, empty settings file as gamedir/server/conf/settings.py.") help="Create a new, empty settings file as gamedir/server/conf/settings.py.")
parser.add_argument(
'--external-runner', action='store_true', dest="doexit",
default=False,
help="Handle server restart with an external process manager.")
parser.add_argument( parser.add_argument(
"operation", nargs='?', default="noop", "operation", nargs='?', default="noop",
help="Operation to perform: 'start', 'stop', 'reload' or 'menu'.") help="Operation to perform: 'start', 'stop', 'reload' or 'menu'.")
@ -1315,7 +1325,7 @@ def main():
elif option in ('start', 'reload', 'stop'): elif option in ('start', 'reload', 'stop'):
# operate the server directly # operate the server directly
init_game_directory(CURRENT_DIR, check_db=True) init_game_directory(CURRENT_DIR, check_db=True)
server_operation(option, service, args.interactive, args.profiler, args.logserver) server_operation(option, service, args.interactive, args.profiler, args.logserver, doexit=args.doexit)
elif option != "noop": elif option != "noop":
# pass-through to django manager # pass-through to django manager
check_db = False check_db = False

View file

@ -75,6 +75,7 @@ PROCESS_IOERROR = \
PROCESS_RESTART = "{component} restarting ..." PROCESS_RESTART = "{component} restarting ..."
PROCESS_DOEXIT = "Deferring to external runner."
# Functions # Functions
@ -134,7 +135,7 @@ def cycle_logfile(logfile):
# Start program management # Start program management
def start_services(server_argv, portal_argv): def start_services(server_argv, portal_argv, doexit=False):
""" """
This calls a threaded loop that launces the Portal and Server This calls a threaded loop that launces the Portal and Server
and then restarts them when they finish. and then restarts them when they finish.
@ -162,7 +163,7 @@ def start_services(server_argv, portal_argv):
if portal_argv: if portal_argv:
try: try:
if get_restart_mode(PORTAL_RESTART) == "True": if not doexit and get_restart_mode(PORTAL_RESTART) == "True":
# start portal as interactive, reloadable thread # start portal as interactive, reloadable thread
PORTAL = thread.start_new_thread(portal_waiter, (processes, )) PORTAL = thread.start_new_thread(portal_waiter, (processes, ))
else: else:
@ -175,12 +176,19 @@ def start_services(server_argv, portal_argv):
try: try:
if server_argv: if server_argv:
if doexit:
SERVER = Popen(server_argv, env=getenv())
else:
# start server as a reloadable thread # start server as a reloadable thread
SERVER = thread.start_new_thread(server_waiter, (processes, )) SERVER = thread.start_new_thread(server_waiter, (processes, ))
except IOError as e: except IOError as e:
print(PROCESS_IOERROR.format(component="Server", traceback=e)) print(PROCESS_IOERROR.format(component="Server", traceback=e))
return return
if doexit:
# Exit immediately
return
# Reload loop # Reload loop
while True: while True:
@ -234,6 +242,8 @@ def main():
default=False, help='Profile Portal') default=False, help='Profile Portal')
parser.add_argument('--nologcycle', action='store_false', dest='nologcycle', parser.add_argument('--nologcycle', action='store_false', dest='nologcycle',
default=True, help='Do not cycle log files') default=True, help='Do not cycle log files')
parser.add_argument('--doexit', action='store_true', dest='doexit',
default=False, help='Immediately exit after processes have started.')
parser.add_argument('gamedir', help="path to game dir") parser.add_argument('gamedir', help="path to game dir")
parser.add_argument('twistdbinary', help="path to twistd binary") parser.add_argument('twistdbinary', help="path to twistd binary")
parser.add_argument('slogfile', help="path to server log file") parser.add_argument('slogfile', help="path to server log file")
@ -327,6 +337,8 @@ def main():
if args.pportal: if args.pportal:
portal_argv.extend(pportal_argv) portal_argv.extend(pportal_argv)
print("\nRunning Evennia Portal under cProfile.") print("\nRunning Evennia Portal under cProfile.")
if args.doexit:
print(PROCESS_DOEXIT)
# Windows fixes (Windows don't support pidfiles natively) # Windows fixes (Windows don't support pidfiles natively)
if os.name == 'nt': if os.name == 'nt':
@ -336,7 +348,7 @@ def main():
del portal_argv[-2] del portal_argv[-2]
# Start processes # Start processes
start_services(server_argv, portal_argv) start_services(server_argv, portal_argv, doexit=args.doexit)
if __name__ == '__main__': if __name__ == '__main__':
main() main()