Home | History | Annotate | Download | only in scheduler
      1 import BaseHTTPServer, cgi, threading, urllib, fcntl, logging
      2 import common
      3 from autotest_lib.scheduler import drone_manager, scheduler_config
      4 
      5 _PORT = 13467
      6 
      7 _HEADER = """
      8 <html>
      9 <head><title>Scheduler status</title></head>
     10 <body>
     11 Actions:<br>
     12 <a href="?reparse_config=1">Reparse global config values</a><br>
     13 <br>
     14 """
     15 
     16 _FOOTER = """
     17 </body>
     18 </html>
     19 """
     20 
     21 class StatusServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
     22     def _send_headers(self):
     23         self.send_response(200, 'OK')
     24         self.send_header('Content-Type', 'text/html')
     25         self.end_headers()
     26 
     27 
     28     def _parse_arguments(self):
     29         path_parts = self.path.split('?', 1)
     30         if len(path_parts) == 1:
     31             return {}
     32 
     33         encoded_args = path_parts[1]
     34         return cgi.parse_qs(encoded_args)
     35 
     36 
     37     def _write_line(self, line=''):
     38         self.wfile.write(line + '<br>\n')
     39 
     40 
     41     def _write_field(self, field, value):
     42         self._write_line('%s=%s' % (field, value))
     43 
     44 
     45     def _write_all_fields(self):
     46         self._write_line('Config values:')
     47         for field, datatype in scheduler_config.SchedulerConfig.FIELDS:
     48             self._write_field(field, getattr(scheduler_config.config, field))
     49         self._write_line()
     50 
     51 
     52     def _write_drone(self, drone):
     53         if drone.allowed_users:
     54             allowed_users = ', '.join(drone.allowed_users)
     55         else:
     56             allowed_users = 'all'
     57         line = ('%s: %s/%s processes, users: %s'
     58                 % (drone.hostname, drone.active_processes, drone.max_processes,
     59                    allowed_users))
     60         if not drone.enabled:
     61             line += ' (disabled)'
     62         self._write_line(line)
     63 
     64 
     65     def _write_drone_list(self):
     66         self._write_line('Drones:')
     67         for drone in self.server._drone_manager.get_drones():
     68             self._write_drone(drone)
     69         self._write_line()
     70 
     71 
     72     def _execute_actions(self, arguments):
     73         if 'reparse_config' in arguments:
     74             scheduler_config.config.read_config()
     75             self.server._drone_manager.refresh_drone_configs()
     76             self._write_line('Reparsed config!')
     77         elif 'restart_scheduler' in arguments:
     78             self.server._shutdown_scheduler = True
     79             self._write_line('Posted the shutdown request')
     80         self._write_line()
     81 
     82 
     83     def do_GET(self):
     84         self._send_headers()
     85         self.wfile.write(_HEADER)
     86 
     87         arguments = self._parse_arguments()
     88         self._execute_actions(arguments)
     89         self._write_all_fields()
     90         self._write_drone_list()
     91 
     92         self.wfile.write(_FOOTER)
     93 
     94 
     95 class StatusServer(BaseHTTPServer.HTTPServer):
     96     def __init__(self):
     97         address = ('', _PORT)
     98         # HTTPServer is an old-style class :(
     99         BaseHTTPServer.HTTPServer.__init__(self, address,
    100                                            StatusServerRequestHandler)
    101         self._shutting_down = False
    102         self._drone_manager = drone_manager.instance()
    103         self._shutdown_scheduler = False
    104 
    105         # ensure the listening socket is not inherited by child processes
    106         old_flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
    107         fcntl.fcntl(self.fileno(), fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
    108 
    109 
    110     def shutdown(self):
    111         if self._shutting_down:
    112             return
    113         logging.info('Shutting down server...')
    114         self._shutting_down = True
    115         # make one last request to awaken the server thread and make it exit
    116         urllib.urlopen('http://localhost:%s' % _PORT)
    117 
    118 
    119     def _serve_until_shutdown(self):
    120         logging.info('Status server running on %s', self.server_address)
    121         while not self._shutting_down:
    122             self.handle_request()
    123 
    124 
    125     def start(self):
    126         self._thread = threading.Thread(target=self._serve_until_shutdown,
    127                                         name='status_server')
    128         self._thread.start()
    129