127 lines
3.8 KiB
Python
127 lines
3.8 KiB
Python
|
#!/usr/bin/env python3
|
||
|
import subprocess
|
||
|
import select
|
||
|
import time
|
||
|
|
||
|
import config
|
||
|
import logging.config
|
||
|
logging.config.dictConfig(config.logging)
|
||
|
log = logging.getLogger('progrunner')
|
||
|
|
||
|
from flask import Flask, request, session
|
||
|
from flask_restful import Api, Resource
|
||
|
|
||
|
app = Flask(__name__)
|
||
|
api = Api(app)
|
||
|
app.config.from_object(config)
|
||
|
|
||
|
class Authenticator(Resource):
|
||
|
def post(self):
|
||
|
if request.json and request.json.get("token", None) == config.token:
|
||
|
session["authorized"] = True
|
||
|
log.info("session: %s", session)
|
||
|
return { "authorized": True }
|
||
|
else:
|
||
|
session["authorized"] = False
|
||
|
log.info("session: %s", session)
|
||
|
return { "authorized": False }
|
||
|
|
||
|
running = []
|
||
|
|
||
|
class Starter(Resource):
|
||
|
def post(self):
|
||
|
log.info("session: %s", session)
|
||
|
if not session.get("authorized", False):
|
||
|
return {}
|
||
|
|
||
|
log.info("json: %s", request.json)
|
||
|
args = [ request.json["program"] ]
|
||
|
if "args" in request.json:
|
||
|
args += request.json["args"]
|
||
|
log.info("args: %s", args)
|
||
|
proc = subprocess.Popen(args, stdout=subprocess.PIPE, universal_newlines=True)
|
||
|
procinfo = { "popen": proc, "pid": proc.pid, "started": time.time(), "stdout": [] }
|
||
|
running.append(procinfo)
|
||
|
pid = proc.pid
|
||
|
log.info("pid: %s", pid)
|
||
|
timeout = request.json.get("timeout", 0.0)
|
||
|
if timeout:
|
||
|
fd = proc.stdout
|
||
|
p = select.poll()
|
||
|
p.register(fd)
|
||
|
t0 = time.time()
|
||
|
t1 = t0 + timeout
|
||
|
while True:
|
||
|
dt = t1 - time.time()
|
||
|
if dt < 0:
|
||
|
break
|
||
|
ready = p.poll(dt * 1000)
|
||
|
log.debug("ready: %s", ready)
|
||
|
if ready:
|
||
|
ln = fd.readline()
|
||
|
log.debug("ln: %s", ln)
|
||
|
if ln == "":
|
||
|
break;
|
||
|
procinfo["stdout"].append(ln)
|
||
|
try:
|
||
|
procinfo["status"] = proc.wait(0.1)
|
||
|
for ln in proc.stdout:
|
||
|
procinfo["stdout"].append(ln)
|
||
|
procinfo["finished"] = time.time()
|
||
|
except subprocess.TimeoutExpired:
|
||
|
pass
|
||
|
return { x: procinfo[x]
|
||
|
for x in ["pid", "started", "stdout", "status", "finished"]
|
||
|
if x in procinfo }
|
||
|
|
||
|
class Status(Resource):
|
||
|
def get(self, pid):
|
||
|
for procinfo in running:
|
||
|
if "finished" not in procinfo:
|
||
|
p = select.poll()
|
||
|
p.register(procinfo["popen"].stdout)
|
||
|
t1 = time.time() + 1.0
|
||
|
while True:
|
||
|
dt = t1 - time.time()
|
||
|
if dt < 0:
|
||
|
break
|
||
|
ready = p.poll(dt * 1000)
|
||
|
log.debug("ready: %s", ready)
|
||
|
if ready:
|
||
|
ln = procinfo["popen"].stdout.readline()
|
||
|
log.debug("ln: %s", ln)
|
||
|
if ln == "":
|
||
|
break;
|
||
|
procinfo["stdout"].append(ln)
|
||
|
try:
|
||
|
procinfo["status"] = procinfo["popen"].wait(0.1)
|
||
|
procinfo["finished"] = time.time()
|
||
|
except subprocess.TimeoutExpired:
|
||
|
pass
|
||
|
for procinfo in running:
|
||
|
if procinfo["pid"] == pid:
|
||
|
return { x: procinfo[x]
|
||
|
for x in ["pid", "started", "stdout", "status", "finished"]
|
||
|
if x in procinfo }
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
api.add_resource(
|
||
|
Authenticator,
|
||
|
"/login"
|
||
|
)
|
||
|
api.add_resource(
|
||
|
Starter,
|
||
|
"/start"
|
||
|
)
|
||
|
api.add_resource(
|
||
|
Status,
|
||
|
"/status/<int:pid>"
|
||
|
)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
app.run()
|