Skip to content
Snippets Groups Projects
Select Git revision
  • 03b0b78b5141df34722e868a59272486a7a9a062
  • develop default protected
  • inventory-auth
  • master protected
  • feature/POL1-813-error-report-sensu-check
  • 0.21
  • 0.20
  • 0.19
  • 0.18
  • 0.17
  • 0.16
  • 0.15
  • 0.14
  • 0.13
  • 0.12
  • 0.11
  • 0.10
  • 0.9
  • 0.8
  • 0.7
  • 0.6
  • 0.5
  • 0.4
  • 0.3
  • 0.2
25 results

api.py

Blame
  • Erik Reid's avatar
    Erik Reid authored
    f60dcd17
    History
    api.py 4.15 KiB
    """
    These endpoints are used to update BRIAN snmp polling checks to Sensu
    
    .. contents:: :local:
    
    
    /api/version
    ---------------------
    
    .. autofunction:: brian_polling_manager.api.version
    
    
    /api/update
    -----------------------------
    
    .. autofunction:: brian_polling_manager.api.update
    
    """
    import functools
    import logging
    import pkg_resources
    
    from flask import Blueprint, current_app, request, Response, jsonify
    
    from brian_polling_manager import CONFIG_KEY
    from brian_polling_manager import main
    
    routes = Blueprint("api-routes", __name__)
    logger = logging.getLogger(__name__)
    
    
    API_VERSION = '0.1'
    
    VERSION_SCHEMA = {
        '$schema': 'http://json-schema.org/draft-07/schema#',
    
        'definitions': {
            'latch': {
                'type': 'object',
                'properties': {
                    'current': {'type': 'integer'},
                    'next': {'type': 'integer'},
                    'this': {'type': 'integer'},
                    'failure': {'type': 'boolean'},
                    'pending': {'type': 'boolean'},
                    'timestamp': {'type': 'number'}
                },
                'required': ['current', 'next', 'this', 'pending', 'failure'],
                'additionalProperties': False
            }
        },
    
        'type': 'object',
        'properties': {
            'api': {
                'type': 'string',
                'pattern': r'\d+\.\d+'
            },
            'module': {
                'type': 'string',
                'pattern': r'\d+\.\d+'
            },
            'latch': {'$ref': '#/definitions/latch'}
        },
        'required': ['api', 'module'],
        'additionalProperties': False
    }
    
    
    class APIUpstreamError(Exception):
        def __init__(self, message, status_code=504):
            super().__init__()
            self.message = message
            self.status_code = status_code
    
    
    @routes.errorhandler(APIUpstreamError)
    def handle_request_error(error):
        logger.error(f'api error: {error.message}')
        return Response(
            response=error.message,
            status=error.status_code,
            mimetype='text/html')
    
    
    def require_accepts_json(f):
        """
        used as a route handler decorator to return an error
        unless the request allows responses with type "application/json"
        :param f: the function to be decorated
        :return: the decorated function
        """
        @functools.wraps(f)
        def decorated_function(*args, **kwargs):
            # TODO: use best_match to disallow */* ...?
            if not request.accept_mimetypes.accept_json:
                return Response(
                    response="response will be json",
                    status=406,
                    mimetype="text/html")
            return f(*args, **kwargs)
        return decorated_function
    
    
    @routes.after_request
    def after_request(response):
        """
        Generic function to do additional logging of requests & responses.
    
        :param response:
        :return:
        """
        if response.status_code != 200:
    
            try:
                data = response.data.decode('utf-8')
            except Exception:
                # never expected to happen, but we don't want any failures here
                logging.exception('INTERNAL DECODING ERROR')
                data = 'decoding error (see logs)'
    
            logger.warning(
                f'[{response.status_code}] {request.method} {request.path} {data}')
        else:
            logger.info(
                f'[{response.status_code}] {request.method} {request.path}')
        return response
    
    
    @routes.route('/version', methods=['GET', 'POST'])
    @require_accepts_json
    def version():
        """
        Returns a json object with information about the module version.
    
        The response will be formatted according to the following schema:
    
        .. asjson:: brian_polling_manager.api.VERSION_SCHEMA
    
        :return:
        """
        version_params = {
            'api': API_VERSION,
            'module':
                pkg_resources.get_distribution('brian_polling_manager').version
        }
        return jsonify(version_params)
    
    
    @routes.route('/update', methods=['GET', 'POST'])
    @require_accepts_json
    def update():
        """
        Update checks based on current inventory provider state.
    
        The response will be formatted according to the following schema:
    
        .. asjson:: brian_polling_manager.main.REFRESH_RESULT_SCHEMA
    
        :return:
        """
        response = main.refresh(config=current_app.config[CONFIG_KEY])
        return jsonify(response)