Merge "Add storage subsystem field for rsd v2_1"
This commit is contained in:
commit
71c64f5c9c
105
rsd_lib/resources/v2_1/system/storage_subsystem.py
Normal file
105
rsd_lib/resources/v2_1/system/storage_subsystem.py
Normal file
@ -0,0 +1,105 @@
|
||||
# Copyright 2018 99cloud, Inc.
|
||||
# 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.resources import base
|
||||
from sushy import utils
|
||||
|
||||
from rsd_lib import utils as rsd_lib_utils
|
||||
|
||||
|
||||
class StatusField(base.CompositeField):
|
||||
state = base.Field('State')
|
||||
health = base.Field('Health')
|
||||
health_rollup = base.Field('HealthRollup')
|
||||
|
||||
|
||||
class IdentifiersField(base.ListField):
|
||||
durable_name = base.Field('DurableName')
|
||||
durable_name_format = base.Field('DurableNameFormat')
|
||||
|
||||
|
||||
class StorageControllersField(base.ListField):
|
||||
member_id = base.Field('MemberId')
|
||||
status = StatusField('Status')
|
||||
manufacturer = base.Field('Manufacturer')
|
||||
model = base.Field('Model')
|
||||
sku = base.Field('SKU')
|
||||
serial_number = base.Field('SerialNumber')
|
||||
part_number = base.Field('PartNumber')
|
||||
asset_tag = base.Field('AssetTag')
|
||||
speed_gbps = base.Field('SpeedGbps', adapter=rsd_lib_utils.int_or_none)
|
||||
firmware_version = base.Field('FirmwareVersion')
|
||||
supported_controller_protocols = base.Field(
|
||||
'SupportedControllerProtocols', adapter=list)
|
||||
supported_device_protocols = base.Field(
|
||||
'SupportedDeviceProtocols', adapter=list)
|
||||
identifiers = IdentifiersField('Identifiers')
|
||||
|
||||
|
||||
class StorageSubsystem(base.ResourceBase):
|
||||
|
||||
name = base.Field('Name')
|
||||
"""The storage subsystem name"""
|
||||
|
||||
identity = base.Field('Id', required=True)
|
||||
"""The storage subsystem identity string"""
|
||||
|
||||
description = base.Field('Description')
|
||||
"""The storage subsystem description"""
|
||||
|
||||
status = StatusField('Status')
|
||||
"""The storage subsystem status"""
|
||||
|
||||
storage_controllers = StorageControllersField('StorageControllers')
|
||||
"""The storage subsystem controllers"""
|
||||
|
||||
drives = base.Field('Drives', adapter=utils.get_members_identities)
|
||||
"""The storage subsystem drives"""
|
||||
|
||||
volumes = base.Field('Volumes',
|
||||
adapter=rsd_lib_utils.get_resource_identity)
|
||||
"""The storage subsystem volumes"""
|
||||
|
||||
def __init__(self, connector, identity, redfish_version=None):
|
||||
"""A class representing a Storage Subsystem
|
||||
|
||||
:param connector: A Connector instance
|
||||
:param identity: The identity of the storage subsystem
|
||||
:param redfish_version: The version of RedFish. Used to construct
|
||||
the object according to schema of the given version.
|
||||
"""
|
||||
super(StorageSubsystem, self).__init__(connector,
|
||||
identity,
|
||||
redfish_version)
|
||||
|
||||
|
||||
class StorageSubsystemCollection(base.ResourceCollectionBase):
|
||||
|
||||
@property
|
||||
def _resource_type(self):
|
||||
return StorageSubsystem
|
||||
|
||||
def __init__(self, connector, path, redfish_version=None):
|
||||
"""A class representing a StorageSubsystemCollection
|
||||
|
||||
:param connector: A Connector instance
|
||||
:param path: The canonical path to the storage subsystem collection
|
||||
resource
|
||||
:param redfish_version: The version of RedFish. Used to construct
|
||||
the object according to schema of the given version.
|
||||
"""
|
||||
super(StorageSubsystemCollection, self).__init__(connector,
|
||||
path,
|
||||
redfish_version)
|
@ -17,12 +17,14 @@ from sushy import exceptions
|
||||
from sushy.resources.system import system
|
||||
|
||||
from rsd_lib.resources.v2_1.system import memory
|
||||
from rsd_lib.resources.v2_1.system import storage_subsystem
|
||||
from rsd_lib import utils
|
||||
|
||||
|
||||
class System(system.System):
|
||||
|
||||
_memory = None # ref to System memory collection instance
|
||||
_storage_subsystem = None # ref to storage subsystem collection instance
|
||||
|
||||
def _get_memory_collection_path(self):
|
||||
"""Helper function to find the memory path"""
|
||||
@ -46,9 +48,33 @@ class System(system.System):
|
||||
|
||||
return self._memory
|
||||
|
||||
def _get_storage_subsystem_collection_path(self):
|
||||
"""Helper function to find the storage subsystem path"""
|
||||
storage_subsystem_col = self.json.get('Storage')
|
||||
if not storage_subsystem_col:
|
||||
raise exceptions.MissingAttributeError(
|
||||
attribute='StorageSubsystem',
|
||||
resource=self._path)
|
||||
return utils.get_resource_identity(storage_subsystem_col)
|
||||
|
||||
@property
|
||||
def storage_subsystem(self):
|
||||
"""Property to provide reference to `StorageSubsystem` instance
|
||||
|
||||
It is calculated once the first time it is queried. On refresh,
|
||||
this property is reset.
|
||||
"""
|
||||
if self._storage_subsystem is None:
|
||||
self._storage_subsystem = storage_subsystem.\
|
||||
StorageSubsystemCollection(
|
||||
self._conn, self._get_storage_subsystem_collection_path(),
|
||||
redfish_version=self.redfish_version)
|
||||
return self._storage_subsystem
|
||||
|
||||
def refresh(self):
|
||||
super(System, self).refresh()
|
||||
self._memory = None
|
||||
self._storage_subsystem = None
|
||||
|
||||
|
||||
class SystemCollection(system.SystemCollection):
|
||||
|
63
rsd_lib/tests/unit/json_samples/v2_1/storage_subsystem.json
Normal file
63
rsd_lib/tests/unit/json_samples/v2_1/storage_subsystem.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#Systems/Members/1/Storage/Members/$entity",
|
||||
"@odata.id": "/redfish/v1/Systems/1/Storage/SATA",
|
||||
"@odata.type": "#Storage.v1_0_0.Storage",
|
||||
"Id": "1",
|
||||
"Name": "SATA Storage System",
|
||||
"Description": "System SATA",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup": "OK"
|
||||
},
|
||||
"StorageControllers": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/1/Storage/SATA#/StorageControllers/0",
|
||||
"@odata.type": "#Storage.v1_1_0.StorageController",
|
||||
"MemberId": "0",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK"
|
||||
},
|
||||
"Manufacturer": "ManufacturerName",
|
||||
"Model": "ProductModelName",
|
||||
"SKU": "",
|
||||
"SerialNumber": "2M220100SL",
|
||||
"PartNumber": "",
|
||||
"AssetTag": "CustomerWritableThingy",
|
||||
"SpeedGbps": 6,
|
||||
"FirmwareVersion": null,
|
||||
"SupportedControllerProtocols": [
|
||||
"PCIe"
|
||||
],
|
||||
"SupportedDeviceProtocols": [
|
||||
"SATA"
|
||||
],
|
||||
"Identifiers": [
|
||||
{
|
||||
"DurableName": "123e4567-e89b-12d3-a456-426655440000",
|
||||
"DurableNameFormat": "UUID"
|
||||
}
|
||||
],
|
||||
"Links": {
|
||||
"Endpoints": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"Drives": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Blade1/Drives/Disk1"
|
||||
}
|
||||
],
|
||||
"Volumes": {
|
||||
"@odata.id": "/redfish/v1/Systems/1/Storage/SATA/Volumes"
|
||||
},
|
||||
"Links": {
|
||||
"Enclosures": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Blade1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#StorageCollection.StorageCollection",
|
||||
"@odata.id": "/redfish/v1/Systems/1/Storage",
|
||||
"@odata.type": "#StorageCollection.StorageCollection",
|
||||
"Name": "Storage Collection",
|
||||
"Members@odata.count": 1,
|
||||
"Members": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/1/Storage/SATA"
|
||||
}
|
||||
]
|
||||
}
|
@ -93,6 +93,9 @@
|
||||
"SimpleStorage": {
|
||||
"@odata.id": "/redfish/v1/Systems/437XR1138R2/SimpleStorage"
|
||||
},
|
||||
"Storage": {
|
||||
"@odata.id": "/redfish/v1/Systems/1/Storage"
|
||||
},
|
||||
"LogServices": {
|
||||
"@odata.id": "/redfish/v1/Systems/437XR1138R2/LogServices"
|
||||
},
|
||||
|
@ -0,0 +1,144 @@
|
||||
# Copyright 2018 99cloud, Inc.
|
||||
# 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.
|
||||
|
||||
import json
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from rsd_lib.resources.v2_1.system import storage_subsystem
|
||||
|
||||
|
||||
class StorageSubsystemTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(StorageSubsystemTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/'
|
||||
'storage_subsystem.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
|
||||
self.storage_subsystem_inst = storage_subsystem.StorageSubsystem(
|
||||
self.conn, '/redfish/v1/Systems/1/Storage/SATA',
|
||||
redfish_version='1.1.0')
|
||||
|
||||
def test__parse_attributes(self):
|
||||
self.storage_subsystem_inst._parse_attributes()
|
||||
self.assertEqual('SATA Storage System',
|
||||
self.storage_subsystem_inst.name)
|
||||
self.assertEqual('1', self.storage_subsystem_inst.identity)
|
||||
self.assertEqual('System SATA',
|
||||
self.storage_subsystem_inst.description)
|
||||
self.assertEqual('Enabled',
|
||||
self.storage_subsystem_inst.status.state)
|
||||
self.assertEqual('OK',
|
||||
self.storage_subsystem_inst.status.health)
|
||||
self.assertEqual('OK',
|
||||
self.storage_subsystem_inst.status.health_rollup)
|
||||
self.assertEqual(
|
||||
'0',
|
||||
self.storage_subsystem_inst.storage_controllers[0].member_id)
|
||||
self.assertEqual(
|
||||
'Enabled',
|
||||
self.storage_subsystem_inst.storage_controllers[0].status.state)
|
||||
self.assertEqual(
|
||||
'OK',
|
||||
self.storage_subsystem_inst.storage_controllers[0].status.health)
|
||||
self.assertEqual(
|
||||
'ManufacturerName',
|
||||
self.storage_subsystem_inst.storage_controllers[0].manufacturer)
|
||||
self.assertEqual(
|
||||
'ProductModelName',
|
||||
self.storage_subsystem_inst.storage_controllers[0].model)
|
||||
self.assertEqual(
|
||||
'',
|
||||
self.storage_subsystem_inst.storage_controllers[0].sku)
|
||||
self.assertEqual(
|
||||
'2M220100SL',
|
||||
self.storage_subsystem_inst.storage_controllers[0].serial_number)
|
||||
self.assertEqual(
|
||||
'',
|
||||
self.storage_subsystem_inst.storage_controllers[0].part_number)
|
||||
self.assertEqual(
|
||||
'CustomerWritableThingy',
|
||||
self.storage_subsystem_inst.storage_controllers[0].asset_tag)
|
||||
self.assertEqual(
|
||||
6,
|
||||
self.storage_subsystem_inst.storage_controllers[0].speed_gbps)
|
||||
self.assertEqual(
|
||||
None,
|
||||
self.storage_subsystem_inst.storage_controllers[0].
|
||||
firmware_version)
|
||||
self.assertEqual(
|
||||
'PCIe',
|
||||
self.storage_subsystem_inst.storage_controllers[0].
|
||||
supported_controller_protocols[0])
|
||||
self.assertEqual(
|
||||
'SATA',
|
||||
self.storage_subsystem_inst.storage_controllers[0].
|
||||
supported_device_protocols[0])
|
||||
self.assertEqual(
|
||||
'123e4567-e89b-12d3-a456-426655440000',
|
||||
self.storage_subsystem_inst.storage_controllers[0].identifiers[0].
|
||||
durable_name)
|
||||
self.assertEqual(
|
||||
'UUID',
|
||||
self.storage_subsystem_inst.storage_controllers[0].identifiers[0].
|
||||
durable_name_format)
|
||||
self.assertEqual(('/redfish/v1/Chassis/Blade1/Drives/Disk1',),
|
||||
self.storage_subsystem_inst.drives)
|
||||
self.assertEqual('/redfish/v1/Systems/1/Storage/SATA/Volumes',
|
||||
self.storage_subsystem_inst.volumes)
|
||||
|
||||
|
||||
class StorageSubsystemCollectionTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(StorageSubsystemCollectionTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/'
|
||||
'storage_subsystem_collection.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
self.storage_subsystem_col = storage_subsystem.\
|
||||
StorageSubsystemCollection(
|
||||
self.conn, '/redfish/v1/Systems/1/Storage',
|
||||
redfish_version='1.1.0')
|
||||
|
||||
def test__parse_attributes(self):
|
||||
self.storage_subsystem_col._parse_attributes()
|
||||
self.assertEqual('1.1.0', self.storage_subsystem_col.redfish_version)
|
||||
self.assertEqual(('/redfish/v1/Systems/1/Storage/SATA',),
|
||||
self.storage_subsystem_col.members_identities)
|
||||
|
||||
@mock.patch.object(storage_subsystem, 'StorageSubsystem', autospec=True)
|
||||
def test_get_member(self, mock_storage_subsystem):
|
||||
self.storage_subsystem_col.get_member(
|
||||
'/redfish/v1/Systems/1/Storage/SATA')
|
||||
mock_storage_subsystem.assert_called_once_with(
|
||||
self.storage_subsystem_col._conn,
|
||||
'/redfish/v1/Systems/1/Storage/SATA',
|
||||
redfish_version=self.storage_subsystem_col.redfish_version)
|
||||
|
||||
@mock.patch.object(storage_subsystem, 'StorageSubsystem', autospec=True)
|
||||
def test_get_members(self, mock_storage_subsystem):
|
||||
members = self.storage_subsystem_col.get_members()
|
||||
calls = [
|
||||
mock.call(self.storage_subsystem_col._conn,
|
||||
'/redfish/v1/Systems/1/Storage/SATA',
|
||||
redfish_version=self.storage_subsystem_col.
|
||||
redfish_version)
|
||||
]
|
||||
mock_storage_subsystem.assert_has_calls(calls)
|
||||
self.assertIsInstance(members, list)
|
||||
self.assertEqual(1, len(members))
|
@ -21,6 +21,7 @@ from sushy import exceptions
|
||||
from sushy.resources.system import system as sushy_system
|
||||
|
||||
from rsd_lib.resources.v2_1.system import memory
|
||||
from rsd_lib.resources.v2_1.system import storage_subsystem
|
||||
from rsd_lib.resources.v2_1.system import system
|
||||
|
||||
|
||||
@ -99,6 +100,65 @@ class SystemTestCase(testtools.TestCase):
|
||||
self.assertIsInstance(self.system_inst.memory,
|
||||
memory.MemoryCollection)
|
||||
|
||||
def test__get_storage_collection_path(self):
|
||||
self.assertEqual(
|
||||
'/redfish/v1/Systems/1/Storage',
|
||||
self.system_inst._get_storage_subsystem_collection_path())
|
||||
|
||||
def test__get_storage_collection_path_missing_systems_attr(self):
|
||||
self.system_inst._json.pop('Storage')
|
||||
with self.assertRaisesRegex(
|
||||
exceptions.MissingAttributeError, 'attribute StorageSubsystem'):
|
||||
self.system_inst._get_storage_subsystem_collection_path()
|
||||
|
||||
def test_storage_subsystem(self):
|
||||
# check for the underneath variable value
|
||||
self.assertIsNone(self.system_inst._storage_subsystem)
|
||||
# | GIVEN |
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/'
|
||||
'storage_subsystem_collection.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN |
|
||||
actual_storage_subsystem_col = self.system_inst.storage_subsystem
|
||||
# | THEN |
|
||||
self.assertIsInstance(actual_storage_subsystem_col,
|
||||
storage_subsystem.StorageSubsystemCollection)
|
||||
self.conn.get.return_value.json.assert_called_once_with()
|
||||
|
||||
# reset mock
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
# | WHEN & THEN |
|
||||
# tests for same object on invoking subsequently
|
||||
self.assertIs(actual_storage_subsystem_col,
|
||||
self.system_inst.storage_subsystem)
|
||||
self.conn.get.return_value.json.assert_not_called()
|
||||
|
||||
def test_storage_subsystem_on_refresh(self):
|
||||
# | GIVEN |
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/'
|
||||
'storage_subsystem_collection.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN & THEN |
|
||||
self.assertIsInstance(self.system_inst.storage_subsystem,
|
||||
storage_subsystem.StorageSubsystemCollection)
|
||||
|
||||
# on refreshing the system instance...
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/system.json',
|
||||
'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
self.system_inst.refresh()
|
||||
# | WHEN & THEN |
|
||||
self.assertIsNone(self.system_inst._storage_subsystem)
|
||||
|
||||
# | GIVEN |
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/'
|
||||
'storage_subsystem_collection.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN & THEN |
|
||||
self.assertIsInstance(self.system_inst.storage_subsystem,
|
||||
storage_subsystem.StorageSubsystemCollection)
|
||||
|
||||
|
||||
class SystemCollectionTestCase(testtools.TestCase):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user