Optimize the OpQueryNodes for names only
[ganeti-local] / daemons / ganeti-rapi
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21 """ Ganeti Remote API master script.
22 """
23
24 import glob
25 import logging
26 import optparse
27 import sys
28 import os
29 import signal
30
31 from ganeti import logger
32 from ganeti import constants
33 from ganeti import errors
34 from ganeti import http
35 from ganeti import ssconf
36 from ganeti import utils
37 from ganeti.rapi import connector
38
39
40 class RESTRequestHandler(http.HTTPRequestHandler):
41   """REST Request Handler Class.
42
43   """
44   def setup(self):
45     super(RESTRequestHandler, self).setup()
46     self._resmap = connector.Mapper()
47
48   def HandleRequest(self):
49     """ Handels a request.
50
51     """
52     (HandlerClass, items, args) = self._resmap.getController(self.path)
53     handler = HandlerClass(self, items, args, self.post_data)
54
55     command = self.command.upper()
56     try:
57       fn = getattr(handler, command)
58     except AttributeError, err:
59       raise http.HTTPBadRequest()
60
61     try:
62       try:
63         result = fn()
64       except:
65         logging.exception("Error while handling the %s request", command)
66         raise
67
68     except errors.OpPrereqError, err:
69       # TODO: "Not found" is not always the correct error. Ganeti's core must
70       # differentiate between different error types.
71       raise http.HTTPNotFound(message=str(err))
72
73     return result
74
75
76 class RESTHttpServer(http.HTTPServer):
77   def serve_forever(self):
78     """Handle one request at a time until told to quit."""
79     sighandler = utils.SignalHandler([signal.SIGINT, signal.SIGTERM])
80     try:
81       while not sighandler.called:
82         self.handle_request()
83     finally:
84       sighandler.Reset()
85
86
87 def ParseOptions():
88   """Parse the command line options.
89
90   Returns:
91     (options, args) as from OptionParser.parse_args()
92
93   """
94   parser = optparse.OptionParser(description="Ganeti Remote API",
95                     usage="%prog [-d] [-p port]",
96                     version="%%prog (ganeti) %s" %
97                                  constants.RAPI_VERSION)
98   parser.add_option("-d", "--debug", dest="debug",
99                     help="Enable some debug messages",
100                     default=False, action="store_true")
101   parser.add_option("-p", "--port", dest="port",
102                     help="Port to run API (%s default)." %
103                                  constants.RAPI_PORT,
104                     default=constants.RAPI_PORT, type="int")
105   parser.add_option("-S", "--https", dest="ssl",
106                     help="Secure HTTP protocol with SSL",
107                     default=False, action="store_true")
108   parser.add_option("-K", "--ssl-key", dest="ssl_key",
109                     help="SSL key",
110                     default=None, type="string")
111   parser.add_option("-C", "--ssl-cert", dest="ssl_cert",
112                     help="SSL certificate",
113                     default=None, type="string")
114   parser.add_option("-f", "--foreground", dest="fork",
115                     help="Don't detach from the current terminal",
116                     default=True, action="store_false")
117
118   options, args = parser.parse_args()
119
120   if len(args) != 0:
121     print >> sys.stderr, "Usage: %s [-d] [-p port]" % sys.argv[0]
122     sys.exit(1)
123
124   if options.ssl and not (options.ssl_cert and options.ssl_key):
125     print >> sys.stderr, ("For secure mode please provide "
126                          "--ssl-key and --ssl-cert arguments")
127     sys.exit(1)
128
129   return options, args
130
131
132 def main():
133   """Main function.
134
135   """
136   options, args = ParseOptions()
137
138   ssconf.CheckMaster(options.debug)
139
140   if options.fork:
141     utils.Daemonize(logfile=constants.LOG_RAPISERVER)
142
143   logger.SetupLogging(constants.LOG_RAPISERVER, debug=options.debug,
144                      stderr_logging=not options.fork)
145
146   utils.WritePidFile(constants.RAPI_PID)
147
148   log_fd = open(constants.LOG_RAPIACCESS, 'a')
149   try:
150     apache_log = http.ApacheLogfile(log_fd)
151     httpd = RESTHttpServer(("", options.port), RESTRequestHandler,
152                            httplog=apache_log)
153     try:
154       httpd.serve_forever()
155     finally:
156       httpd.server_close()
157       utils.RemovePidFile(constants.RAPI_PID)
158
159   finally:
160     log_fd.close()
161
162   sys.exit(0)
163
164
165 if __name__ == '__main__':
166   
167   main()