Parallel start/stop/reload systems, for testing
This commit is contained in:
parent
ff887a07ab
commit
5741eef9bc
5 changed files with 109 additions and 39 deletions
|
|
@ -58,7 +58,7 @@ class CmdReload(COMMAND_DEFAULT_CLASS):
|
||||||
if self.args:
|
if self.args:
|
||||||
reason = "(Reason: %s) " % self.args.rstrip(".")
|
reason = "(Reason: %s) " % self.args.rstrip(".")
|
||||||
SESSIONS.announce_all(" Server restart initiated %s..." % reason)
|
SESSIONS.announce_all(" Server restart initiated %s..." % reason)
|
||||||
SESSIONS.server.shutdown(mode='reload')
|
SESSIONS.portal_restart_server()
|
||||||
|
|
||||||
|
|
||||||
class CmdReset(COMMAND_DEFAULT_CLASS):
|
class CmdReset(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -91,7 +91,7 @@ class CmdReset(COMMAND_DEFAULT_CLASS):
|
||||||
Reload the system.
|
Reload the system.
|
||||||
"""
|
"""
|
||||||
SESSIONS.announce_all(" Server resetting/restarting ...")
|
SESSIONS.announce_all(" Server resetting/restarting ...")
|
||||||
SESSIONS.server.shutdown(mode='reset')
|
SESSIONS.portal_reset_server()
|
||||||
|
|
||||||
|
|
||||||
class CmdShutdown(COMMAND_DEFAULT_CLASS):
|
class CmdShutdown(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,16 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
"""
|
"""
|
||||||
# sending AMP data
|
# sending AMP data
|
||||||
|
|
||||||
|
def connectionMade(self):
|
||||||
|
"""
|
||||||
|
Called when a new connection is established.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super(AMPServerClientProtocol, self).connectionMade()
|
||||||
|
# first thing we do is to request the Portal to sync all sessions
|
||||||
|
# back with the Server side
|
||||||
|
self.send_AdminServer2Portal(amp.DUMMYSESSION, operation=amp.PSYNC)
|
||||||
|
|
||||||
def send_MsgServer2Portal(self, session, **kwargs):
|
def send_MsgServer2Portal(self, session, **kwargs):
|
||||||
"""
|
"""
|
||||||
Access method - executed on the Server for sending data
|
Access method - executed on the Server for sending data
|
||||||
|
|
@ -118,7 +128,7 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
operation (char, optional): Identifier for the server
|
operation (char, optional): Identifier for the server
|
||||||
operation, as defined by the global variables in
|
operation, as defined by the global variables in
|
||||||
`evennia/server/amp.py`.
|
`evennia/server/amp.py`.
|
||||||
data (str or dict, optional): Data going into the adminstrative.
|
kwargs (dict, optional): Data going into the adminstrative.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.data_out(amp.AdminServer2Portal, session.sessid, operation=operation, **kwargs)
|
return self.data_out(amp.AdminServer2Portal, session.sessid, operation=operation, **kwargs)
|
||||||
|
|
@ -180,12 +190,17 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
server_sessionhandler.portal_disconnect_all()
|
server_sessionhandler.portal_disconnect_all()
|
||||||
|
|
||||||
elif operation == amp.PSYNC: # portal_session_sync
|
elif operation == amp.PSYNC: # portal_session_sync
|
||||||
# force a resync of sessions when portal reconnects to
|
# force a resync of sessions from the portal side
|
||||||
# server (e.g. after a server reboot) the data kwarg
|
|
||||||
# contains a dict {sessid: {arg1:val1,...}}
|
|
||||||
# representing the attributes to sync for each
|
|
||||||
# session.
|
|
||||||
server_sessionhandler.portal_sessions_sync(kwargs.get("sessiondata"))
|
server_sessionhandler.portal_sessions_sync(kwargs.get("sessiondata"))
|
||||||
|
elif operation == amp.SRELOAD: # server reload
|
||||||
|
# shut down in reload mode
|
||||||
|
server_sessionhandler.server.shutdown(mode='reload')
|
||||||
|
elif operation == amp.SRESET:
|
||||||
|
# shut down in reset mode
|
||||||
|
server_sessionhandler.server.shutdown(mode='reset')
|
||||||
|
elif operation == amp.SSHUTD: # server shutdown
|
||||||
|
# shutdown in stop mode
|
||||||
|
server_sessionhandler.server.shutdown(mode='shutdown')
|
||||||
else:
|
else:
|
||||||
raise Exception("operation %(op)s not recognized." % {'op': operation})
|
raise Exception("operation %(op)s not recognized." % {'op': operation})
|
||||||
return {}
|
return {}
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,12 @@ SSYNC = chr(8) # server session sync
|
||||||
SCONN = chr(11) # server creating new connection (for irc bots and etc)
|
SCONN = chr(11) # server creating new connection (for irc bots and etc)
|
||||||
PCONNSYNC = chr(12) # portal post-syncing a session
|
PCONNSYNC = chr(12) # portal post-syncing a session
|
||||||
PDISCONNALL = chr(13) # portal session disconnect all
|
PDISCONNALL = chr(13) # portal session disconnect all
|
||||||
SRELOAD = chr(14) # server reloading (have portal start a new server)
|
SRELOAD = chr(14) # server shutdown in reload mode
|
||||||
SSTART = chr(15) # server start (portal must already be running anyway)
|
SSTART = chr(15) # server start (portal must already be running anyway)
|
||||||
PSHUTD = chr(16) # portal (+server) shutdown
|
PSHUTD = chr(16) # portal (+server) shutdown
|
||||||
SSHUTD = chr(17) # server-only shutdown
|
SSHUTD = chr(17) # server shutdown
|
||||||
PSTATUS = chr(18) # ping server or portal status
|
PSTATUS = chr(18) # ping server or portal status
|
||||||
|
SRESET = chr(19) # server shutdown in reset mode
|
||||||
|
|
||||||
AMP_MAXLEN = amp.MAX_VALUE_LENGTH # max allowed data length in AMP protocol (cannot be changed)
|
AMP_MAXLEN = amp.MAX_VALUE_LENGTH # max allowed data length in AMP protocol (cannot be changed)
|
||||||
BATCH_RATE = 250 # max commands/sec before switching to batch-sending
|
BATCH_RATE = 250 # max commands/sec before switching to batch-sending
|
||||||
|
|
@ -271,9 +272,22 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
else:
|
else:
|
||||||
super(AMPMultiConnectionProtocol, self).dataReceived(data)
|
super(AMPMultiConnectionProtocol, self).dataReceived(data)
|
||||||
|
|
||||||
|
def makeConnection(self, transport):
|
||||||
|
"""
|
||||||
|
Swallow connection log message here. Copied from original
|
||||||
|
in the amp protocol.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# copied from original, removing the log message
|
||||||
|
if not self._ampInitialized:
|
||||||
|
amp.AMP.__init__(self)
|
||||||
|
self._transportPeer = transport.getPeer()
|
||||||
|
self._transportHost = transport.getHost()
|
||||||
|
amp.BinaryBoxProtocol.makeConnection(self, transport)
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
"""
|
"""
|
||||||
This is called when an AMP connection is (re-)established AMP calls it on both sides.
|
This is called when an AMP connection is (re-)established. AMP calls it on both sides.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.factory.broadcasts.append(self)
|
self.factory.broadcasts.append(self)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import sys
|
||||||
from twisted.internet import protocol
|
from twisted.internet import protocol
|
||||||
from evennia.server.portal import amp
|
from evennia.server.portal import amp
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
|
from evennia.utils import logger
|
||||||
|
|
||||||
|
|
||||||
def getenv():
|
def getenv():
|
||||||
|
|
@ -69,20 +70,6 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
Protocol subclass for the AMP-server run by the Portal.
|
Protocol subclass for the AMP-server run by the Portal.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def connectionMade(self):
|
|
||||||
"""
|
|
||||||
Called when a new connection is established.
|
|
||||||
|
|
||||||
"""
|
|
||||||
super(AMPServerProtocol, self).connectionMade()
|
|
||||||
|
|
||||||
if len(self.factory.broadcasts) < 2:
|
|
||||||
sessdata = self.factory.portal.sessions.get_all_sync_data()
|
|
||||||
self.send_AdminPortal2Server(amp.DUMMYSESSION,
|
|
||||||
amp.PSYNC,
|
|
||||||
sessiondata=sessdata)
|
|
||||||
self.factory.portal.sessions.at_server_connection()
|
|
||||||
|
|
||||||
def start_server(self, server_twistd_cmd):
|
def start_server(self, server_twistd_cmd):
|
||||||
"""
|
"""
|
||||||
(Re-)Launch the Evennia server.
|
(Re-)Launch the Evennia server.
|
||||||
|
|
@ -92,10 +79,29 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
to pass to POpen to start the server.
|
to pass to POpen to start the server.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# start the server
|
# start the Server
|
||||||
process = Popen(server_twistd_cmd, env=getenv())
|
process = Popen(server_twistd_cmd, env=getenv())
|
||||||
# store the pid for future reference
|
# store the pid for future reference
|
||||||
self.portal.server_process_id = process.pid
|
self.factory.portal.server_process_id = process.pid
|
||||||
|
self.factory.portal.server_twistd_cmd = server_twistd_cmd
|
||||||
|
return process.pid
|
||||||
|
|
||||||
|
def stop_server(self, mode='reload'):
|
||||||
|
"""
|
||||||
|
Shut down server in one or more modes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mode (str): One of 'shutdown', 'reload' or 'reset'.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if mode == 'reload':
|
||||||
|
self.send_AdminPortal2Server(amp.DUMMYSESSION, amp.SRELOAD)
|
||||||
|
return self.start_server(self.factory.portal.server_twistd_cmd)
|
||||||
|
if mode == 'reset':
|
||||||
|
self.send_AdminPortal2Server(amp.DUMMYSESSION, amp.SRESET)
|
||||||
|
return self.start_server(self.factory.portal.server_twistd_cmd)
|
||||||
|
if mode == 'shutdown':
|
||||||
|
self.send_AdminPortal2Server(amp.DUMMYSESSION, amp.SSHUTD)
|
||||||
|
|
||||||
# sending amp data
|
# sending amp data
|
||||||
|
|
||||||
|
|
@ -173,28 +179,44 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
launcher. It can obviously only accessed when the Portal is already up and running.
|
launcher. It can obviously only accessed when the Portal is already up and running.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
def _retval(success, txt):
|
||||||
|
return {"result": amp.dumps((success, txt))}
|
||||||
|
|
||||||
server_connected = any(1 for prtcl in self.factory.broadcasts
|
server_connected = any(1 for prtcl in self.factory.broadcasts
|
||||||
if prtcl is not self and prtcl.transport.connected)
|
if prtcl is not self and prtcl.transport.connected)
|
||||||
|
server_pid = self.factory.portal.server_process_id
|
||||||
|
|
||||||
print("AMP SERVER operation == %s received" % (ord(operation)))
|
logger.log_msg("AMP SERVER operation == %s received" % (ord(operation)))
|
||||||
print("AMP SERVER arguments: %s" % (amp.loads(arguments)))
|
logger.log_msg("AMP SERVER arguments: %s" % (amp.loads(arguments)))
|
||||||
return {"result": "Received."}
|
|
||||||
|
|
||||||
if operation == amp.SSTART: # portal start
|
if operation == amp.SSTART: # portal start
|
||||||
# first, check if server is already running
|
# first, check if server is already running
|
||||||
if server_connected:
|
if server_connected:
|
||||||
return {"result": "Server already running at PID={}"}
|
return _retval(False,
|
||||||
|
"Server already running at PID={spid}".format(spid=server_pid))
|
||||||
else:
|
else:
|
||||||
self.start_server(amp.loads(arguments))
|
spid = self.start_server(amp.loads(arguments))
|
||||||
return {"result": "Server started with PID {}.".format(0)} # TODO
|
return _retval(True, "Server started with PID {spid}.".format(spid=spid))
|
||||||
elif operation == amp.SRELOAD: # reload server
|
elif operation == amp.SRELOAD: # reload server
|
||||||
if server_connected:
|
if server_connected:
|
||||||
self.reload_server(amp.loads(arguments))
|
spid = self.reload_server(amp.loads(arguments))
|
||||||
|
return _retval(True, "Server started with PID {spid}.".format(spid=spid))
|
||||||
else:
|
else:
|
||||||
self.start_server(amp.loads(arguments))
|
self.start_server(amp.loads(arguments))
|
||||||
|
spid = self.start_server(amp.loads(arguments))
|
||||||
|
return _retval(True, "Server started with PID {spid}.".format(spid=spid))
|
||||||
|
elif operation == amp.SRESET: # reload server
|
||||||
|
if server_connected:
|
||||||
|
spid = self.reload_server(amp.loads(arguments))
|
||||||
|
return _retval(True, "Server started with PID {spid}.".format(spid=spid))
|
||||||
|
else:
|
||||||
|
self.start_server(amp.loads(arguments))
|
||||||
|
spid = self.start_server(amp.loads(arguments))
|
||||||
|
return _retval(True, "Server started with PID {spid}.".format(spid=spid))
|
||||||
elif operation == amp.PSHUTD: # portal + server shutdown
|
elif operation == amp.PSHUTD: # portal + server shutdown
|
||||||
if server_connected:
|
if server_connected:
|
||||||
self.stop_server(amp.loads(arguments))
|
self.stop_server()
|
||||||
|
return _retval(True, "Server stopped.")
|
||||||
self.factory.portal.shutdown(restart=False)
|
self.factory.portal.shutdown(restart=False)
|
||||||
else:
|
else:
|
||||||
raise Exception("operation %(op)s not recognized." % {'op': operation})
|
raise Exception("operation %(op)s not recognized." % {'op': operation})
|
||||||
|
|
@ -257,6 +279,14 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
elif operation == amp.SRELOAD: # server reload
|
elif operation == amp.SRELOAD: # server reload
|
||||||
self.factory.portal.server_reload(**kwargs)
|
self.factory.portal.server_reload(**kwargs)
|
||||||
|
|
||||||
|
elif operation == amp.PSYNC: # portal sync
|
||||||
|
# Server has (re-)connected and wants the session data from portal
|
||||||
|
sessdata = self.factory.portal.sessions.get_all_sync_data()
|
||||||
|
self.send_AdminPortal2Server(amp.DUMMYSESSION,
|
||||||
|
amp.PSYNC,
|
||||||
|
sessiondata=sessdata)
|
||||||
|
self.factory.portal.sessions.at_server_connection()
|
||||||
|
|
||||||
elif operation == amp.SSYNC: # server_session_sync
|
elif operation == amp.SSYNC: # server_session_sync
|
||||||
# server wants to save session data to the portal,
|
# server wants to save session data to the portal,
|
||||||
# maybe because it's about to shut down.
|
# maybe because it's about to shut down.
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,11 @@ SCONN = chr(11) # server portal connection (for bots)
|
||||||
PCONNSYNC = chr(12) # portal post-syncing session
|
PCONNSYNC = chr(12) # portal post-syncing session
|
||||||
PDISCONNALL = chr(13) # portal session discnnect all
|
PDISCONNALL = chr(13) # portal session discnnect all
|
||||||
SRELOAD = chr(14) # server reloading (have portal start a new server)
|
SRELOAD = chr(14) # server reloading (have portal start a new server)
|
||||||
|
SSTART = chr(15) # server start (portal must already be running anyway)
|
||||||
|
PSHUTD = chr(16) # portal (+server) shutdown
|
||||||
|
SSHUTD = chr(17) # server shutdown
|
||||||
|
PSTATUS = chr(18) # ping server or portal status
|
||||||
|
SRESET = chr(19) # server shutdown in reset mode
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
@ -439,10 +443,19 @@ class ServerSessionHandler(SessionHandler):
|
||||||
Called by server when reloading. We tell the portal to start a new server instance.
|
Called by server when reloading. We tell the portal to start a new server instance.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SRELOAD)
|
||||||
|
|
||||||
|
def portal_reset_server(self):
|
||||||
|
"""
|
||||||
|
Called by server when reloading. We tell the portal to start a new server instance.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SRESET)
|
||||||
|
|
||||||
def portal_shutdown(self):
|
def portal_shutdown(self):
|
||||||
"""
|
"""
|
||||||
Called by server when shutting down the portal (usually because server is going down too).
|
Called by server when it's time to shut down (the portal will shut us down and then shut
|
||||||
|
itself down)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION,
|
self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION,
|
||||||
|
|
@ -572,8 +585,6 @@ class ServerSessionHandler(SessionHandler):
|
||||||
sessiondata=session_data,
|
sessiondata=session_data,
|
||||||
clean=False)
|
clean=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def disconnect_all_sessions(self, reason="You have been disconnected."):
|
def disconnect_all_sessions(self, reason="You have been disconnected."):
|
||||||
"""
|
"""
|
||||||
Cleanly disconnect all of the connected sessions.
|
Cleanly disconnect all of the connected sessions.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue