diff --git a/keystone/common/environment/eventlet_server.py b/keystone/common/environment/eventlet_server.py index 6ae278bdb9..9a3bc82647 100644 --- a/keystone/common/environment/eventlet_server.py +++ b/keystone/common/environment/eventlet_server.py @@ -71,16 +71,6 @@ class Server(object): self.keepidle = keepidle self.socket = None - def start(self, key=None, backlog=128): - """Run a WSGI server with the given application.""" - - if self.socket is None: - self.listen(key=key, backlog=backlog) - - self.greenthread = self.pool.spawn(self._run, - self.application, - self.socket) - def listen(self, key=None, backlog=128): """Create and start listening on socket. @@ -89,49 +79,65 @@ class Server(object): Raises Exception if this has already been called. """ - if self.socket is not None: - raise Exception(_('Server can only listen once.')) + # 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. + # Please refer below link + # (https://bitbucket.org/eventlet/eventlet/ + # src/e0f578180d7d82d2ed3d8a96d520103503c524ec/eventlet/support/ + # greendns.py?at=0.12#cl-163) + info = socket.getaddrinfo(self.host, + self.port, + socket.AF_UNSPEC, + socket.SOCK_STREAM)[0] + + try: + self.socket = eventlet.listen(info[-1], family=info[0], + backlog=backlog) + except EnvironmentError: + LOG.error(_("Could not bind to %(host)s:%(port)s"), + {'host': self.host, 'port': self.port}) + raise LOG.info(_('Starting %(arg0)s on %(host)s:%(port)s'), {'arg0': sys.argv[0], 'host': self.host, 'port': self.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 - info = socket.getaddrinfo(self.host, - self.port, - socket.AF_UNSPEC, - socket.SOCK_STREAM)[0] - _socket = eventlet.listen(info[-1], - family=info[0], - backlog=backlog) + def start(self, key=None, backlog=128): + """Run a WSGI server with the given application.""" + + if self.socket is None: + self.listen(key=key, backlog=backlog) + + dup_socket = self.socket.dup() if key: - self.socket_info[key] = _socket.getsockname() + self.socket_info[key] = self.socket.getsockname() # SSL is enabled if self.do_ssl: if self.cert_required: cert_reqs = ssl.CERT_REQUIRED else: cert_reqs = ssl.CERT_NONE - sslsocket = eventlet.wrap_ssl(_socket, certfile=self.certfile, - keyfile=self.keyfile, - server_side=True, - cert_reqs=cert_reqs, - ca_certs=self.ca_certs) - _socket = sslsocket + + dup_socket = eventlet.wrap_ssl(dup_socket, certfile=self.certfile, + keyfile=self.keyfile, + server_side=True, + cert_reqs=cert_reqs, + ca_certs=self.ca_certs) # Optionally enable keepalive on the wsgi socket. if self.keepalive: - _socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + dup_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # This option isn't available in the OS X version of eventlet if hasattr(socket, 'TCP_KEEPIDLE') and self.keepidle is not None: - _socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, - self.keepidle) + dup_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, + self.keepidle) - self.socket = _socket + self.greenthread = self.pool.spawn(self._run, + self.application, + dup_socket) def set_ssl(self, certfile, keyfile=None, ca_certs=None, cert_required=True): @@ -141,13 +147,10 @@ class Server(object): self.cert_required = cert_required self.do_ssl = True - def kill(self): + def stop(self): if self.greenthread is not None: self.greenthread.kill() - def stop(self): - self.kill() - def wait(self): """Wait until all servers have completed running.""" try: @@ -157,6 +160,17 @@ class Server(object): except greenlet.GreenletExit: pass + def reset(self): + """Required by the service interface. + + The service interface is used by the launcher when receiving a + SIGHUP. The service interface is defined in + keystone.openstack.common.service.Service. + + Keystone does not need to do anything here. + """ + pass + def _run(self, application, socket): """Start a WSGI server in a new green thread.""" logger = log.getLogger('eventlet.wsgi.server') diff --git a/keystone/tests/ksfixtures/appserver.py b/keystone/tests/ksfixtures/appserver.py index 63b86dae43..3d2307eefa 100644 --- a/keystone/tests/ksfixtures/appserver.py +++ b/keystone/tests/ksfixtures/appserver.py @@ -56,7 +56,7 @@ class AppServer(fixtures.Fixture): self.port = self.server.socket_info['socket'][1] self._update_config_opt() - self.addCleanup(self.server.kill) + self.addCleanup(self.server.stop) def _setup_SSL_if_requested(self): # TODO(dstanek): fix environment.Server to take a SSLOpts instance diff --git a/keystone/tests/test_wsgi.py b/keystone/tests/test_wsgi.py index 74b8e72504..11dfb133b4 100644 --- a/keystone/tests/test_wsgi.py +++ b/keystone/tests/test_wsgi.py @@ -382,40 +382,34 @@ class ServerTest(tests.TestCase): @mock.patch('socket.getaddrinfo') def test_keepalive_unset(self, mock_getaddrinfo, mock_listen): mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)] - mock_sock = mock.Mock() - mock_sock.setsockopt = mock.Mock() + mock_sock_dup = mock_listen.return_value.dup.return_value - mock_listen.return_value = mock_sock server = environment.Server(mock.MagicMock(), host=self.host, port=self.port) server.start() self.assertTrue(mock_listen.called) - self.assertFalse(mock_sock.setsockopt.called) + self.assertFalse(mock_sock_dup.setsockopt.called) @mock.patch('eventlet.listen') @mock.patch('socket.getaddrinfo') def test_keepalive_set(self, mock_getaddrinfo, mock_listen): mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)] - mock_sock = mock.Mock() - mock_sock.setsockopt = mock.Mock() + mock_sock_dup = mock_listen.return_value.dup.return_value - mock_listen.return_value = mock_sock server = environment.Server(mock.MagicMock(), host=self.host, port=self.port, keepalive=True) server.start() - mock_sock.setsockopt.assert_called_once_with(socket.SOL_SOCKET, - socket.SO_KEEPALIVE, - 1) + mock_sock_dup.setsockopt.assert_called_once_with(socket.SOL_SOCKET, + socket.SO_KEEPALIVE, + 1) self.assertTrue(mock_listen.called) @mock.patch('eventlet.listen') @mock.patch('socket.getaddrinfo') def test_keepalive_and_keepidle_set(self, mock_getaddrinfo, mock_listen): mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)] - mock_sock = mock.Mock() - mock_sock.setsockopt = mock.Mock() + mock_sock_dup = mock_listen.return_value.dup.return_value - mock_listen.return_value = mock_sock server = environment.Server(mock.MagicMock(), host=self.host, port=self.port, keepalive=True, keepidle=1) @@ -423,13 +417,13 @@ class ServerTest(tests.TestCase): # keepidle isn't available in the OS X version of eventlet if hasattr(socket, 'TCP_KEEPIDLE'): - self.assertEqual(mock_sock.setsockopt.call_count, 2) + self.assertEqual(mock_sock_dup.setsockopt.call_count, 2) # Test the last set of call args i.e. for the keepidle - mock_sock.setsockopt.assert_called_with(socket.IPPROTO_TCP, - socket.TCP_KEEPIDLE, - 1) + mock_sock_dup.setsockopt.assert_called_with(socket.IPPROTO_TCP, + socket.TCP_KEEPIDLE, + 1) else: - self.assertEqual(mock_sock.setsockopt.call_count, 1) + self.assertEqual(mock_sock_dup.setsockopt.call_count, 1) self.assertTrue(mock_listen.called)