28 Sep 10, 01:26AM
Hi all,
I guess all those nasty shell scripts and screen`ing and other unclear stuff just get your time wasted if all you want is just to run some servers, stop and restart them... and if you're not there to restart it, sheesh...
I spent this evening coding this script up:Name this file as nmpx.py, and then run it with
Now you have an ncurses interface inside screen to start/stop your servers. Simply press 1/2/3/4/5/... to stop or start that server.
I've never written a Python script before (this is my first time...) so don't expect my code to be ueber-great. Yes, there are a lot of dodgy things (obviously), so I hope that I can get help from people who know what they're doing, and will submit any updates and improvements here.
What I'd like to have in the end is an interface where:
* You can select more than 10 servers and control them with a nice ncurses interface
* Read all logs, live, at the same time, and also dump them into one single file
* Interact with this application by other means, especially for remote control
Hope this helps :)
Thank you :D
I guess all those nasty shell scripts and screen`ing and other unclear stuff just get your time wasted if all you want is just to run some servers, stop and restart them... and if you're not there to restart it, sheesh...
I spent this evening coding this script up:
[SELECT ALL] Code:
#!/usr/bin/env python2.6
"""
Process manager by Drakas
Under zlib license.
Revision 1
Create a file nmpx.cfg.py:
config = {
"curses": True,
"procs": [
{'name':'Public Server 1','dir':'/home/user/AssaultCube_v1.0.4/','autorestart':True,
'args':['bash','-c','./bin_unix/linux_server -mlocalhost -f1234 -nPS1 -Nps1']},
{'name':'Public Server 2','dir':'/home/user/AssaultCube_v1.0.4/','autorestart':False,
'args':['bash','-c','./bin_unix/linux_server -mlocalhost -f1236 -nPS2 -Nps2']},
{'name':'Private Server 1','dir':'/home/user/AssaultCube_v1.0.4/','autorestart':False,
'args':['bash','-c','./bin_unix/linux_server -mlocalhost -f1238 -nPS3 -Npr1']},
{'name':'Private Server 2','dir':'/home/user/AssaultCube_v1.0.4/','autorestart':True,
'args':['./bin_unix/linux_server','-mlocalhost','-f1240','-nPS4 xyz','-Npr2']}
]
}
name: the service name, for your own reference
dir: the working directory of the service
autorestart: automatically restart if it died, or if the configuration was changed (!)
args: the command concerned. Every argument written separately,
but you can use bash -c 'command' to make it easier on yourself.
The configuration file is reloaded every 5 seconds, and new processes are started if necessary.
"""
import time
import datetime
import subprocess
import curses
import os
procs = {}
ll = []
ml = []
myscreen = None
mcwd = None
# Not implemented yet:
# This function would permit us to read all our logs live, and perhaps dump them as well.
# Need help here
def mlog(txt,service_name):
global myscreen
if myscreen is not None:
global ml
if len(ml) > 20:
ml.pop(0)
ml.append("[%s] %s" % service_name.rjust(25," "), txt)
# This function just takes any logmessages and outputs them
def alog(txt):
global myscreen
if myscreen is not None:
global ll
if len(ll) > 5:
ll.pop(0)
ll.append("[%s] %s"% (time.strftime('%Y%m%d %H:%M:%S'), txt))
else:
print(txt)
# This function loads the configuration.
def lconfig():
global procs
global config
global mcwd
g = {}
os.chdir(mcwd)
execfile("nmpx.cfg.py",g)
config = g["config"]
for pro in config["procs"]:
if not pro["name"] in procs:
print "Adding new process, name %s" % pro["name"]
procs[pro["name"]] = {"ran":False,"process":None,"name":None,"kill":False,"dir":None,"args":None,"modified":True}
p = procs[pro["name"]]
if p["name"] != pro["name"]:
p["name"] = pro["name"]
p["modified"] = True
if p["dir"] != pro["dir"]:
p["dir"] = pro["dir"]
p["autorestart"] = pro["autorestart"]
if p["args"] != pro["args"]:
p["args"] = pro["args"]
p["modified"] = True
# This function outputs the status
def status():
global myscreen
n = 0
if myscreen is not None:
myscreen.clear
myscreen.border(0)
myscreen.addstr(2, 2, "Process manager")
for k in procs.keys():
p = procs[k]
if myscreen is None:
#print ...
alog("True")
else:
n=n+1
sstring = "(%s) %s " % (['started','stopped'][p["process"] is None],p["name"].rjust(25," "))
myscreen.addstr(3+n,2,sstring)
if myscreen is not None:
ni = 0
global ll
for az in ll:
ni = ni + 1
myscreen.addstr (4+n+ni,2, az)
myscreen.refresh()
# This function checks the processes
def checkProcesses():
global procs
for k in procs.keys():
p = procs[k]
if p["process"] is not None and p["process"].poll() is not None:
alog("Process %s died" % p["name"])
p["process"] = None
if p["modified"] and p["autorestart"]:
if p["process"] is not None:
alog("Process %s was modified, so stopping" % p["name"])
p["process"].kill()
p["process"] = None
if p["process"] is not None and p["kill"]:
alog("Killing process %s" % p["name"])
p["process"].kill()
p["process"] = None
if p["process"] is None and (p["autorestart"] or p["ran"] is False) and not p["kill"]:
alog("Process %s starting" % p["name"])
os.chdir(p["dir"])
p["process"] = subprocess.Popen(p["args"],stdout=subprocess.PIPE)
p["modified"] = False
p["ran"] = True
def gquit():
global procs
cquit()
for k in procs.keys():
if procs[k]["process"] is not None:
alog("Killing %s "%procs[k]["name"])
procs[k]["process"].kill()
alog("Quitting")
quit()
def cquit():
global myscreen
myscreen.keypad(0)
curses.echo()
curses.nocbreak()
curses.endwin()
myscreen = None
def main():
global mcwd
mcwd = os.getcwd()
alog("Loading config")
lconfig()
global myscreen
#
if config["curses"]:
myscreen = curses.initscr()
myscreen.nodelay(1)
else:
myscreen = None
alog("Checking processes")
checkProcesses()
timer = 0
while True:
timer = timer + 1
time.sleep(0.1)
# Reload the configuration every 5 seconds
if timer % 50 is 0:
timer = 0
lconfig()
checkProcesses()
if myscreen is not None:
ch = myscreen.getch()
if ch > 0 and ch < 256:
cc = chr(ch)
# Allow only 1 - 10, so only 10 processes can be managed for now
if cc.isdigit():
k = procs.keys()
cc = int(cc)
if cc == 0:
cc = 10
if cc <= len(k):
p = procs[k[cc-1]]
if p["process"] is not None:
# We tell it to get killed and stop
procs[k[cc-1]]["kill"] = True
else:
# We tell it to start like before
procs[k[cc-1]]["ran"] = False
elif cc == "q":
gquit()
status()
alog("Quitting")
if __name__ == "__main__":
try:
main()
if myscreen is not None:
cquit()
except:
if myscreen is not None:
cquit()
# Bring back the error into a reset terminal, of course
raise
[SELECT ALL] Code:
screen python nmpx.py
Now you have an ncurses interface inside screen to start/stop your servers. Simply press 1/2/3/4/5/... to stop or start that server.
I've never written a Python script before (this is my first time...) so don't expect my code to be ueber-great. Yes, there are a lot of dodgy things (obviously), so I hope that I can get help from people who know what they're doing, and will submit any updates and improvements here.
What I'd like to have in the end is an interface where:
* You can select more than 10 servers and control them with a nice ncurses interface
* Read all logs, live, at the same time, and also dump them into one single file
* Interact with this application by other means, especially for remote control
Hope this helps :)
Thank you :D