Redfish: Adds `create_raid_configuration` API to create raid.
This commit adds functionality to create logical drives in a SmartStorageConfig redfish systems. Co-Authored-By: Paresh Sao <paresh.sao@hpe.com> Change-Id: I679deac7e6b15d7a0de980fb16c9e283780fca80 Closes-Bug: 1716329
This commit is contained in:
parent
87a311cfa4
commit
8bda342451
|
@ -25,6 +25,7 @@ from proliantutils.redfish import redfish
|
||||||
SUPPORTED_RIS_METHODS = [
|
SUPPORTED_RIS_METHODS = [
|
||||||
'activate_license',
|
'activate_license',
|
||||||
'clear_secure_boot_keys',
|
'clear_secure_boot_keys',
|
||||||
|
'create_raid_configuration',
|
||||||
'delete_raid_configuration',
|
'delete_raid_configuration',
|
||||||
'eject_virtual_media',
|
'eject_virtual_media',
|
||||||
'get_current_bios_settings',
|
'get_current_bios_settings',
|
||||||
|
@ -69,6 +70,7 @@ SUPPORTED_RIS_METHODS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
SUPPORTED_REDFISH_METHODS = [
|
SUPPORTED_REDFISH_METHODS = [
|
||||||
|
'create_raid_configuration',
|
||||||
'delete_raid_configuration',
|
'delete_raid_configuration',
|
||||||
'get_product_name',
|
'get_product_name',
|
||||||
'get_host_post_state',
|
'get_host_post_state',
|
||||||
|
@ -652,6 +654,21 @@ class IloClient(operations.IloOperations):
|
||||||
"""
|
"""
|
||||||
return self._call_method('delete_raid_configuration')
|
return self._call_method('delete_raid_configuration')
|
||||||
|
|
||||||
|
def create_raid_configuration(self, raid_config):
|
||||||
|
"""Create the raid configuration on the hardware.
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'physical_disks': ['6I:1:5'],
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
:raises: IloError, on an error from iLO.
|
||||||
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
||||||
|
on the server
|
||||||
|
"""
|
||||||
|
return self._call_method('create_raid_configuration', raid_config)
|
||||||
|
|
||||||
def update_firmware(self, firmware_url, component_type):
|
def update_firmware(self, firmware_url, component_type):
|
||||||
"""Updates the given firmware on the server
|
"""Updates the given firmware on the server
|
||||||
|
|
||||||
|
|
|
@ -454,3 +454,18 @@ class IloOperations(object):
|
||||||
not supported on the server.
|
not supported on the server.
|
||||||
"""
|
"""
|
||||||
raise exception.IloCommandNotSupportedError(ERRMSG)
|
raise exception.IloCommandNotSupportedError(ERRMSG)
|
||||||
|
|
||||||
|
def create_raid_configuration(self, raid_config):
|
||||||
|
"""Create the raid configuration on the hardware.
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'controller':
|
||||||
|
'HPE Smart Array P408i-p SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
:raises: IloError, on an error from iLO.
|
||||||
|
:raises: IloCommandNotSupportedError, if the command is
|
||||||
|
not supported on the server.
|
||||||
|
"""
|
||||||
|
raise exception.IloCommandNotSupportedError(ERRMSG)
|
||||||
|
|
|
@ -1215,11 +1215,25 @@ class RIBCLOperations(operations.IloOperations):
|
||||||
Loops through each SmartStorageConfig controller and clears the
|
Loops through each SmartStorageConfig controller and clears the
|
||||||
raid configuration.
|
raid configuration.
|
||||||
|
|
||||||
:raises: IloError, on an error from iLO
|
|
||||||
:raises: IloCommandNotSupportedError
|
:raises: IloCommandNotSupportedError
|
||||||
"""
|
"""
|
||||||
self._raise_command_not_supported("delete_raid_configuration")
|
self._raise_command_not_supported("delete_raid_configuration")
|
||||||
|
|
||||||
|
def create_raid_configuration(self, raid_config):
|
||||||
|
"""Create the raid configuration on the hardware.
|
||||||
|
|
||||||
|
Based on user raid_config input, it will create raid
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'physical_disks': ['6I:1:5'],
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
:raises: IloCommandNotSupportedError
|
||||||
|
"""
|
||||||
|
self._raise_command_not_supported("create_raid_configuration")
|
||||||
|
|
||||||
|
|
||||||
# The below block of code is there only for backward-compatibility
|
# The below block of code is there only for backward-compatibility
|
||||||
# reasons (before commit 47608b6 for ris-support).
|
# reasons (before commit 47608b6 for ris-support).
|
||||||
|
|
|
@ -1987,7 +1987,21 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations):
|
||||||
Loops through each SmartStorageConfig controller and clears the
|
Loops through each SmartStorageConfig controller and clears the
|
||||||
raid configuration.
|
raid configuration.
|
||||||
|
|
||||||
:raises: IloError, on an error from iLO
|
|
||||||
:raises: IloCommandNotSupportedError
|
:raises: IloCommandNotSupportedError
|
||||||
"""
|
"""
|
||||||
self._raise_command_not_supported("delete_raid_configuration")
|
self._raise_command_not_supported("delete_raid_configuration")
|
||||||
|
|
||||||
|
def create_raid_configuration(self, raid_config):
|
||||||
|
"""Create the raid configuration on the hardware.
|
||||||
|
|
||||||
|
Based on user raid_config input, it will create raid
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'physical_disks': ['6I:1:5'],
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
:raises: IloCommandNotSupportedError
|
||||||
|
"""
|
||||||
|
self._raise_command_not_supported("create_raid_configuration")
|
||||||
|
|
|
@ -1168,3 +1168,19 @@ class RedfishOperations(operations.IloOperations):
|
||||||
return common_utils.apply_bios_properties_filter(
|
return common_utils.apply_bios_properties_filter(
|
||||||
settings, ilo_cons.SUPPORTED_REDFISH_BIOS_PROPERTIES)
|
settings, ilo_cons.SUPPORTED_REDFISH_BIOS_PROPERTIES)
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
def create_raid_configuration(self, raid_config):
|
||||||
|
"""Create the raid configuration on the hardware.
|
||||||
|
|
||||||
|
Based on user raid_config input, it will create raid
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'physical_disks': ['6I:1:5'],
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
:raises: IloError, on an error from iLO.
|
||||||
|
"""
|
||||||
|
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||||
|
sushy_system.create_raid(raid_config)
|
||||||
|
|
|
@ -16,6 +16,9 @@ from proliantutils import exception
|
||||||
from proliantutils import log
|
from proliantutils import log
|
||||||
from sushy.resources import base
|
from sushy.resources import base
|
||||||
|
|
||||||
|
from proliantutils.hpssa import constants
|
||||||
|
from proliantutils.hpssa import manager
|
||||||
|
|
||||||
|
|
||||||
LOG = log.get_logger(__name__)
|
LOG = log.get_logger(__name__)
|
||||||
|
|
||||||
|
@ -32,6 +35,8 @@ class HPESmartStorageConfig(base.ResourceBase):
|
||||||
|
|
||||||
logical_drives = LogicalDriveListField("LogicalDrives", default=[])
|
logical_drives = LogicalDriveListField("LogicalDrives", default=[])
|
||||||
|
|
||||||
|
location = base.Field("Location")
|
||||||
|
|
||||||
settings_uri = base.Field(["@Redfish.Settings",
|
settings_uri = base.Field(["@Redfish.Settings",
|
||||||
"SettingsObject", "@odata.id"])
|
"SettingsObject", "@odata.id"])
|
||||||
|
|
||||||
|
@ -53,3 +58,47 @@ class HPESmartStorageConfig(base.ResourceBase):
|
||||||
|
|
||||||
data = {'LogicalDrives': lds, 'DataGuard': 'Permissive'}
|
data = {'LogicalDrives': lds, 'DataGuard': 'Permissive'}
|
||||||
self._conn.put(self.settings_uri, data=data)
|
self._conn.put(self.settings_uri, data=data)
|
||||||
|
|
||||||
|
def create_raid(self, raid_config):
|
||||||
|
"""Create the raid configuration on the hardware.
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'physical_disks': ['6I:1:5'],
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
"""
|
||||||
|
manager.validate(raid_config)
|
||||||
|
logical_drives = raid_config['logical_disks']
|
||||||
|
redfish_logical_disk = []
|
||||||
|
for ld in logical_drives:
|
||||||
|
ld_attr = {"Raid": "Raid" + ld["raid_level"]}
|
||||||
|
ld_attr[
|
||||||
|
"CapacityGiB"] = -1 if ld[
|
||||||
|
"size_gb"] == "MAX" else int(ld["size_gb"])
|
||||||
|
if 'physical_disks' in ld:
|
||||||
|
ld_attr["DataDrives"] = ld["physical_disks"]
|
||||||
|
else:
|
||||||
|
datadrives = {}
|
||||||
|
if 'number_of_physical_disks' in ld:
|
||||||
|
datadrives["DataDriveCount"] = (
|
||||||
|
ld["number_of_physical_disks"])
|
||||||
|
else:
|
||||||
|
datadrives["DataDriveCount"] = (constants.
|
||||||
|
RAID_LEVEL_MIN_DISKS
|
||||||
|
[ld["raid_level"]])
|
||||||
|
if 'disk_type' in ld:
|
||||||
|
datadrives["DataDriveMediaType"] = ld["disk_type"]
|
||||||
|
if 'interface_type' in ld:
|
||||||
|
datadrives["DataDriveInterfaceType"] = ld["interface_type"]
|
||||||
|
ld_attr["DataDrives"] = datadrives
|
||||||
|
if 'volume_name' in ld:
|
||||||
|
ld_attr["LogicalDriveName"] = ld["volume_name"]
|
||||||
|
redfish_logical_disk.append(ld_attr)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"DataGuard": "Disabled",
|
||||||
|
"LogicalDrives": redfish_logical_disk
|
||||||
|
}
|
||||||
|
self._conn.put(self.settings_uri, data=data)
|
||||||
|
|
|
@ -34,6 +34,12 @@ class HPEArrayController(base.ResourceBase):
|
||||||
description = base.Field('Description')
|
description = base.Field('Description')
|
||||||
"""Description"""
|
"""Description"""
|
||||||
|
|
||||||
|
model = base.Field('Model')
|
||||||
|
"""Controller model"""
|
||||||
|
|
||||||
|
location = base.Field('Location')
|
||||||
|
"""Controller slot location"""
|
||||||
|
|
||||||
_logical_drives = None
|
_logical_drives = None
|
||||||
_physical_drives = None
|
_physical_drives = None
|
||||||
|
|
||||||
|
@ -89,6 +95,8 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase):
|
||||||
_has_rotational = None
|
_has_rotational = None
|
||||||
_logical_raid_levels = None
|
_logical_raid_levels = None
|
||||||
_drive_rotational_speed_rpm = None
|
_drive_rotational_speed_rpm = None
|
||||||
|
_get_models = None
|
||||||
|
_get_default_controller = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _resource_type(self):
|
def _resource_type(self):
|
||||||
|
@ -166,6 +174,34 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase):
|
||||||
member.physical_drives.drive_rotational_speed_rpm)
|
member.physical_drives.drive_rotational_speed_rpm)
|
||||||
return self._drive_rotational_speed_rpm
|
return self._drive_rotational_speed_rpm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_default_controller(self):
|
||||||
|
"""Gets default array controller
|
||||||
|
|
||||||
|
:returns default array controller
|
||||||
|
"""
|
||||||
|
if self._get_default_controller is None:
|
||||||
|
self._get_default_controller = self.get_members()[0]
|
||||||
|
return self._get_default_controller
|
||||||
|
|
||||||
|
def array_controller_by_location(self, location):
|
||||||
|
"""Returns array controller instance by location
|
||||||
|
|
||||||
|
:returns Instance of array controller
|
||||||
|
"""
|
||||||
|
for member in self.get_members():
|
||||||
|
if member.location == location:
|
||||||
|
return member
|
||||||
|
|
||||||
|
def array_controller_by_model(self, model):
|
||||||
|
"""Returns array controller instance by model
|
||||||
|
|
||||||
|
:returns Instance of array controller
|
||||||
|
"""
|
||||||
|
for member in self.get_members():
|
||||||
|
if member.model == model:
|
||||||
|
return member
|
||||||
|
|
||||||
def _do_refresh(self, force):
|
def _do_refresh(self, force):
|
||||||
"""Do custom resource specific refresh activities
|
"""Do custom resource specific refresh activities
|
||||||
|
|
||||||
|
@ -179,3 +215,5 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase):
|
||||||
self._has_rotational = None
|
self._has_rotational = None
|
||||||
self._logical_raid_levels = None
|
self._logical_raid_levels = None
|
||||||
self._drive_rotational_speed_rpm = None
|
self._drive_rotational_speed_rpm = None
|
||||||
|
self._get_models = None
|
||||||
|
self._get_default_controller = None
|
||||||
|
|
|
@ -337,6 +337,18 @@ class HPESystem(system.System):
|
||||||
HPESmartStorageConfig(self._conn, smart_storage_config_url,
|
HPESmartStorageConfig(self._conn, smart_storage_config_url,
|
||||||
redfish_version=self.redfish_version))
|
redfish_version=self.redfish_version))
|
||||||
|
|
||||||
|
def _get_smart_storage_config_by_controller_model(self, controller_model):
|
||||||
|
"""Returns a SmartStorageConfig Instance for controller by model.
|
||||||
|
|
||||||
|
:returns: SmartStorageConfig Instance for controller
|
||||||
|
"""
|
||||||
|
ac = self.smart_storage.array_controllers.array_controller_by_model(
|
||||||
|
controller_model)
|
||||||
|
for ssc_id in self.smart_storage_config_identities:
|
||||||
|
ssc_obj = self.get_smart_storage_config(ssc_id)
|
||||||
|
if ac.location == ssc_obj.location:
|
||||||
|
return ssc_obj
|
||||||
|
|
||||||
def check_smart_storage_config_ids(self):
|
def check_smart_storage_config_ids(self):
|
||||||
"""Check SmartStorageConfig controllers is there in hardware.
|
"""Check SmartStorageConfig controllers is there in hardware.
|
||||||
|
|
||||||
|
@ -378,3 +390,67 @@ class HPESystem(system.System):
|
||||||
msg = ('No logical drives are found in any controllers. Nothing '
|
msg = ('No logical drives are found in any controllers. Nothing '
|
||||||
'to delete.')
|
'to delete.')
|
||||||
raise exception.IloLogicalDriveNotFoundError(msg)
|
raise exception.IloLogicalDriveNotFoundError(msg)
|
||||||
|
|
||||||
|
def _parse_raid_config_data(self, raid_config):
|
||||||
|
"""It will parse raid config data based on raid controllers
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'controller':
|
||||||
|
'HPE Smart Array P408i-a SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
:returns: A dictionary of controllers, each containing list of
|
||||||
|
their respected logical drives.
|
||||||
|
"""
|
||||||
|
default = (
|
||||||
|
self.smart_storage.array_controllers.get_default_controller.model)
|
||||||
|
controllers = {default: []}
|
||||||
|
for ld in raid_config['logical_disks']:
|
||||||
|
if 'controller' not in ld.keys():
|
||||||
|
controllers[default].append(ld)
|
||||||
|
else:
|
||||||
|
ctrl = ld['controller']
|
||||||
|
if ctrl not in controllers:
|
||||||
|
controllers[ctrl] = []
|
||||||
|
controllers[ctrl].append(ld)
|
||||||
|
return controllers
|
||||||
|
|
||||||
|
def create_raid(self, raid_config):
|
||||||
|
"""Create the raid configuration on the hardware.
|
||||||
|
|
||||||
|
:param raid_config: A dictionary containing target raid configuration
|
||||||
|
data. This data stucture should be as follows:
|
||||||
|
raid_config = {'logical_disks': [{'raid_level': 1,
|
||||||
|
'size_gb': 100, 'physical_disks': ['6I:1:5'],
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'},
|
||||||
|
<info-for-logical-disk-2>]}
|
||||||
|
:raises: IloError, on an error from iLO.
|
||||||
|
"""
|
||||||
|
self.check_smart_storage_config_ids()
|
||||||
|
any_exceptions = []
|
||||||
|
controllers = self._parse_raid_config_data(raid_config)
|
||||||
|
# Creating raid on rest of the controllers
|
||||||
|
for controller in controllers:
|
||||||
|
try:
|
||||||
|
config = {'logical_disks': controllers[controller]}
|
||||||
|
ssc_obj = (
|
||||||
|
self._get_smart_storage_config_by_controller_model(
|
||||||
|
controller))
|
||||||
|
if ssc_obj:
|
||||||
|
ssc_obj.create_raid(config)
|
||||||
|
else:
|
||||||
|
members = (
|
||||||
|
self.smart_storage.array_controllers.get_members())
|
||||||
|
models = [member.model for member in members]
|
||||||
|
msg = ('Controller not found. Available controllers are: '
|
||||||
|
'%(models)s' % {'models': models})
|
||||||
|
any_exceptions.append((controller, msg))
|
||||||
|
except sushy.exceptions.SushyError as e:
|
||||||
|
any_exceptions.append((controller, str(e)))
|
||||||
|
|
||||||
|
if any_exceptions:
|
||||||
|
msg = ('The Redfish controller failed to create the '
|
||||||
|
'raid configuration for one or more controllers with '
|
||||||
|
'Error: %(error)s' % {'error': str(any_exceptions)})
|
||||||
|
raise exception.IloError(msg)
|
||||||
|
|
|
@ -779,6 +779,38 @@ class IloClientTestCase(testtools.TestCase):
|
||||||
'on ProLiant DL380 G8',
|
'on ProLiant DL380 G8',
|
||||||
self.client.delete_raid_configuration)
|
self.client.delete_raid_configuration)
|
||||||
|
|
||||||
|
@mock.patch.object(client.IloClient, '_call_method')
|
||||||
|
def test_create_raid_configuration(self, call_mock):
|
||||||
|
ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True}
|
||||||
|
raid_config = {"logical_disks": [ld1]}
|
||||||
|
self.client.create_raid_configuration(raid_config)
|
||||||
|
call_mock.assert_called_once_with('create_raid_configuration',
|
||||||
|
raid_config)
|
||||||
|
|
||||||
|
@mock.patch.object(ris.RISOperations, 'get_product_name')
|
||||||
|
def test_create_raid_configuration_gen9(self, get_product_mock):
|
||||||
|
self.client.model = 'Gen9'
|
||||||
|
ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True}
|
||||||
|
raid_config = {"logical_disks": [ld1]}
|
||||||
|
get_product_mock.return_value = 'ProLiant BL460c Gen9'
|
||||||
|
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
|
||||||
|
'`create_raid_configuration` is not supported '
|
||||||
|
'on ProLiant BL460c Gen9',
|
||||||
|
self.client.create_raid_configuration,
|
||||||
|
raid_config)
|
||||||
|
|
||||||
|
@mock.patch.object(ribcl.RIBCLOperations, 'get_product_name')
|
||||||
|
def test_create_raid_configuration_gen8(self, get_product_mock):
|
||||||
|
self.client.model = 'Gen8'
|
||||||
|
ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True}
|
||||||
|
raid_config = {"logical_disks": [ld1]}
|
||||||
|
get_product_mock.return_value = 'ProLiant DL380 G8'
|
||||||
|
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
|
||||||
|
'`create_raid_configuration` is not supported '
|
||||||
|
'on ProLiant DL380 G8',
|
||||||
|
self.client.create_raid_configuration,
|
||||||
|
raid_config)
|
||||||
|
|
||||||
@mock.patch.object(ris.RISOperations, 'eject_virtual_media')
|
@mock.patch.object(ris.RISOperations, 'eject_virtual_media')
|
||||||
def test_eject_virtual_media_gen9(self, eject_virtual_media_mock):
|
def test_eject_virtual_media_gen9(self, eject_virtual_media_mock):
|
||||||
self.client.model = 'Gen9'
|
self.client.model = 'Gen9'
|
||||||
|
|
|
@ -1062,6 +1062,15 @@ class IloRibclTestCaseBeforeRisSupport(unittest.TestCase):
|
||||||
'ProLiant DL380 G7',
|
'ProLiant DL380 G7',
|
||||||
self.ilo.delete_raid_configuration)
|
self.ilo.delete_raid_configuration)
|
||||||
|
|
||||||
|
@mock.patch.object(ribcl.RIBCLOperations, 'get_product_name')
|
||||||
|
def test_create_raid_configuration(self, product_name_mock):
|
||||||
|
ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True}
|
||||||
|
raid_config = {"logical_disks": [ld1]}
|
||||||
|
product_name_mock.return_value = constants.GET_PRODUCT_NAME
|
||||||
|
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
|
||||||
|
'ProLiant DL380 G7',
|
||||||
|
self.ilo.create_raid_configuration,
|
||||||
|
raid_config)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -2558,3 +2558,13 @@ class TestRISOperationsPrivateMethods(testtools.TestCase):
|
||||||
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
|
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
|
||||||
'ProLiant BL460c Gen9',
|
'ProLiant BL460c Gen9',
|
||||||
self.client.delete_raid_configuration)
|
self.client.delete_raid_configuration)
|
||||||
|
|
||||||
|
@mock.patch.object(ris.RISOperations, 'get_product_name')
|
||||||
|
def test_create_raid_configuration(self, product_name_mock):
|
||||||
|
ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True}
|
||||||
|
raid_config = {"logical_disks": [ld1]}
|
||||||
|
product_name_mock.return_value = 'ProLiant BL460c Gen9'
|
||||||
|
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
|
||||||
|
'ProLiant BL460c Gen9',
|
||||||
|
self.client.create_raid_configuration,
|
||||||
|
raid_config)
|
||||||
|
|
|
@ -38,6 +38,9 @@ class HPEArrayControllerTestCase(testtools.TestCase):
|
||||||
def test__parse_attributes(self):
|
def test__parse_attributes(self):
|
||||||
self.sys_stor._parse_attributes()
|
self.sys_stor._parse_attributes()
|
||||||
self.assertEqual('1.0.2', self.sys_stor.redfish_version)
|
self.assertEqual('1.0.2', self.sys_stor.redfish_version)
|
||||||
|
self.assertEqual('HPE Smart Array P408i-a SR Gen10',
|
||||||
|
self.sys_stor.model)
|
||||||
|
self.assertEqual('Slot 0', self.sys_stor.location)
|
||||||
|
|
||||||
def test_logical_drives(self):
|
def test_logical_drives(self):
|
||||||
log_coll = None
|
log_coll = None
|
||||||
|
@ -252,3 +255,33 @@ class HPEArrayControllerCollectionTestCase(testtools.TestCase):
|
||||||
expected = set([10000])
|
expected = set([10000])
|
||||||
self.assertEqual(expected,
|
self.assertEqual(expected,
|
||||||
self.sys_stor_col.drive_rotational_speed_rpm)
|
self.sys_stor_col.drive_rotational_speed_rpm)
|
||||||
|
|
||||||
|
def test_get_default_controller(self):
|
||||||
|
self.assertIsNone(self.sys_stor_col._get_default_controller)
|
||||||
|
self.conn.get.return_value.json.reset_mock()
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller.json', 'r') as f:
|
||||||
|
ac_json = json.loads(f.read())
|
||||||
|
self.conn.get.return_value.json.return_value = ac_json
|
||||||
|
result_location = self.sys_stor_col.get_default_controller.location
|
||||||
|
self.assertEqual(result_location, 'Slot 0')
|
||||||
|
|
||||||
|
def test_array_controller_by_location(self):
|
||||||
|
self.conn.get.return_value.json.reset_mock()
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller.json', 'r') as f:
|
||||||
|
ac_json = json.loads(f.read())
|
||||||
|
self.conn.get.return_value.json.return_value = ac_json
|
||||||
|
model_result = (
|
||||||
|
self.sys_stor_col.array_controller_by_location('Slot 0').model)
|
||||||
|
self.assertEqual(model_result, 'HPE Smart Array P408i-a SR Gen10')
|
||||||
|
|
||||||
|
def test_array_controller_by_model(self):
|
||||||
|
self.conn.get.return_value.json.reset_mock()
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller.json', 'r') as f:
|
||||||
|
ac_json = json.loads(f.read())
|
||||||
|
self.conn.get.return_value.json.return_value = ac_json
|
||||||
|
model = 'HPE Smart Array P408i-a SR Gen10'
|
||||||
|
result_model = self.sys_stor_col.array_controller_by_model(model).model
|
||||||
|
self.assertEqual(result_model, model)
|
||||||
|
|
|
@ -19,6 +19,7 @@ import mock
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from proliantutils import exception
|
from proliantutils import exception
|
||||||
|
from proliantutils.hpssa import manager
|
||||||
from proliantutils.redfish.resources.system import smart_storage_config
|
from proliantutils.redfish.resources.system import smart_storage_config
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ class HPESmartStorageConfigTestCase(testtools.TestCase):
|
||||||
|
|
||||||
def test_attributes(self):
|
def test_attributes(self):
|
||||||
self.assertEqual('smartstorageconfig', self.ssc_inst.controller_id)
|
self.assertEqual('smartstorageconfig', self.ssc_inst.controller_id)
|
||||||
|
self.assertEqual('Slot 0', self.ssc_inst.location)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'600508B1001C045A9BAAC9F4F49498AE',
|
'600508B1001C045A9BAAC9F4F49498AE',
|
||||||
self.ssc_inst.logical_drives[0].volume_unique_identifier)
|
self.ssc_inst.logical_drives[0].volume_unique_identifier)
|
||||||
|
@ -61,3 +63,42 @@ class HPESmartStorageConfigTestCase(testtools.TestCase):
|
||||||
return_value=[])
|
return_value=[])
|
||||||
self.assertRaises(exception.IloLogicalDriveNotFoundError,
|
self.assertRaises(exception.IloLogicalDriveNotFoundError,
|
||||||
self.ssc_inst.delete_raid)
|
self.ssc_inst.delete_raid)
|
||||||
|
|
||||||
|
@mock.patch.object(manager, 'validate', autospec=True)
|
||||||
|
def test_create_raid(self, validate_mock):
|
||||||
|
settings_uri = "/redfish/v1/systems/1/smartstorageconfig/settings/"
|
||||||
|
ld1 = {'size_gb': 50,
|
||||||
|
'raid_level': '1',
|
||||||
|
'physical_disks': ['5I:1:1', '5I:1:2']}
|
||||||
|
raid_config = {'logical_disks': [ld1]}
|
||||||
|
validate_mock.return_value = True
|
||||||
|
self.ssc_inst.create_raid(raid_config)
|
||||||
|
data = {"DataGuard": "Disabled",
|
||||||
|
"LogicalDrives": [
|
||||||
|
{"CapacityGiB": 50, "Raid": "Raid1",
|
||||||
|
"DataDrives": ['5I:1:1', '5I:1:2']}]}
|
||||||
|
validate_mock.assert_called_once_with(raid_config)
|
||||||
|
self.ssc_inst._conn.put.assert_called_once_with(settings_uri,
|
||||||
|
data=data)
|
||||||
|
|
||||||
|
@mock.patch.object(manager, 'validate', autospec=True)
|
||||||
|
def test_create_raid_multiple_logical_drives(self, validate_mock):
|
||||||
|
settings_uri = "/redfish/v1/systems/1/smartstorageconfig/settings/"
|
||||||
|
ld1 = {'size_gb': 50,
|
||||||
|
'raid_level': '0',
|
||||||
|
'physical_disks': ['5I:1:1']}
|
||||||
|
ld2 = {'size_gb': 100,
|
||||||
|
'raid_level': '1',
|
||||||
|
'number_of_physical_disks': 2}
|
||||||
|
raid_config = {'logical_disks': [ld1, ld2]}
|
||||||
|
validate_mock.return_value = True
|
||||||
|
self.ssc_inst.create_raid(raid_config)
|
||||||
|
data = {"DataGuard": "Disabled",
|
||||||
|
"LogicalDrives": [
|
||||||
|
{"CapacityGiB": 50, "Raid": "Raid0",
|
||||||
|
"DataDrives": ['5I:1:1']},
|
||||||
|
{"CapacityGiB": 100, "Raid": "Raid1",
|
||||||
|
"DataDrives": {"DataDriveCount": 2}}]}
|
||||||
|
validate_mock.assert_called_once_with(raid_config)
|
||||||
|
self.ssc_inst._conn.put.assert_called_once_with(settings_uri,
|
||||||
|
data=data)
|
||||||
|
|
|
@ -26,6 +26,7 @@ from proliantutils.redfish.resources.system import ethernet_interface
|
||||||
from proliantutils.redfish.resources.system import memory
|
from proliantutils.redfish.resources.system import memory
|
||||||
from proliantutils.redfish.resources.system import secure_boot
|
from proliantutils.redfish.resources.system import secure_boot
|
||||||
from proliantutils.redfish.resources.system import smart_storage_config
|
from proliantutils.redfish.resources.system import smart_storage_config
|
||||||
|
from proliantutils.redfish.resources.system.storage import array_controller
|
||||||
from proliantutils.redfish.resources.system.storage import simple_storage
|
from proliantutils.redfish.resources.system.storage import simple_storage
|
||||||
from proliantutils.redfish.resources.system.storage import smart_storage
|
from proliantutils.redfish.resources.system.storage import smart_storage
|
||||||
from proliantutils.redfish.resources.system.storage import storage
|
from proliantutils.redfish.resources.system.storage import storage
|
||||||
|
@ -560,3 +561,126 @@ class HPESystemTestCase(testtools.TestCase):
|
||||||
"The Redfish controller failed to get the SmartStorageConfig "
|
"The Redfish controller failed to get the SmartStorageConfig "
|
||||||
"controller configurations",
|
"controller configurations",
|
||||||
self.sys_inst.check_smart_storage_config_ids)
|
self.sys_inst.check_smart_storage_config_ids)
|
||||||
|
|
||||||
|
@mock.patch.object(system.HPESystem, 'check_smart_storage_config_ids')
|
||||||
|
@mock.patch.object(system.HPESystem, '_parse_raid_config_data')
|
||||||
|
@mock.patch.object(system.HPESystem,
|
||||||
|
'_get_smart_storage_config_by_controller_model')
|
||||||
|
def test_create_raid(self, get_smart_storage_config_model_mock,
|
||||||
|
parse_raid_config_mock,
|
||||||
|
check_smart_storage_config_ids_mock):
|
||||||
|
ld1 = {'raid_level': '0', 'is_root_volume': True,
|
||||||
|
'size_gb': 150,
|
||||||
|
'controller': 'HPE Smart Array P408i-p SR Gen10'}
|
||||||
|
ld2 = {'raid_level': '1', 'size_gb': 200,
|
||||||
|
'controller': 'HPE Smart Array P408i-p SR Gen10'}
|
||||||
|
raid_config = {'logical_disks': [ld1, ld2]}
|
||||||
|
parse_data = {'HPE Smart Array P408i-p SR Gen10': [ld1, ld2]}
|
||||||
|
parse_raid_config_mock.return_value = parse_data
|
||||||
|
check_smart_storage_config_ids_mock.return_value = None
|
||||||
|
self.sys_inst.create_raid(raid_config)
|
||||||
|
get_smart_storage_config_model_mock.assert_called_once_with(
|
||||||
|
'HPE Smart Array P408i-p SR Gen10')
|
||||||
|
(get_smart_storage_config_model_mock.return_value.
|
||||||
|
create_raid.assert_called_once_with(raid_config))
|
||||||
|
|
||||||
|
@mock.patch.object(system.HPESystem, 'check_smart_storage_config_ids')
|
||||||
|
@mock.patch.object(system.HPESystem, '_parse_raid_config_data')
|
||||||
|
@mock.patch.object(system.HPESystem,
|
||||||
|
'_get_smart_storage_config_by_controller_model')
|
||||||
|
def test_create_raid_controller_not_found(
|
||||||
|
self, get_smart_storage_config_model_mock, parse_raid_config_mock,
|
||||||
|
check_smart_storage_config_ids_mock):
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/smart_storage.json', 'r') as f:
|
||||||
|
ss_json = json.loads(f.read())
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller_collection.json', 'r') as f:
|
||||||
|
acc_json = json.loads(f.read())
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller.json', 'r') as f:
|
||||||
|
ac_json = json.loads(f.read())
|
||||||
|
self.conn.get.return_value.json.reset_mock()
|
||||||
|
self.conn.get.return_value.json.side_effect = [
|
||||||
|
ss_json, acc_json, ac_json]
|
||||||
|
ld1 = {'raid_level': '1', 'size_gb': 200,
|
||||||
|
'controller': 'HPE Gen10 Controller'}
|
||||||
|
raid_config = {'logical_disks': [ld1]}
|
||||||
|
parse_data = {'HPE Gen10 Controller': [ld1]}
|
||||||
|
parse_raid_config_mock.return_value = parse_data
|
||||||
|
check_smart_storage_config_ids_mock.return_value = None
|
||||||
|
get_smart_storage_config_model_mock.return_value = None
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
exception.IloError,
|
||||||
|
"The Redfish controller failed to create the raid "
|
||||||
|
"configuration for one or more controllers with",
|
||||||
|
self.sys_inst.create_raid, raid_config)
|
||||||
|
|
||||||
|
@mock.patch.object(system.HPESystem, 'check_smart_storage_config_ids')
|
||||||
|
@mock.patch.object(system.HPESystem, '_parse_raid_config_data')
|
||||||
|
@mock.patch.object(system.HPESystem,
|
||||||
|
'_get_smart_storage_config_by_controller_model')
|
||||||
|
def test_create_raid_failed(self, get_smart_storage_config_model_mock,
|
||||||
|
parse_raid_config_mock,
|
||||||
|
check_smart_storage_config_ids_mock):
|
||||||
|
ld1 = {'raid_level': '0', 'is_root_volume': True,
|
||||||
|
'size_gb': 150,
|
||||||
|
'controller': 'HPE Smart Array P408i-p SR Gen10'}
|
||||||
|
ld2 = {'raid_level': '1', 'size_gb': 200,
|
||||||
|
'controller': 'HPE Smart Array P408i-p SR Gen10'}
|
||||||
|
raid_config = {'logical_disks': [ld1, ld2]}
|
||||||
|
parse_data = {'HPE Smart Array P408i-p SR Gen10': [ld1, ld2]}
|
||||||
|
check_smart_storage_config_ids_mock.return_value = None
|
||||||
|
parse_raid_config_mock.return_value = parse_data
|
||||||
|
(get_smart_storage_config_model_mock.
|
||||||
|
return_value.create_raid.side_effect) = sushy.exceptions.SushyError
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
exception.IloError,
|
||||||
|
"The Redfish controller failed to create the "
|
||||||
|
"raid configuration for one or more controllers with Error:",
|
||||||
|
self.sys_inst.create_raid, raid_config)
|
||||||
|
|
||||||
|
def test__parse_raid_config_data(self):
|
||||||
|
ld1 = {'raid_level': '0', 'is_root_volume': True,
|
||||||
|
'size_gb': 150,
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'}
|
||||||
|
ld2 = {'raid_level': '1', 'size_gb': 200,
|
||||||
|
'controller': 'HPE Smart Array P408i-a SR Gen10'}
|
||||||
|
raid_config = {'logical_disks': [ld1, ld2]}
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/smart_storage.json', 'r') as f:
|
||||||
|
ss_json = json.loads(f.read())
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller_collection.json', 'r') as f:
|
||||||
|
acc_json = json.loads(f.read())
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller.json', 'r') as f:
|
||||||
|
ac_json = json.loads(f.read())
|
||||||
|
self.conn.get.return_value.json.reset_mock()
|
||||||
|
self.conn.get.return_value.json.side_effect = [
|
||||||
|
ss_json, acc_json, ac_json]
|
||||||
|
expected = {'HPE Smart Array P408i-a SR Gen10': [ld1, ld2]}
|
||||||
|
self.assertEqual(expected,
|
||||||
|
self.sys_inst._parse_raid_config_data(raid_config))
|
||||||
|
|
||||||
|
@mock.patch.object(array_controller.HPEArrayControllerCollection,
|
||||||
|
'array_controller_by_model')
|
||||||
|
@mock.patch.object(system.HPESystem, 'get_smart_storage_config')
|
||||||
|
def test__get_smart_storage_config_by_controller_model(
|
||||||
|
self, get_smart_storage_config_mock,
|
||||||
|
array_controller_by_model_mock):
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/smart_storage.json', 'r') as f:
|
||||||
|
ss_json = json.loads(f.read())
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/array_controller_collection.json', 'r') as f:
|
||||||
|
acc_json = json.loads(f.read())
|
||||||
|
self.conn.get.return_value.json.reset_mock()
|
||||||
|
self.conn.get.return_value.json.side_effect = [ss_json, acc_json]
|
||||||
|
type(array_controller_by_model_mock.return_value).location = 'Slot 0'
|
||||||
|
ssc_obj_mock = mock.Mock(location='Slot 0')
|
||||||
|
get_smart_storage_config_mock.return_value = ssc_obj_mock
|
||||||
|
self.assertEqual(
|
||||||
|
ssc_obj_mock.location,
|
||||||
|
self.sys_inst._get_smart_storage_config_by_controller_model(
|
||||||
|
'HPE Smart Array P408i-a SR Gen10').location)
|
||||||
|
|
|
@ -1675,3 +1675,11 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
||||||
def test_delete_raid_configuration(self, get_system_mock):
|
def test_delete_raid_configuration(self, get_system_mock):
|
||||||
self.rf_client.delete_raid_configuration()
|
self.rf_client.delete_raid_configuration()
|
||||||
get_system_mock.return_value.delete_raid.assert_called_once_with()
|
get_system_mock.return_value.delete_raid.assert_called_once_with()
|
||||||
|
|
||||||
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
def test_create_raid_configuration(self, get_system_mock):
|
||||||
|
ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True}
|
||||||
|
raid_config = {"logical_disks": [ld1]}
|
||||||
|
self.rf_client.create_raid_configuration(raid_config)
|
||||||
|
get_system_mock.return_value.create_raid.assert_called_once_with(
|
||||||
|
raid_config)
|
||||||
|
|
Loading…
Reference in New Issue