diff --git a/oslo_service/eventlet_backdoor.py b/oslo_service/eventlet_backdoor.py index f4ecbf49..a82d5e2b 100644 --- a/oslo_service/eventlet_backdoor.py +++ b/oslo_service/eventlet_backdoor.py @@ -21,13 +21,13 @@ import gc import logging import os import pprint -import socket import sys import traceback import eventlet.backdoor import greenlet +from eventlet.green import socket from oslo_service._i18n import _ from oslo_service import _options @@ -121,11 +121,24 @@ def _parse_port_range(port_range): port_range, ex, _options.help_for_backdoor_port) -def _listen(host, start_port, end_port, listen_func): +def _listen_func(host, port): + # eventlet is setting SO_REUSEPORT by default from v0.20. + # But we can configure it by passing reuse_port argument + # from v0.22 + try: + return eventlet.listen((host, port), reuse_port=False) + except TypeError: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind((host, port)) + sock.listen(50) + return sock + + +def _listen(host, start_port, end_port): try_port = start_port while True: try: - return listen_func((host, try_port)) + return _listen_func(host, try_port) except socket.error as exc: if (exc.errno != errno.EADDRINUSE or try_port >= end_port): @@ -169,7 +182,7 @@ def _initialize_if_enabled(conf): if conf.backdoor_socket is None: start_port, end_port = _parse_port_range(str(conf.backdoor_port)) - sock = _listen('localhost', start_port, end_port, eventlet.listen) + sock = _listen('localhost', start_port, end_port) # In the case of backdoor port being zero, a port number is assigned by # listen(). In any case, pull the port number out here. where_running = sock.getsockname()[1] diff --git a/oslo_service/tests/test_eventlet_backdoor.py b/oslo_service/tests/test_eventlet_backdoor.py index 4c00c57f..480761e1 100644 --- a/oslo_service/tests/test_eventlet_backdoor.py +++ b/oslo_service/tests/test_eventlet_backdoor.py @@ -105,6 +105,14 @@ class BackdoorPortTest(base.ServiceBaseTestCase): self.assertRaises(socket.error, eventlet_backdoor.initialize_if_enabled, self.conf) + @mock.patch.object(eventlet, 'spawn') + def test_backdoor_port_range_inuse(self, spawn_mock): + self.config(backdoor_port='8800:8801') + port = eventlet_backdoor.initialize_if_enabled(self.conf) + self.assertEqual(8800, port) + port = eventlet_backdoor.initialize_if_enabled(self.conf) + self.assertEqual(8801, port) + @mock.patch.object(eventlet, 'spawn') @mock.patch.object(eventlet, 'listen') def test_backdoor_port_range(self, listen_mock, spawn_mock):