summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2020-08-17 17:16:56 +0000
committerGerrit Code Review <review@openstack.org>2020-08-17 17:16:56 +0000
commita5ca82e362aba6ef5084918a948a312eb8fb485e (patch)
tree5a9ae2fcf9a896dbb6f6ad839e609be7d06bad4f
parentbdf5ebd57b0c1e7dd75cc49f3e9d4392533887fc (diff)
parent619ec3dc8876e6fc2e4ce7f20814347f39e71198 (diff)
downloadironic-a5ca82e362aba6ef5084918a948a312eb8fb485e.tar.gz
Merge "DRAC: Drives conversion from JBOD to RAID" into stable/train
-rw-r--r--ironic/conf/drac.py8
-rw-r--r--ironic/drivers/modules/drac/job.py28
-rw-r--r--ironic/drivers/modules/drac/raid.py115
-rw-r--r--ironic/tests/unit/drivers/modules/drac/test_raid.py238
-rw-r--r--ironic/tests/unit/drivers/third_party_driver_mock_specs.py7
-rw-r--r--ironic/tests/unit/drivers/third_party_driver_mocks.py4
-rw-r--r--releasenotes/notes/idrac-drives-conversion-jbod-to-raid-1a229627708e10b9.yaml5
7 files changed, 356 insertions, 49 deletions
diff --git a/ironic/conf/drac.py b/ironic/conf/drac.py
index 7696bb03f..0b3b2bdc6 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 cab1af32e..6cce9387e 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 31040fb51..4a60fd95c 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 2c9603c91..b5b2d04fb 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 a89573094..6699c74ff 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 af4689d9d..fde0dc5d3 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 000000000..ff3a47d11
--- /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.