Merge "Replace oslo's loopingcall with tenacity"

This commit is contained in:
Zuul 2020-09-12 15:24:41 +00:00 committed by Gerrit Code Review
commit a3b10db95a
4 changed files with 59 additions and 41 deletions

View File

@ -18,8 +18,8 @@ from distutils.version import StrictVersion
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log from oslo_log import log
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_service import loopingcall
import requests import requests
import tenacity
from ironic_python_agent import encoding from ironic_python_agent import encoding
from ironic_python_agent import errors from ironic_python_agent import errors
@ -138,24 +138,22 @@ class APIClient(object):
raise errors.HeartbeatError(msg) raise errors.HeartbeatError(msg)
def lookup_node(self, hardware_info, timeout, starting_interval, def lookup_node(self, hardware_info, timeout, starting_interval,
node_uuid=None): node_uuid=None, max_interval=30):
timer = loopingcall.BackOffLoopingCall( retry = tenacity.retry(
self._do_lookup, retry=tenacity.retry_if_result(lambda r: r is False),
hardware_info=hardware_info, stop=tenacity.stop_after_delay(timeout),
node_uuid=node_uuid) wait=tenacity.wait_random_exponential(min=starting_interval,
max=max_interval),
reraise=True)
try: try:
node_content = timer.start(starting_interval=starting_interval, return retry(self._do_lookup)(hardware_info=hardware_info,
timeout=timeout).wait() node_uuid=node_uuid)
except loopingcall.LoopingCallTimeOut: except tenacity.RetryError:
raise errors.LookupNodeError('Could not look up node info. Check ' raise errors.LookupNodeError('Could not look up node info. Check '
'logs for details.') 'logs for details.')
return node_content
def _do_lookup(self, hardware_info, node_uuid): def _do_lookup(self, hardware_info, node_uuid):
"""The actual call to lookup a node. """The actual call to lookup a node."""
Should be called as a `loopingcall.BackOffLoopingCall`.
"""
params = { params = {
'addresses': ','.join(iface.mac_address 'addresses': ','.join(iface.mac_address
for iface in hardware_info['interfaces'] for iface in hardware_info['interfaces']
@ -241,7 +239,7 @@ class APIClient(object):
return False return False
# Got valid content # Got valid content
raise loopingcall.LoopingCallDone(retvalue=content) return content
def _get_agent_url(self, advertise_address, advertise_protocol='http'): def _get_agent_url(self, advertise_address, advertise_protocol='http'):
return '{}://{}:{}'.format(advertise_protocol, return '{}://{}:{}'.format(advertise_protocol,

View File

@ -15,7 +15,6 @@
from unittest import mock from unittest import mock
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_service import loopingcall
import requests import requests
from ironic_python_agent import errors from ironic_python_agent import errors
@ -244,7 +243,7 @@ class TestBaseIronicPythonAgent(base.IronicAgentTest):
uuid='meow', uuid='meow',
advertise_address=('192.0.2.1', '9999')) advertise_address=('192.0.2.1', '9999'))
@mock.patch('eventlet.greenthread.sleep', autospec=True) @mock.patch('time.sleep', autospec=True)
@mock.patch('ironic_python_agent.ironic_api_client.APIClient._do_lookup', @mock.patch('ironic_python_agent.ironic_api_client.APIClient._do_lookup',
autospec=True) autospec=True)
def test_lookup_node(self, lookup_mock, sleep_mock): def test_lookup_node(self, lookup_mock, sleep_mock):
@ -256,8 +255,7 @@ class TestBaseIronicPythonAgent(base.IronicAgentTest):
'heartbeat_timeout': 300 'heartbeat_timeout': 300
} }
} }
lookup_mock.side_effect = loopingcall.LoopingCallDone( lookup_mock.return_value = content
retvalue=content)
returned_content = self.api_client.lookup_node( returned_content = self.api_client.lookup_node(
hardware_info=self.hardware_info, hardware_info=self.hardware_info,
timeout=300, timeout=300,
@ -265,34 +263,54 @@ class TestBaseIronicPythonAgent(base.IronicAgentTest):
self.assertEqual(content, returned_content) self.assertEqual(content, returned_content)
@mock.patch('eventlet.greenthread.sleep', autospec=True) @mock.patch('time.sleep', autospec=True)
@mock.patch('ironic_python_agent.ironic_api_client.APIClient._do_lookup', @mock.patch('ironic_python_agent.ironic_api_client.APIClient._do_lookup',
autospec=True) autospec=True)
def test_lookup_timeout(self, lookup_mock, sleep_mock): def test_lookup_node_retries(self, lookup_mock, sleep_mock):
lookup_mock.side_effect = loopingcall.LoopingCallTimeOut() content = {
self.assertRaises(errors.LookupNodeError,
self.api_client.lookup_node,
hardware_info=self.hardware_info,
timeout=300,
starting_interval=1)
def test_do_lookup(self):
response = FakeResponse(status_code=200, content={
'node': { 'node': {
'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c' 'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c'
}, },
'config': { 'config': {
'heartbeat_timeout': 300 'heartbeat_timeout': 300
} }
}) }
lookup_mock.side_effect = [False, content]
returned_content = self.api_client.lookup_node(
hardware_info=self.hardware_info,
timeout=300,
starting_interval=0.001)
self.assertEqual(content, returned_content)
@mock.patch('time.sleep', autospec=True)
@mock.patch('ironic_python_agent.ironic_api_client.APIClient._do_lookup',
autospec=True)
def test_lookup_timeout(self, lookup_mock, sleep_mock):
lookup_mock.return_value = False
self.assertRaises(errors.LookupNodeError,
self.api_client.lookup_node,
hardware_info=self.hardware_info,
timeout=0.1,
starting_interval=0.001)
def test_do_lookup(self):
content = {
'node': {
'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c'
},
'config': {
'heartbeat_timeout': 300
}
}
response = FakeResponse(status_code=200, content=content)
self.api_client.session.request = mock.Mock() self.api_client.session.request = mock.Mock()
self.api_client.session.request.return_value = response self.api_client.session.request.return_value = response
self.assertRaises(loopingcall.LoopingCallDone, self.assertEqual(content, self.api_client._do_lookup(
self.api_client._do_lookup, hardware_info=self.hardware_info,
hardware_info=self.hardware_info, node_uuid=None))
node_uuid=None)
url = '{api_url}v1/lookup'.format(api_url=API_URL) url = '{api_url}v1/lookup'.format(api_url=API_URL)
request_args = self.api_client.session.request.call_args[0] request_args = self.api_client.session.request.call_args[0]
@ -303,22 +321,22 @@ class TestBaseIronicPythonAgent(base.IronicAgentTest):
params) params)
def test_do_lookup_with_uuid(self): def test_do_lookup_with_uuid(self):
response = FakeResponse(status_code=200, content={ content = {
'node': { 'node': {
'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c' 'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c'
}, },
'config': { 'config': {
'heartbeat_timeout': 300 'heartbeat_timeout': 300
} }
}) }
response = FakeResponse(status_code=200, content=content)
self.api_client.session.request = mock.Mock() self.api_client.session.request = mock.Mock()
self.api_client.session.request.return_value = response self.api_client.session.request.return_value = response
self.assertRaises(loopingcall.LoopingCallDone, self.assertEqual(content, self.api_client._do_lookup(
self.api_client._do_lookup, hardware_info=self.hardware_info,
hardware_info=self.hardware_info, node_uuid='someuuid'))
node_uuid='someuuid')
url = '{api_url}v1/lookup'.format(api_url=API_URL) url = '{api_url}v1/lookup'.format(api_url=API_URL)
request_args = self.api_client.session.request.call_args[0] request_args = self.api_client.session.request.call_args[0]

View File

@ -78,6 +78,7 @@ Sphinx==2.0.0
sphinxcontrib-websupport==1.0.1 sphinxcontrib-websupport==1.0.1
stestr==1.0.0 stestr==1.0.0
stevedore==1.20.0 stevedore==1.20.0
tenacity==6.2.0
testrepository==0.0.20 testrepository==0.0.20
testtools==2.2.0 testtools==2.2.0
traceback2==1.4.0 traceback2==1.4.0

View File

@ -16,5 +16,6 @@ pyudev>=0.18 # LGPLv2.1+
requests>=2.14.2 # Apache-2.0 requests>=2.14.2 # Apache-2.0
rtslib-fb>=2.1.65 # Apache-2.0 rtslib-fb>=2.1.65 # Apache-2.0
stevedore>=1.20.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0
tenacity>=6.2.0 # Apache-2.0
ironic-lib>=4.1.0 # Apache-2.0 ironic-lib>=4.1.0 # Apache-2.0
Werkzeug>=1.0.1 # BSD License Werkzeug>=1.0.1 # BSD License