Merge "DRAC: Fix a bug for delete_config with multiple controllers"
This commit is contained in:
commit
388a38fa3a
@ -754,6 +754,25 @@ def _filter_logical_disks(logical_disks, include_root_volume,
|
||||
return filtered_disks
|
||||
|
||||
|
||||
def _create_config_job(node, controller, reboot=False, realtime=False,
|
||||
raid_config_job_ids=[],
|
||||
raid_config_parameters=[]):
|
||||
job_id = commit_config(node, raid_controller=controller,
|
||||
reboot=reboot, realtime=realtime)
|
||||
|
||||
raid_config_job_ids.append(job_id)
|
||||
if controller not in raid_config_parameters:
|
||||
raid_config_parameters.append(controller)
|
||||
|
||||
LOG.info('Change has been committed to RAID controller '
|
||||
'%(controller)s on node %(node)s. '
|
||||
'DRAC job id: %(job_id)s',
|
||||
{'controller': controller, 'node': node.uuid,
|
||||
'job_id': job_id})
|
||||
return {'raid_config_job_ids': raid_config_job_ids,
|
||||
'raid_config_parameters': raid_config_parameters}
|
||||
|
||||
|
||||
def _commit_to_controllers(node, controllers, substep="completed"):
|
||||
"""Commit changes to RAID controllers on the node.
|
||||
|
||||
@ -776,9 +795,18 @@ def _commit_to_controllers(node, controllers, substep="completed"):
|
||||
configuration is in progress asynchronously or None if it is
|
||||
completed.
|
||||
"""
|
||||
# remove controller which does not require configuration job
|
||||
controllers = [controller for controller in controllers
|
||||
if controller['is_commit_required']]
|
||||
|
||||
if not controllers:
|
||||
LOG.debug('No changes on any of the controllers on node %s',
|
||||
node.uuid)
|
||||
driver_internal_info = node.driver_internal_info
|
||||
driver_internal_info['raid_config_substep'] = substep
|
||||
driver_internal_info['raid_config_parameters'] = []
|
||||
node.driver_internal_info = driver_internal_info
|
||||
node.save()
|
||||
return
|
||||
|
||||
driver_internal_info = node.driver_internal_info
|
||||
@ -788,43 +816,35 @@ def _commit_to_controllers(node, controllers, substep="completed"):
|
||||
if 'raid_config_job_ids' not in driver_internal_info:
|
||||
driver_internal_info['raid_config_job_ids'] = []
|
||||
|
||||
# remove controller which does not require configuration job
|
||||
controllers = [controller for controller in controllers
|
||||
if controller['is_commit_required']]
|
||||
|
||||
all_realtime = True
|
||||
optional = drac_constants.RebootRequired.optional
|
||||
for controller in controllers:
|
||||
raid_controller = controller['raid_controller']
|
||||
all_realtime = all(cntlr['is_reboot_required'] == optional
|
||||
for cntlr in controllers)
|
||||
raid_config_job_ids = []
|
||||
raid_config_parameters = []
|
||||
if all_realtime:
|
||||
for controller in controllers:
|
||||
realtime_controller = controller['raid_controller']
|
||||
job_details = _create_config_job(
|
||||
node, controller=realtime_controller,
|
||||
reboot=False, realtime=True,
|
||||
raid_config_job_ids=raid_config_job_ids,
|
||||
raid_config_parameters=raid_config_parameters)
|
||||
|
||||
# Commit the configuration
|
||||
# The logic below will reboot the node if there is at least one
|
||||
# controller without real time support. In that case the reboot
|
||||
# is triggered when the configuration is committed to the last
|
||||
# controller.
|
||||
realtime = controller['is_reboot_required'] == optional
|
||||
all_realtime = all_realtime and realtime
|
||||
if controller == controllers[-1]:
|
||||
job_id = commit_config(node, raid_controller=raid_controller,
|
||||
reboot=not all_realtime,
|
||||
realtime=realtime)
|
||||
else:
|
||||
job_id = commit_config(node, raid_controller=raid_controller,
|
||||
reboot=False,
|
||||
realtime=realtime)
|
||||
else:
|
||||
for controller in controllers:
|
||||
mix_controller = controller['raid_controller']
|
||||
reboot = True if controller == controllers[-1] else False
|
||||
job_details = _create_config_job(
|
||||
node, controller=mix_controller,
|
||||
reboot=reboot, realtime=False,
|
||||
raid_config_job_ids=raid_config_job_ids,
|
||||
raid_config_parameters=raid_config_parameters)
|
||||
|
||||
LOG.info('Change has been committed to RAID controller '
|
||||
'%(controller)s on node %(node)s. '
|
||||
'DRAC job id: %(job_id)s',
|
||||
{'controller': controller, 'node': node.uuid,
|
||||
'job_id': job_id})
|
||||
driver_internal_info['raid_config_job_ids'] = job_details[
|
||||
'raid_config_job_ids']
|
||||
|
||||
driver_internal_info['raid_config_job_ids'].append(job_id)
|
||||
|
||||
if raid_controller not in driver_internal_info[
|
||||
'raid_config_parameters']:
|
||||
driver_internal_info['raid_config_parameters'].append(
|
||||
raid_controller)
|
||||
driver_internal_info['raid_config_parameters'] = job_details[
|
||||
'raid_config_parameters']
|
||||
|
||||
node.driver_internal_info = driver_internal_info
|
||||
|
||||
@ -1094,10 +1114,10 @@ class DracWSManRAID(base.RAIDInterface):
|
||||
'raid_config_parameters']:
|
||||
controller_cap = clear_foreign_config(
|
||||
node, controller_id)
|
||||
controller = {'raid_controller': controller_id,
|
||||
'is_reboot_required':
|
||||
controller_cap[
|
||||
'is_reboot_required']}
|
||||
controller = {
|
||||
'raid_controller': controller_id,
|
||||
'is_reboot_required': controller_cap['is_reboot_required'],
|
||||
'is_commit_required': controller_cap['is_commit_required']}
|
||||
controllers.append(controller)
|
||||
jobs_required = jobs_required or controller_cap[
|
||||
'is_commit_required']
|
||||
|
@ -362,8 +362,7 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest):
|
||||
substep=substep)
|
||||
|
||||
self.assertEqual(0, mock_commit_config.call_count)
|
||||
self.assertEqual([],
|
||||
self.node.driver_internal_info['raid_config_job_ids'])
|
||||
self.assertNotIn('raid_config_job_ids', self.node.driver_internal_info)
|
||||
self.assertEqual(substep,
|
||||
self.node.driver_internal_info['raid_config_substep'])
|
||||
|
||||
@ -1702,6 +1701,71 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
|
||||
self.node.save()
|
||||
self._test_delete_configuration(states.DEPLOYWAIT)
|
||||
|
||||
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(drac_raid, 'list_raid_controllers', autospec=True)
|
||||
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(drac_raid, 'commit_config', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(drac_raid, '_reset_raid_config', spec_set=True,
|
||||
autospec=True)
|
||||
def test_delete_configuration_with_non_realtime_controller(
|
||||
self, mock__reset_raid_config, mock_commit_config,
|
||||
mock_validate_job_queue, mock_list_raid_controllers,
|
||||
mock_get_drac_client):
|
||||
mock_client = mock.Mock()
|
||||
mock_get_drac_client.return_value = mock_client
|
||||
expected_raid_config_params = ['AHCI.Slot.3-1', 'RAID.Integrated.1-1']
|
||||
mix_controllers = [{'id': 'AHCI.Slot.3-1',
|
||||
'description': 'AHCI controller in slot 3',
|
||||
'manufacturer': 'DELL',
|
||||
'model': 'BOSS-S1',
|
||||
'primary_status': 'unknown',
|
||||
'firmware_version': '2.5.13.3016',
|
||||
'bus': '5E',
|
||||
'supports_realtime': False},
|
||||
{'id': 'RAID.Integrated.1-1',
|
||||
'description': 'Integrated RAID Controller 1',
|
||||
'manufacturer': 'DELL',
|
||||
'model': 'PERC H740 Mini',
|
||||
'primary_status': 'unknown',
|
||||
'firmware_version': '50.5.0-1750',
|
||||
'bus': '3C',
|
||||
'supports_realtime': True}]
|
||||
|
||||
mock_list_raid_controllers.return_value = [
|
||||
test_utils.make_raid_controller(controller) for
|
||||
controller in mix_controllers]
|
||||
|
||||
mock_commit_config.side_effect = ['42', '12']
|
||||
mock__reset_raid_config.side_effect = [{
|
||||
'is_reboot_required': constants.RebootRequired.optional,
|
||||
'is_commit_required': True
|
||||
}, {
|
||||
'is_reboot_required': constants.RebootRequired.true,
|
||||
'is_commit_required': True
|
||||
}]
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
return_value = task.driver.raid.delete_configuration(task)
|
||||
|
||||
mock_commit_config.assert_has_calls(
|
||||
[mock.call(mock.ANY, raid_controller='AHCI.Slot.3-1',
|
||||
reboot=False, realtime=False),
|
||||
mock.call(mock.ANY, raid_controller='RAID.Integrated.1-1',
|
||||
reboot=True, realtime=False)],
|
||||
any_order=True)
|
||||
|
||||
self.assertEqual(states.CLEANWAIT, return_value)
|
||||
self.node.refresh()
|
||||
self.assertEqual(expected_raid_config_params,
|
||||
self.node.driver_internal_info[
|
||||
'raid_config_parameters'])
|
||||
self.assertEqual(['42', '12'],
|
||||
self.node.driver_internal_info['raid_config_job_ids'])
|
||||
|
||||
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(drac_raid, 'list_raid_controllers', autospec=True)
|
||||
|
@ -0,0 +1,9 @@
|
||||
fixes:
|
||||
- |
|
||||
Fixes a bug in the ``idrac`` hardware type where a race condition
|
||||
can occur on a host that has a mix of controllers where some support
|
||||
realtime mode and some do not. The approach is to use only realtime
|
||||
mode if all controllers support realtime. This removes the race
|
||||
condition.
|
||||
See bug `2006502 https://storyboard.openstack.org/#!/story/2006502`
|
||||
for details
|
Loading…
x
Reference in New Issue
Block a user