3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 import logging.handlers
27 from ganeti.rapi import resources
29 """RESTfull HTTPS Server module.
34 """Set up logging to the syslog.
37 log = logging.getLogger('ganeti-rapi')
38 slh = logging.handlers.SysLogHandler('/dev/log',
39 logging.handlers.SysLogHandler.LOG_DAEMON)
40 fmt = logging.Formatter('ganeti-rapi[%(process)d]:%(levelname)s: %(message)s')
43 log.setLevel(logging.INFO)
44 log.debug("Logging initialized")
49 class RESTHTTPServer(BaseHTTPServer.HTTPServer):
50 """The class to provide HTTP/HTTPS server.
53 def __init__(self, server_address, HandlerClass, options):
54 """REST Server Constructor.
57 server_address - a touple with pair:
58 ip - a string with IP address, localhost if null-string
59 port - port number, integer
60 HandlerClass - HTTPRequestHandler object
61 options - Command-line options
63 BaseHTTPServer.HTTPServer.__init__(self, server_address, HandlerClass)
66 context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
67 context.use_privatekey_file(options.ssl_key)
68 context.use_certificate_file(options.ssl_cert)
69 self.socket = OpenSSL.SSL.Connection(context,
70 socket.socket(self.address_family,
73 self.socket = socket.socket(self.address_family, self.socket_type)
76 self.server_activate()
79 class RESTRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
80 """REST Request Handler Class."""
82 def authenticate(self):
83 """This method performs authentication check."""
87 """Setup secure read and write file objects."""
88 self.connection = self.request
89 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
90 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
91 self.map = resources.Mapper()
93 self.log.debug("Request handler setup.")
95 def handle_one_request(self):
96 """Handle a single REST request. """
97 self.raw_requestline = None
99 self.raw_requestline = self.rfile.readline()
100 except OpenSSL.SSL.Error, ex:
101 self.log.exception("Error in SSL: %s" % str(ex))
102 if not self.raw_requestline:
103 self.close_connection = 1
105 if not self.parse_request(): # An error code has been sent, just exit
107 if not self.authenticate():
108 self.send_error(401, "Acces Denied")
111 rname = self.R_Resource(self.path)
112 mname = 'do_' + self.command
113 if not hasattr(rname, mname):
114 self.send_error(501, "Unsupported method (%r)" % self.command)
116 method = getattr(rname, mname)
118 except AttributeError, msg:
119 self.send_error(501, "Resource is not available: %s" % msg)
121 def log_message(self, format, *args):
122 """Log an arbitrary message.
124 This is used by all other logging functions.
126 The first argument, FORMAT, is a format string for the
127 message to be logged. If the format string contains
128 any % escapes requiring parameters, they should be
129 specified as subsequent arguments (it's just like
132 The client host and current date/time are prefixed to
138 origin = inspect.stack()[1][0].f_code.co_name
139 if origin == "log_error":
140 level = logging.ERROR
142 self.log.log(level, "%s - - %s\n" %
143 (self.address_string(),
146 def R_Resource(self, uri):
147 """Create controller from the URL.
150 uri - a string with requested URL.
153 R_Generic class inheritor.
155 controller = self.map.getController(uri)
157 return eval("resources.%s(self, %s, %s)" % controller)
159 raise exceptions.AttribureError
163 httpd = RESTHTTPServer(("", options.port), RESTRequestHandler, options)
165 httpd.serve_forever()
170 if __name__ == "__main__":