diff options
-rw-r--r-- | bindep.txt | 4 | ||||
-rw-r--r-- | devstack/files/debs/ironic | 35 | ||||
-rw-r--r-- | devstack/lib/ironic | 23 | ||||
-rw-r--r-- | ironic/db/sqlalchemy/api.py | 12 | ||||
-rw-r--r-- | ironic/drivers/modules/drac/raid.py | 165 | ||||
-rw-r--r-- | ironic/tests/unit/drivers/modules/drac/test_raid.py | 174 | ||||
-rw-r--r-- | ironic/tests/unit/drivers/modules/drac/utils.py | 5 | ||||
-rw-r--r-- | releasenotes/notes/idrac-add-ehba-support-10b90c92b8865364.yaml | 15 | ||||
-rw-r--r-- | zuul.d/ironic-jobs.yaml | 4 |
9 files changed, 387 insertions, 50 deletions
diff --git a/bindep.txt b/bindep.txt index 80de701b3..80df57a52 100644 --- a/bindep.txt +++ b/bindep.txt @@ -34,9 +34,11 @@ libvirt-devel [platform:rpm devstack] qemu [platform:dpkg devstack build-image-dib] qemu-kvm [platform:dpkg devstack] qemu-utils [platform:dpkg devstack build-image-dib] -sgabios [devstack] +qemu-system-data [platform:dpkg devstack] +sgabios [platform: rpm devstack] ipxe-qemu [platform:dpkg devstack] edk2-ovmf [platform:rpm devstack] +ovmf [platform:dpkg devstack] ipxe-roms-qemu [platform:rpm devstack] openvswitch [platform:rpm devstack] iptables [devstack] diff --git a/devstack/files/debs/ironic b/devstack/files/debs/ironic index e9907b0be..375f34439 100644 --- a/devstack/files/debs/ironic +++ b/devstack/files/debs/ironic @@ -6,36 +6,39 @@ # but only recommends it in Jessie/Xenial. # Make sure syslinux-common is installed for those distros as it provides # *.c32 modules for syslinux -# TODO remove distro pinning when Wheezy / Trusty are EOLed (May 2019) -# or DevStack stops supporting those. # In the mean time, new Debian-based release codenames will have to be added # as distros can not be pinned with 'if-later-than' specified. apparmor docker.io +gnupg ipmitool iptables ipxe +ipxe-qemu isolinux -gnupg -libguestfs0 +jq libguestfs-tools -libvirt-bin # dist:xenial,bionic NOPRIME +libguestfs0 +libvirt-bin # dist:bionic +libvirt-daemon-driver-storage-gluster # dist:focal +libvirt-daemon-driver-lxc # dist:focal +libvirt-daemon-driver-storage-rbd # dist:focal +libvirt-daemon-system # dist:focal +libvirt-dev open-iscsi openssh-client -# TODO (etingof) pinning to older version in devstack/lib/ironic -#ovmf -pxelinux # dist:xenial,bionic -python-libguestfs +ovmf +pxelinux +python-libguestfs # dist:bionic +python3-guestfs # dist:focal qemu qemu-kvm qemu-utils -sgabios +qemu-system-data # dist:focal +sgabios # dist:bionic shellinabox -syslinux-common # dist:xenial,bionic +socat +squashfs-tools +syslinux-common tftpd-hpa xinetd -squashfs-tools -libvirt-dev -socat -ipxe-qemu -jq diff --git a/devstack/lib/ironic b/devstack/lib/ironic index 4477557a5..a4d5fc07c 100644 --- a/devstack/lib/ironic +++ b/devstack/lib/ironic @@ -1063,28 +1063,7 @@ function install_ironic { # Replace the default virtio PXE ROM in QEMU with an EFI capable # one. The EFI ROM should work on with both boot modes, Legacy # BIOS and UEFI. - if is_ubuntu; then - # (rpittau) in bionic the UEFI in the ovmf 0~20180205.c0d9813c-2 - # package is broken: EFI v2.70 by EDK II - # As a workaround, here we download and install the old working - # version from the multiverse repository: EFI v2.60 by EDK II - # Bug reference: - # https://bugs.launchpad.net/ubuntu/+source/edk2/+bug/1821729 - local temp_deb - temp_deb="$(mktemp)" - wget http://archive.ubuntu.com/ubuntu/pool/multiverse/e/edk2/ovmf_0~20160408.ffea0a2c-2_all.deb -O "$temp_deb" - sudo dpkg -i "$temp_deb" - rm -f "$temp_deb" - - # NOTE(TheJulia): This no longer seems required as the ovmf images - # DO correctly network boot. The effect of this is making the - # default boot loader iPXE, which is not always desired nor - # realistic for hardware in the field. - # If it is after Train, we should likely just delete the lines - # below and consider the same for Fedora. - # sudo rm /usr/share/qemu/pxe-virtio.rom - # sudo ln -s /usr/lib/ipxe/qemu/efi-virtio.rom /usr/share/qemu/pxe-virtio.rom - elif is_fedora; then + if is_fedora; then sudo rm /usr/share/qemu/pxe-virtio.rom sudo ln -s /usr/share/ipxe.efi/1af41000.rom /usr/share/qemu/pxe-virtio.rom fi diff --git a/ironic/db/sqlalchemy/api.py b/ironic/db/sqlalchemy/api.py index e8f4c737a..a0ca90ef1 100644 --- a/ironic/db/sqlalchemy/api.py +++ b/ironic/db/sqlalchemy/api.py @@ -1520,12 +1520,12 @@ class Connection(api.Connection): per-node trait limit. """ if num_traits > MAX_TRAITS_PER_NODE: - msg = _("Could not modify traits for node %(node_id)s as it would " - "exceed the maximum number of traits per node " - "(%(num_traits)d vs. %(max_traits)d)") - raise exception.InvalidParameterValue( - msg, node_id=node_id, num_traits=num_traits, - max_traits=MAX_TRAITS_PER_NODE) + msg = (_("Could not modify traits for node %(node_id)s as it " + "would exceed the maximum number of traits per node " + "(%(num_traits)d vs. %(max_traits)d)") + % {'node_id': node_id, 'num_traits': num_traits, + 'max_traits': MAX_TRAITS_PER_NODE}) + raise exception.InvalidParameterValue(err=msg) @oslo_db_api.retry_on_deadlock def set_node_traits(self, node_id, traits, version): diff --git a/ironic/drivers/modules/drac/raid.py b/ironic/drivers/modules/drac/raid.py index c7cc08610..facc0aeba 100644 --- a/ironic/drivers/modules/drac/raid.py +++ b/ironic/drivers/modules/drac/raid.py @@ -42,6 +42,11 @@ LOG = logging.getLogger(__name__) METRICS = metrics_utils.get_metrics_logger(__name__) +_CURRENT_RAID_CONTROLLER_MODE = "RAIDCurrentControllerMode" +_REQUESTED_RAID_CONTROLLER_MODE = "RAIDRequestedControllerMode" +_EHBA_MODE = "Enhanced HBA" +_RAID_MODE = "RAID" + RAID_LEVELS = { '0': { 'min_disks': 1, @@ -310,6 +315,71 @@ def clear_foreign_config(node, raid_controller): raise exception.DracOperationError(error=exc) +def set_raid_settings(node, controller_fqdd, settings): + """Sets the RAID configuration + + It sets the pending_value parameter for each of the attributes + passed in. For the values to be applied, a config job must + be created. + + :param node: an ironic node object. + :param controller_fqdd: the ID of the RAID controller. + :param settings: a dictionary containing the proposed values, with + each key being the name of attribute and the value + being the proposed value. + :returns: a dictionary containing: + - The is_commit_required key with a boolean value indicating + whether a config job must be created for the values to be + applied. + - The is_reboot_required key with a RebootRequired enumerated + value indicating whether the server must be rebooted for the + values to be applied. Possible values are true and false. + :raises: DRACOperationFailed on error reported back by the DRAC + interface + """ + try: + + drac_job.validate_job_queue(node) + + client = drac_common.get_drac_client(node) + return client.set_raid_settings(controller_fqdd, settings) + except drac_exceptions.BaseClientException as exc: + LOG.error('DRAC driver failed to set raid settings ' + 'on %(raid_controller_fqdd)s ' + 'for node %(node_uuid)s. ' + 'Reason: %(error)s.', + {'raid_controller_fqdd': controller_fqdd, + 'node_uuid': node.uuid, + 'error': exc}) + raise exception.DracOperationError(error=exc) + + +def list_raid_settings(node): + """List the RAID configuration settings + + :param node: an ironic node object. + :returns: a dictionary with the RAID settings using InstanceID as the + key. The attributes are RAIDEnumerableAttribute, + RAIDStringAttribute and RAIDIntegerAttribute objects. + :raises: DRACOperationFailed on error reported back by the DRAC + interface + """ + try: + + drac_job.validate_job_queue(node) + + client = drac_common.get_drac_client(node) + return client.list_raid_settings() + except drac_exceptions.BaseClientException as exc: + LOG.error('DRAC driver failed to list raid settings' + 'on %(raid_controller_fqdd)s ' + 'for node %(node_uuid)s. ' + 'Reason: %(error)s.', + {'node_uuid': node.uuid, + 'error': exc}) + raise exception.DracOperationError(error=exc) + + def change_physical_disk_state(node, mode=None, controllers_to_physical_disk_ids=None): """Convert disks RAID status @@ -874,6 +944,36 @@ def _validate_volume_size(node, logical_disks): return logical_disks +def _switch_to_raid_mode(node, controller_fqdd): + """Convert the controller mode from Enhanced HBA to RAID mode + + :param node: an ironic node object + :param controller_fqdd: the ID of the RAID controller. + :returns: a dictionary containing + - The raid_controller key with a ID of the + RAID controller value. + - The is_commit_required needed key with a + boolean value indicating whether a config job must be created + for the values to be applied. + - The is_reboot_required key with a RebootRequired enumerated + value indicating whether the server must be rebooted to + switch the controller mode to RAID. + """ + # wait for pending jobs to complete + drac_job.wait_for_job_completion(node) + + raid_attr = "{}:{}".format(controller_fqdd, + _REQUESTED_RAID_CONTROLLER_MODE) + settings = {raid_attr: _RAID_MODE} + settings_results = set_raid_settings( + node, controller_fqdd, settings) + controller = { + 'raid_controller': controller_fqdd, + 'is_reboot_required': settings_results['is_reboot_required'], + 'is_commit_required': settings_results['is_commit_required']} + return controller + + def _commit_to_controllers(node, controllers, substep="completed"): """Commit changes to RAID controllers on the node. @@ -918,8 +1018,17 @@ def _commit_to_controllers(node, controllers, substep="completed"): driver_internal_info['raid_config_job_ids'] = [] optional = drac_constants.RebootRequired.optional - all_realtime = all(cntlr['is_reboot_required'] == optional - for cntlr in controllers) + + # all realtime controllers + all_realtime = all( + (cntlr['is_reboot_required'] == optional) + and not(cntlr.get('is_ehba_mode')) + for cntlr in controllers) + + # check any controller with ehba mode + any_ehba_controllers = any( + cntrl.get('is_ehba_mode') is True for cntrl in controllers) + raid_config_job_ids = [] raid_config_parameters = [] if all_realtime: @@ -931,6 +1040,35 @@ def _commit_to_controllers(node, controllers, substep="completed"): raid_config_job_ids=raid_config_job_ids, raid_config_parameters=raid_config_parameters) + elif any_ehba_controllers: + commit_to_ehba_controllers = [] + for controller in controllers: + if controller.get('is_ehba_mode'): + job_details = _create_config_job( + node, controller=controller['raid_controller'], + reboot=False, realtime=True, + raid_config_job_ids=raid_config_job_ids, + raid_config_parameters=raid_config_parameters) + + ehba_controller = _switch_to_raid_mode( + node, controller['raid_controller']) + commit_to_ehba_controllers.append( + ehba_controller['raid_controller']) + else: + job_details = _create_config_job( + node, controller=controller['raid_controller'], + reboot=False, realtime=False, + raid_config_job_ids=raid_config_job_ids, + raid_config_parameters=raid_config_parameters) + + for controller in commit_to_ehba_controllers: + LOG.debug("Create job with Reboot to apply configuration " + "changes for ehba controllers") + job_details = _create_config_job( + node, controller=controller, + reboot=(controller == commit_to_ehba_controllers[-1]), + realtime=False, raid_config_job_ids=raid_config_job_ids, + raid_config_parameters=raid_config_parameters) else: for controller in controllers: mix_controller = controller['raid_controller'] @@ -996,6 +1134,23 @@ def _create_virtual_disks(task, node): return _commit_to_controllers(node, controllers) +def _controller_in_hba_mode(raid_settings, controller_fqdd): + controller_mode = raid_settings.get( + '{}:{}'.format(controller_fqdd, _CURRENT_RAID_CONTROLLER_MODE)) + + return _EHBA_MODE in controller_mode.current_value + + +def _controller_supports_ehba_mode(settings, controller_fqdd): + raid_cntrl_attr = "{}:{}".format(controller_fqdd, + _CURRENT_RAID_CONTROLLER_MODE) + current_cntrl_mode = settings.get(raid_cntrl_attr) + if not current_cntrl_mode: + return False + else: + return _EHBA_MODE in current_cntrl_mode.possible_values + + def _get_disk_free_size_mb(disk, pending_delete): """Return the size of free space on the disk in MB. @@ -1363,9 +1518,15 @@ class DracWSManRAID(base.RAIDInterface): node = task.node controllers = list() drac_raid_controllers = list_raid_controllers(node) + drac_raid_settings = list_raid_settings(node) for cntrl in drac_raid_controllers: if _is_raid_controller(node, cntrl.id, drac_raid_controllers): controller = dict() + if _controller_supports_ehba_mode( + drac_raid_settings, + cntrl.id) and _controller_in_hba_mode( + drac_raid_settings, cntrl.id): + controller['is_ehba_mode'] = True controller_cap = _reset_raid_config(node, cntrl.id) controller["raid_controller"] = cntrl.id controller["is_reboot_required"] = controller_cap[ diff --git a/ironic/tests/unit/drivers/modules/drac/test_raid.py b/ironic/tests/unit/drivers/modules/drac/test_raid.py index 5a9a26e9b..d741b491f 100644 --- a/ironic/tests/unit/drivers/modules/drac/test_raid.py +++ b/ironic/tests/unit/drivers/modules/drac/test_raid.py @@ -15,6 +15,7 @@ Test class for DRAC RAID interface """ +from collections import defaultdict from unittest import mock from dracclient import constants @@ -286,6 +287,33 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest): @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) + def test_set_raid_settings(self, mock_validate_job_queue, + mock_get_drac_client): + mock_client = mock.Mock() + mock_get_drac_client.return_value = mock_client + controller_fqdd = "RAID.Integrated.1-1" + raid_cntrl_attr = "RAID.Integrated.1-1:RAIDRequestedControllerMode" + raid_settings = {raid_cntrl_attr: 'RAID'} + drac_raid.set_raid_settings(self.node, controller_fqdd, raid_settings) + + mock_validate_job_queue.assert_called_once_with( + self.node) + mock_client.set_raid_settings.assert_called_once_with( + controller_fqdd, raid_settings) + + @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, + autospec=True) + def test_list_raid_settings(self, mock_validate_job_queue, + mock_get_drac_client): + mock_client = mock.Mock() + mock_get_drac_client.return_value = mock_client + drac_raid.list_raid_settings(self.node) + mock_validate_job_queue.assert_called_once_with( + self.node) + mock_client.list_raid_settings.assert_called_once_with() + + @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): @@ -389,6 +417,7 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest): mock_get_drac_client): controllers = [{'is_reboot_required': 'true', 'is_commit_required': True, + 'is_ehba_mode': False, 'raid_controller': 'AHCI.Slot.3-1'}] substep = "delete_foreign_config" @@ -1065,6 +1094,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): autospec=True) @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_raid_settings', 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) @@ -1077,6 +1107,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): mock_validate_job_queue, mock_change_physical_disk_state, mock_list_physical_disks, + mock_list_raid_settings, mock_list_virtual_disks, mock__reset_raid_config, mock_get_drac_client): @@ -1097,6 +1128,18 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): 'supports_realtime': True} raid_controller = test_utils.make_raid_controller( raid_controller_dict) + + raid_attr = "RAID.Integrated.1-1:RAIDCurrentControllerMode" + raid_controller_config = { + 'id': 'RAID.Integrated.1-1:RAIDCurrentControllerMode', + 'current_value': ['RAID'], + 'read_only': True, + 'name': 'RAIDCurrentControllerMode', + 'possible_values': ['RAID', 'Enhanced HBA']} + raid_cntrl_settings = { + raid_attr: test_utils.create_raid_setting(raid_controller_config)} + + mock_list_raid_settings.return_value = raid_cntrl_settings mock_list_physical_disks.return_value = physical_disks mock_commit_config.side_effect = ['12'] mock_client.list_raid_controllers.return_value = [raid_controller] @@ -1806,6 +1849,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): autospec=True) @mock.patch.object(drac_raid, '_reset_raid_config', autospec=True) @mock.patch.object(drac_raid, 'list_raid_controllers', autospec=True) + @mock.patch.object(drac_raid, 'list_raid_settings', 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, @@ -1813,11 +1857,23 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): def _test_delete_configuration(self, expected_state, mock_commit_config, mock_validate_job_queue, + mock_list_raid_settings, mock_list_raid_controllers, mock__reset_raid_config, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client + raid_attr = "RAID.Integrated.1-1:RAIDCurrentControllerMode" + raid_controller_config = { + 'id': 'RAID.Integrated.1-1:RAIDCurrentControllerMode', + 'current_value': ['RAID'], + 'read_only': True, + 'name': 'RAIDCurrentControllerMode', + 'possible_values': ['RAID', 'Enhanced HBA']} + + raid_cntrl_settings = { + raid_attr: test_utils.create_raid_setting(raid_controller_config)} + raid_controller_dict = { 'id': 'RAID.Integrated.1-1', 'description': 'Integrated RAID Controller 1', @@ -1830,6 +1886,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): mock_list_raid_controllers.return_value = [ test_utils.make_raid_controller(raid_controller_dict)] + mock_list_raid_settings.return_value = raid_cntrl_settings mock_commit_config.return_value = '42' mock__reset_raid_config.return_value = { 'is_reboot_required': constants.RebootRequired.optional, @@ -1859,16 +1916,17 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @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_raid, 'list_raid_settings', 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( + def test_delete_configuration_with_mix_realtime_controller_in_raid_mode( self, mock__reset_raid_config, mock_commit_config, - mock_validate_job_queue, mock_list_raid_controllers, - mock_get_drac_client): + mock_validate_job_queue, mock_list_raid_settings, + 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'] @@ -1893,6 +1951,25 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): test_utils.make_raid_controller(controller) for controller in mix_controllers] + raid_controller_config = [ + {'id': 'AHCI.Slot.3-1:RAIDCurrentControllerMode', + 'current_value': ['RAID'], + 'read_only': True, + 'name': 'RAIDCurrentControllerMode', + 'possible_values': ['RAID', 'Enhanced HBA']}, + {'id': 'RAID.Integrated.1-1:RAIDCurrentControllerMode', + 'current_value': ['RAID'], + 'read_only': True, + 'name': 'RAIDCurrentControllerMode', + 'possible_values': ['RAID', 'Enhanced HBA']}] + + raid_settings = defaultdict() + for sett in raid_controller_config: + raid_settings[sett.get('id')] = test_utils.create_raid_setting( + sett) + + mock_list_raid_settings.return_value = raid_settings + mock_commit_config.side_effect = ['42', '12'] mock__reset_raid_config.side_effect = [{ 'is_reboot_required': constants.RebootRequired.optional, @@ -1924,6 +2001,97 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest): @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_raid, 'list_raid_settings', autospec=True) + @mock.patch.object(drac_job, 'list_unfinished_jobs', autospec=True) + @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, + autospec=True) + @mock.patch.object(drac_raid, 'set_raid_settings', 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_mix_realtime_controller_in_ehba_mode( + self, mock__reset_raid_config, mock_commit_config, + mock_set_raid_settings, mock_validate_job_queue, + mock_list_unfinished_jobs, mock_list_raid_settings, + mock_list_raid_controllers, mock_get_drac_client): + mock_client = mock.Mock() + mock_get_drac_client.return_value = mock_client + expected_raid_config_params = ['RAID.Integrated.1-1', 'AHCI.Slot.3-1'] + mix_controllers = [{'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}, + {'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}] + + mock_list_raid_controllers.return_value = [ + test_utils.make_raid_controller(controller) for + controller in mix_controllers] + raid_controller_config = [ + {'id': 'RAID.Integrated.1-1:RAIDCurrentControllerMode', + 'current_value': ['Enhanced HBA'], + 'read_only': True, + 'name': 'RAIDCurrentControllerMode', + 'possible_values': ['RAID', 'Enhanced HBA']}, + {'id': 'AHCI.Slot.3-1:RAIDCurrentControllerMode', + 'current_value': ['RAID'], + 'read_only': True, + 'name': 'RAIDCurrentControllerMode', + 'possible_values': ['RAID', 'Enhanced HBA']}] + + raid_settings = defaultdict() + for sett in raid_controller_config: + raid_settings[sett.get('id')] = test_utils.create_raid_setting( + sett) + + mock_list_raid_settings.return_value = raid_settings + mock_list_unfinished_jobs.return_value = [] + mock_commit_config.side_effect = ['42', '12', '13'] + 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 + }] + mock_set_raid_settings.return_value = { + '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='RAID.Integrated.1-1', + reboot=False, realtime=True), + 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', '13'], + 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) @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, autospec=True) @mock.patch.object(drac_raid, 'commit_config', spec_set=True, diff --git a/ironic/tests/unit/drivers/modules/drac/utils.py b/ironic/tests/unit/drivers/modules/drac/utils.py index 44b92e77b..bc248b237 100644 --- a/ironic/tests/unit/drivers/modules/drac/utils.py +++ b/ironic/tests/unit/drivers/modules/drac/utils.py @@ -96,3 +96,8 @@ def make_physical_disk(physical_disk_dict): tuple_class = dracclient_raid.PhysicalDisk if dracclient_raid else None return dict_to_namedtuple(values=physical_disk_dict, tuple_class=tuple_class) + + +def create_raid_setting(raid_settings_dict): + """Returns the raid configuration tuple object""" + return dict_to_namedtuple(values=raid_settings_dict) diff --git a/releasenotes/notes/idrac-add-ehba-support-10b90c92b8865364.yaml b/releasenotes/notes/idrac-add-ehba-support-10b90c92b8865364.yaml new file mode 100644 index 000000000..baf41b3e1 --- /dev/null +++ b/releasenotes/notes/idrac-add-ehba-support-10b90c92b8865364.yaml @@ -0,0 +1,15 @@ +fixes: + - | + Fixes the virtual disks creation by changing PERC H740P controller + mode from `Enhanced HBA` to `RAID` in delete_configuration clean + step. + PERC H740P controllers supports RAID mode and Enhanced HBA mode. + When the controller is in Enhanced HBA, it creates single disk + RAID0 virtual disks of NON-RAID physical disks. + Hence the request for VD creation with supported RAID + fails due to no available physical disk. + This patch converts the PERC H740P RAID controllers to RAID mode + if enhanced HBA mode found enabled + See bug + `bug 2007711 <https://storyboard.openstack.org/#!/story/2007711>`_ + for more details diff --git a/zuul.d/ironic-jobs.yaml b/zuul.d/ironic-jobs.yaml index 39f88a8c0..fda847d03 100644 --- a/zuul.d/ironic-jobs.yaml +++ b/zuul.d/ironic-jobs.yaml @@ -316,6 +316,7 @@ name: ironic-tempest-ipa-partition-uefi-pxe_ipmitool description: ironic-tempest-ipa-partition-uefi-pxe_ipmitool parent: ironic-base + nodeset: openstack-single-node-focal timeout: 5400 vars: devstack_localrc: @@ -635,6 +636,7 @@ name: ironic-tempest-ipa-partition-uefi-pxe-grub2 description: Ironic tempest scenario test utilizing PXE, UEFI, and Grub2 parent: ironic-base + nodeset: openstack-single-node-focal vars: devstack_localrc: IRONIC_ENABLED_HARDWARE_TYPES: ipmi @@ -643,6 +645,8 @@ IRONIC_BOOT_MODE: uefi IRONIC_AUTOMATED_CLEAN_ENABLED: False IRONIC_DEFAULT_BOOT_OPTION: netboot + IRONIC_VM_SPECS_DISK: 10 + IRONIC_VM_SPECS_RAM: 4096 - job: # Security testing for known issues |