oggit/api_server/api_server.py

153 lines
4.0 KiB
Python

#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, "/usr/share/opengnsys-modules/python3/dist-packages")
import importlib
import logging
import uuid
import argparse
import yaml
from flask import Flask, request
from flask_executor import Executor
from flask_restx import Api
from flasgger import Swagger
from werkzeug.exceptions import HTTPException
from systemd.journal import JournalHandler
parser = argparse.ArgumentParser(
prog="api_server.py",
description="OpenGnsys Repository API Server",
)
debug_enabled = False
listen_host = '0.0.0.0'
listen_port = 8006
parser.add_argument('--debug', action='store_true', help="Enable debug output")
parser.add_argument('--listen', metavar="HOST", help="Listen address")
parser.add_argument('--port', metavar="PORT", help="Listen port")
parser.add_argument("-v", "--verbose", action="store_true", help = "Verbose console output")
args = parser.parse_args()
log = logging.getLogger('api_server')
log.addHandler(JournalHandler())
if args.verbose:
log.addHandler(logging.StreamHandler(stream=sys.stderr))
log.setLevel(logging.DEBUG)
else:
log.setLevel(logging.INFO)
if args.listen:
listen_host = args.listen
if args.port:
listen_port = args.port
if args.debug:
debug_enabled = True
api_base_dir = os.path.dirname(os.path.realpath(__file__))
blueprints_dir = os.path.join(api_base_dir, 'blueprints')
installer_dir = os.path.join(api_base_dir, '../installer')
sys.path.insert(0, installer_dir)
# Create an instance of the Flask class
app = Flask(__name__)
api = Api(app,
version='0.50',
title = "OpenGnsys Git API",
description = "API for managing disk images stored in Git",
doc = "/apidocs/")
executor = Executor(app)
log.info("Loading blueprints from %s", blueprints_dir)
sys.path.insert(0, blueprints_dir)
for filename in os.listdir(blueprints_dir):
if filename.endswith('.py'):
log.info("Loading %s/%s", blueprints_dir, filename)
module_name = filename.replace(".py", "")
swagger_file = os.path.join(blueprints_dir, filename.replace(".py", ".yaml"))
log.info("Importing %s", module_name)
importlib.invalidate_caches()
module = importlib.import_module(module_name)
log.debug("Returned: %s", module)
app.register_blueprint(module.blueprint)
if os.path.exists(swagger_file):
log.info("Loading Swagger documentation from %s...", swagger_file)
with open(swagger_file, "r", encoding='utf-8') as file:
swagger_template = yaml.safe_load(file)
swagger = Swagger(app, template=swagger_template)
else:
log.warning("Swagger not found for this module, looked in %s", swagger_file)
@app.errorhandler(HTTPException)
def handle_exception(e):
"""Return JSON for HTTP errors.
We create and log an error UUID for each error, and use journald's additional fields for easier searching.
"""
# start with the correct headers and status code from the error
response = e.get_response()
errid = uuid.uuid4().hex
response = {
"errcode": e.code,
"errname": e.name,
"description": e.description,
}
log.error("Error ID %s: code %i, name %s, description %s", errid, e.code, e.name, e.description, extra = { "error_id" : errid, "errcode" : e.code, "errname" : e.name, "description" : e.description })
return response, 500
@app.after_request
def after_request(response):
log.info("Request from %s: %s %s %s %s", request.remote_addr, request.method, request.scheme, request.full_path, response.status,
extra = {"remote_addr" : request.remote_addr, "method" : request.method, "scheme" : request.scheme, "full_path" : request.full_path, "status" : response.status})
if debug_enabled:
log.debug("Response: %s", response.data, extra = {"response" : response.data})
return response
# Run the Flask app
if __name__ == '__main__':
print(f"Map: {app.url_map}")
app.run(debug=debug_enabled, host=listen_host, port=listen_port)