<commit_before>from celery.task.control import inspect
from errno import errorcode
from ceph_installer import models
from ceph_installer.util import which
from pecan import render
from pecan.hooks import PecanHook
from sqlalchemy.exc import OperationalError
from webob.exc import WSGIHTTPException
import logging


logger = logging.getLogger(__name__)


class SystemCheckError(Exception):

    def __init__(self, message):
        self.message = message


def ansible_exists():
    """
    Perform a simple check to see if ``ansibl-playbook`` executable is present
    in the system where the service is running.
    """
    ansible_path = which('ansible-playbook')
    if not ansible_path:
        raise SystemCheckError('Could not find ansible in system paths')


def celery_has_workers():
    """
    The ``stats()`` call will return different stats/metadata information about
    celery worker(s).  An empty/None result will mean that there aren't any
    celery workers in use.
    """
    stats = inspect().stats()
    if not stats:
        raise SystemCheckError('No running Celery worker was found')


def rabbitmq_is_running():
    """
    If checking for worker stats, an ``IOError`` may be raised depending on the
    problem for the RabbitMQ connection.
    """
    try:
        celery_has_workers()
    except IOError as e:
        msg = "Error connecting to RabbitMQ: " + str(e)
        if len(e.args):
            if errorcode.get(e.args[0]) == 'ECONNREFUSED':
                msg = "RabbitMQ is not running or not reachable"
        raise SystemCheckError(msg)


def database_connection():
    """
    A very simple connection that should succeed if there is a good/correct
    database connection.
    """
    try:
        models.Task.get(1)
    except OperationalError as exc:
        raise SystemCheckError(
            "Could not connect or retrieve information from the database: %s" % exc.message)


class SystemCheckHook(PecanHook):
    """
    Perform a series of system checks which prevents the service from doing any
    work unless everything required checks out.
    """

    def before(self, state):
        """
        Executed before a controller gets called. When an error condition is
        detected (one of the callables raises a ``SystemCheckError``) it sets
        the response status to 500 and returns a JSON response with the
        appropriate reason.
        """
        for check in [ansible_exists, rabbitmq_is_running, celery_has_workers,
                      database_connection]:
            try:
                check()
            except SystemCheckError as system_error:
                message = render('json', {'message': system_error.message})
                raise WSGIHTTPException(content_type='application/json', body=message)


class CustomErrorHook(PecanHook):
    """
    Only needed for prod environments where it looks like multi-worker servers
    will swallow exceptions. This will ensure a traceback is logged correctly.
    """

    def on_error(self, state, exc):
        # TODO: do not complain with a traceback when the error is an
        # HTTPNotFound
        logger.exception('unhandled error by ceph-installer')
<commit_msg>do the system check on error conditions, log the exception always<commit_after>from celery.task.control import inspect
from errno import errorcode
from ceph_installer import models
from ceph_installer.util import which
from pecan import render
from pecan.hooks import PecanHook
from sqlalchemy.exc import OperationalError
from webob.exc import WSGIHTTPException
import logging


logger = logging.getLogger(__name__)


class SystemCheckError(Exception):

    def __init__(self, message):
        self.message = message


def ansible_exists():
    """
    Perform a simple check to see if ``ansibl-playbook`` executable is present
    in the system where the service is running.
    """
    ansible_path = which('ansible-playbook')
    if not ansible_path:
        raise SystemCheckError('Could not find ansible in system paths')


def celery_has_workers():
    """
    The ``stats()`` call will return different stats/metadata information about
    celery worker(s).  An empty/None result will mean that there aren't any
    celery workers in use.
    """
    stats = inspect().stats()
    if not stats:
        raise SystemCheckError('No running Celery worker was found')


def rabbitmq_is_running():
    """
    If checking for worker stats, an ``IOError`` may be raised depending on the
    problem for the RabbitMQ connection.
    """
    try:
        celery_has_workers()
    except IOError as e:
        msg = "Error connecting to RabbitMQ: " + str(e)
        if len(e.args):
            if errorcode.get(e.args[0]) == 'ECONNREFUSED':
                msg = "RabbitMQ is not running or not reachable"
        raise SystemCheckError(msg)


def database_connection():
    """
    A very simple connection that should succeed if there is a good/correct
    database connection.
    """
    try:
        models.Task.get(1)
    except OperationalError as exc:
        raise SystemCheckError(
            "Could not connect or retrieve information from the database: %s" % exc.message)


system_checks = (
    ansible_exists,
    rabbitmq_is_running,
    celery_has_workers,
    database_connection
)


class CustomErrorHook(PecanHook):
    """
    Ensure a traceback is logged correctly on error conditions.

    When an error condition is detected (one of the callables raises
    a ``SystemCheckError``) it sets the response status to 500 and returns
    a JSON response with the appropriate reason.
    """

    def on_error(self, state, exc):
        if isinstance(exc, WSGIHTTPException):
            if exc.code == 404:
                logger.error("Not Found: %s" % state.request.url)
                return
        logger.exception('unhandled error by ceph-installer')
        for check in system_checks:
            try:
                check()
            except SystemCheckError as system_error:
                state.response.json_body = {'message': system_error.message}
                state.response.status = 500
                state.response.content_type = 'application/json'
                return state.response

{'fraction_non_alphanumeric': 0.0746029199422429, 'fraction_numerical': 0.002566982191561046, 'mean_word_length': 2.787363304981774, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7521618', 'n_tokens_mistral': 1695, 'n_tokens_neox': 1621, 'n_words': 611}