3 # Copyright (C) 2006, 2007 Google Inc.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 import logging.handlers
29 from ganeti.rapi import resources
31 """RESTfull HTTPS Server module.
36 """Set up logging to the syslog.
39 log = logging.getLogger('ganeti-rapi')
40 slh = logging.handlers.SysLogHandler('/dev/log',
41 logging.handlers.SysLogHandler.LOG_DAEMON)
42 fmt = logging.Formatter('ganeti-rapi[%(process)d]:%(levelname)s: %(message)s')
45 log.setLevel(logging.INFO)
46 log.debug("Logging initialized")
51 class RESTHTTPServer(BaseHTTPServer.HTTPServer):
52 """The class to provide HTTP/HTTPS server.
55 def __init__(self, server_address, HandlerClass, options):
56 """REST Server Constructor.
59 server_address - a touple with pair:
60 ip - a string with IP address, localhost if null-string
61 port - port number, integer
62 HandlerClass - HTTPRequestHandler object
63 options - Command-line options
65 BaseHTTPServer.HTTPServer.__init__(self,server_address, HandlerClass)
68 context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
69 context.use_privatekey_file(options.ssl_key)
70 context.use_certificate_file(options.ssl_cert)
71 self.socket = OpenSSL.SSL.Connection(context,
72 socket.socket(self.address_family,
75 self.socket = socket.socket(self.address_family, self.socket_type)
78 self.server_activate()
81 class RESTRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
82 """REST Request Handler Class."""
84 def authenticate(self):
85 """This method performs authentication check."""
89 """Setup secure read and write file objects."""
90 self.connection = self.request
91 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
92 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
93 self.map = resources.Mapper()
95 self.log.debug("Request handler setup.")
97 def handle_one_request(self):
98 """Handle a single REST request. """
99 self.raw_requestline = None
101 self.raw_requestline = self.rfile.readline()
102 except OpenSSL.SSL.Error, ex:
103 self.log.exception("Error in SSL: %s" % str(ex))
104 if not self.raw_requestline:
105 self.close_connection = 1
107 if not self.parse_request(): # An error code has been sent, just exit
109 if not self.authenticate():
110 self.send_error(401, "Acces Denied")
113 rname = self.R_Resource(self.path)
114 mname = 'do_' + self.command
115 if not hasattr(rname, mname):
116 self.send_error(501, "Unsupported method (%r)" % self.command)
118 method = getattr(rname, mname)
120 except AttributeError, msg:
121 self.send_error(501, "Resource is not available: %s" % msg)
123 def log_message(self, format, *args):
124 """Log an arbitrary message.
126 This is used by all other logging functions.
128 The first argument, FORMAT, is a format string for the
129 message to be logged. If the format string contains
130 any % escapes requiring parameters, they should be
131 specified as subsequent arguments (it's just like
134 The client host and current date/time are prefixed to
140 origin = inspect.stack()[1][0].f_code.co_name
141 if origin == "log_error":
142 level = logging.ERROR
144 self.log.log(level, "%s - - %s\n" %
145 (self.address_string(),
148 def R_Resource(self, uri):
149 """Create controller from the URL.
152 uri - a string with requested URL.
155 R_Generic class inheritor.
157 controller = self.map.getController(uri)
159 return eval("resources.%s(self, %s, %s)" % controller)
161 raise exceptions.AttribureError
165 httpd = RESTHTTPServer(("", options.port), RESTRequestHandler, options)
167 httpd.serve_forever()
172 if __name__ == "__main__":