Select Git revision
Erik Reid authored
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)