Skip to content

Commit c09753b

Browse files
committed
Add a configurable sleep before IPA starts working
Some kernel modules take substantial time to initialize. For example, with mpt2sas RAID driver inspection and deployment randomly fail due to IPA starting before the driver finishes initialization. As much as I hate it, the only way to guarantee that the hardware is truely initalized is to wait for it. Apparently all hardware in Linux is treated as hotplugged, so there is no such thing as "hardware initialization is finished". Operators can add a sleep based on their knowledge of their hardware. The default behaviour remains the same. Change-Id: I0446ae81d760dacaf31eea6ad9f9eaa098cf5e93 Partial-Bug: #1582797
1 parent 015fad6 commit c09753b

File tree

4 files changed

+51
-2
lines changed

4 files changed

+51
-2
lines changed

ironic_python_agent/agent.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
147147

148148
def __init__(self, api_url, advertise_address, listen_address,
149149
ip_lookup_attempts, ip_lookup_sleep, network_interface,
150-
lookup_timeout, lookup_interval, driver_name, standalone):
150+
lookup_timeout, lookup_interval, driver_name, standalone,
151+
hardware_initialization_delay=0):
151152
super(IronicPythonAgent, self).__init__()
152153
self.ext_mgr = extension.ExtensionManager(
153154
namespace='ironic_python_agent.extensions',
@@ -175,6 +176,7 @@ def __init__(self, api_url, advertise_address, listen_address,
175176
self.ip_lookup_sleep = ip_lookup_sleep
176177
self.network_interface = network_interface
177178
self.standalone = standalone
179+
self.hardware_initialization_delay = hardware_initialization_delay
178180

179181
def get_status(self):
180182
"""Retrieve a serializable status.
@@ -289,6 +291,12 @@ def run(self):
289291

290292
# Cached hw managers at runtime, not load time. See bug 1490008.
291293
hardware.load_managers()
294+
# Operator-settable delay before hardware actually comes up.
295+
# Helps with slow RAID drivers - see bug 1582797.
296+
if self.hardware_initialization_delay > 0:
297+
LOG.info('Waiting %d seconds before proceeding',
298+
self.hardware_initialization_delay)
299+
time.sleep(self.hardware_initialization_delay)
292300

293301
if not self.standalone:
294302
# Inspection should be started before call to lookup, otherwise

ironic_python_agent/cmd/agent.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@
129129
help='Whether to wait for all interfaces to get their IP '
130130
'addresses before inspection. If set to false '
131131
'(the default), only waits for the PXE interface.'),
132+
133+
cfg.IntOpt('hardware_initialization_delay',
134+
default=APARAMS.get('ipa-hardware-initialization-delay', 0),
135+
help='How much time (in seconds) to wait for hardware to '
136+
'initialize before proceeding with any actions.'),
132137
]
133138

134139
CONF.register_cli_opts(cli_opts)
@@ -153,4 +158,5 @@ def run():
153158
CONF.lookup_timeout,
154159
CONF.lookup_interval,
155160
CONF.driver_name,
156-
CONF.standalone).run()
161+
CONF.standalone,
162+
CONF.hardware_initialization_delay).run()

ironic_python_agent/tests/unit/test_agent.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,37 @@ def test_run_with_inspection(self, mocked_list_hardware, wsgi_server_cls,
232232

233233
self.agent.heartbeater.start.assert_called_once_with()
234234

235+
@mock.patch.object(time, 'sleep', autospec=True)
236+
@mock.patch('wsgiref.simple_server.make_server', autospec=True)
237+
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info')
238+
def test_run_with_sleep(self, mocked_list_hardware, wsgi_server_cls,
239+
mocked_sleep):
240+
CONF.set_override('inspection_callback_url', '', enforce_type=True)
241+
wsgi_server = wsgi_server_cls.return_value
242+
wsgi_server.start.side_effect = KeyboardInterrupt()
243+
244+
self.agent.hardware_initialization_delay = 10
245+
self.agent.heartbeater = mock.Mock()
246+
self.agent.api_client.lookup_node = mock.Mock()
247+
self.agent.api_client.lookup_node.return_value = {
248+
'node': {
249+
'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c'
250+
},
251+
'heartbeat_timeout': 300
252+
}
253+
self.agent.run()
254+
255+
listen_addr = ('192.0.2.1', 9999)
256+
wsgi_server_cls.assert_called_once_with(
257+
listen_addr[0],
258+
listen_addr[1],
259+
self.agent.api,
260+
server_class=simple_server.WSGIServer)
261+
wsgi_server.serve_forever.assert_called_once_with()
262+
263+
self.agent.heartbeater.start.assert_called_once_with()
264+
mocked_sleep.assert_called_once_with(10)
265+
235266
def test_async_command_success(self):
236267
result = base.AsyncCommandResult('foo_command', {'fail': False},
237268
foo_execute)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- Add a configurable (and disabled by default) sleep before IPA does any
4+
actions.

0 commit comments

Comments
 (0)