summaryrefslogtreecommitdiff
path: root/client/diagnose.py
blob: 9e2c68fb9a6df4d64945647857a8909f654b4c15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env python2
import time
import re
import socket
import os
import jasperpath
import subprocess
import logging
import sys
from distutils.spawn import find_executable
from pip.req import parse_requirements
import pip.util

logger = logging.getLogger(__name__)

class Diagnostics:

    """
    Set of diagnostics to be run for determining the health of the
    host running Jasper

    To add new checks, add a boolean returning method with a name that starts
    with `check_`
    """

    @classmethod
    def check_network_connection(cls):
        try:
            # see if we can resolve the host name -- tells us if there is
            # a DNS listening
            host = socket.gethostbyname("www.google.com")
            # connect to the host -- tells us if the host is actually
            # reachable
            socket.create_connection((host, 80), 2)
        except Exception:
            return False
        else:
            return True

    @classmethod
    def check_phonetisaurus_dictionary_file(cls):
        return os.path.isfile(os.path.join(jasperpath.APP_PATH, "..", "phonetisaurus/g014b2b.fst"))

    @classmethod
    def check_phonetisaurus_program(cls):
        return cls.do_check_program('phonetisaurus-g2p')

    @classmethod
    def check_espeak_program(cls):
        return cls.do_check_program('espeak')

    @classmethod
    def check_say_program(cls):
        return cls.do_check_program('say')

    @classmethod
    def do_check_program(cls, program):
        return find_executable(program) is not None

    @classmethod
    def check_all_pip_requirements_installed(cls):
        distributions = pip.util.get_installed_distributions()
        requirements_lines = [line.strip() for line in open('requirements.txt').readlines()]
        requirements = [ name.split('==')[0] for name in list(filter(None, requirements_lines))]
        installed_packages = [ pkg.project_name for pkg in list(distributions)]
        missing_packages = [ pkg for pkg in requirements if pkg not in installed_packages ]
        if missing_packages:
            logger.info("Missing packages: "+', '.join(missing_packages))
            return False
        else:
            return True

    @classmethod
    def info_git_revision(cls):
        return subprocess.check_output(['git', 'rev-parse', 'HEAD'])


class DiagnosticRunner:

    """
    Performs a series of checks against the system, printing the results to the
    console and also saving them to diagnostic.log
    """

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

    def run(self):
        self.initialize_log()
        self.perform_checks()

    def perform_checks(self):
        self.failed_checks = 0
        for check in self.select_methods('check'):
            self.do_check(check)
        for info in self.select_methods('info'):
            self.get_info(info)
        if self.failed_checks == 0:
            logger.info("All checks passed")
        else:
            logger.info("%d checks failed" % self.failed_checks)

    def select_methods(self, prefix):
        def is_match(method_name):
            return callable(getattr(self.diagnostics, method_name)) and re.match(r"\A" + prefix + "_", method_name)

        return [method_name for method_name in dir(self.diagnostics) if is_match(method_name)]

    def initialize_log(self):
        logger.info("Starting jasper diagnostic at %s" % time.strftime("%c"))

    def get_info(self, info_name):
        message = info_name.replace("info_", "").replace("_", " ")
        info_method = getattr(self.diagnostics, info_name)
        info = info_method()
        logger.info("%s: %s" % (message, info))

    def do_check(self, check_name):
        message = check_name.replace("check_", "").replace("_", " ")
        check = getattr(self.diagnostics, check_name)
        if check():
            result = "OK"
        else:
            self.failed_checks += 1
            result = "FAILED"

        logger.info("Checking %s... %s" % (message, result))


if __name__ == '__main__':
    logging.basicConfig(stream=sys.stdout, level=logging.INFO)

    DiagnosticRunner(Diagnostics).run()