Add workaround for eventlet.greendns bug
Issue[1] workaround: A wrapper class which determines if socket module
was eventlet patched and request std lib socket module instead.
Also adding LOG.warning into the exception block so we dont miss
issues like this in the future.
Closes-Bug: #1980967
Related-Bug: #1926693
[1]https://github.com/eventlet/eventlet/issues/764
Change-Id: I41c4cbc1aaea95f7808e6c6dca47ecd0402351c9
(cherry picked from commit ea22307284
)
This commit is contained in:
parent
81809eac06
commit
363f0a972e
|
@ -16,9 +16,11 @@
|
|||
import os
|
||||
import socket
|
||||
|
||||
from eventlet import patcher
|
||||
from neutron_lib.utils import runtime
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import eventletutils
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from neutron.conf.agent import common as config
|
||||
|
@ -70,6 +72,37 @@ def is_agent_down(heart_beat_time):
|
|||
cfg.CONF.agent_down_time)
|
||||
|
||||
|
||||
class _SocketWrapper():
|
||||
"""Determines if socket module is patched by eventlet
|
||||
and unpatches it.
|
||||
|
||||
If python standard library socket module is patched, it will request
|
||||
an unpached version of the socket module. The sole purpose of this
|
||||
class is to workaround eventlet bug
|
||||
https://github.com/eventlet/eventlet/issues/764 and for the
|
||||
class to be used with get_hypervisor_hostname. This class also helps
|
||||
with socket mocks as it abstracts eventlet under the hood module
|
||||
imports which can be tricky to target with mocks.
|
||||
TODO(mtomaska): This class(workaround) can be removed once eventlet
|
||||
issue is resolved.
|
||||
"""
|
||||
def __init__(self):
|
||||
if eventletutils.is_monkey_patched(socket.__name__):
|
||||
LOG.debug("Std library socket module is patched by eventlet. "
|
||||
"Requesting std library socket module from eventlet.")
|
||||
self._socket = patcher.original(socket.__name__)
|
||||
else:
|
||||
LOG.debug("Std library socket module is not patched by eventlet. "
|
||||
"Using socket module as imported from std library.")
|
||||
self._socket = socket
|
||||
|
||||
def getaddrinfo(self, host, port, family, flags):
|
||||
return self._socket.getaddrinfo(host=host,
|
||||
port=port,
|
||||
family=family,
|
||||
flags=flags)
|
||||
|
||||
|
||||
def get_hypervisor_hostname():
|
||||
"""Get hypervisor hostname
|
||||
|
||||
|
@ -81,18 +114,21 @@ def get_hypervisor_hostname():
|
|||
'.' in hypervisor_hostname):
|
||||
return hypervisor_hostname
|
||||
|
||||
_socket_wrap = _SocketWrapper()
|
||||
try:
|
||||
addrinfo = socket.getaddrinfo(host=hypervisor_hostname,
|
||||
port=None,
|
||||
family=socket.AF_UNSPEC,
|
||||
flags=socket.AI_CANONNAME)
|
||||
addrinfo = _socket_wrap.getaddrinfo(host=hypervisor_hostname,
|
||||
port=None,
|
||||
family=socket.AF_UNSPEC,
|
||||
flags=socket.AI_CANONNAME)
|
||||
# getaddrinfo returns a list of 5-tuples with;
|
||||
# (family, type, proto, canonname, sockaddr)
|
||||
if (addrinfo and addrinfo[0][3] and
|
||||
not addrinfo[0][3].startswith('localhost')):
|
||||
return addrinfo[0][3]
|
||||
except OSError:
|
||||
pass
|
||||
except OSError as os_err:
|
||||
LOG.warning("Error: %s, occured while querying for fqdn. "
|
||||
"get_hypervisor_hostname will just "
|
||||
"return %s", os_err, hypervisor_hostname)
|
||||
|
||||
return hypervisor_hostname
|
||||
|
||||
|
@ -102,15 +138,13 @@ def default_rp_hypervisors(hypervisors, device_mappings,
|
|||
default_hypervisor=None):
|
||||
"""Fill config option 'resource_provider_hypervisors' with defaults.
|
||||
|
||||
Default hypervisor names to socket.gethostname() unless default_hypervisor
|
||||
is set.
|
||||
|
||||
:param hypervisors: Config option 'resource_provider_hypervisors'
|
||||
as parsed by oslo.config, that is a dict with keys of physical devices
|
||||
and values of hypervisor names.
|
||||
:param device_mappings: Device mappings standardized to the list-valued
|
||||
format.
|
||||
:param default_hypervisor: Default hypervisor hostname.
|
||||
:param default_hypervisor: Default hypervisor hostname. If not set,
|
||||
it tries to default to fully qualified domain name (fqdn)
|
||||
"""
|
||||
_default_hypervisor = default_hypervisor or get_hypervisor_hostname()
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ class TestLoadInterfaceDriver(base.BaseTestCase):
|
|||
|
||||
class TestGetHypervisorHostname(base.BaseTestCase):
|
||||
|
||||
@mock.patch('socket.getaddrinfo')
|
||||
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||
@mock.patch('socket.gethostname')
|
||||
def test_get_hypervisor_hostname_gethostname_fqdn(self, hostname_mock,
|
||||
addrinfo_mock):
|
||||
|
@ -90,7 +90,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
|||
utils.get_hypervisor_hostname())
|
||||
addrinfo_mock.assert_not_called()
|
||||
|
||||
@mock.patch('socket.getaddrinfo')
|
||||
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||
@mock.patch('socket.gethostname')
|
||||
def test_get_hypervisor_hostname_gethostname_localhost(self, hostname_mock,
|
||||
addrinfo_mock):
|
||||
|
@ -100,7 +100,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
|||
utils.get_hypervisor_hostname())
|
||||
addrinfo_mock.assert_not_called()
|
||||
|
||||
@mock.patch('socket.getaddrinfo')
|
||||
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||
@mock.patch('socket.gethostname')
|
||||
def test_get_hypervisor_hostname_getaddrinfo(self, hostname_mock,
|
||||
addrinfo_mock):
|
||||
|
@ -113,7 +113,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
|||
host='host', port=None, family=socket.AF_UNSPEC,
|
||||
flags=socket.AI_CANONNAME)
|
||||
|
||||
@mock.patch('socket.getaddrinfo')
|
||||
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||
@mock.patch('socket.gethostname')
|
||||
def test_get_hypervisor_hostname_getaddrinfo_no_canonname(self,
|
||||
hostname_mock,
|
||||
|
@ -127,7 +127,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
|||
host='host', port=None, family=socket.AF_UNSPEC,
|
||||
flags=socket.AI_CANONNAME)
|
||||
|
||||
@mock.patch('socket.getaddrinfo')
|
||||
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||
@mock.patch('socket.gethostname')
|
||||
def test_get_hypervisor_hostname_getaddrinfo_localhost(self, hostname_mock,
|
||||
addrinfo_mock):
|
||||
|
@ -141,7 +141,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
|||
host='host', port=None, family=socket.AF_UNSPEC,
|
||||
flags=socket.AI_CANONNAME)
|
||||
|
||||
@mock.patch('socket.getaddrinfo')
|
||||
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||
@mock.patch('socket.gethostname')
|
||||
def test_get_hypervisor_hostname_getaddrinfo_fail(self, hostname_mock,
|
||||
addrinfo_mock):
|
||||
|
|
Loading…
Reference in New Issue