153 lines
4.0 KiB
Python
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)
|