diff --git a/lib/daemon.py b/lib/daemon.py index 1fd1b741eb928a3f7e8d96f9ad9fe45173993d96..86e6c02da5351309b23f584b2a3d433f0d41ccd8 100644 --- a/lib/daemon.py +++ b/lib/daemon.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2006, 2007, 2008 Google Inc. +# Copyright (C) 2006, 2007, 2008, 2010 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 @@ -100,23 +100,6 @@ class GanetiBaseAsyncoreDispatcher(asyncore.dispatcher): return False -def FormatAddress(family, address): - """Format a client's address - - @type family: integer - @param family: socket family (one of socket.AF_*) - @type address: family specific (usually tuple) - @param address: address, as reported by this class - - """ - if family == socket.AF_INET and len(address) == 2: - return "%s:%d" % address - elif family == socket.AF_UNIX and len(address) == 3: - return "pid=%s, uid=%s, gid=%s" % address - else: - return str(address) - - class AsyncStreamServer(GanetiBaseAsyncoreDispatcher): """A stream server to use with asyncore. @@ -159,7 +142,7 @@ class AsyncStreamServer(GanetiBaseAsyncoreDispatcher): # is passed in from accept anyway client_address = netutils.GetSocketCredentials(connected_socket) logging.info("Accepted connection from %s", - FormatAddress(self.family, client_address)) + netutils.FormatAddress(self.family, client_address)) self.handle_connection(connected_socket, client_address) def handle_connection(self, connected_socket, client_address): @@ -290,7 +273,7 @@ class AsyncTerminatedMessageStream(asynchat.async_chat): def close_log(self): logging.info("Closing connection from %s", - FormatAddress(self.family, self.peer_address)) + netutils.FormatAddress(self.family, self.peer_address)) self.close() # this method is overriding an asyncore.dispatcher method diff --git a/lib/http/client.py b/lib/http/client.py index 8658aed45b22ab1bbb57e1b0c8837efd4929a219..891865f39fb414862ab68f679c8f9cf33b2d404f 100644 --- a/lib/http/client.py +++ b/lib/http/client.py @@ -28,6 +28,7 @@ from cStringIO import StringIO from ganeti import http from ganeti import compat +from ganeti import netutils class HttpClientRequest(object): @@ -104,8 +105,13 @@ class HttpClientRequest(object): """Returns the full URL for this requests. """ + if netutils.IPAddress.IsValid(self.host): + family = netutils.IPAddress.GetAddressFamily(self.host) + address = netutils.FormatAddress(family, (self.host, self.port)) + else: + address = "%s:%s" % (self.host, self.port) # TODO: Support for non-SSL requests - return "https://%s:%s%s" % (self.host, self.port, self.path) + return "https://%s%s" % (address, self.path) @property def identity(self): diff --git a/lib/netutils.py b/lib/netutils.py index edbebb4e66b9ecc151e11dc988b6d9853bc8774e..3ab98546790e2f8d0bd5f38020bde0788dd29049 100644 --- a/lib/netutils.py +++ b/lib/netutils.py @@ -472,3 +472,29 @@ class IP6Address(IPAddress): address_int = (address_int << 16) + int(part or '0', 16) return address_int + +def FormatAddress(family, address): + """Format a socket address + + @type family: integer + @param family: socket family (one of socket.AF_*) + @type address: family specific (usually tuple) + @param address: address, as reported by this class + + """ + if family == socket.AF_UNIX and len(address) == 3: + return "pid=%s, uid=%s, gid=%s" % address + + if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2: + host, port = address + if family == socket.AF_INET6: + res = "[%s]" % host + else: + res = host + + if port is not None: + res += ":%s" % port + + return res + + raise errors.ParameterError(family, address) diff --git a/lib/rapi/client.py b/lib/rapi/client.py index 4989831545422df4d79021501863c35e48f2e8fd..8c2c75c0960cc5f8ff13ef96ab462a672c654859 100644 --- a/lib/rapi/client.py +++ b/lib/rapi/client.py @@ -35,6 +35,7 @@ import logging import simplejson +import socket import urllib import threading import pycurl @@ -265,7 +266,13 @@ class GanetiRapiClient(object): self._curl_config_fn = curl_config_fn self._curl_factory = curl_factory - self._base_url = "https://%s:%s" % (host, port) + try: + socket.inet_pton(socket.AF_INET6, host) + address = "[%s]:%s" % (host, port) + except socket.error: + address = "%s:%s" % (host, port) + + self._base_url = "https://%s" % address if username is not None: if password is None: diff --git a/test/ganeti.netutils_unittest.py b/test/ganeti.netutils_unittest.py index 6e324439a3cc6207f8e57591d94845b0633942ab..109fe44bf96196dc85d05a539c8f516358495fbb 100755 --- a/test/ganeti.netutils_unittest.py +++ b/test/ganeti.netutils_unittest.py @@ -385,5 +385,35 @@ class TestIP6TcpPingDeaf(unittest.TestCase, _BaseTcpPingDeafTest): _BaseTcpPingDeafTest.tearDown(self) +class TestFormatAddress(unittest.TestCase): + """Testcase for FormatAddress""" + + def testFormatAddressUnixSocket(self): + res1 = netutils.FormatAddress(socket.AF_UNIX, ("12352", 0, 0)) + self.assertEqual(res1, "pid=12352, uid=0, gid=0") + + def testFormatAddressIP4(self): + res1 = netutils.FormatAddress(socket.AF_INET, ("127.0.0.1", 1234)) + self.assertEqual(res1, "127.0.0.1:1234") + res2 = netutils.FormatAddress(socket.AF_INET, ("192.0.2.32", None)) + self.assertEqual(res2, "192.0.2.32") + + def testFormatAddressIP6(self): + res1 = netutils.FormatAddress(socket.AF_INET6, ("::1", 1234)) + self.assertEqual(res1, "[::1]:1234") + res2 = netutils.FormatAddress(socket.AF_INET6, ("::1", None)) + self.assertEqual(res2, "[::1]") + res2 = netutils.FormatAddress(socket.AF_INET6, ("2001:db8::beef", "80")) + self.assertEqual(res2, "[2001:db8::beef]:80") + + def testInvalidFormatAddress(self): + self.assertRaises(errors.ParameterError, + netutils.FormatAddress, None, ("::1", None)) + self.assertRaises(errors.ParameterError, + netutils.FormatAddress, socket.AF_INET, "127.0.0.1") + self.assertRaises(errors.ParameterError, + netutils.FormatAddress, socket.AF_INET, ("::1")) + + if __name__ == "__main__": testutils.GanetiTestProgram()