summaryrefslogtreecommitdiff
path: root/scheduler/status_server.py
blob: 0d9198db43e65a5adea606e0adcf4b04be6fa0a4 (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
import os, BaseHTTPServer, cgi, threading, urllib, fcntl
import common
from autotest_lib.scheduler import 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):
        line = '%s %s/%s processes' % (drone.hostname, drone.active_processes,
                                       drone.max_processes)
        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, drone_manager):
        address = ('', _PORT)
        # HTTPServer is an old-style class :(
        BaseHTTPServer.HTTPServer.__init__(self, address,
                                           StatusServerRequestHandler)
        self._shutting_down = False
        self._drone_manager = drone_manager

        # 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
        print '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):
        print 'Status server running on', 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()