Fixes console port conflict occurs in certain path

The dynamically allocated console port for a node is saved
into database and reused on subsequent console operations.
In certain code path the port record cann't be trusted and
we should do a re-allocation.

This patch fixes the issue by ignores previous allocation
record. The extra cleanup in the takeover is not required
anymore and removed as well.

Change-Id: I1a07ea9b30a2c760af7a6a4e39f3ff227df28fff
Story: 2010489
Task: 47061
This commit is contained in:
Kaifeng Wang 2022-12-25 12:14:06 +08:00 committed by Julia Kreger
parent ef772c2c1e
commit c9c9b3100d
5 changed files with 24 additions and 42 deletions

View File

@ -1773,10 +1773,6 @@ class ConductorManager(base_manager.BaseConductorManager):
if task.node.console_enabled: if task.node.console_enabled:
notify_utils.emit_console_notification( notify_utils.emit_console_notification(
task, 'console_restore', fields.NotificationStatus.START) task, 'console_restore', fields.NotificationStatus.START)
# NOTE(kaifeng) Clear allocated_ipmi_terminal_port if exists,
# so current conductor can allocate a new free port from local
# resources.
task.node.del_driver_internal_info('allocated_ipmi_terminal_port')
try: try:
task.driver.console.start_console(task) task.driver.console.start_console(task)
except Exception as err: except Exception as err:

View File

@ -1556,6 +1556,9 @@ class IPMIShellinaboxConsole(IPMIConsole):
created created
:raises: ConsoleSubprocessFailed when invoking the subprocess failed :raises: ConsoleSubprocessFailed when invoking the subprocess failed
""" """
# Dealloc allocated port if any, so the same host can never has
# duplicated port.
_release_allocated_port(task)
driver_info = _parse_driver_info(task.node) driver_info = _parse_driver_info(task.node)
if not driver_info['port']: if not driver_info['port']:
driver_info['port'] = _allocate_port(task) driver_info['port'] = _allocate_port(task)
@ -1611,6 +1614,9 @@ class IPMISocatConsole(IPMIConsole):
created created
:raises: ConsoleSubprocessFailed when invoking the subprocess failed :raises: ConsoleSubprocessFailed when invoking the subprocess failed
""" """
# Dealloc allocated port if any, so the same host can never has
# duplicated port.
_release_allocated_port(task)
driver_info = _parse_driver_info(task.node) driver_info = _parse_driver_info(task.node)
if not driver_info['port']: if not driver_info['port']:
driver_info['port'] = _allocate_port( driver_info['port'] = _allocate_port(

View File

@ -7244,44 +7244,6 @@ class DoNodeTakeOverTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
mock.call(task, 'console_restore', mock.call(task, 'console_restore',
obj_fields.NotificationStatus.ERROR)]) obj_fields.NotificationStatus.ERROR)])
@mock.patch.object(notification_utils, 'emit_console_notification',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeConsole.start_console',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.take_over',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.prepare',
autospec=True)
def test__do_takeover_with_console_port_cleaned(self, mock_prepare,
mock_take_over,
mock_start_console,
mock_notify):
self._start_service(start_consoles=False)
node = obj_utils.create_test_node(self.context, driver='fake-hardware',
console_enabled=True)
di_info = node.driver_internal_info
di_info['allocated_ipmi_terminal_port'] = 12345
node.driver_internal_info = di_info
node.save()
task = task_manager.TaskManager(self.context, node.uuid)
self.service._do_takeover(task)
node.refresh()
self.assertIsNone(node.last_error)
self.assertTrue(node.console_enabled)
self.assertIsNone(
node.driver_internal_info.get('allocated_ipmi_terminal_port',
None))
mock_prepare.assert_called_once_with(task.driver.deploy, task)
mock_take_over.assert_called_once_with(task.driver.deploy, task)
mock_start_console.assert_called_once_with(task.driver.console, task)
mock_notify.assert_has_calls(
[mock.call(task, 'console_restore',
obj_fields.NotificationStatus.START),
mock.call(task, 'console_restore',
obj_fields.NotificationStatus.END)])
@mgr_utils.mock_record_keepalive @mgr_utils.mock_record_keepalive
class DoNodeAdoptionTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase): class DoNodeAdoptionTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):

View File

@ -3255,6 +3255,11 @@ class IPMIToolShellinaboxTestCase(db_base.DbTestCase):
mock_start.return_value = None mock_start.return_value = None
mock_info.return_value = {'port': None} mock_info.return_value = {'port': None}
mock_alloc.return_value = 1234 mock_alloc.return_value = 1234
# Ensure allocated port is not re-used
dii = self.node.driver_internal_info
dii['allocated_ipmi_terminal_port'] = 4321
self.node.driver_internal_info = dii
self.node.save()
with task_manager.acquire(self.context, with task_manager.acquire(self.context,
self.node.uuid) as task: self.node.uuid) as task:
@ -3468,6 +3473,11 @@ class IPMIToolSocatDriverTestCase(IPMIToolShellinaboxTestCase):
mock_start.return_value = None mock_start.return_value = None
mock_info.return_value = {'port': None} mock_info.return_value = {'port': None}
mock_alloc.return_value = 1234 mock_alloc.return_value = 1234
# Ensure allocated port is not re-used
dii = self.node.driver_internal_info
dii['allocated_ipmi_terminal_port'] = 4321
self.node.driver_internal_info = dii
self.node.save()
with task_manager.acquire(self.context, with task_manager.acquire(self.context,
self.node.uuid) as task: self.node.uuid) as task:

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Fixes issues that auto-allocated console port could conflict on the same
host under certain circumstances related to conductor takeover.
For more information, see `story 2010489
<https://storyboard.openstack.org/#!/story/2010489>`_.