Fix Redfish RAID interface_type physical disk hint

Map between sushy's protocol and RAID config schema's
interface_type constants.

Not supporting `scsi` from RAID schema as Redfish does
not support it in Protocol property.

Change-Id: Ic042f0a87ff0723f313adb3888c24c5963624182
This commit is contained in:
Aija Jauntēva 2021-06-09 04:57:24 -04:00
parent 416a0951c8
commit c41ed8414d
6 changed files with 168 additions and 3 deletions

View File

@ -22,6 +22,16 @@ from ironic.common.i18n import _
from ironic.common import utils
SATA = 'sata'
"Serial AT Attachment"
SCSI = 'scsi'
"Small Computer System Interface"
SAS = 'sas'
"Serial Attached SCSI"
def _check_and_return_root_volumes(raid_config):
"""Returns root logical disks after validating RAID config.

View File

@ -22,6 +22,7 @@ from oslo_utils import units
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import raid
from ironic.common import states
from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils
@ -90,6 +91,12 @@ RAID_LEVELS = {
sushy = importutils.try_import('sushy')
if sushy:
PROTOCOL_MAP = {
sushy.PROTOCOL_TYPE_SAS: raid.SAS,
sushy.PROTOCOL_TYPE_SATA: raid.SATA
}
def convert_drive_units(logical_disks, node):
"""Convert size in logical_disks from gb to bytes"""
@ -393,7 +400,7 @@ def _assign_disks_to_volume(logical_disks, physical_disks_by_type,
continue
if ('interface_type' in logical_disk
and logical_disk['interface_type'].lower()
!= protocol.lower()):
!= PROTOCOL_MAP[protocol].lower()):
continue
# filter out disks without free disk space
@ -715,6 +722,24 @@ class RedfishRAID(base.RAIDInterface):
self._validate_vendor(task)
super(RedfishRAID, self).validate(task)
def validate_raid_config(self, task, raid_config):
"""Validates the given RAID configuration.
:param task: A TaskManager instance.
:param raid_config: The RAID configuration to validate.
:raises: InvalidParameterValue, if the RAID configuration is invalid.
"""
super(RedfishRAID, self).validate_raid_config(task, raid_config)
# Check if any interface_type is scsi that is not supported by Redfish
scsi_disks = ([x for x in raid_config['logical_disks']
if x.get('interface_type') == raid.SCSI])
if len(scsi_disks) > 0:
raise exception.InvalidParameterValue(
_('interface type `scsi` not supported by Redfish RAID'))
@base.deploy_step(priority=0,
argsinfo=base.RAID_APPLY_CONFIGURATION_ARGSINFO)
def apply_configuration(self, task, raid_config, create_root_volume=True,

View File

@ -89,11 +89,13 @@ class RedfishRAIDTestCase(db_base.DbTestCase):
self.drive_id4]:
mock_drives.append(_mock_drive(
identity=i, block_size_bytes=512, capacity_bytes=899527000000,
media_type='HDD', name='Drive', protocol='SAS'))
media_type='HDD', name='Drive',
protocol='Serial Attached SCSI'))
for i in [self.drive_id5, self.drive_id6, self.drive_id7]:
mock_drives.append(_mock_drive(
identity=i, block_size_bytes=512, capacity_bytes=479559942144,
media_type='SSD', name='Solid State Drive', protocol='SATA'))
media_type='SSD', name='Solid State Drive',
protocol='Serial AT Attachment'))
self.mock_storage.drives = mock_drives
mock_identifier = mock.Mock()
mock_identifier.durable_name = '345C59DBD970859C'
@ -767,6 +769,79 @@ class RedfishRAIDTestCase(db_base.DbTestCase):
task.driver.raid.create_configuration(task)
"""
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot, 'prepare_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(deploy_utils, 'build_agent_options', autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
@mock.patch.object(deploy_utils, 'get_async_step_return_state',
autospec=True)
@mock.patch.object(deploy_utils, 'set_async_step_flags', autospec=True)
def test_create_config_interface_type(
self,
mock_set_async_step_flags,
mock_get_async_step_return_state,
mock_node_power_action,
mock_build_agent_options,
mock_prepare_ramdisk,
mock_get_system):
target_raid_config = {
'logical_disks': [
{
'size_gb': 100,
'raid_level': '5',
'is_root_volume': True,
'interface_type': 'sata'
},
{
'size_gb': 500,
'raid_level': '1',
'interface_type': 'sas'
}
]
}
mock_get_system.return_value.storage.get_members.return_value = [
self.mock_storage]
self.node.target_raid_config = target_raid_config
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.driver.raid.create_configuration(task)
pre = '/redfish/v1/Systems/1/Storage/1/Drives/'
expected_payload1 = {
'Encrypted': False,
'VolumeType': 'StripedWithParity',
'RAIDType': 'RAID5',
'CapacityBytes': 107374182400,
'Links': {
'Drives': [
{'@odata.id': pre + self.drive_id5},
{'@odata.id': pre + self.drive_id6},
{'@odata.id': pre + self.drive_id7}
]
}
}
expected_payload2 = {
'Encrypted': False,
'VolumeType': 'Mirrored',
'RAIDType': 'RAID1',
'CapacityBytes': 536870912000,
'Links': {
'Drives': [
{'@odata.id': pre + self.drive_id1},
{'@odata.id': pre + self.drive_id2}
]
}
}
self.assertEqual(
self.mock_storage.volumes.create.call_count, 2)
self.mock_storage.volumes.create.assert_any_call(
expected_payload1, apply_time=None
)
self.mock_storage.volumes.create.assert_any_call(
expected_payload2, apply_time=None
)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot, 'prepare_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(deploy_utils, 'build_agent_options', autospec=True)
@ -900,6 +975,50 @@ class RedfishRAIDTestCase(db_base.DbTestCase):
"with vendor Dell.Inc.",
task.driver.raid.validate, task)
def test_validate_raid_config(self, mock_get_system):
raid_config = {
'logical_disks': [
{
'size_gb': 500,
'raid_level': '1+0',
'interface_type': 'sata'
},
{
'size_gb': 100,
'raid_level': '5',
'is_root_volume': True,
}
]
}
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.driver.raid.validate_raid_config(task, raid_config)
def test_validate_raid_config_scsi(self, mock_get_system):
raid_config = {
'logical_disks': [
{
'size_gb': 500,
'raid_level': '1+0',
'interface_type': 'sata'
},
{
'size_gb': 100,
'raid_level': '5',
'is_root_volume': True,
'interface_type': 'scsi'
}
]
}
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertRaisesRegex(
exception.InvalidParameterValue,
"interface type `scsi` not supported by Redfish RAID",
task.driver.raid.validate_raid_config, task, raid_config)
def test_get_physical_disks(self, mock_get_system):
nonraid_controller = mock.Mock()
nonraid_controller.raid_types = []

View File

@ -150,6 +150,9 @@ SUSHY_SPEC = (
'PROCESSOR_ARCH_ARM',
'PROCESSOR_ARCH_MIPS',
'PROCESSOR_ARCH_OEM',
'PROTOCOL_TYPE_iSCSI',
'PROTOCOL_TYPE_SAS',
'PROTOCOL_TYPE_SATA',
'STATE_ENABLED',
'STATE_DISABLED',
'STATE_ABSENT',

View File

@ -212,6 +212,9 @@ if not sushy:
PROCESSOR_ARCH_ARM='ARM',
PROCESSOR_ARCH_MIPS='MIPS',
PROCESSOR_ARCH_OEM='OEM-defined',
PROTOCOL_TYPE_iSCSI='Internet SCSI',
PROTOCOL_TYPE_SAS='Serial Attached SCSI',
PROTOCOL_TYPE_SATA='Serial AT Attachment',
STATE_ENABLED='enabled',
STATE_DISABLED='disabled',
STATE_ABSENT='absent',

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixes configuring Redfish RAID using ``interface_type`` when error "failed
to find matching physical disks for all logical disks" occurs.