"""
-import BaseHTTPServer
-import OpenSSL
-import re
-import socket
-import time
-
from ganeti import constants
+from ganeti import http
from ganeti import errors
-from ganeti import logger
from ganeti import rpc
-from ganeti import serializer
-
from ganeti.rapi import connector
from ganeti.rapi import httperror
-class HttpLogfile:
- """Utility class to write HTTP server log files.
-
- The written format is the "Common Log Format" as defined by Apache:
- http://httpd.apache.org/docs/2.2/mod/mod_log_config.html#examples
-
- """
- MONTHNAME = [None,
- 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
-
- def __init__(self, path):
- self._fd = open(path, 'a', 1)
-
- def __del__(self):
- try:
- self.Close()
- except:
- # Swallow exceptions
- pass
-
- def Close(self):
- if self._fd is not None:
- self._fd.close()
- self._fd = None
-
- def LogRequest(self, request, format, *args):
- if self._fd is None:
- raise errors.ProgrammerError("Logfile already closed")
-
- request_time = self._FormatCurrentTime()
-
- self._fd.write("%s %s %s [%s] %s\n" % (
- # Remote host address
- request.address_string(),
-
- # RFC1413 identity (identd)
- "-",
-
- # Remote user
- "-",
-
- # Request time
- request_time,
-
- # Message
- format % args,
- ))
-
- def _FormatCurrentTime(self):
- """Formats current time in Common Log Format.
-
- """
- return self._FormatLogTime(time.time())
-
- def _FormatLogTime(self, seconds):
- """Formats time for Common Log Format.
-
- All timestamps are logged in the UTC timezone.
-
- Args:
- - seconds: Time in seconds since the epoch
-
- """
- (_, month, _, _, _, _, _, _, _) = tm = time.gmtime(seconds)
- format = "%d/" + self.MONTHNAME[month] + "/%Y:%H:%M:%S +0000"
- return time.strftime(format, tm)
-
-
-class RESTHTTPServer(BaseHTTPServer.HTTPServer):
- """Class to provide an HTTP/HTTPS server.
-
- """
- allow_reuse_address = True
-
- def __init__(self, server_address, HandlerClass, options):
- """REST Server Constructor.
-
- Args:
- server_address: a touple containing:
- ip: a string with IP address, localhost if empty string
- port: port number, integer
- HandlerClass: HTTPRequestHandler object
- options: Command-line options
-
- """
- logger.SetupLogging(debug=options.debug, program='ganeti-rapi')
-
- self.httplog = HttpLogfile(constants.LOG_RAPIACCESS)
-
- BaseHTTPServer.HTTPServer.__init__(self, server_address, HandlerClass)
- if options.ssl:
- # Set up SSL
- context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
- context.use_privatekey_file(options.ssl_key)
- context.use_certificate_file(options.ssl_cert)
- self.socket = OpenSSL.SSL.Connection(context,
- socket.socket(self.address_family,
- self.socket_type))
- else:
- self.socket = socket.socket(self.address_family, self.socket_type)
-
- self.server_bind()
- self.server_activate()
-
-
-class JsonResponse:
- CONTENT_TYPE = "application/json"
-
- def Encode(self, data):
- return serializer.DumpJson(data)
-
-
-class RESTRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+class RESTRequestHandler(http.HTTPRequestHandler):
"""REST Request Handler Class.
"""
def setup(self):
- """Setup secure read and write file objects.
-
- """
- self.connection = self.request
- self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
- self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
+ super(RESTRequestHandler, self).setup()
self._resmap = connector.Mapper()
-
- def handle_one_request(self):
- """Handle a single REST request.
+
+ def HandleRequest(self):
+ """ Handels a request.
"""
- self.raw_requestline = None
- try:
- self.raw_requestline = self.rfile.readline()
- except OpenSSL.SSL.Error, ex:
- logger.Error("Error in SSL: %s" % str(ex))
- if not self.raw_requestline:
- self.close_connection = 1
- return
- if not self.parse_request(): # An error code has been sent, just exit
- return
+ (HandlerClass, items, args) = self._resmap.getController(self.path)
+ handler = HandlerClass(self, items, args)
+ command = self.command.upper()
try:
- (HandlerClass, items, args) = self._resmap.getController(self.path)
- handler = HandlerClass(self, items, args)
+ fn = getattr(handler, command)
+ except AttributeError, err:
+ raise httperror.HTTPBadRequest()
- command = self.command.upper()
- try:
- fn = getattr(handler, command)
- except AttributeError, err:
- raise httperror.HTTPBadRequest()
-
- try:
- result = fn()
-
- except errors.OpPrereqError, err:
- # TODO: "Not found" is not always the correct error. Ganeti's core must
- # differentiate between different error types.
- raise httperror.HTTPNotFound(message=str(err))
-
- encoder = JsonResponse()
- encoded_result = encoder.Encode(result)
-
- self.send_response(200)
- self.send_header("Content-Type", encoder.CONTENT_TYPE)
- self.end_headers()
- self.wfile.write(encoded_result)
-
- except httperror.HTTPException, err:
- self.send_error(err.code, message=err.message)
-
- except Exception, err:
- self.send_error(httperror.HTTPInternalError.code, message=str(err))
-
- def log_message(self, format, *args):
- """Log an arbitrary message.
-
- This is used by all other logging functions.
-
- The first argument, FORMAT, is a format string for the
- message to be logged. If the format string contains
- any % escapes requiring parameters, they should be
- specified as subsequent arguments (it's just like
- printf!).
+ try:
+ result = fn()
- """
- self.server.httplog.LogRequest(self, format, *args)
+ except errors.OpPrereqError, err:
+ # TODO: "Not found" is not always the correct error. Ganeti's core must
+ # differentiate between different error types.
+ raise httperror.HTTPNotFound(message=str(err))
+
+ return result
def start(options):
- # Disable signal handlers, otherwise we can't exit the daemon in a clean way
- # by sending a signal.
- rpc.install_twisted_signal_handlers = False
-
- httpd = RESTHTTPServer(("", options.port), RESTRequestHandler, options)
+ log_fd = open(constants.LOG_RAPIACCESS, 'a')
try:
- httpd.serve_forever()
+ apache_log = http.ApacheLogfile(log_fd)
+ httpd = http.HTTPServer(("", options.port), RESTRequestHandler,
+ httplog=apache_log)
+ try:
+ httpd.serve_forever()
+ finally:
+ httpd.server_close()
+
finally:
- httpd.server_close()
+ log_fd.close()
--- /dev/null
+#!/usr/bin/python
+#
+
+# Copyright (C) 2007, 2008 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script for unittesting the http module"""
+
+
+import os
+import unittest
+import tempfile
+import time
+
+from ganeti import http
+
+
+class HttpLogfileTests(unittest.TestCase):
+ """Rests for ApacheLogfile class."""
+
+ class FakeRequest:
+ FAKE_ADDRESS = "1.2.3.4"
+
+ def address_string(self):
+ return self.FAKE_ADDRESS
+
+ def setUp(self):
+ self.tmpfile = tempfile.NamedTemporaryFile()
+ self.logfile = http.ApacheLogfile(self.tmpfile)
+
+ def tearDown(self):
+ self.tmpfile.close()
+
+ def testFormatLogTime(self):
+ self._TestInTimezone(1208646123.0, "Europe/London",
+ "19/Apr/2008:23:02:03 +0000")
+ self._TestInTimezone(1208646123, "Europe/Zurich",
+ "19/Apr/2008:23:02:03 +0000")
+ self._TestInTimezone(1208646123, "Australia/Sydney",
+ "19/Apr/2008:23:02:03 +0000")
+
+ def _TestInTimezone(self, seconds, timezone, expected):
+ """Tests HttpLogfile._FormatLogTime with a specific timezone
+
+ """
+ # Preserve environment
+ old_TZ = os.environ.get("TZ", None)
+ try:
+ os.environ["TZ"] = timezone
+ time.tzset()
+ result = self.logfile._FormatLogTime(seconds)
+ finally:
+ # Restore environment
+ if old_TZ is not None:
+ os.environ["TZ"] = old_TZ
+ elif "TZ" in os.environ:
+ del os.environ["TZ"]
+ time.tzset()
+
+ self.assertEqual(result, expected)
+
+
+ def testLogRequest(self):
+ request = self.FakeRequest()
+ self.logfile.LogRequest(request, "This is only a %s", "test")
+
+
+if __name__ == '__main__':
+ unittest.main()
"""Script for unittesting the RAPI resources module"""
-import os
import unittest
import tempfile
-import time
from ganeti import errors
+from ganeti import http
+
from ganeti.rapi import connector
-from ganeti.rapi import httperror
from ganeti.rapi import RESTHTTPServer
from ganeti.rapi import rlib1
self.assertEquals(self.map.getController(uri), result)
def _TestFailingUri(self, uri):
- self.failUnlessRaises(httperror.HTTPNotFound, self.map.getController, uri)
+ self.failUnlessRaises(http.HTTPNotFound, self.map.getController, uri)
def testMapper(self):
"""Testing Mapper"""
self.assertEquals(self.root.GET(), expected)
-class HttpLogfileTests(unittest.TestCase):
- """Rests for HttpLogfile class."""
-
- class FakeRequest:
- FAKE_ADDRESS = "1.2.3.4"
-
- def address_string(self):
- return self.FAKE_ADDRESS
-
- def setUp(self):
- self.tmpfile = tempfile.NamedTemporaryFile()
- self.logfile = RESTHTTPServer.HttpLogfile(self.tmpfile.name)
-
- def testFormatLogTime(self):
- self._TestInTimezone(1208646123.0, "Europe/London",
- "19/Apr/2008:23:02:03 +0000")
- self._TestInTimezone(1208646123, "Europe/Zurich",
- "19/Apr/2008:23:02:03 +0000")
- self._TestInTimezone(1208646123, "Australia/Sydney",
- "19/Apr/2008:23:02:03 +0000")
-
- def _TestInTimezone(self, seconds, timezone, expected):
- """Tests HttpLogfile._FormatLogTime with a specific timezone
-
- """
- # Preserve environment
- old_TZ = os.environ.get("TZ", None)
- try:
- os.environ["TZ"] = timezone
- time.tzset()
- result = self.logfile._FormatLogTime(seconds)
- finally:
- # Restore environment
- if old_TZ is not None:
- os.environ["TZ"] = old_TZ
- elif "TZ" in os.environ:
- del os.environ["TZ"]
- time.tzset()
-
- self.assertEqual(result, expected)
-
- def testClose(self):
- self.logfile.Close()
-
- def testCloseAndWrite(self):
- request = self.FakeRequest()
- self.logfile.Close()
- self.assertRaises(errors.ProgrammerError, self.logfile.LogRequest,
- request, "Message")
-
- def testLogRequest(self):
- request = self.FakeRequest()
- self.logfile.LogRequest(request, "This is only a %s", "test")
- self.logfile.Close()
-
-
if __name__ == '__main__':
unittest.main()