summaryrefslogtreecommitdiff
path: root/scheduler/status_server.py
blob: 29d23db19bacba7eac71208884d22f5466d39388 (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
import os, BaseHTTPServer, cgi, threading, urllib, fcntl, logging
import common
from autotest_lib.scheduler import drone_manager, scheduler_config

_PORT = 13467

_HEADER = """
<html>
<head><title>Scheduler status</title></head>
<body>
Actions:<br>
<a href="?reparse_config=1">Reparse global config values</a><br>
<br>
"""

_FOOTER = """
</body>
</html>
"""

class StatusServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def _send_headers(self):
        self.send_response(200, 'OK')
        self.send_header('Content-Type', 'text/html')
        self.end_headers()


    def _parse_arguments(self):
        path_parts = self.path.split('?', 1)
        if len(path_parts) == 1:
            return {}

        encoded_args = path_parts[1]
        return cgi.parse_qs(encoded_args)


    def _write_line(self, line=''):
        self.wfile.write(line + '<br>\n')


    def _write_field(self, field, value):
        self._write_line('%s=%s' % (field, value))


    def _write_all_fields(self):
        self._write_line('Config values:')
        for field in scheduler_config.SchedulerConfig.FIELDS:
            self._write_field(field, getattr(scheduler_config.config, field))
        self._write_line()


    def _write_drone(self, drone):
        if drone.allowed_users:
            allowed_users = ', '.join(drone.allowed_users)
        else:
            allowed_users = 'all'
        line = ('%s: %s/%s processes, users: %s'
                % (drone.hostname, drone.active_processes, drone.max_processes,
                   allowed_users))
        if not drone.enabled:
            line += ' (disabled)'
        self._write_line(line)


    def _write_drone_list(self):
        self._write_line('Drones:')
        for drone in self.server._drone_manager.get_drones():
            self._write_drone(drone)
        self._write_line()


    def _execute_actions(self, arguments):
        if 'reparse_config' in arguments:
            scheduler_config.config.read_config()
            self.server._drone_manager.refresh_drone_configs()
            self._write_line('Reparsed config!')
        self._write_line()


    def do_GET(self):
        self._send_headers()
        self.wfile.write(_HEADER)

        arguments = self._parse_arguments()
        self._execute_actions(arguments)
        self._write_all_fields()
        self._write_drone_list()

        self.wfile.write(_FOOTER)


class StatusServer(BaseHTTPServer.HTTPServer):
    def __init__(self):
        address = ('', _PORT)
        # HTTPServer is an old-style class :(
        BaseHTTPServer.HTTPServer.__init__(self, address,
                                           StatusServerRequestHandler)
        self._shutting_down = False
        self._drone_manager = drone_manager.instance()

        # ensure the listening socket is not inherited by child processes
        old_flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
        fcntl.fcntl(self.fileno(), fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)


    def shutdown(self):
        if self._shutting_down:
            return
        logging.info('Shutting down server...')
        self._shutting_down = True
        # make one last request to awaken the server thread and make it exit
        urllib.urlopen('http://localhost:%s' % _PORT)


    def _serve_until_shutdown(self):
        logging.info('Status server running on %s', self.server_address)
        while not self._shutting_down:
            self.handle_request()


    def start(self):
        self._thread = threading.Thread(target=self._serve_until_shutdown,
                                        name='status_server')
        self._thread.start()