#!/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.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)