Merge "Add workaround for eventlet.greendns bug" into stable/train
This commit is contained in:
commit
c7e955658e
|
@ -16,9 +16,11 @@
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
from eventlet import patcher
|
||||||
from neutron_lib.utils import runtime
|
from neutron_lib.utils import runtime
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import eventletutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
|
|
||||||
from neutron.conf.agent import common as config
|
from neutron.conf.agent import common as config
|
||||||
|
@ -70,6 +72,34 @@ def is_agent_down(heart_beat_time):
|
||||||
cfg.CONF.agent_down_time)
|
cfg.CONF.agent_down_time)
|
||||||
|
|
||||||
|
|
||||||
|
class _SocketWrapper(object):
|
||||||
|
"""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, port, family, 0, 0, flags)
|
||||||
|
|
||||||
|
|
||||||
def get_hypervisor_hostname():
|
def get_hypervisor_hostname():
|
||||||
"""Get hypervisor hostname
|
"""Get hypervisor hostname
|
||||||
|
|
||||||
|
@ -81,18 +111,21 @@ def get_hypervisor_hostname():
|
||||||
'.' in hypervisor_hostname):
|
'.' in hypervisor_hostname):
|
||||||
return hypervisor_hostname
|
return hypervisor_hostname
|
||||||
|
|
||||||
|
_socket_wrap = _SocketWrapper()
|
||||||
try:
|
try:
|
||||||
addrinfo = socket.getaddrinfo(host=hypervisor_hostname,
|
addrinfo = _socket_wrap.getaddrinfo(hypervisor_hostname,
|
||||||
port=None,
|
None,
|
||||||
family=socket.AF_UNSPEC,
|
socket.AF_UNSPEC,
|
||||||
flags=socket.AI_CANONNAME)
|
socket.AI_CANONNAME)
|
||||||
# getaddrinfo returns a list of 5-tuples with;
|
# getaddrinfo returns a list of 5-tuples with;
|
||||||
# (family, type, proto, canonname, sockaddr)
|
# (family, type, proto, canonname, sockaddr)
|
||||||
if (addrinfo and addrinfo[0][3] and
|
if (addrinfo and addrinfo[0][3] and
|
||||||
not addrinfo[0][3].startswith('localhost')):
|
not addrinfo[0][3].startswith('localhost')):
|
||||||
return addrinfo[0][3]
|
return addrinfo[0][3]
|
||||||
except (OSError, socket.gaierror):
|
except (OSError, socket.gaierror) as err:
|
||||||
pass
|
LOG.warning("Error: %s, occured while querying for fqdn. "
|
||||||
|
"get_hypervisor_hostname will just "
|
||||||
|
"return %s", err, hypervisor_hostname)
|
||||||
|
|
||||||
return hypervisor_hostname
|
return hypervisor_hostname
|
||||||
|
|
||||||
|
@ -101,15 +134,13 @@ def default_rp_hypervisors(hypervisors, device_mappings,
|
||||||
default_hypervisor=None):
|
default_hypervisor=None):
|
||||||
"""Fill config option 'resource_provider_hypervisors' with defaults.
|
"""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'
|
:param hypervisors: Config option 'resource_provider_hypervisors'
|
||||||
as parsed by oslo.config, that is a dict with keys of physical devices
|
as parsed by oslo.config, that is a dict with keys of physical devices
|
||||||
and values of hypervisor names.
|
and values of hypervisor names.
|
||||||
:param device_mappings: Device mappings standardized to the list-valued
|
:param device_mappings: Device mappings standardized to the list-valued
|
||||||
format.
|
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()
|
_default_hypervisor = default_hypervisor or get_hypervisor_hostname()
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class TestLoadInterfaceDriver(base.BaseTestCase):
|
||||||
|
|
||||||
class TestGetHypervisorHostname(base.BaseTestCase):
|
class TestGetHypervisorHostname(base.BaseTestCase):
|
||||||
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||||
@mock.patch('socket.gethostname')
|
@mock.patch('socket.gethostname')
|
||||||
def test_get_hypervisor_hostname_gethostname_fqdn(self, hostname_mock,
|
def test_get_hypervisor_hostname_gethostname_fqdn(self, hostname_mock,
|
||||||
addrinfo_mock):
|
addrinfo_mock):
|
||||||
|
@ -91,7 +91,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
||||||
utils.get_hypervisor_hostname())
|
utils.get_hypervisor_hostname())
|
||||||
addrinfo_mock.assert_not_called()
|
addrinfo_mock.assert_not_called()
|
||||||
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||||
@mock.patch('socket.gethostname')
|
@mock.patch('socket.gethostname')
|
||||||
def test_get_hypervisor_hostname_gethostname_localhost(self, hostname_mock,
|
def test_get_hypervisor_hostname_gethostname_localhost(self, hostname_mock,
|
||||||
addrinfo_mock):
|
addrinfo_mock):
|
||||||
|
@ -101,7 +101,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
||||||
utils.get_hypervisor_hostname())
|
utils.get_hypervisor_hostname())
|
||||||
addrinfo_mock.assert_not_called()
|
addrinfo_mock.assert_not_called()
|
||||||
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||||
@mock.patch('socket.gethostname')
|
@mock.patch('socket.gethostname')
|
||||||
def test_get_hypervisor_hostname_getaddrinfo(self, hostname_mock,
|
def test_get_hypervisor_hostname_getaddrinfo(self, hostname_mock,
|
||||||
addrinfo_mock):
|
addrinfo_mock):
|
||||||
|
@ -111,10 +111,9 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
||||||
'host.domain',
|
'host.domain',
|
||||||
utils.get_hypervisor_hostname())
|
utils.get_hypervisor_hostname())
|
||||||
addrinfo_mock.assert_called_once_with(
|
addrinfo_mock.assert_called_once_with(
|
||||||
host='host', port=None, family=socket.AF_UNSPEC,
|
'host', None, socket.AF_UNSPEC, socket.AI_CANONNAME)
|
||||||
flags=socket.AI_CANONNAME)
|
|
||||||
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||||
@mock.patch('socket.gethostname')
|
@mock.patch('socket.gethostname')
|
||||||
def test_get_hypervisor_hostname_getaddrinfo_no_canonname(self,
|
def test_get_hypervisor_hostname_getaddrinfo_no_canonname(self,
|
||||||
hostname_mock,
|
hostname_mock,
|
||||||
|
@ -125,10 +124,9 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
||||||
'host',
|
'host',
|
||||||
utils.get_hypervisor_hostname())
|
utils.get_hypervisor_hostname())
|
||||||
addrinfo_mock.assert_called_once_with(
|
addrinfo_mock.assert_called_once_with(
|
||||||
host='host', port=None, family=socket.AF_UNSPEC,
|
'host', None, socket.AF_UNSPEC, socket.AI_CANONNAME)
|
||||||
flags=socket.AI_CANONNAME)
|
|
||||||
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||||
@mock.patch('socket.gethostname')
|
@mock.patch('socket.gethostname')
|
||||||
def test_get_hypervisor_hostname_getaddrinfo_localhost(self, hostname_mock,
|
def test_get_hypervisor_hostname_getaddrinfo_localhost(self, hostname_mock,
|
||||||
addrinfo_mock):
|
addrinfo_mock):
|
||||||
|
@ -139,10 +137,9 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
||||||
'host',
|
'host',
|
||||||
utils.get_hypervisor_hostname())
|
utils.get_hypervisor_hostname())
|
||||||
addrinfo_mock.assert_called_once_with(
|
addrinfo_mock.assert_called_once_with(
|
||||||
host='host', port=None, family=socket.AF_UNSPEC,
|
'host', None, socket.AF_UNSPEC, socket.AI_CANONNAME)
|
||||||
flags=socket.AI_CANONNAME)
|
|
||||||
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
@mock.patch.object(utils._SocketWrapper, 'getaddrinfo')
|
||||||
@mock.patch('socket.gethostname')
|
@mock.patch('socket.gethostname')
|
||||||
def test_get_hypervisor_hostname_getaddrinfo_fail(self, hostname_mock,
|
def test_get_hypervisor_hostname_getaddrinfo_fail(self, hostname_mock,
|
||||||
addrinfo_mock):
|
addrinfo_mock):
|
||||||
|
@ -152,8 +149,7 @@ class TestGetHypervisorHostname(base.BaseTestCase):
|
||||||
'host',
|
'host',
|
||||||
utils.get_hypervisor_hostname())
|
utils.get_hypervisor_hostname())
|
||||||
addrinfo_mock.assert_called_once_with(
|
addrinfo_mock.assert_called_once_with(
|
||||||
host='host', port=None, family=socket.AF_UNSPEC,
|
'host', None, socket.AF_UNSPEC, socket.AI_CANONNAME)
|
||||||
flags=socket.AI_CANONNAME)
|
|
||||||
|
|
||||||
|
|
||||||
class TestDefaultRpHypervisors(base.BaseTestCase):
|
class TestDefaultRpHypervisors(base.BaseTestCase):
|
||||||
|
|
Loading…
Reference in New Issue