diff --git a/proliantutils/redfish/resources/system/storage/array_controller.py b/proliantutils/redfish/resources/system/storage/array_controller.py index 454e07ab..fbb55e58 100644 --- a/proliantutils/redfish/resources/system/storage/array_controller.py +++ b/proliantutils/redfish/resources/system/storage/array_controller.py @@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__) class HPEArrayController(base.ResourceBase): + """This class represents the HPEArrayControllers resource""" identity = base.Field('Id') """The identity string""" @@ -38,6 +39,8 @@ class HPEArrayController(base.ResourceBase): @property def logical_drives(self): + """Gets the resource HPELogicalDriveCollection of ArrayControllers""" + if self._logical_drives is None: self._logical_drives = ( logical_drive.HPELogicalDriveCollection( @@ -48,6 +51,8 @@ class HPEArrayController(base.ResourceBase): @property def physical_drives(self): + """Gets the resource HPEPhysicalDriveCollection of ArrayControllers""" + if self._physical_drives is None: self._physical_drives = ( physical_drive.HPEPhysicalDriveCollection( @@ -63,6 +68,7 @@ class HPEArrayController(base.ResourceBase): class HPEArrayControllerCollection(base.ResourceCollectionBase): + """This class represents the collection of HPEArrayControllers""" _logical_drives_maximum_size_mib = None _physical_drives_maximum_size_mib = None diff --git a/proliantutils/redfish/resources/system/storage/constants.py b/proliantutils/redfish/resources/system/storage/constants.py new file mode 100644 index 00000000..cc20af68 --- /dev/null +++ b/proliantutils/redfish/resources/system/storage/constants.py @@ -0,0 +1,53 @@ +# Copyright 2017 Hewlett Packard Enterprise Development LP +# +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Supported device protocols +# Taken from http://redfish.dmtf.org/schemas/Storage.v1_2_0.json +PROTOCOL_PCIe = "PCI Express (Vendor Proprietary)." +PROTOCOL_AHCI = "Advanced Host Controller Interface." +PROTOCOL_UHCI = "Universal Host Controller Interface." +PROTOCOL_SAS = "Serial Attached SCSI." +PROTOCOL_SATA = "Serial AT Attachment." +PROTOCOL_USB = "Universal Serial Bus." +PROTOCOL_NVMe = "Non-Volatile Memory Express." +PROTOCOL_FC = "Fibre Channel." +PROTOCOL_iSCSI = "Internet SCSI." +PROTOCOL_FCoE = "Fibre Channel over Ethernet." +PROTOCOL_FCP = "Fibre Channel Protocol for SCSI." +PROTOCOL_FICON = "FIbre CONnection (FICON)." +PROTOCOL_NVMeOverFabrics = "NVMe over Fabrics." +PROTOCOL_SMB = "Server Message Block (aka CIFS Common Internet File System)." +PROTOCOL_NFSv3 = "Network File System version 3." +PROTOCOL_NFSv4 = "Network File System version 4." +PROTOCOL_HTTP = "Hypertext Transport Protocol." +PROTOCOL_HTTPS = "Secure Hypertext Transport Protocol." +PROTOCOL_FTP = "File Transfer Protocol." +PROTOCOL_SFTP = "Secure File Transfer Protocol." + +# Media types +MEDIA_TYPE_SSD = "SSD device" +MEDIA_TYPE_HDD = "HDD device" + +# RAID level constants +RAID_0 = 'raid 0' +RAID_1 = 'raid 1' +RAID_5 = 'raid 5' +RAID_1_0 = 'raid 1+0' +RAID_5_0 = 'raid 5+0' +RAID_6 = 'raid 6' +RAID_6_0 = 'raid 6+0' +RAID_1ADM = 'raid 1adm' +RAID_10ADM = 'raid_10adm' diff --git a/proliantutils/redfish/resources/system/storage/drive.py b/proliantutils/redfish/resources/system/storage/drive.py index 36733a1a..c3fe4a8e 100644 --- a/proliantutils/redfish/resources/system/storage/drive.py +++ b/proliantutils/redfish/resources/system/storage/drive.py @@ -16,18 +16,21 @@ # "/redfish/v1/Systems/1/Storage/1/Drives" will be an invalid URI. +from proliantutils.redfish.resources.system.storage import mappings + from sushy.resources import base class Drive(base.ResourceBase): + """This class represents the Drive resource""" capacity_bytes = base.Field('CapacityBytes', adapter=int) """The size in bytes of this Drive""" - media_type = base.Field('MediaType') + media_type = base.MappedField('MediaType', mappings.MEDIA_TYPE_MAP) """The type of media contained in this drive""" - protocol = base.Field('Protocol') + protocol = base.MappedField('Protocol', mappings.DEVICE_PROTOCOLS_MAP) """The protocol this drive is using to communicate to the storage controller.""" diff --git a/proliantutils/redfish/resources/system/storage/logical_drive.py b/proliantutils/redfish/resources/system/storage/logical_drive.py index 279c74c1..1dd206c6 100644 --- a/proliantutils/redfish/resources/system/storage/logical_drive.py +++ b/proliantutils/redfish/resources/system/storage/logical_drive.py @@ -16,8 +16,11 @@ from sushy.resources import base from proliantutils.redfish import utils +from proliantutils.redfish.resources.system.storage import mappings + class HPELogicalDrive(base.ResourceBase): + """This class represents the LogicalDrives resource""" identity = base.Field('Id') @@ -27,10 +30,11 @@ class HPELogicalDrive(base.ResourceBase): capacity_mib = base.Field('CapacityMiB', adapter=int) - raid = base.Field('Raid') + raid = base.MappedField('Raid', mappings.RAID_LEVEL_MAP) class HPELogicalDriveCollection(base.ResourceCollectionBase): + """This class represents the collection of LogicalDrives resource""" _maximum_size_mib = None diff --git a/proliantutils/redfish/resources/system/storage/mappings.py b/proliantutils/redfish/resources/system/storage/mappings.py new file mode 100644 index 00000000..3c8b7cb8 --- /dev/null +++ b/proliantutils/redfish/resources/system/storage/mappings.py @@ -0,0 +1,62 @@ +# Copyright 2017 Hewlett Packard Enterprise Development LP +# +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sushy import utils + +from proliantutils.redfish.resources.system.storage import constants + +DEVICE_PROTOCOLS_MAP = { + 'PCIe': constants.PROTOCOL_PCIe, + 'AHCI': constants.PROTOCOL_AHCI, + 'UHCI': constants.PROTOCOL_UHCI, + 'SAS': constants.PROTOCOL_SAS, + 'SATA': constants.PROTOCOL_SATA, + 'USB': constants.PROTOCOL_USB, + 'NVMe': constants.PROTOCOL_NVMe, + 'FC': constants.PROTOCOL_FC, + 'iSCSI': constants.PROTOCOL_iSCSI, + 'FCoE': constants.PROTOCOL_FCoE, + 'FCP': constants.PROTOCOL_FCP, + 'FICON': constants.PROTOCOL_FICON, + 'NVMeOverFabrics': constants.PROTOCOL_NVMeOverFabrics, + 'SMB': constants.PROTOCOL_SMB, + 'NFSv3': constants.PROTOCOL_NFSv3, + 'NFSv4': constants.PROTOCOL_NFSv4, + 'HTTP': constants.PROTOCOL_HTTP, + 'HTTPS': constants.PROTOCOL_HTTPS, + 'FTP': constants.PROTOCOL_FTP, + 'SFTP': constants.PROTOCOL_SFTP +} + +MEDIA_TYPE_MAP = { + 'SSD': constants.MEDIA_TYPE_SSD, + 'HDD': constants.MEDIA_TYPE_HDD +} + +RAID_LEVEL_MAP = { + '0': constants.RAID_0, + '1': constants.RAID_1, + '5': constants.RAID_5, + '10': constants.RAID_1_0, + '50': constants.RAID_5_0, + '6': constants.RAID_6, + '60': constants.RAID_6_0, + '1ADM': constants.RAID_1ADM, + '10ADM': constants.RAID_10ADM, +} + +RAID_LEVEL_MAP_REV = ( + utils.revert_dictionary(RAID_LEVEL_MAP)) diff --git a/proliantutils/redfish/resources/system/storage/physical_drive.py b/proliantutils/redfish/resources/system/storage/physical_drive.py index 8b382fbf..e8b079c2 100644 --- a/proliantutils/redfish/resources/system/storage/physical_drive.py +++ b/proliantutils/redfish/resources/system/storage/physical_drive.py @@ -12,16 +12,15 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from sushy.resources import base from proliantutils.redfish import utils -LOG = logging.getLogger(__name__) +from proliantutils.redfish.resources.system.storage import mappings class HPEPhysicalDrive(base.ResourceBase): + """This class represents the HPEPhysicalDrives resource""" identity = base.Field('Id', required=True) @@ -31,12 +30,13 @@ class HPEPhysicalDrive(base.ResourceBase): capacity_mib = base.Field('CapacityMiB', adapter=int) - media_type = base.Field('MediaType') + media_type = base.MappedField('MediaType', mappings.MEDIA_TYPE_MAP) rotational_speed_rpm = base.Field('RotationalSpeedRpm', adapter=int) class HPEPhysicalDriveCollection(base.ResourceCollectionBase): + """This class represents the collection of HPEPhysicalDrives resource""" _maximum_size_mib = None diff --git a/proliantutils/redfish/resources/system/storage/smart_storage.py b/proliantutils/redfish/resources/system/storage/smart_storage.py index 5818e1c1..48f30015 100644 --- a/proliantutils/redfish/resources/system/storage/smart_storage.py +++ b/proliantutils/redfish/resources/system/storage/smart_storage.py @@ -22,6 +22,7 @@ LOG = logging.getLogger(__name__) class HPESmartStorage(base.ResourceBase): + """This class represents the HPE OEM SmartStorage resource""" name = base.Field('Name') """The name of the resource or array element""" diff --git a/proliantutils/redfish/resources/system/storage/storage.py b/proliantutils/redfish/resources/system/storage/storage.py index 300cbeeb..868c4fe8 100644 --- a/proliantutils/redfish/resources/system/storage/storage.py +++ b/proliantutils/redfish/resources/system/storage/storage.py @@ -24,6 +24,7 @@ LOG = logging.getLogger(__name__) class Storage(base.ResourceBase): + """This class represents the Storage resource""" identity = base.Field('Id', required=True) """The Storage identity string""" @@ -34,9 +35,6 @@ class Storage(base.ResourceBase): description = base.Field('Description') """Description""" - storage_controllers = base.Field('StorageControllers') - """The set of storage controllers""" - drives = base.Field('Drives') """The set of drives attached to the storage controllers""" @@ -56,7 +54,7 @@ class Storage(base.ResourceBase): return self._volumes def _drives_list(self): - """This property prepares the list of drives + """Gets the list of drives :return a list of drives. """ @@ -85,6 +83,7 @@ class Storage(base.ResourceBase): class StorageCollection(base.ResourceCollectionBase): + """This class represents the collection of Storage resource""" _volumes_maximum_size_bytes = None _drives_maximum_size_bytes = None diff --git a/proliantutils/redfish/resources/system/storage/volume.py b/proliantutils/redfish/resources/system/storage/volume.py index e33f3f11..2812fb6c 100644 --- a/proliantutils/redfish/resources/system/storage/volume.py +++ b/proliantutils/redfish/resources/system/storage/volume.py @@ -12,13 +12,13 @@ # License for the specific language governing permissions and limitations # under the License. - from sushy.resources import base from proliantutils.redfish import utils class Volume(base.ResourceBase): + """This class represents the Volume resource""" identity = base.Field('Id', required=True) """The processor identity string""" @@ -26,11 +26,9 @@ class Volume(base.ResourceBase): capacity_bytes = base.Field('CapacityBytes', adapter=int) """The size in bytes of this Volume""" - volume_type = base.Field('VolumeType') - """The type of this volume.""" - class VolumeCollection(base.ResourceCollectionBase): + """This class represents the collection of Volume resource""" _maximum_size_bytes = None diff --git a/proliantutils/tests/redfish/resources/system/storage/test_drive.py b/proliantutils/tests/redfish/resources/system/storage/test_drive.py index 32f0dfc3..134a5ec2 100644 --- a/proliantutils/tests/redfish/resources/system/storage/test_drive.py +++ b/proliantutils/tests/redfish/resources/system/storage/test_drive.py @@ -17,6 +17,7 @@ import json import mock import testtools +from proliantutils.redfish.resources.system.storage import constants from proliantutils.redfish.resources.system.storage import drive @@ -38,3 +39,6 @@ class DriveTestCase(testtools.TestCase): def test__parse_attributes(self): self.sys_drive._parse_attributes() self.assertEqual('1.0.2', self.sys_drive.redfish_version) + self.assertEqual(899527000000, self.sys_drive.capacity_bytes) + self.assertEqual(constants.PROTOCOL_SAS, self.sys_drive.protocol) + self.assertEqual(constants.MEDIA_TYPE_HDD, self.sys_drive.media_type) diff --git a/proliantutils/tests/redfish/resources/system/storage/test_storage.py b/proliantutils/tests/redfish/resources/system/storage/test_storage.py index f5cbb5c0..07707064 100644 --- a/proliantutils/tests/redfish/resources/system/storage/test_storage.py +++ b/proliantutils/tests/redfish/resources/system/storage/test_storage.py @@ -41,8 +41,6 @@ class StorageTestCase(testtools.TestCase): self.assertEqual('Local Storage Controller', self.sys_stor.name) self.assertEqual('Integrated RAID Controller', self.sys_stor.description) - self.assertEqual(self.stor_json.get('StorageControllers'), - self.sys_stor.storage_controllers) self.assertEqual(self.stor_json.get('Drives'), self.sys_stor.drives) @@ -64,7 +62,7 @@ class StorageTestCase(testtools.TestCase): self.sys_stor.refresh() self.assertIsNone(self.sys_stor._volumes) - def test_drives_list(self): + def test__drives_list(self): self.conn.get.return_value.json.reset_mock() with open('proliantutils/tests/redfish/' 'json_samples/drive.json') as f: diff --git a/proliantutils/tests/redfish/resources/system/storage/test_volume.py b/proliantutils/tests/redfish/resources/system/storage/test_volume.py index 13d4e2c1..b9cf1895 100644 --- a/proliantutils/tests/redfish/resources/system/storage/test_volume.py +++ b/proliantutils/tests/redfish/resources/system/storage/test_volume.py @@ -37,7 +37,6 @@ class VolumeTestCase(testtools.TestCase): self.sys_vol._parse_attributes() self.assertEqual('1.0.2', self.sys_vol.redfish_version) self.assertEqual('1', self.sys_vol.identity) - self.assertEqual('Mirrored', self.sys_vol.volume_type) self.assertEqual(899527000000, self.sys_vol.capacity_bytes) diff --git a/proliantutils/tests/redfish/resources/system/test_system.py b/proliantutils/tests/redfish/resources/system/test_system.py index 267238e5..ea39e781 100644 --- a/proliantutils/tests/redfish/resources/system/test_system.py +++ b/proliantutils/tests/redfish/resources/system/test_system.py @@ -358,9 +358,8 @@ class HPESystemTestCase(testtools.TestCase): value = (json.loads(f.read())) self.conn.get.return_value.json.return_value = value self.assertIsNone(self.sys_inst._smart_storage) - self.sys_inst.smart_storage - self.assertIsInstance(self.sys_inst._smart_storage, - smart_storage.HPESmartStorage) + value = self.sys_inst.smart_storage + self.assertIsInstance(value, smart_storage.HPESmartStorage) def test_storages(self): self.conn.get.return_value.json.reset_mock() @@ -375,9 +374,8 @@ class HPESystemTestCase(testtools.TestCase): value = (json.loads(f.read())) self.conn.get.return_value.json.side_effect = [coll, value] self.assertIsNone(self.sys_inst._storages) - self.sys_inst.storages - self.assertIsInstance(self.sys_inst._storages, - storage.StorageCollection) + value = self.sys_inst.storages + self.assertIsInstance(value, storage.StorageCollection) def test_simple_storages(self): self.conn.get.return_value.json.reset_mock() @@ -392,35 +390,25 @@ class HPESystemTestCase(testtools.TestCase): value = (json.loads(f.read())) self.conn.get.return_value.json.side_effect = [coll, value] self.assertIsNone(self.sys_inst._simple_storages) - self.sys_inst.simple_storages - self.assertIsInstance(self.sys_inst._simple_storages, - simple_storage.SimpleStorageCollection) + value = self.sys_inst.simple_storages + self.assertIsInstance(value, simple_storage.SimpleStorageCollection) def test_simple_storage_on_refresh(self): - # | GIVEN | with open('proliantutils/tests/redfish/json_samples/' 'simple_storage_collection.json', 'r') as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) - # | WHEN & THEN | self.assertIsInstance(self.sys_inst.simple_storages, simple_storage.SimpleStorageCollection) - - # On refreshing the system instance... with open('proliantutils/tests/redfish/' 'json_samples/system.json', 'r') as f: self.conn.get.return_value.json.return_value = ( json.loads(f.read())['default']) self.sys_inst.refresh() - - # | WHEN & THEN | self.assertIsNone(self.sys_inst._simple_storages) - - # | GIVEN | with open('proliantutils/tests/redfish/json_samples/' 'simple_storage_collection.json', 'r') as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) - # | WHEN & THEN | self.assertIsInstance(self.sys_inst.simple_storages, simple_storage.SimpleStorageCollection) @@ -466,3 +454,24 @@ class HPESystemTestCase(testtools.TestCase): # | WHEN & THEN | self.assertIsInstance(self.sys_inst.memory, memory.MemoryCollection) + + def test_storage_on_refresh(self): + with open('proliantutils/tests/redfish/json_samples/' + 'storage_collection.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + self.assertIsInstance(self.sys_inst.storages, + storage.StorageCollection) + # On refreshing the system instance... + with open('proliantutils/tests/redfish/' + 'json_samples/system.json', 'r') as f: + self.conn.get.return_value.json.return_value = ( + json.loads(f.read())['default']) + self.sys_inst.refresh() + self.assertIsNone(self.sys_inst._storages) + + with open('proliantutils/tests/redfish/json_samples/' + 'simple_storage_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + self.assertIsInstance(self.sys_inst.storages, + storage.StorageCollection) diff --git a/proliantutils/tests/redfish/test_redfish.py b/proliantutils/tests/redfish/test_redfish.py index 4b6990b6..d9d53a20 100644 --- a/proliantutils/tests/redfish/test_redfish.py +++ b/proliantutils/tests/redfish/test_redfish.py @@ -743,11 +743,6 @@ class RedfishOperationsTestCase(testtools.TestCase): @mock.patch.object(redfish.RedfishOperations, '_get_sushy_manager') def test_get_server_capabilities_optional_capabilities_absent( self, get_manager_mock, get_system_mock): - val = [] - path = ('proliantutils/tests/redfish/json_samples/' - 'pci_device.json') - with open(path, 'r') as f: - val.append(json.loads(f.read())) type(get_system_mock.return_value.pci_devices).gpu_devices = ( [mock.MagicMock(spec=pci_device.PCIDevice)]) type(get_system_mock.return_value.bios_settings).sriov = (