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