Fix pylint errors for RAPI
[ganeti-local] / lib / rapi / RESTHTTPServer.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2006, 2007 Google Inc.
4 #
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.
9 #
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.
14 #
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
18 # 02110-1301, USA.
19
20 import socket
21 import inspect
22 import exceptions
23 import SocketServer
24 import BaseHTTPServer
25 import OpenSSL
26 import logging
27 import logging.handlers
28
29 from ganeti.rapi import resources
30
31 """RESTfull HTTPS Server module.
32
33 """
34
35 def OpenLog():
36   """Set up logging to the syslog.
37
38   """
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')
43   slh.setFormatter(fmt)
44   log.addHandler(slh)
45   log.setLevel(logging.INFO)
46   log.debug("Logging initialized")
47
48   return log
49
50
51 class RESTHTTPServer(BaseHTTPServer.HTTPServer):
52   """The class to provide HTTP/HTTPS server.
53
54   """
55   def __init__(self, server_address, HandlerClass, options):
56     """REST Server Constructor.
57
58     Args:
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
64     """
65     BaseHTTPServer.HTTPServer.__init__(self,server_address, HandlerClass)
66     if options.ssl:
67       # Set up SSL
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,
73                                            self.socket_type))
74     else:
75       self.socket = socket.socket(self.address_family, self.socket_type)
76
77     self.server_bind()
78     self.server_activate()
79
80
81 class RESTRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
82   """REST Request Handler Class."""
83
84   def authenticate(self):
85     """This method performs authentication check."""
86     return True
87
88   def setup(self):
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()
94     self.log = OpenLog()
95     self.log.debug("Request handler setup.")
96
97   def handle_one_request(self):
98     """Handle a single REST request. """
99     self.raw_requestline = None
100     try:
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
106       return
107     if not self.parse_request(): # An error code has been sent, just exit
108       return
109     if not self.authenticate():
110       self.send_error(401, "Acces Denied")
111       return
112     try:
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)
117         return
118       method = getattr(rname, mname)
119       method()
120     except AttributeError, msg:
121       self.send_error(501, "Resource is not available: %s" % msg)
122
123   def log_message(self, format, *args):
124     """Log an arbitrary message.
125
126     This is used by all other logging functions.
127
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
132     printf!).
133
134     The client host and current date/time are prefixed to
135     every message.
136
137     """
138     level = logging.INFO
139     # who is calling?
140     origin = inspect.stack()[1][0].f_code.co_name
141     if origin == "log_error":
142       level = logging.ERROR
143
144     self.log.log(level, "%s - - %s\n" %
145                      (self.address_string(),
146                       format%args))
147
148   def R_Resource(self, uri):
149     """Create controller from the URL.
150
151     Args:
152       uri - a string with requested URL.
153
154     Returns:
155       R_Generic class inheritor.
156     """
157     controller = self.map.getController(uri)
158     if controller:
159       return eval("resources.%s(self, %s, %s)" % controller)
160     else:
161       raise exceptions.AttribureError
162
163
164 def start(options):
165   httpd = RESTHTTPServer(("", options.port), RESTRequestHandler, options)
166   try:
167     httpd.serve_forever()
168   finally:
169     httpd.server_close()
170
171
172 if __name__ == "__main__":
173   pass