普通文本  |  129行  |  3.76 KB

import 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, datatype 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!')
        elif 'restart_scheduler' in arguments:
            self.server._shutdown_scheduler = True
            self._write_line('Posted the shutdown request')
        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()
        self._shutdown_scheduler = False

        # 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()