Last active
October 18, 2017 13:10
-
-
Save zekroTJA/384d56e19f2118ec94bcbea3875c0e54 to your computer and use it in GitHub Desktop.
Python script for managing multiple minecraft servers on an VPS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# # # # # # # # # # # # # # # # # # # # # # # # # | |
# # | |
# © 2017 Ringo Hoffmann (zekro Development) # | |
# # | |
# If you want to use this code, please read # | |
# this first: http://s.zekro.de/codepolicy # | |
# # | |
# With usage of this code or parts of it in # | |
# any way, you agree to the terms above. # | |
# # | |
# # # # # # # # # # # # # # # # # # # # # # # # # | |
import sys | |
import os | |
import subprocess | |
import json | |
import psutil # 'pip install psutil' | |
args = sys.argv | |
dirs = os.listdir(".") | |
servers = [] | |
inp = "" | |
VERSION = "1.3.0" | |
class Color: | |
W = '\033[0m' # white (normal) | |
R = '\033[31m' # red | |
G = '\033[32m' # green | |
O = '\033[33m' # orange | |
B = '\033[34m' # blue | |
P = '\033[35m' # purple | |
C = '\033[36m' # cyan | |
def clr(content, color): | |
""" | |
Returns colored text | |
<color> -> type of class Color | |
""" | |
return color + content + Color.W | |
def error(content): | |
""" | |
Displays red colored error message with button press pause | |
""" | |
print(clr("[ERROR] "+ content, Color.R)) | |
input() | |
def isRunning(name): | |
""" | |
Returns boolean if current screen is running by checking | |
if 'screen -ls' contains server name | |
""" | |
proc = subprocess.Popen(["screen", "-ls"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
return name in str(proc.stdout.read()) | |
def clear(): | |
subprocess.call("clear") | |
def getSys(): | |
""" | |
Retuns system status string from psutil data | |
""" | |
cpulaod = psutil.cpu_percent() # CPU-Load in % | |
mem = psutil.virtual_memory() # Memory data container | |
memtotal = mem.total / 1024 / 1024 # Total memory in MiByte | |
memused = mem.used / 1024 / 1024 # Used memory in MiByte | |
memload = mem.percent # Used memory in % of total | |
disk = psutil.disk_usage(".") # disk usage data container | |
disktotal = disk.total / 1024 / 1024 / 1024 # total disk space in GiByte | |
diskused = disk.used / 1024 / 1024 / 1024 # used disk space in GiByte | |
diskload = disk.percent # used disk space in % of total | |
return str("CPU load: %d %%\n" | |
"Memory (used): %d MiB / %d MiB (%d %%)\n" | |
"Space (used): %d GiB / %d GiB (%d %%)" | |
% (cpulaod, memused, memtotal, memload, diskused, disktotal, diskload)) | |
def printMenu(): | |
""" | |
Printing the main menu and returning the input command | |
""" | |
global servers | |
clear() | |
print(clr("Server Management System v." + VERSION + " - (c) 2017 zekro\n", Color.C)) | |
# Print system stats from getSys() | |
print("SYSTEM STATS: \n" + getSys() + "\n") | |
# collecting local directory names which dont start with '.' | |
servers = [d for d in dirs if os.path.isdir(d) and not d.startswith('.')] | |
# Printing server table '[STATE] [#] DIRNAME' | |
for i, d in enumerate(servers): | |
# getting status of server with isRunning(<servername>) | |
running = clr("[RUNNING]", Color.G) if isRunning(d) else clr("[STOPPED]", Color.O) | |
print("%s [%d] %s" % (running, i + 1, d)) | |
# waiting for console input and retun it | |
return input("\nPlease enter a command\nType 'help' for command list\n\n> ") | |
def getStart(name): | |
""" | |
Getting the start command from <servername>/STARTCMD.txt file | |
""" | |
setfile = name + "/STARTCMD.txt" | |
if not os.path.isfile(setfile): | |
error("'STARTCMD.txt' was not found!") | |
exit() | |
else: | |
with open(setfile, "r") as f: | |
out = f.readline() | |
if not out: | |
error("No start command was found in 'STARTCMD.txt'!") | |
exit() | |
return out | |
def runMain(): | |
""" | |
Saving returned console input of printMenu() in global inp var | |
This is essential for menu input loop later | |
""" | |
global inp | |
inp = printMenu() | |
# <--------------==[ MAIN PROGRAM ]==--------------> | |
runMain() | |
# While 'exit' command was not executed or exception | |
# was thrown redraw menue and collect input | |
while not "exit" in inp: | |
invoke = inp.split()[0].lower() | |
args = inp.split()[1:] | |
if len(args) > 0: | |
# COMMAND: 'start <index>' | |
# Starting a server by index | |
if invoke == "start": | |
index = int(args[0]) - 1 | |
# if index is out of range return error | |
if index >= len(servers) or index < 0: | |
error("Please enter a valid number!") | |
else: | |
server = servers[index] | |
if isRunning(server): | |
error("This server is currently still running!") | |
else: | |
# splitting start command of 'STARTCMD.txt' into arguments | |
command = getStart(server).split() | |
# default start arguments for screen | |
# '-S <servername>' -> setting screen name | |
# '-L' logging cmd output in 'screenlog.0' file | |
start = ["screen", "-S", server, "-L"] | |
os.chdir(server) | |
# injecting start command into screen command arguments | |
for arg in command: | |
start.append(arg) | |
# calling screen process | |
subprocess.call(start) | |
exit() | |
# COMMAND: 'stop <index>' | |
# Stopping a running server by index | |
if invoke == "stop": | |
index = int(args[0]) - 1 | |
if index >= len(servers) or index < 0: | |
error("Please enter a valid number!") | |
else: | |
server = servers[index] | |
if not isRunning(server): | |
error("This server is not running!") | |
else: | |
# calling 'screen -X -S <screenname> quit' to stop the screen | |
subprocess.call(["screen", "-X", "-S", server, "quit"]) | |
# COMMAND: 'resume <index>' | |
# Resuming a screen of server by index | |
if invoke == "resume": | |
index = int(args[0]) - 1 | |
if index >= len(servers) or index < 0: | |
error("Please enter a valid number!") | |
else: | |
server = servers[index] | |
if not isRunning(server): | |
error("This server is not running!") | |
else: | |
# resuming screen by command 'screen -r <screenname>' | |
subprocess.call(["screen", "-r", server]) | |
exit() | |
elif invoke == "help": | |
print( | |
"\n" | |
"start <index> - Start a stopped server\n" | |
"stop <index> - Stop a running server\n" | |
"resume <index> - Resuming a running screen session\n" | |
"exit - Exit this tool" | |
) | |
print("[Press any key to continue...]") | |
input() | |
else: | |
error("Please enter a valid command or type 'help' for all commands!") | |
runMain() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment