From 811650783d32accd2b49f907ddc520955146962c Mon Sep 17 00:00:00 2001 From: venkata anil Date: Wed, 2 Jan 2019 05:11:12 -0500 Subject: [PATCH] Avoid eventlet_backdoor listing on same port Oslo.service is binding to same port when we provide a port range as eventlet is internally setting SO_REUSEPORT flag (starting from eventlet version v0.20). And there is a flag (reuse_port) introduced in v0.22 to give control to user to avoid SO_REUSEPORT. In this patch, first we try passing reuse_port=False, if this fails then directly open socket and listen instead of eventlist.listen. Closes-Bug: #1810280 Change-Id: Idc842acc7e430199c76fe12785b0bf0e7a58e121 --- oslo_service/eventlet_backdoor.py | 21 ++++++++++++++++---- oslo_service/tests/test_eventlet_backdoor.py | 8 ++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) 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):