From 619ec3dc8876e6fc2e4ce7f20814347f39e71198 Mon Sep 17 00:00:00 2001 From: Rachit7194 Date: Wed, 28 Aug 2019 11:57:34 -0400 Subject: [PATCH] DRAC: Drives conversion from JBOD to RAID Added physical drives conversion from JBOD to RAID mode before RAID create_configuration cleaning step called in iDRAC driver. Note - for this backport, python-dracclient was unchanged in driver-requirements.txt. Because of this, the return from change_physical_disk_state() was adjusted to not use the updated python-dracclient. Change-Id: Ie6d1a9314d1543b73889f6d419541e75b7e06c89 Story: #2006479 Task: #36422 (cherry picked from commit 8e3f682e29830a9a5a4795b5d775564a21f93733) --- ironic/conf/drac.py | 8 +- ironic/drivers/modules/drac/job.py | 28 ++- ironic/drivers/modules/drac/raid.py | 115 ++++++++- .../unit/drivers/modules/drac/test_raid.py | 238 +++++++++++++++--- .../drivers/third_party_driver_mock_specs.py | 7 +- .../unit/drivers/third_party_driver_mocks.py | 4 + ...version-jbod-to-raid-1a229627708e10b9.yaml | 5 + 7 files changed, 356 insertions(+), 49 deletions(-) create mode 100644 releasenotes/notes/idrac-drives-conversion-jbod-to-raid-1a229627708e10b9.yaml diff --git a/ironic/conf/drac.py b/ironic/conf/drac.py index 7696bb03f6..0b3b2bdc66 100644 --- a/ironic/conf/drac.py +++ b/ironic/conf/drac.py @@ -28,7 +28,13 @@ opts = [ help=_('Maximum amount of time (in seconds) to wait for ' 'the boot device configuration job to transition ' 'to the correct state to allow a reboot or power ' - 'on to complete.')) + 'on to complete.')), + cfg.IntOpt('config_job_max_retries', + default=240, + min=1, + help=_('Maximum number of retries for ' + 'the configuration job to complete ' + 'successfully.')) ] diff --git a/ironic/drivers/modules/drac/job.py b/ironic/drivers/modules/drac/job.py index cab1af32e2..6cce9387ec 100644 --- a/ironic/drivers/modules/drac/job.py +++ b/ironic/drivers/modules/drac/job.py @@ -17,21 +17,23 @@ DRAC Lifecycle job specific methods from oslo_log import log as logging from oslo_utils import importutils +import retrying from ironic.common import exception from ironic.common.i18n import _ +from ironic.conf import CONF from ironic.drivers.modules.drac import common as drac_common drac_exceptions = importutils.try_import('dracclient.exceptions') LOG = logging.getLogger(__name__) +WAIT_CLOCK = 5 def validate_job_queue(node, name_prefix=None): """Validates the job queue on the node. It raises an exception if an unfinished configuration job exists. - :param node: an ironic node object. :param name_prefix: A name prefix for jobs to validate. :raises: DracOperationError on an error from python-dracclient. @@ -86,3 +88,27 @@ def list_unfinished_jobs(node): {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc) + + +@retrying.retry( + retry_on_exception=lambda e: isinstance(e, exception.DracOperationError), + stop_max_attempt_number=CONF.drac.config_job_max_retries, + wait_fixed=WAIT_CLOCK * 1000) +def wait_for_job_completion(node, + retries=CONF.drac.config_job_max_retries): + """Wait for job to complete + + It will wait for the job to complete for 20 minutes and raises timeout + if job never complete within given interval of time. + :param node: an ironic node object. + :param retries: no of retries to make conductor wait. + :raises: DracOperationError on exception raised from python-dracclient + or a timeout while waiting for job completion. + """ + if not list_unfinished_jobs(node): + return + err_msg = _( + 'There are unfinished jobs in the job ' + 'queue on node %(node_uuid)r ') % {'node_uuid': node.uuid} + LOG.warning(err_msg) + raise exception.DracOperationError(error=err_msg) diff --git a/ironic/drivers/modules/drac/raid.py b/ironic/drivers/modules/drac/raid.py index 31040fb514..4a60fd95cc 100644 --- a/ironic/drivers/modules/drac/raid.py +++ b/ironic/drivers/modules/drac/raid.py @@ -15,6 +15,7 @@ DRAC RAID specific methods """ +from collections import defaultdict import math from futurist import periodics @@ -309,6 +310,43 @@ def clear_foreign_config(node, raid_controller): raise exception.DracOperationError(error=exc) +def change_physical_disk_state(node, mode=None, + controllers_to_physical_disk_ids=None): + """Convert disks RAID status + + This method converts the requested physical disks from + RAID to JBOD or vice versa. It does this by only converting the + disks that are not already in the correct state. + + :param node: an ironic node object. + :param mode: the mode to change the disks either to RAID or JBOD. + :param controllers_to_physical_disk_ids: Dictionary of controllers and + corresponding disk ids to convert to the requested mode. + :return: a dictionary containing: + - conversion_results, a dictionary that maps controller ids + to the conversion results for that controller. + The conversion results are a dict that contains: + - The is_commit_required key with the value always set to + True indicating that a config job must be created to + complete disk conversion. + - The is_reboot_required key with a RebootRequired + enumerated value indicating whether the server must be + rebooted to complete disk conversion. + :raises: DRACOperationError on an error from python-dracclient. + """ + try: + drac_job.validate_job_queue(node) + client = drac_common.get_drac_client(node) + return client.change_physical_disk_state( + mode, controllers_to_physical_disk_ids) + except drac_exceptions.BaseClientException as exc: + LOG.error('DRAC driver failed to change physical drives ' + 'to %(mode)s mode for node %(node_uuid)s. ' + 'Reason: %(error)s.', + {'mode': mode, 'node_uuid': node.uuid, 'error': exc}) + raise exception.DracOperationError(error=exc) + + def commit_config(node, raid_controller, reboot=False, realtime=False): """Apply all pending changes on a RAID controller. @@ -338,6 +376,42 @@ def commit_config(node, raid_controller, reboot=False, realtime=False): raise exception.DracOperationError(error=exc) +def _change_physical_disk_mode(node, mode=None, + controllers_to_physical_disk_ids=None): + """Physical drives conversion from RAID to JBOD or vice-versa. + + :param node: an ironic node object. + :param mode: the mode to change the disks either to RAID or JBOD. + :param controllers_to_physical_disk_ids: Dictionary of controllers and + corresponding disk ids to convert to the requested mode. + :returns: states.CLEANWAIT if deletion is in progress asynchronously + or None if it is completed. + """ + change_disk_state = change_physical_disk_state( + node, mode, controllers_to_physical_disk_ids) + + controllers = list() + if 'conversion_results' in change_disk_state: + conversion_results = change_disk_state['conversion_results'] + for controller_id, result in conversion_results.items(): + controller = {'raid_controller': controller_id, + 'is_reboot_required': result['is_reboot_required'], + 'is_commit_required': result['is_commit_required']} + controllers.append(controller) + else: + controller_ids = change_disk_state['commit_required_ids'] + for controller_id in controller_ids: + controller = {'raid_controller': controller_id, + 'is_reboot_required': + change_disk_state['is_reboot_required'], + 'is_commit_required': True} + controllers.append(controller) + + return _commit_to_controllers( + node, + controllers, substep='completed') + + def abandon_config(node, raid_controller): """Deletes all pending changes on a RAID controller. @@ -833,18 +907,18 @@ def _commit_to_controllers(node, controllers, substep="completed"): else: for controller in controllers: mix_controller = controller['raid_controller'] - reboot = True if controller == controllers[-1] else False + reboot = (controller == controllers[-1]) 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) - driver_internal_info['raid_config_job_ids'] = job_details[ - 'raid_config_job_ids'] + driver_internal_info['raid_config_job_ids'].extend(job_details[ + 'raid_config_job_ids']) - driver_internal_info['raid_config_parameters'] = job_details[ - 'raid_config_parameters'] + driver_internal_info['raid_config_parameters'].extend(job_details[ + 'raid_config_parameters']) node.driver_internal_info = driver_internal_info @@ -940,6 +1014,7 @@ class DracWSManRAID(base.RAIDInterface): node = task.node logical_disks = node.target_raid_config['logical_disks'] + for disk in logical_disks: if disk['size_gb'] == 'MAX' and 'physical_disks' not in disk: raise exception.InvalidParameterValue( @@ -969,6 +1044,36 @@ class DracWSManRAID(base.RAIDInterface): logical_disks_to_create = _filter_logical_disks( logical_disks, create_root_volume, create_nonroot_volumes) + controllers_to_physical_disk_ids = defaultdict(list) + for logical_disk in logical_disks_to_create: + # Not applicable to JBOD logical disks. + if logical_disk['raid_level'] == 'JBOD': + continue + + for physical_disk_name in logical_disk['physical_disks']: + controllers_to_physical_disk_ids[ + logical_disk['controller']].append( + physical_disk_name) + + if logical_disks_to_create: + LOG.debug( + "Converting physical disks configured to back RAID " + "logical disks to RAID mode for node %(node_uuid)s ", + {"node_uuid": node.uuid}) + raid = drac_constants.RaidStatus.raid + _change_physical_disk_mode( + node, raid, controllers_to_physical_disk_ids) + + LOG.debug("Waiting for physical disk conversion to complete " + "for node %(node_uuid)s. ", {"node_uuid": node.uuid}) + drac_job.wait_for_job_completion(node) + + LOG.info( + "Completed converting physical disks configured to back RAID " + "logical disks to RAID mode for node %(node_uuid)s", + {'node_uuid': node.uuid}) + + controllers = list() for logical_disk in logical_disks_to_create: controller = dict() controller_cap = create_virtual_disk( diff --git a/ironic/tests/unit/drivers/modules/drac/test_raid.py b/ironic/tests/unit/drivers/modules/drac/test_raid.py index 2c9603c91c..b5b2d04fbf 100644 --- a/ironic/tests/unit/drivers/modules/drac/test_raid.py +++ b/ironic/tests/unit/drivers/modules/drac/test_raid.py @@ -284,6 +284,59 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest): exception.DracOperationError, drac_raid.clear_foreign_config, self.node, 'RAID.Integrated.1-1') + @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, + autospec=True) + def test_change_physical_disk_state(self, + mock_validate_job_queue, + mock_get_drac_client): + mock_client = mock.Mock() + mock_get_drac_client.return_value = mock_client + controllers_to_physical_disk_ids = {'RAID.Integrated.1-1': [ + 'Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1', + 'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1']} + expected_change_disk_state = { + 'is_reboot_required': True, + 'commit_required_ids': ['RAID.Integrated.1-1']} + mode = constants.RaidStatus.raid + mock_client.change_physical_disk_state.return_value = \ + expected_change_disk_state + actual_change_disk_state = drac_raid.change_physical_disk_state( + self.node, + mode=mode, + controllers_to_physical_disk_ids=controllers_to_physical_disk_ids) + + mock_validate_job_queue.assert_called_once_with(self.node) + mock_client.change_physical_disk_state.assert_called_once_with( + mode, controllers_to_physical_disk_ids) + self.assertEqual(expected_change_disk_state, actual_change_disk_state) + + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_raid, 'commit_config', spec_set=True, + autospec=True) + def test__change_physical_disk_mode(self, + mock_commit_config, + mock_change_physical_disk_state, + mock_get_drac_client): + mock_commit_config.return_value = '42' + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} + + actual_change_disk_state = drac_raid._change_physical_disk_mode( + self.node, mode=constants.RaidStatus.raid) + self.assertEqual(['42'], + self.node.driver_internal_info['raid_config_job_ids']) + self.assertEqual('completed', + self.node.driver_internal_info['raid_config_substep']) + self.assertEqual( + ['RAID.Integrated.1-1'], + self.node.driver_internal_info['raid_config_parameters']) + mock_commit_config.assert_called_once_with( + self.node, raid_controller='RAID.Integrated.1-1', reboot=False, + realtime=True) + self.assertEqual(states.DEPLOYWAIT, actual_change_disk_state) + def test_commit_config(self, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -746,12 +799,17 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def _test_create_configuration( - self, expected_state, mock_commit_config, mock_validate_job_queue, - mock_list_physical_disks, mock__reset_raid_config, - mock_get_drac_client): + self, expected_state, mock_commit_config, + mock_wait_for_job_completion, mock_change_physical_disk_state, + mock_validate_job_queue, mock_list_physical_disks, + mock__reset_raid_config, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client physical_disks = self._generate_physical_disks() @@ -772,6 +830,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): mock__reset_raid_config.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True} + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} + mock_commit_config.side_effect = ['42', '12'] mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True} @@ -787,13 +849,17 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): 'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1'], '1', 51200, None, 2, 1) - mock_commit_config.assert_called_once_with( + mock_commit_config.assert_called_with( task.node, raid_controller='RAID.Integrated.1-1', reboot=False, realtime=True) self.assertEqual(expected_state, return_value) + self.assertEqual(2, mock_commit_config.call_count) + self.assertEqual(1, mock_client.create_virtual_disk.call_count) + self.assertEqual(1, mock_change_physical_disk_state.call_count) + self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) def test_create_configuration_in_clean(self): @@ -809,16 +875,24 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_no_change( - self, mock_commit_config, mock_validate_job_queue, - mock_list_physical_disks, - mock_get_drac_client): + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, + mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} + mock_commit_config.return_value = '42' mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True} @@ -842,6 +916,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, '_reset_raid_config', autospec=True) @mock.patch.object(drac_raid, 'list_virtual_disks', autospec=True) @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + 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, @@ -849,6 +927,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): def test_create_configuration_delete_existing( self, mock_commit_config, mock_validate_job_queue, + mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_list_physical_disks, mock_list_virtual_disks, mock__reset_raid_config, @@ -871,12 +951,16 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): raid_controller = test_utils.make_raid_controller( raid_controller_dict) mock_list_physical_disks.return_value = physical_disks - mock_commit_config.return_value = '42' + mock_commit_config.side_effect = ['42', '12'] mock_client.list_raid_controllers.return_value = [raid_controller] mock__reset_raid_config.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True} + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} + mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True @@ -892,15 +976,15 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): ['Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1', 'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1'], '1', 51200, None, 2, 1) - mock_commit_config.assert_called_once_with( + mock_commit_config.assert_called_with( task.node, raid_controller='RAID.Integrated.1-1', realtime=True, reboot=False) - self.assertEqual(1, mock_commit_config.call_count) + self.assertEqual(2, mock_commit_config.call_count) self.assertEqual(states.DEPLOYWAIT, return_value) self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -908,10 +992,15 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_nested_raid_level( - self, mock_commit_config, mock_validate_job_queue, + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -929,7 +1018,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks - mock_commit_config.return_value = '42' + mock_commit_config.side_effect = ['42', '12'] + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True} @@ -951,12 +1043,16 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): '5+0', 102400, None, 3, 2) # Commits to the controller - mock_commit_config.assert_called_once_with( + mock_commit_config.assert_called_with( mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=False, realtime=True) + self.assertEqual(2, mock_commit_config.call_count) + self.assertEqual(1, mock_client.create_virtual_disk.call_count) + self.assertEqual(1, mock_change_physical_disk_state.call_count) + self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -964,12 +1060,16 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_nested_raid_10( - self, mock_commit_config, - mock_validate_job_queue, mock_list_physical_disks, - mock_get_drac_client): + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, + mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -986,7 +1086,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks - mock_commit_config.return_value = '42' + mock_commit_config.side_effect = ['42', '12'] + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True} @@ -1006,12 +1109,16 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): '1+0', 102400, None, None, None) # Commits to the controller - mock_commit_config.assert_called_once_with( + mock_commit_config.assert_called_with( mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=False, realtime=True) + self.assertEqual(2, mock_commit_config.call_count) + self.assertEqual(1, mock_client.create_virtual_disk.call_count) + self.assertEqual(1, mock_change_physical_disk_state.call_count) + self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -1019,10 +1126,15 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_multiple_controllers( - self, mock_commit_config, mock_validate_job_queue, + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -1032,7 +1144,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks - mock_commit_config.side_effect = ['42', '12', '13'] + mock_commit_config.side_effect = ['42', '12', '13', '14'] + + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} + mock_client.create_virtual_disk.side_effect = [{ 'is_reboot_required': constants.RebootRequired.true, 'is_commit_required': True @@ -1079,7 +1196,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): any_order=True) self.node.refresh() - self.assertEqual(['42', '12', '13'], + self.assertEqual(['42', '12', '13', '14'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -1087,10 +1204,15 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_backing_physical_disks( - self, mock_commit_config, mock_validate_job_queue, + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -1107,7 +1229,11 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks - mock_commit_config.side_effect = ['42', '12', '13'] + mock_commit_config.side_effect = ['42', '12'] + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} + mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True @@ -1144,7 +1270,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): reboot=False, realtime=True) self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -1152,12 +1278,16 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_predefined_number_of_phyisical_disks( - self, mock_commit_config, mock_validate_job_queue, + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, mock_list_physical_disks, mock_get_drac_client): - mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -1172,6 +1302,9 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks mock_commit_config.side_effect = ['42', '12'] + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True @@ -1204,7 +1337,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): reboot=False, realtime=True) self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -1212,10 +1345,15 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_max_size( - self, mock_commit_config, mock_validate_job_queue, + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -1237,7 +1375,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks - mock_commit_config.side_effect = ['42', '12', '13'] + mock_commit_config.side_effect = ['42', '12'] + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True @@ -1275,7 +1416,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): realtime=True) self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -1312,10 +1453,15 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_share_physical_disks( - self, mock_commit_config, mock_validate_job_queue, + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -1332,6 +1478,9 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): mock_list_physical_disks.return_value = physical_disks mock_commit_config.side_effect = ['42', '12'] + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True @@ -1363,7 +1512,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): realtime=True) self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @@ -1420,12 +1569,16 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + @mock.patch.object(drac_raid, 'change_physical_disk_state', spec_set=True, + autospec=True) + @mock.patch.object(drac_job, 'wait_for_job_completion', spec_set=True, + autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, autospec=True) def test_create_configuration_with_max_size_and_share_physical_disks( - self, mock_commit_config, mock_validate_job_queue, - mock_list_physical_disks, - mock_get_drac_client): + self, mock_commit_config, mock_wait_for_job_completion, + mock_change_physical_disk_state, mock_validate_job_queue, + mock_list_physical_disks, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client @@ -1445,7 +1598,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): physical_disks = self._generate_physical_disks() mock_list_physical_disks.return_value = physical_disks - mock_commit_config.return_value = '42' + mock_commit_config.side_effect = ['42', '12'] + mock_change_physical_disk_state.return_value = { + 'is_reboot_required': constants.RebootRequired.optional, + 'commit_required_ids': ['RAID.Integrated.1-1']} mock_client.create_virtual_disk.return_value = { 'is_reboot_required': constants.RebootRequired.optional, 'is_commit_required': True @@ -1478,7 +1634,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): realtime=True) self.node.refresh() - self.assertEqual(['42'], + self.assertEqual(['42', '12'], self.node.driver_internal_info['raid_config_job_ids']) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py index a895730944..6699c74ffc 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py +++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py @@ -31,7 +31,8 @@ DRACCLIENT_CONSTANTS_MOD_SPEC = ( 'POWER_OFF', 'POWER_ON', 'REBOOT', - 'RebootRequired' + 'RebootRequired', + 'RaidStatus' ) DRACCLIENT_CONSTANTS_REBOOT_REQUIRED_MOD_SPEC = ( @@ -40,6 +41,10 @@ DRACCLIENT_CONSTANTS_REBOOT_REQUIRED_MOD_SPEC = ( 'false' ) +DRACCLIENT_CONSTANTS_RAID_STATUS_MOD_SPEC = ( + 'jbod', + 'raid' +) # proliantutils PROLIANTUTILS_SPEC = ( diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py index af4689d9d5..fde0dc5d3b 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mocks.py +++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py @@ -95,6 +95,10 @@ if not dracclient: true=mock.sentinel.true, optional=mock.sentinel.optional, false=mock.sentinel.false) + dracclient.constants.RaidStatus = mock.MagicMock( + spec_set=mock_specs.DRACCLIENT_CONSTANTS_RAID_STATUS_MOD_SPEC, + jbod=mock.sentinel.jbod, + raid=mock.sentinel.raid) sys.modules['dracclient'] = dracclient sys.modules['dracclient.client'] = dracclient.client diff --git a/releasenotes/notes/idrac-drives-conversion-jbod-to-raid-1a229627708e10b9.yaml b/releasenotes/notes/idrac-drives-conversion-jbod-to-raid-1a229627708e10b9.yaml new file mode 100644 index 0000000000..ff3a47d116 --- /dev/null +++ b/releasenotes/notes/idrac-drives-conversion-jbod-to-raid-1a229627708e10b9.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Hardware type ``idrac`` converts physical drives from + ``JBOD`` to ``RAID`` mode before building RAID on them.