1#!/usr/bin/python
2#
3# Copyright 2010 Google Inc. All Rights Reserved.
4
5import logging
6import optparse
7import pickle
8import signal
9from SimpleXMLRPCServer import SimpleXMLRPCServer
10import sys
11
12from automation.common import logger
13from automation.common.command_executer import CommandExecuter
14from automation.server import machine_manager
15from automation.server.job_group_manager import JobGroupManager
16from automation.server.job_manager import JobManager
17
18
19class Server(object):
20  """Plays a role of external interface accessible over XMLRPC."""
21
22  def __init__(self, machines_file=None, dry_run=False):
23    """Default constructor.
24
25    Args:
26      machines_file: Path to file storing a list of machines.
27      dry_run: If True, the server only simulates command execution.
28    """
29    CommandExecuter.Configure(dry_run)
30
31    self.job_manager = JobManager(
32        machine_manager.MachineManager.FromMachineListFile(
33            machines_file or machine_manager.DEFAULT_MACHINES_FILE))
34
35    self.job_group_manager = JobGroupManager(self.job_manager)
36
37    self._logger = logging.getLogger(self.__class__.__name__)
38
39  def ExecuteJobGroup(self, job_group, dry_run=False):
40    job_group = pickle.loads(job_group)
41    self._logger.info('Received ExecuteJobGroup(%r, dry_run=%s) request.',
42                      job_group, dry_run)
43
44    for job in job_group.jobs:
45      job.dry_run = dry_run
46    return self.job_group_manager.AddJobGroup(job_group)
47
48  def GetAllJobGroups(self):
49    self._logger.info('Received GetAllJobGroups() request.')
50    return pickle.dumps(self.job_group_manager.GetAllJobGroups())
51
52  def KillJobGroup(self, job_group_id):
53    self._logger.info('Received KillJobGroup(%d) request.', job_group_id)
54    self.job_group_manager.KillJobGroup(pickle.loads(job_group_id))
55
56  def GetJobGroup(self, job_group_id):
57    self._logger.info('Received GetJobGroup(%d) request.', job_group_id)
58
59    return pickle.dumps(self.job_group_manager.GetJobGroup(job_group_id))
60
61  def GetJob(self, job_id):
62    self._logger.info('Received GetJob(%d) request.', job_id)
63
64    return pickle.dumps(self.job_manager.GetJob(job_id))
65
66  def GetMachineList(self):
67    self._logger.info('Received GetMachineList() request.')
68
69    return pickle.dumps(self.job_manager.machine_manager.GetMachineList())
70
71  def StartServer(self):
72    self.job_manager.StartJobManager()
73
74  def StopServer(self):
75    self.job_manager.StopJobManager()
76    self.job_manager.join()
77
78
79def GetServerOptions():
80  """Get server's settings from command line options."""
81  parser = optparse.OptionParser()
82  parser.add_option('-m',
83                    '--machines-file',
84                    dest='machines_file',
85                    help='The location of the file '
86                    'containing the machines database',
87                    default=machine_manager.DEFAULT_MACHINES_FILE)
88  parser.add_option('-n',
89                    '--dry-run',
90                    dest='dry_run',
91                    help='Start the server in dry-run mode, where jobs will '
92                    'not actually be executed.',
93                    action='store_true',
94                    default=False)
95  return parser.parse_args()[0]
96
97
98def Main():
99  logger.SetUpRootLogger(filename='server.log', level=logging.DEBUG)
100
101  options = GetServerOptions()
102  server = Server(options.machines_file, options.dry_run)
103  server.StartServer()
104
105  def _HandleKeyboardInterrupt(*_):
106    server.StopServer()
107    sys.exit(1)
108
109  signal.signal(signal.SIGINT, _HandleKeyboardInterrupt)
110
111  try:
112    xmlserver = SimpleXMLRPCServer(
113        ('localhost', 8000),
114        allow_none=True,
115        logRequests=False)
116    xmlserver.register_instance(server)
117    xmlserver.serve_forever()
118  except Exception as ex:
119    logging.error(ex)
120    server.StopServer()
121    sys.exit(1)
122
123
124if __name__ == '__main__':
125  Main()
126