Remove eventlet from Ironic Python Agent

This change removes several usages of eventlet from IPA:
- Upgrades all requirements on oslo library versions to new ones that
  support non-eventlet use.
- Removes use of the eventlet wsgi server (via oslo_service.wsgi) and
  replaces it with the cheroot wsgi server.
- Removes explicit patching of python modules with eventlet

Note that due to some oslo libraries still using ``eventlet`` to detect
and workaround it's use. This means that it is still installed in
environments alongside IPA, even if it's not used or patched into any
modules.

Depends-On: https://review.opendev.org/c/openstack/requirements/+/947727
Change-Id: I9accab2d5e9529a88ef5d3db85e76901f14114eb
This commit is contained in:
cid 2025-04-04 17:08:12 +01:00 committed by Jay Faulkner
parent a15680f51e
commit c03021fee2
6 changed files with 109 additions and 212 deletions

View File

@ -1,21 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import eventlet
import os
os.environ["EVENTLET_NO_GREENDNS"] = "yes"
# NOTE(JayF) Without monkey_patching socket, API requests will hang with TLS
# enabled. Enabling more than just socket for monkey patching causes failures
# in image streaming. In an ideal world, we track down all those errors and
# monkey patch everything as suggested in eventlet documentation.
eventlet.monkey_patch(all=False, socket=True)

View File

@ -22,7 +22,6 @@ import threading
import time
from urllib import parse as urlparse
import eventlet
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log
@ -106,7 +105,7 @@ class IronicPythonAgentHeartbeater(threading.Thread):
self.agent.set_agent_advertise_addr()
while self._run_next():
eventlet.sleep(0)
time.sleep(0.1)
def _run_next(self):
# The logic here makes sure we don't wait exactly 5 seconds more or
@ -438,7 +437,7 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
self.heartbeater.start()
try:
while self.serve_api:
eventlet.sleep(0.1)
time.sleep(0.1)
except KeyboardInterrupt:
LOG.info('Caught keyboard interrupt, exiting')
self.api.stop()

View File

