diff --git a/opv_status_api/__main__.py b/opv_status_api/__main__.py index 6f52508..c2c9cbb 100644 --- a/opv_status_api/__main__.py +++ b/opv_status_api/__main__.py @@ -16,11 +16,10 @@ # Email: team@openpathview.fr # Description: OPV status api -from http.server import HTTPServer -from opv_status_api.httpHandler import HttpHandler import docopt import logging from opv_status_api.logging import setup_logging +from opv_status_api.service import Service __doc__ = """ An api for opv-status front @@ -43,8 +42,9 @@ def main(): server_address = ('', int(args["--port"])) logger.info("Starting http server at http://localhost:{} open to {}".format(server_address[1], server_address[0])) - httpd = HTTPServer(server_address, HttpHandler) - httpd.serve_forever() + + service = Service(host=server_address[0], port=server_address[1]) + service.start() if __name__ == "__main__": diff --git a/opv_status_api/celery/__init__.py b/opv_status_api/celery/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/opv_status_api/celery/celery.py b/opv_status_api/celery/celery.py new file mode 100644 index 0000000..2365296 --- /dev/null +++ b/opv_status_api/celery/celery.py @@ -0,0 +1,37 @@ +# coding: utf-8 + +# Copyright (C) 2017 Open Path View, Maison Du Libre +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . + +# Contributors: Simon Archieri +# Email: team@openpathview.fr +# Description: OPV status api +import logging +from opv_celery.__main__ import launch + + +class Celery: + logger = logging.getLogger("opv_status_api") + + def LaunchCelery(self, data): + output = { + "answer": None, + "error": None + } + if "campaign_id" in data and "malette_id" in data: + launch(data["campaign_id"], data["malette_id"]) + + output["answer"] = "Celery launched you should check the api" + else: + output["error"] = "Missing parameter" + + return output diff --git a/opv_status_api/httpHandler.py b/opv_status_api/httpHandler.py deleted file mode 100644 index 7f08396..0000000 --- a/opv_status_api/httpHandler.py +++ /dev/null @@ -1,85 +0,0 @@ -# coding: utf-8 - -# Copyright (C) 2017 Open Path View, Maison Du Libre -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . - -# Contributors: Simon Archieri -# Email: team@openpathview.fr -# Description: OPV status api - -from http.server import BaseHTTPRequestHandler -from opv_status_api.importData.importData import ImportData -from opv_status_api.spark.spark import Spark -import json -import logging - - -class HttpHandler(BaseHTTPRequestHandler): - importData = ImportData() - spark = Spark() - logger = logging.getLogger("opv_status_api") - - def do_GET(self): - self.logger.info("New GET request at {}".format(self.path)) - asked = self.path.split("/") - del asked[0] - - if asked[0] == "import" and len(asked) >= 2: - del asked[0] - answer = self.importData.newCommand(path=asked, type=self.command) - elif asked[0] == "spark" and len(asked) >= 2: - del asked[0] - answer = self.spark.newCommand(path=asked, type=self.command) - else: - answer = { - "httpCode": 404, - "answer": { - "error": "404", - "answer": "This api work =). But you look lost" - } - } - - self.logger.debug("Answer of task : {}".format(answer)) - self.send_response(answer["httpCode"]) - self.send_header('Content-Type', 'application/json') - self.send_header('Access-Control-Allow-Origin', '*') - self.end_headers() - self.wfile.write(json.dumps(answer["answer"]).encode()) - - def do_POST(self): - self.logger.info("New GET request at {}".format(self.path)) - asked = self.path.split("/") - del asked[0] - - data = json.loads(self.rfile.read(int(self.headers.get("Content-Length"))).decode()) - - if asked[0] == "import" and len(asked) >= 2: - del asked[0] - answer = self.importData.newCommand(path=asked, type=self.command, data=data) - elif asked[0] == "spark" and len(asked) >= 2: - del asked[0] - answer = self.spark.newCommand(path=asked, type=self.command, data=data) - else: - answer = { - "httpCode": 404, - "answer": { - "error": "404", - "answer": "This api work =). But you look lost" - } - } - - self.logger.debug("Answer of task : {}".format(answer)) - self.send_response(answer["httpCode"]) - self.send_header('Content-Type', 'application/json') - self.send_header('Access-Control-Allow-Origin', '*') - self.end_headers() - self.wfile.write(json.dumps(answer["answer"]).encode()) diff --git a/opv_status_api/importData/importData.py b/opv_status_api/importData/importData.py index 2323f22..a3ca13b 100644 --- a/opv_status_api/importData/importData.py +++ b/opv_status_api/importData/importData.py @@ -23,86 +23,45 @@ class ImportData: - defaultOutput = { - "httpCode": 200, - "answer": { - "error": None, - "answer": {} - } - } - logger = logging.getLogger("opv_status_api") defaultLogFile = "/tmp/importData.log" importDataThread = ImportDataThread() - def getStatus(self, path=[], data={}, type="GET"): - output = copy.deepcopy(self.defaultOutput).copy() - - if type == "GET": - info = self.importDataThread.getInfo() - - output["answer"]["answer"] = info - - else: - output["httpCode"] = 400 - output["answer"]["error"] = "You must use GET" - - return output - - def launchImport(self, path=[], data={}, type="POST"): - output = copy.deepcopy(self.defaultOutput).copy() - - if type == "POST": - if "path" in data and "id_malette" in data and "camera_number" in data and "description" in data and "campaign_name" in data and "id_rederbro" in data: - self.importDataThread = ImportDataThread(data=data) - self.importDataThread.start() + def getStatus(self): + return { + "answer": self.importDataThread.getInfo(), + "error": None + } - output["answer"]["answer"] = "Launched, you should check status to know if it work" - else: - output["httpCode"] = 400 - output["answer"]["error"] = "You must set all param" + def launchImport(self, data): + output = { + "answer": None, + "error": None + } + if "path" in data and "id_malette" in data and "camera_number" in data and "description" in data and "campaign_name" in data and "id_rederbro" in data: + self.importDataThread = ImportDataThread(data=data) + self.importDataThread.start() + output["answer"] = "Launched, you should check status to know if it work" else: - output["httpCode"] = 400 - output["answer"]["error"] = "You must use POST" + output["error"] = "Missing param" return output - def getLog(self, path=[], data={}, type="POST"): - output = copy.deepcopy(self.defaultOutput).copy() - - if type == "POST": - logFile = self.defaultLogFile if "logFile" not in data else data["logFile"] - - if os.path.isfile(logFile): - with open(logFile) as log: - output["answer"]["answer"] = log.read() + def getLog(self, data): + output = { + "answer": None, + "error": None + } + logFile = self.defaultLogFile if "logFile" not in data else data["logFile"] - else: - output["httpCode"] = 400 - output["answer"]["error"] = "Can't find log file" + if os.path.isfile(logFile): + with open(logFile) as log: + output["answer"] = log.read() else: - output["httpCode"] = 400 - output["answer"]["error"] = "You must use POST" + output["error"] = "Log file not found" - return output - - command = { - "status": getStatus, - "log": getLog, - "launch": launchImport - } - - def newCommand(self, path=[], data={}, type="GET"): - if path[0] in self.command: - self.logger.debug("Launch command {} with path={}, data={}, type={}".format(path[0], path, data, type)) - return self.command[path[0]](self, path=path, data=data, type=type) - else: - self.logger.debug("Asked ressource ({}) can't be find in importData".format(path[0])) - output = copy.deepcopy(self.defaultOutput).copy() - output["httpCode"] = 400 - output["answer"]["error"] = "We can't find {}".format(path[0]) - return output + return output \ No newline at end of file diff --git a/opv_status_api/service.py b/opv_status_api/service.py new file mode 100644 index 0000000..d6dc284 --- /dev/null +++ b/opv_status_api/service.py @@ -0,0 +1,77 @@ +from flask import Flask, request, abort +from flask_cors import CORS +from gevent.pywsgi import WSGIServer +from opv_status_api.importData.importData import ImportData +from opv_status_api.spark.spark import Spark +from opv_status_api.celery.celery import Celery +import json +import logging + + +class Service: + def __init__(self, host="", port=5001): + self.port = port + self.host = host + + def start(self): + app = Flask("opv-status-api") + CORS(app) + + logger = logging.getLogger("opv_status_api") + + importService = ImportData() + spark = Spark() + celery = Celery() + + mimetype = {'Content-Type': 'application/json'} + + service = { + "import": { + "log": (importService.getLog, "POST"), + "launch": (importService.launchImport, "POST"), + "status": (importService.getStatus, "GET") + }, + "spark": { + "launch": (spark.launchSpark, "POST"), + "port": (spark.getSparkPort, "GET") + }, + "celery": { + "launch": (celery.LaunchCelery, "POST") + } + } + + def check_args(service_name, command_name): + logger.debug("---- New request ----") + logger.debug("Request type : {}".format(request.method)) + logger.debug("Body : {}".format(request.data.decode())) + logger.debug("Service : {}, Command : {}".format(service_name, command_name)) + if (service_name in service and + command_name in service[service_name]): + if request.method == service[service_name][command_name][1]: + if request.method == "POST": + try: + json.loads(request.data.decode()) + logger.info("-- Valid request for {} {} --".format(service_name, command_name)) + except ValueError: + logger.debug("-- Error ==> POST request + body isn't json --") + abort(415) + return + logger.debug("-- Error ==> unsupported method for this command --") + abort(405) + logger.debug("-- Error ==> unknow command --") + abort(404) + + @app.route("//", methods=["POST", "GET"]) + def launchCommand(service_name, command_name): + check_args(service_name, command_name) + if request.method == "POST": + answer = service[service_name][command_name][0](json.loads(request.data.decode())) + else: + answer = service[service_name][command_name][0]() + + answer = json.dumps(answer), 400 if answer["error"] else 200, mimetype + logger.debug("Request answer : {}".format(answer)) + return answer + + http_server = WSGIServer((self.host, self.port), app) + http_server.serve_forever() \ No newline at end of file diff --git a/opv_status_api/spark/spark.py b/opv_status_api/spark/spark.py index 0a9c0e8..a52efd6 100644 --- a/opv_status_api/spark/spark.py +++ b/opv_status_api/spark/spark.py @@ -23,14 +23,6 @@ class Spark: - defaultOutput = { - "httpCode": 200, - "answer": { - "error": None, - "answer": {} - } - } - logger = logging.getLogger("opv_status_api") def getPort(self): @@ -45,51 +37,27 @@ def getPort(self): return port - def getSparkPort(self, path=[], data={}, type="GET"): - output = copy.deepcopy(self.defaultOutput).copy() - if type == "GET": - output["answer"]["answer"] = self.getPort() - else: - output["httpCode"] = 400 - output["answer"]["error"] = "You must use GET" - - return output - - def launchSpark(self, path=[], data={}, type="POST"): - output = copy.deepcopy(self.defaultOutput).copy() - - if type == "POST": - if "campaign_id" in data and "malette_id" in data: - self.sparkThread = LaunchSparkThread(campaign_id=data["campaign_id"], malette_id=data["malette_id"]) + def getSparkPort(self): + return { + "answer": self.getPort(), + "error": None + } - if "customLaunchScript" in data: - self.sparkThread = LaunchSparkThread(launchScript=data["customLaunchScript"], campaign_id=data["campaign_id"], malette_id=data["malette_id"]) + def launchSpark(self, data): + output = { + "answer": None, + "error": None + } + if "campaign_id" in data and "malette_id" in data: + self.sparkThread = LaunchSparkThread(campaign_id=data["campaign_id"], malette_id=data["malette_id"]) - self.sparkThread.start() + if "customLaunchScript" in data: + self.sparkThread = LaunchSparkThread(launchScript=data["customLaunchScript"], campaign_id=data["campaign_id"], malette_id=data["malette_id"]) - output["answer"]["answer"] = "Spark launched you should check the api" - else: - output["httpCode"] = 400 - output["answer"]["error"] = "You must set the campaign id and malette id" + self.sparkThread.start() + output["answer"] = "Spark launched you should check the api" else: - output["httpCode"] = 400 - output["answer"]["error"] = "You must use POST" + output["error"] = "Missing parameter" return output - - command = { - "launch": launchSpark, - "port": getSparkPort - } - - def newCommand(self, path=[], data={}, type="GET"): - if path[0] in self.command: - self.logger.debug("Launch command {} with path={}, data={}, type={}".format(path[0], path, data, type)) - return self.command[path[0]](self, path=path, data=data, type=type) - else: - self.logger.debug("Asked ressource ({}) can't be find in Spark".format(path[0])) - output = copy.deepcopy(self.defaultOutput).copy() - output["httpCode"] = 400 - output["answer"]["error"] = "We can't find {}".format(path[0]) - return output diff --git a/requirements.txt b/requirements.txt index e1f3bc6..26ec8c5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,10 @@ psutil path.py docopt PyYAML +Flask +Flask-Cors +gevent -e git+https://github.com/OpenPathView/OPV_importData/@master#egg=opv_import -e git+https://github.com/OpenPathView/DirectoryManagerClient@stable#egg=opv_directorymanagerclient -e git+https://github.com/OpenPathView/OPV_DBRest-client@stable#egg=opv_api_client +-e git+https://github.com/OpenPathView/OPV_Celery@master#egg=opv_celery \ No newline at end of file diff --git a/setup.py b/setup.py index 2acdcfd..0a08967 100755 --- a/setup.py +++ b/setup.py @@ -31,7 +31,8 @@ dependency_links=[ "git+https://github.com/OpenPathView/DirectoryManagerClient@stable#egg=opv_directorymanagerclient", "git+https://github.com/OpenPathView/OPV_DBRest-client@stable#egg=opv_api_client", - "git+https://github.com/OpenPathView/OPV_importData/@master#egg=opv_import" + "git+https://github.com/OpenPathView/OPV_importData/@master#egg=opv_import", + "git+https://github.com/OpenPathView/OPV_Celery/@master#egg=opv_celery" ], install_requires=[ "path.py", @@ -40,7 +41,10 @@ "opv_directorymanagerclient", "opv_api_client", "docopt", - "PyYAML" + "PyYAML", + "Flask", + "Flask-Cors", + "gevent" ], # Active la prise en compte du fichier MANIFEST.in include_package_data=True,