From 20314e0ba06588d3ac7855d1b4ae7f3c75cab1a1 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Thu, 10 Jan 2013 11:26:25 -0500 Subject: [PATCH] Enhance wsgi to listen on ipv6 address Check if the hostname is ipv6 and set the family appropriately. Picked up the code snippet from glance to determine the address_family per markmclain's comment Picked up some code from nova as well to get the test case running properly Fixes LP# 1101341 Change-Id: I67166dc030e4ea0dd82888abfbc9db555747de27 --- quantum/tests/unit/test_wsgi.py | 69 +++++++++++++++++++++++++++++++++ quantum/wsgi.py | 37 +++++++++++++++++- 2 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 quantum/tests/unit/test_wsgi.py diff --git a/quantum/tests/unit/test_wsgi.py b/quantum/tests/unit/test_wsgi.py new file mode 100644 index 000000000..45dedc1c4 --- /dev/null +++ b/quantum/tests/unit/test_wsgi.py @@ -0,0 +1,69 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +import socket + +import unittest2 as unittest + +from quantum import wsgi + + +class TestWSGIServer(unittest.TestCase): + """WSGI server tests.""" + + def test_start_random_port(self): + server = wsgi.Server("test_random_port") + server.start(None, 0, host="127.0.0.1") + self.assertNotEqual(0, server.port) + server.stop() + server.wait() + + def test_start_random_port_with_ipv6(self): + server = wsgi.Server("test_random_port") + server.start(None, 0, host="::1") + self.assertEqual("::1", server.host) + self.assertNotEqual(0, server.port) + server.stop() + server.wait() + + +class TestWSGIServer2(unittest.TestCase): + def setUp(self): + self.eventlet_p = mock.patch.object(wsgi, 'eventlet') + self.eventlet = self.eventlet_p.start() + self.server = wsgi.Server("test_app") + + def tearDown(self): + self.eventlet_p.stop() + + def test_ipv6_with_link_local_start(self): + mock_app = mock.Mock() + with mock.patch.object(self.server, 'pool') as pool: + self.server.start(mock_app, + 0, + host="fe80::204:acff:fe96:da87%eth0") + self.eventlet.assert_has_calls([ + mock.call.listen(('fe80::204:acff:fe96:da87%eth0', 0, 0, 2), + backlog=128, + family=10) + ]) + pool.spawn.assert_has_calls([mock.call( + self.server._run, + mock_app, + self.eventlet.listen.mock_calls[0].return_value) + ]) diff --git a/quantum/wsgi.py b/quantum/wsgi.py index afef2db41..c9e6a5219 100644 --- a/quantum/wsgi.py +++ b/quantum/wsgi.py @@ -18,6 +18,7 @@ """ Utility methods for working with WSGI servers """ +import socket import sys from xml.dom import minidom from xml.parsers import expat @@ -51,8 +52,40 @@ class Server(object): def start(self, application, port, host='0.0.0.0', backlog=128): """Run a WSGI server with the given application.""" - socket = eventlet.listen((host, port), backlog=backlog) - self.pool.spawn_n(self._run, application, socket) + self._host = host + self._port = port + + # TODO(dims): eventlet's green dns/socket module does not actually + # support IPv6 in getaddrinfo(). We need to get around this in the + # future or monitor upstream for a fix + try: + info = socket.getaddrinfo(self._host, + self._port, + socket.AF_UNSPEC, + socket.SOCK_STREAM)[0] + family = info[0] + bind_addr = info[-1] + + self._socket = eventlet.listen(bind_addr, + family=family, + backlog=backlog) + except: + LOG.exception(_("Unable to listen on %(host)s:%(port)s") % + {'host': host, 'port': port}) + sys.exit(1) + + self._server = self.pool.spawn(self._run, application, self._socket) + + @property + def host(self): + return self._socket.getsockname()[0] if self._socket else self._host + + @property + def port(self): + return self._socket.getsockname()[1] if self._socket else self._port + + def stop(self): + self._server.kill() def wait(self): """Wait until all servers have completed running.""" -- 2.45.2