@ -13,9 +13,11 @@
# limitations under the License.
import json
import threading
from cheroot.ssl import builtin
from cheroot import wsgi
from oslo_log import log
from oslo_service import wsgi
import werkzeug
from werkzeug import exceptions as http_exc
from werkzeug import routing
@ -92,7 +94,7 @@ class Application(object):
:param conf: configuration object.
"""
self.agent = agent
self.service = None
self.server = None
self._conf = conf
self.url_map = routing.Map([
routing.Rule('/', endpoint='root', methods=['GET']),
@ -128,28 +130,44 @@ class Application(object):
def start(self, tls_cert_file=None, tls_key_file=None):
"""Start the API service in the background."""
if tls_cert_file and tls_key_file:
self._conf.set_override('cert_file', tls_cert_file, group='ssl')
self._conf.set_override('key_file', tls_key_file, group='ssl')
use_tls = True
else:
use_tls = self._conf.listen_tls
self.service = wsgi.Server(self._conf, 'ironic-python-agent', app=self,
host=self.agent.listen_address.hostname,
port=self.agent.listen_address.port,
use_ssl=use_tls)
self.service.start()
ssl_group = getattr(self._conf, 'ssl', {})
self.tls_cert_file = tls_cert_file or getattr(
ssl_group, 'cert_file', None)
self.tls_key_file = tls_key_file or getattr(
ssl_group, 'key_file', None)
bind_addr = (self.agent.listen_address.hostname,
self.agent.listen_address.port)
server = wsgi.Server(bind_addr=bind_addr, wsgi_app=self,
server_name='ironic-python-agent')
if self.tls_cert_file and self.tls_key_file:
server.ssl_adapter = builtin.BuiltinSSLAdapter(
certificate=self.tls_cert_file,
private_key=self.tls_key_file
)
self.server = server
self.server.prepare()
self.server_thread = threading.Thread(target=self.server.serve)
self.server_thread.daemon = True
self.server_thread.start()
LOG.info('Started API service on port %s',
self.agent.listen_address.port)
def stop(self):
"""Stop the API service."""
LOG.debug("Stopping the API service.")
if self.service is None:
return
self.service.stop()
self.service = None
if self.server:
self.server.stop()
self.server_thread.join(timeout=2)
self.server = None
LOG.info('Stopped API service on port %s',
self.agent.listen_address.port)

View File

@ -239,18 +239,14 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run(self, mock_get_managers, mock_wsgi,
mock_wait, mock_dispatch):
def test_run(self, mock_get_managers, mock_wait, mock_dispatch):
CONF.set_override('inspection_callback_url', '')
wsgi_server = mock_wsgi.return_value
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
@ -265,11 +261,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
@ -283,19 +275,17 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_with_ssl(self, mock_get_managers, mock_wsgi,
def test_run_with_ssl(self, mock_get_managers,
mock_wait, mock_dispatch):
CONF.set_override('inspection_callback_url', '')
CONF.set_override('listen_tls', True)
CONF.set_override('md5_enabled', False)
wsgi_server = mock_wsgi.return_value
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
@ -310,11 +300,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=True)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
@ -329,15 +315,12 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_url_from_mdns_by_default(self, mock_get_managers, mock_wsgi,
def test_url_from_mdns_by_default(self, mock_get_managers,
mock_wait, mock_dispatch, mock_mdns):
CONF.set_override('inspection_callback_url', '')
mock_mdns.return_value = 'https://example.com', {}
wsgi_server = mock_wsgi.return_value
self.agent = agent.IronicPythonAgent(None,
agent.Host('203.0.113.1', 9990),
agent.Host('192.0.2.1', 9999),
@ -349,10 +332,10 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
False,
None)
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
@ -366,11 +349,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
@ -384,9 +363,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_url_from_mdns_explicitly(self, mock_get_managers, mock_wsgi,
def test_url_from_mdns_explicitly(self, mock_get_managers,
mock_wait, mock_dispatch, mock_mdns):
CONF.set_override('inspection_callback_url', '')
CONF.set_override('disk_wait_attempts', 0)
@ -395,8 +373,6 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
'ipa_disk_wait_attempts': '42',
}
wsgi_server = mock_wsgi.return_value
self.agent = agent.IronicPythonAgent('mdns',
agent.Host('203.0.113.1', 9990),
agent.Host('192.0.2.1', 9999),
@ -408,10 +384,10 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
False,
None)
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
@ -425,11 +401,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
@ -444,18 +416,15 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_agent_token(self, mock_get_managers, mock_wsgi,
def test_run_agent_token(self, mock_get_managers,
mock_wait, mock_dispatch):
CONF.set_override('inspection_callback_url', '')
wsgi_server = mock_wsgi.return_value
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
@ -470,11 +439,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
@ -489,18 +454,15 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_listen_host_port(self, mock_get_managers, mock_wsgi,
def test_run_listen_host_port(self, mock_get_managers,
mock_wait, mock_dispatch):
CONF.set_override('inspection_callback_url', '')
wsgi_server = mock_wsgi.return_value
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.listen_address = mock.Mock()
self.agent.listen_address.hostname = '2001:db8:dead:beef::cafe'
@ -517,33 +479,25 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host='2001:db8:dead:beef::cafe',
port=9998,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
mock_dispatch.call_args_list)
self.agent.heartbeater.start.assert_called_once_with()
@mock.patch('eventlet.sleep', autospec=True)
@mock.patch('time.sleep', autospec=True)
@mock.patch(
'ironic_python_agent.hardware_managers.cna._detect_cna_card',
mock.Mock())
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_raise_keyboard_interrupt(self, mock_get_managers, mock_wsgi,
def test_run_raise_keyboard_interrupt(self, mock_get_managers,
mock_dispatch, mock_wait,
mock_sleep):
CONF.set_override('inspection_callback_url', '')
wsgi_server = mock_wsgi.return_value
mock_sleep.side_effect = KeyboardInterrupt()
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
@ -556,17 +510,15 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
}
}
self.agent.api.start = mock.Mock()
self.agent.run()
self.assertTrue(mock_wait.called)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
mock_dispatch.call_args_list)
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
self.agent.heartbeater.start.assert_called_once_with()
@mock.patch.object(hardware, '_enable_multipath', autospec=True)
@ -576,7 +528,6 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
'_wait_for_interface', autospec=True)
@mock.patch.object(inspector, 'inspect', autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info',
autospec=True)
@mock.patch(
@ -585,16 +536,14 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
autospec=True
)
def test_run_with_inspection(self, mock_hardware, mock_list_hardware,
mock_wsgi, mock_dispatch, mock_inspector,
mock_dispatch, mock_inspector,
mock_wait, mock_mpath):
CONF.set_override('inspection_callback_url', 'http://foo/bar')
mock_hardware.return_value = 0
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server = mock_wsgi.return_value
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
mock_inspector.return_value = 'uuid'
@ -610,11 +559,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
}
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_inspector.assert_called_once_with()
self.assertEqual(1, self.agent.api_client.lookup_node.call_count)
@ -637,7 +582,6 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
'_wait_for_interface', autospec=True)
@mock.patch.object(inspector, 'inspect', autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info',
autospec=True)
@mock.patch(
@ -648,7 +592,6 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
def test_run_with_inspection_without_apiurl(self,
mock_hardware,
mock_list_hardware,
mock_wsgi,
mock_dispatch,
mock_inspector,
mock_wait,
@ -675,19 +618,13 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.assertFalse(hasattr(self.agent, 'api_client'))
self.assertFalse(hasattr(self.agent, 'heartbeater'))
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server = mock_wsgi.return_value
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_inspector.assert_called_once_with()
@ -703,7 +640,6 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
'_wait_for_interface', autospec=True)
@mock.patch.object(inspector, 'inspect', autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info',
autospec=True)
@mock.patch(
@ -714,7 +650,6 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
def test_run_without_inspection_and_apiurl(self,
mock_hardware,
mock_list_hardware,
mock_wsgi,
mock_dispatch,
mock_inspector,
mock_wait,
@ -741,19 +676,13 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.assertFalse(hasattr(self.agent, 'api_client'))
self.assertFalse(hasattr(self.agent, 'heartbeater'))
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server = mock_wsgi.return_value
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
self.assertFalse(mock_inspector.called)
self.assertTrue(mock_wait.called)
@ -768,20 +697,17 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_then_lockdown(self, mock_get_managers, mock_wsgi,
def test_run_then_lockdown(self, mock_get_managers,
mock_wait, mock_dispatch, mock_interfaces,
mock_exec, mock_sleep):
CONF.set_override('inspection_callback_url', '')
wsgi_server = mock_wsgi.return_value
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.lockdown = True
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
@ -802,11 +728,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.assertRaises(StopTesting, self.agent.run)
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
@ -846,16 +768,13 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@mock.patch.object(agent.IronicPythonAgent, '_wait_for_interface',
autospec=True)
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
def test_run_with_sleep(self, mock_wsgi, mock_dispatch,
def test_run_with_sleep(self, mock_dispatch,
mock_wait, mock_sleep, mock_get_managers):
CONF.set_override('inspection_callback_url', '')
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server = mock_wsgi.return_value
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.hardware_initialization_delay = 10
self.agent.heartbeater = mock.Mock()
@ -870,11 +789,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
}
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
self.agent.heartbeater.start.assert_called_once_with()
mock_sleep.assert_called_once_with(10)
@ -999,18 +914,16 @@ class TestAgentStandalone(ironic_agent_base.IronicAgentTest):
'ironic_python_agent.hardware_managers.cna._detect_cna_card',
mock.Mock())
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info',
autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run(self, mock_get_managers, mock_list_hardware,
mock_wsgi, mock_dispatch):
wsgi_server_request = mock_wsgi.return_value
mock_dispatch):
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server_request.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
mock_dispatch.return_value = tls_utils.TlsCertificate(
'I am a cert', '/path/to/cert', '/path/to/key')
@ -1022,16 +935,11 @@ class TestAgentStandalone(ironic_agent_base.IronicAgentTest):
self.agent.run()
self.assertTrue(mock_get_managers.called)
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=True)
wsgi_server_request.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(
'/path/to/cert', '/path/to/key')
mock_dispatch.assert_called_once_with('generate_tls_certificate',
mock.ANY)
self.assertEqual('/path/to/cert', CONF.ssl.cert_file)
self.assertEqual('/path/to/key', CONF.ssl.key_file)
self.assertEqual('https', self.agent.advertise_protocol)
self.assertFalse(self.agent.heartbeater.called)
@ -1040,19 +948,16 @@ class TestAgentStandalone(ironic_agent_base.IronicAgentTest):
@mock.patch(
'ironic_python_agent.hardware_managers.cna._detect_cna_card',
mock.Mock())
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info',
autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_no_tls(self, mock_get_managers, mock_list_hardware,
mock_wsgi):
def test_run_no_tls(self, mock_get_managers, mock_list_hardware):
CONF.set_override('enable_auto_tls', False)
wsgi_server_request = mock_wsgi.return_value
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server_request.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client = mock.Mock()
@ -1061,11 +966,7 @@ class TestAgentStandalone(ironic_agent_base.IronicAgentTest):
self.agent.run()
self.assertTrue(mock_get_managers.called)
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server_request.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
self.assertEqual('http', self.agent.advertise_protocol)
self.assertFalse(self.agent.heartbeater.called)
@ -1351,17 +1252,15 @@ class TestBaseAgentVMediaToken(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_agent_token_vmedia(self, mock_get_managers, mock_wsgi,
def test_run_agent_token_vmedia(self, mock_get_managers,
mock_wait, mock_dispatch):
CONF.set_override('inspection_callback_url', '')
wsgi_server = mock_wsgi.return_value
def set_serve_api():
def set_serve_api(*args, **kwargs):
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.api.start = mock.Mock(side_effect=set_serve_api)
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
@ -1377,11 +1276,7 @@ class TestBaseAgentVMediaToken(ironic_agent_base.IronicAgentTest):
self.agent.run()
self.assertFalse(self.agent.lockdown)
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.api.start.assert_called_once_with(mock.ANY, mock.ANY)
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],

View File

@ -0,0 +1,6 @@
fixes:
- |
Eventlet support in OpenStack is deprecated, and the oslo libraries used
by Ironic Python Agent is deprecating support for eventlet in 2026.2.
This change removes the use of eventlet directly and ensures none of our
libraries are using eventlet-based code.

View File

@ -1,10 +1,9 @@
pbr>=6.0.0 # Apache-2.0
eventlet>=0.18.2 # MIT
oslo.config>=5.2.0 # Apache-2.0
oslo.concurrency>=3.26.0 # Apache-2.0
oslo.log>=4.6.1 # Apache-2.0
oslo.service>=1.24.0 # Apache-2.0
oslo.utils>=8.0.0 # Apache-2.0
oslo.config>=9.7.1 # Apache-2.0
oslo.concurrency>=7.1.0 # Apache-2.0
oslo.log>=7.1.0 # Apache-2.0
oslo.service>=4.1.1 # Apache-2.0
oslo.utils>=8.2.0 # Apache-2.0
Pint>=0.5 # BSD
psutil>=3.2.2 # BSD
pyudev>=0.18 # LGPLv2.1+
@ -15,3 +14,4 @@ Werkzeug>=2.0.0 # BSD License
cryptography>=2.3 # BSD/Apache-2.0
tooz>=2.7.2 # Apache-2.0
zeroconf>=0.24.0 # LGPL
cheroot>=10.0.1 # BSD