import json, datetime, fcntl, os, re, socket, subprocess, time from flask import request, abort from flask import Blueprint from app import email bp = Blueprint("routes", __name__) # -------------------------------------------------------------- DIR = "/opt/installmgr/" REPOSIT_DIR = "/opt/installmgr/incoming_dumps" MAX_REPOSIT_SIZE = 100 * 20 * 1024 # kB (here, max 100 dumps of 20MB) ALERT_MAIL_FROM = "root@scodoc.iutv.univ-paris13.fr" ALERT_MAIL_TO = "emmanuel.viennet@gmail.com" LOG_FILENAME = os.path.join(DIR, "upload-dump-errors.log") UPLOAD_LOG_FILENAME = os.path.join(DIR, "upload-dump-log.json") DEBUG = False # if false, don't publish error messages @bp.route("/scodoc-installmgr/last_stable_version") def last_stable_version(): # LAST_RELEASE_TAG=$(curl "$GITEA_RELEASE_URL" | jq ".[].tag_name" | tr -d -c "0-9.\n" | sort --version-sort | tail -1) return "9.0.30" @bp.route("/scodoc-installmgr/upload_dump", methods=["POST"]) def upload_dump(): """ Réception d'un fichier de dump uploadé """ log = open(LOG_FILENAME, "a") now = datetime.datetime.now() fulltime = now.isoformat() # client addr: remote_addr = request.environ.get("HTTP_X_Real_IP", request.remote_addr) log.write("{} request from {}\n".format(fulltime, remote_addr)) # Avec seulement alphanum et tiret: clean_deptname = re.sub(r"[^A-Za-z-]", "", request.form["dept_name"]) the_file = request.files["file"] filename = the_file.filename data = the_file.file.read() try: remote_host = socket.gethostbyaddr(remote_addr)[0] except: log.write("reverse DNS lookup failed for {}".format(remote_addr)) remote_host = "" D = { "dept_name": request.form["dept_name"], "serial": request.form["serial"], "sco_user": request.form["sco_user"], "sent_by": request.form["sent_by"], "sco_version": request.form["sco_version"], "sco_subversion": request.form["sco_subversion"], "dump_filename": fulltime + "_" + clean_deptname + ".gz", "dump_size": len(data), "remote_addr": remote_addr, "remote_host": remote_host, } log.write("received data ({} bytes)\n".format(D["dump_size"])) json_descr = json.dumps(D, sort_keys=True, indent=4) # --- Check disk space cur_size = int(subprocess.check_output(["du", "-skx", REPOSIT_DIR]).split("\t")[0]) if (cur_size + len(data) / 1024) > MAX_REPOSIT_SIZE: # out of space ! log.write( "Out of space: cur_size={}kB, limit={}\n".format(cur_size, MAX_REPOSIT_SIZE) ) # Send alert try: email.send_email( "[upload-dump] Out of space !", ALERT_MAIL_FROM, [ALERT_MAIL_TO], "Out space !\nNew upload was canceled:\n" + json_descr, ) except: log.write("exception while sending email !\n") abort(507, "Insufficient Storage") else: log.write("writing dump to {}\n".format(D["dump_filename"])) # dump: f = open(os.path.join(REPOSIT_DIR, D["dump_filename"]), "wb") f.write(data) f.close() uplog = open(UPLOAD_LOG_FILENAME, "a") uplog.write(json_descr) uplog.write("\n,\n") # separator uplog.close() # Send notification try: log.write("sending notification to {}\n".format(ALERT_MAIL_TO)) email.send_email( f"[upload-dump] new dump {D['dept_name']} from {D['remote_addr']} ({D['remote_host']})", ALERT_MAIL_FROM, [ALERT_MAIL_TO], "New upload:\n" + json_descr, ) except: log.write("exception while sending email !\n") return ("", 204) # ok empty response # Lock for counter class Lock: def acquire(self): self.f = open("lock", "w") fcntl.flock(self.f.fileno(), fcntl.LOCK_EX) def release(self): self.f.close() def increment(): L = Lock() L.acquire() try: try: val = int(open(DIR + "/counter").read()) except: val = 0 val += 1 open("counter", "w").write("%d" % val) finally: L.release() return val @bp.route("/scodoc-installmgr/version", methods=["GET"]) def version(): """ echo -e "DATE\tIP\tSVN\tSERIAL\tOP" > installs.log; chown scodoc installs.log """ remote_addr = request.environ.get("HTTP_X_Real_IP", request.remote_addr) mode = request.args.get("mode", "?") sn = request.args.get("sn", "-1") # serial number svn = request.args.get("svn", "-") # installed subversion commit = request.args.get("commit", "-") # installed git commit if mode == "install" or not sn: serial = increment() else: serial = sn f = open(DIR + "installs.log", "a") f.write( "%s\t%s\t%s\t%s\t%s\n" % ( time.strftime("%Y-%m-%d %H:%M:%S"), remote_addr, svn or commit, serial, mode, ) ) f.close()