From e500607b8a3bfcd1af810338423a9b601fa35cfd Mon Sep 17 00:00:00 2001 From: "Peter J. Holzer" Date: Mon, 5 Aug 2019 15:57:35 +0200 Subject: [PATCH] Start programs and report status --- .gitignore | 1 + app.py | 126 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + 3 files changed, 129 insertions(+) create mode 100644 .gitignore create mode 100644 app.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4acd06b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.py diff --git a/app.py b/app.py new file mode 100644 index 0000000..87702e0 --- /dev/null +++ b/app.py @@ -0,0 +1,126 @@ +#!/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/" + ) + + +if __name__ == '__main__': + app.run() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4ad7884 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Flask +Flask-RESTful