Add system simple storage resource support
Adds the simple storage resource of Redfish standard schema. This new resource represents the properties of a storage controller and its directly-attached devices. This patch introduces the property ``max_size_bytes`` of SimpleStorageCollection resource to expose the size of the largest storage size available among all directly attached devices available to the System. Also brought in the common 'Status' (comprising of Health, HealthRollup and State sub-fields) field and refactored the code base to use that field across all the Redfish resources. Story: 1668487 Task: 23041 Change-Id: I512c2507bf78f4a9cf1e2525fd685836387a7581
This commit is contained in:
parent
fdeb8b8d44
commit
d44059483c
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Adds the "SimpleStorage" to the library. It also provides the max size
|
||||
available (in bytes) among all its directly attached devices.
|
|
@ -17,6 +17,7 @@ import logging
|
|||
import pbr.version
|
||||
|
||||
from sushy.main import Sushy
|
||||
from sushy.resources.constants import * # noqa
|
||||
from sushy.resources.system.constants import * # noqa
|
||||
from sushy.resources.manager.constants import * # noqa
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# under the License.
|
||||
|
||||
from sushy.resources import base
|
||||
from sushy.resources import mappings as res_maps
|
||||
|
||||
|
||||
class ActionField(base.CompositeField):
|
||||
|
@ -23,7 +24,22 @@ class ResetActionField(ActionField):
|
|||
|
||||
|
||||
class IdRefField(base.CompositeField):
|
||||
"""Reference to the resource for updating settings"""
|
||||
"""Reference to the resource odata identity field."""
|
||||
|
||||
resource_uri = base.Field('@odata.id')
|
||||
"""The unique identifier for a resource"""
|
||||
|
||||
|
||||
class StatusField(base.CompositeField):
|
||||
"""This Field describes the status of a resource and its children.
|
||||
|
||||
This field shall contain any state or health properties of a resource.
|
||||
"""
|
||||
health = base.MappedField('Health', res_maps.HEALTH_VALUE_MAP)
|
||||
"""Represents health of resource w/o considering its dependent resources"""
|
||||
|
||||
health_rollup = base.MappedField('HealthRollup', res_maps.HEALTH_VALUE_MAP)
|
||||
"""Represents health state of resource and its dependent resources"""
|
||||
|
||||
state = base.MappedField('State', res_maps.STATE_VALUE_MAP)
|
||||
"""Indicates the known state of the resource, such as if it is enabled."""
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# 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.
|
||||
|
||||
# Values comes from the Redfish System json-schema 1.0.0:
|
||||
# http://redfish.dmtf.org/schemas/v1/Resource.json
|
||||
|
||||
# Health related constants.
|
||||
HEALTH_OK = 'ok'
|
||||
HEALTH_WARNING = 'warning'
|
||||
HEALTH_CRITICAL = 'critical'
|
||||
|
||||
# State related constants.
|
||||
STATE_ENABLED = 'enabled'
|
||||
STATE_DISABLED = 'disabled'
|
||||
STATE_STANDBYOFFLINE = 'standby offline'
|
||||
STATE_STANDBYSPARE = 'standby spare'
|
||||
STATE_INTEST = 'in test'
|
||||
STATE_STARTING = 'starting'
|
||||
STATE_ABSENT = 'absent'
|
||||
STATE_UNAVAILABLEOFFLINE = 'unavailable offline'
|
||||
STATE_DEFERRING = 'deferring'
|
||||
STATE_QUIESCED = 'quiesced'
|
||||
STATE_UPDATING = 'updating'
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2017 Red Hat, 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 constants as res_cons
|
||||
from sushy import utils
|
||||
|
||||
|
||||
STATE_VALUE_MAP = {
|
||||
'Enabled': res_cons.STATE_ENABLED,
|
||||
'Disabled': res_cons.STATE_DISABLED,
|
||||
'Absent': res_cons.STATE_ABSENT,
|
||||
}
|
||||
|
||||
STATE_VALUE_MAP_REV = (
|
||||
utils.revert_dictionary(STATE_VALUE_MAP))
|
||||
|
||||
HEALTH_VALUE_MAP = {
|
||||
'OK': res_cons.HEALTH_OK,
|
||||
'Warning': res_cons.HEALTH_WARNING,
|
||||
'Critical': res_cons.HEALTH_CRITICAL
|
||||
}
|
||||
|
||||
HEALTH_VALUE_MAP_REV = (
|
||||
utils.revert_dictionary(HEALTH_VALUE_MAP))
|
|
@ -122,10 +122,3 @@ PROCESSOR_ARCH_IA_64 = 'Intel Itanium'
|
|||
PROCESSOR_ARCH_ARM = 'ARM'
|
||||
PROCESSOR_ARCH_MIPS = 'MIPS'
|
||||
PROCESSOR_ARCH_OEM = 'OEM-defined'
|
||||
|
||||
# Health related constants.
|
||||
HEALTH_STATE_ENABLED = 'enabled'
|
||||
HEALTH_STATE_DISABLED = 'disabled'
|
||||
HEALTH_OK = 'ok'
|
||||
HEALTH_WARNING = 'warning'
|
||||
HEALTH_CRITICAL = 'critical'
|
||||
|
|
|
@ -16,18 +16,12 @@
|
|||
import logging
|
||||
|
||||
from sushy.resources import base
|
||||
from sushy.resources.system import constants as sys_cons
|
||||
from sushy.resources.system import mappings as sys_map
|
||||
from sushy.resources import common
|
||||
from sushy.resources import constants as res_cons
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HealthStatusField(base.CompositeField):
|
||||
state = base.MappedField(
|
||||
'State', sys_map.HEALTH_STATE_VALUE_MAP)
|
||||
health = base.Field('Health')
|
||||
|
||||
|
||||
class EthernetInterface(base.ResourceBase):
|
||||
"""This class adds the EthernetInterface resource"""
|
||||
|
||||
|
@ -49,7 +43,8 @@ class EthernetInterface(base.ResourceBase):
|
|||
speed_mbps = base.Field('SpeedMbps')
|
||||
"""This is the current speed in Mbps of this interface."""
|
||||
|
||||
status = HealthStatusField("Status")
|
||||
status = common.StatusField("Status")
|
||||
"""Describes the status and health of this interface."""
|
||||
|
||||
|
||||
class EthernetInterfaceCollection(base.ResourceCollectionBase):
|
||||
|
@ -69,19 +64,15 @@ class EthernetInterfaceCollection(base.ResourceCollectionBase):
|
|||
are returned.
|
||||
|
||||
:returns: dictionary in the format
|
||||
{'aa:bb:cc:dd:ee:ff': 'Enabled',
|
||||
'aa:bb:aa:aa:aa:aa': 'Disabled'}
|
||||
{'aa:bb:cc:dd:ee:ff': sushy.STATE_ENABLED,
|
||||
'aa:bb:aa:aa:aa:aa': sushy.STATE_DISABLED}
|
||||
"""
|
||||
if self._summary is None:
|
||||
mac_dict = {}
|
||||
for eth in self.get_members():
|
||||
if (eth.mac_address is not None and eth.status is not None):
|
||||
if (eth.status.health ==
|
||||
sys_map.HEALTH_VALUE_MAP_REV.get(
|
||||
sys_cons.HEALTH_OK)):
|
||||
state = sys_map.HEALTH_STATE_VALUE_MAP_REV.get(
|
||||
eth.status.state)
|
||||
mac_dict[eth.mac_address] = state
|
||||
if eth.mac_address is not None and eth.status is not None:
|
||||
if eth.status.health == res_cons.HEALTH_OK:
|
||||
mac_dict[eth.mac_address] = eth.status.state
|
||||
self._summary = mac_dict
|
||||
return self._summary
|
||||
|
||||
|
|
|
@ -89,20 +89,3 @@ PROCESSOR_ARCH_VALUE_MAP = {
|
|||
|
||||
PROCESSOR_ARCH_VALUE_MAP_REV = (
|
||||
utils.revert_dictionary(PROCESSOR_ARCH_VALUE_MAP))
|
||||
|
||||
HEALTH_STATE_VALUE_MAP = {
|
||||
'Enabled': sys_cons.HEALTH_STATE_ENABLED,
|
||||
'Disabled': sys_cons.HEALTH_STATE_DISABLED,
|
||||
}
|
||||
|
||||
HEALTH_STATE_VALUE_MAP_REV = (
|
||||
utils.revert_dictionary(HEALTH_STATE_VALUE_MAP))
|
||||
|
||||
HEALTH_VALUE_MAP = {
|
||||
'OK': sys_cons.HEALTH_OK,
|
||||
'Warning': sys_cons.HEALTH_WARNING,
|
||||
'Critical': sys_cons.HEALTH_CRITICAL
|
||||
}
|
||||
|
||||
HEALTH_VALUE_MAP_REV = (
|
||||
utils.revert_dictionary(HEALTH_VALUE_MAP))
|
||||
|
|
|
@ -16,6 +16,7 @@ import collections
|
|||
import logging
|
||||
|
||||
from sushy.resources import base
|
||||
from sushy.resources import common
|
||||
from sushy.resources.system import mappings as sys_maps
|
||||
|
||||
# Representation of Summary of Processor information
|
||||
|
@ -45,18 +46,6 @@ class ProcessorIdField(base.CompositeField):
|
|||
"""The processor vendor id"""
|
||||
|
||||
|
||||
class StatusField(base.CompositeField):
|
||||
|
||||
health = base.Field('Health')
|
||||
"""The processor health"""
|
||||
|
||||
health_rollup = base.Field('HealthRollup')
|
||||
"""The processor health rollup"""
|
||||
|
||||
state = base.Field('State')
|
||||
"""The processor state"""
|
||||
|
||||
|
||||
class Processor(base.ResourceBase):
|
||||
|
||||
identity = base.Field('Id', required=True)
|
||||
|
@ -89,7 +78,7 @@ class Processor(base.ResourceBase):
|
|||
processor_id = ProcessorIdField('ProcessorId')
|
||||
"""The processor id"""
|
||||
|
||||
status = StatusField('Status')
|
||||
status = common.StatusField('Status')
|
||||
"""The processor status"""
|
||||
|
||||
total_cores = base.Field('TotalCores', adapter=int)
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
# 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.
|
||||
|
||||
# This is referred from Redfish standard schema.
|
||||
# http://redfish.dmtf.org/schemas/v1/SimpleStorage.v1_2_0.json
|
||||
|
||||
import logging
|
||||
|
||||
from sushy.resources import base
|
||||
from sushy.resources import common
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DeviceListField(base.ListField):
|
||||
"""The storage device/s associated with SimpleStorage."""
|
||||
|
||||
name = base.Field('Name', required=True)
|
||||
"""The name of the storage device"""
|
||||
|
||||
capacity_bytes = base.Field('CapacityBytes', adapter=utils.int_or_none)
|
||||
"""The size of the storage device."""
|
||||
|
||||
status = common.StatusField('Status')
|
||||
"""Describes the status and health of a storage device."""
|
||||
|
||||
|
||||
class SimpleStorage(base.ResourceBase):
|
||||
"""This class represents a simple storage.
|
||||
|
||||
It represents the properties of a storage controller and its
|
||||
directly-attached devices. A storage device can be a disk drive or optical
|
||||
media device.
|
||||
"""
|
||||
|
||||
identity = base.Field('Id', required=True)
|
||||
"""The SimpleStorage identity string"""
|
||||
|
||||
name = base.Field('Name')
|
||||
"""The name of the resource"""
|
||||
|
||||
devices = DeviceListField('Devices', default=[])
|
||||
"""The storage devices associated with this resource."""
|
||||
|
||||
|
||||
class SimpleStorageCollection(base.ResourceCollectionBase):
|
||||
"""Represents a collection of simple storage associated with system."""
|
||||
|
||||
_max_size_bytes = None
|
||||
|
||||
@property
|
||||
def _resource_type(self):
|
||||
return SimpleStorage
|
||||
|
||||
@property
|
||||
def max_size_bytes(self):
|
||||
"""Max size available (in bytes) among all enabled device resources.
|
||||
|
||||
It returns the cached value until it (or its parent resource) is
|
||||
refreshed.
|
||||
"""
|
||||
if self._max_size_bytes is None:
|
||||
self._max_size_bytes = (
|
||||
utils.max_safe(device.capacity_bytes
|
||||
for simpl_stor in self.get_members()
|
||||
for device in simpl_stor.devices
|
||||
if (device.status.state ==
|
||||
res_cons.STATE_ENABLED)))
|
||||
return self._max_size_bytes
|
||||
|
||||
def _do_refresh(self, force=False):
|
||||
# Note(deray): undefine the attribute here for fresh creation in
|
||||
# subsequent calls to it's exposed property.
|
||||
self._max_size_bytes = None
|
|
@ -63,12 +63,6 @@ class MemorySummaryField(base.CompositeField):
|
|||
"""
|
||||
|
||||
|
||||
class StatusField(base.CompositeField):
|
||||
state = base.Field('State')
|
||||
health = base.Field('Health')
|
||||
health_rollup = base.Field('HealthRollup')
|
||||
|
||||
|
||||
class System(base.ResourceBase):
|
||||
|
||||
asset_tag = base.Field('AssetTag')
|
||||
|
@ -112,7 +106,7 @@ class System(base.ResourceBase):
|
|||
sku = base.Field('SKU')
|
||||
"""The system stock-keeping unit"""
|
||||
|
||||
status = StatusField('Status')
|
||||
status = common.StatusField('Status')
|
||||
"""The system status"""
|
||||
|
||||
# TODO(lucasagomes): Create mappings for the system_type
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"@odata.type": "#SimpleStorage.v1_2_0.SimpleStorage",
|
||||
"Id": "1",
|
||||
"Name": "Simple Storage Controller",
|
||||
"Description": "System SATA",
|
||||
"UefiDevicePath": "Acpi(PNP0A03,0)/Pci(1F|1)/Ata(Primary,Master)/HD(Part3, Sig00110011)",
|
||||
"Status": {
|
||||
"@odata.type": "#Resource.Status",
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup": "Warning"
|
||||
},
|
||||
"Devices": [
|
||||
{
|
||||
"@odata.type": "#SimpleStorage.v1_1_0.Device",
|
||||
"Name": "SATA Bay 1",
|
||||
"Manufacturer": "Contoso",
|
||||
"Model": "3000GT8",
|
||||
"CapacityBytes": 8000000000000,
|
||||
"Status": {
|
||||
"@odata.type": "#Resource.Status",
|
||||
"State": "Enabled",
|
||||
"Health": "OK"
|
||||
}
|
||||
},
|
||||
{
|
||||
"@odata.type": "#SimpleStorage.v1_1_0.Device",
|
||||
"Name": "SATA Bay 2",
|
||||
"Manufacturer": "Contoso",
|
||||
"Model": "3000GT7",
|
||||
"CapacityBytes": 4000000000000,
|
||||
"Status": {
|
||||
"@odata.type": "#Resource.Status",
|
||||
"State": "Enabled",
|
||||
"Health": "Critical"
|
||||
}
|
||||
},
|
||||
{
|
||||
"@odata.type": "#SimpleStorage.v1_1_0.Device",
|
||||
"Name": "SATA Bay 3",
|
||||
"CapacityBytes": 9000000000000,
|
||||
"Status": {
|
||||
"@odata.type": "#Resource.Status",
|
||||
"State": "Absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"@odata.type": "#SimpleStorage.v1_1_0.Device",
|
||||
"Name": "SATA Bay 4",
|
||||
"Status": {
|
||||
"@odata.type": "#Resource.Status",
|
||||
"State": "Absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@odata.context": "/redfish/v1/$metadata#SimpleStorage.SimpleStorage",
|
||||
"@odata.id": "/redfish/v1/Systems/437XR1138R2/SimpleStorage/1",
|
||||
"@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@odata.type": "#SimpleStorageCollection.SimpleStorageCollection",
|
||||
"Name": "Simple Storage Collection",
|
||||
"Members@odata.count": 1,
|
||||
"Members": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/437XR1138R2/SimpleStorage/1"
|
||||
}
|
||||
],
|
||||
"@odata.context": "/redfish/v1/$metadata#SimpleStorageCollection.SimpleStorageCollection",
|
||||
"@odata.id": "/redfish/v1/Systems/437XR1138R2/SimpleStorage",
|
||||
"@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
|
||||
}
|
|
@ -23,8 +23,7 @@ class DriveTestCase(base.TestCase):
|
|||
def setUp(self):
|
||||
super(DriveTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
drive_file = 'sushy/tests/unit/json_samples/drive.json'
|
||||
with open(drive_file, 'r') as f:
|
||||
with open('sushy/tests/unit/json_samples/drive.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
|
||||
self.stor_drive = drive.Drive(
|
||||
|
|
|
@ -31,8 +31,7 @@ class StorageTestCase(base.TestCase):
|
|||
def setUp(self):
|
||||
super(StorageTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
storage_file = 'sushy/tests/unit/json_samples/storage.json'
|
||||
with open(storage_file, 'r') as f:
|
||||
with open('sushy/tests/unit/json_samples/storage.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
|
||||
self.storage = storage.Storage(
|
||||
|
|
|
@ -14,9 +14,8 @@ import json
|
|||
|
||||
import mock
|
||||
|
||||
from sushy.resources.system import constants as sys_cons
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources.system import ethernet_interface
|
||||
from sushy.resources.system import mappings as sys_map
|
||||
from sushy.tests.unit import base
|
||||
|
||||
|
||||
|
@ -43,8 +42,8 @@ class EthernetInterfaceTestCase(base.TestCase):
|
|||
self.assertEqual(
|
||||
'12:44:6A:3B:04:11', self.sys_eth.permanent_mac_address)
|
||||
self.assertEqual('12:44:6A:3B:04:11', self.sys_eth.mac_address)
|
||||
self.assertEqual('enabled', self.sys_eth.status.state)
|
||||
self.assertEqual('OK', self.sys_eth.status.health)
|
||||
self.assertEqual(res_cons.STATE_ENABLED, self.sys_eth.status.state)
|
||||
self.assertEqual(res_cons.HEALTH_OK, self.sys_eth.status.health)
|
||||
self.assertEqual(1000, self.sys_eth.speed_mbps)
|
||||
|
||||
|
||||
|
@ -99,9 +98,6 @@ class EthernetInterfaceCollectionTestCase(base.TestCase):
|
|||
with open('sushy/tests/unit/json_samples/'
|
||||
'ethernet_interfaces.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
expected_summary = {
|
||||
'12:44:6A:3B:04:11':
|
||||
sys_map.HEALTH_STATE_VALUE_MAP_REV.get(
|
||||
sys_cons.HEALTH_STATE_ENABLED)}
|
||||
expected_summary = {'12:44:6A:3B:04:11': res_cons.STATE_ENABLED}
|
||||
actual_summary = self.sys_eth_col.summary
|
||||
self.assertEqual(expected_summary, actual_summary)
|
||||
|
|
|
@ -17,6 +17,7 @@ import json
|
|||
import mock
|
||||
|
||||
import sushy
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources.system import processor
|
||||
from sushy.tests.unit import base
|
||||
|
||||
|
@ -63,9 +64,11 @@ class ProcessorTestCase(base.TestCase):
|
|||
self.assertEqual(3700, self.sys_processor.max_speed_mhz)
|
||||
self.assertEqual(8, self.sys_processor.total_cores)
|
||||
self.assertEqual(16, self.sys_processor.total_threads)
|
||||
self.assertEqual('Enabled', self.sys_processor.status.state)
|
||||
self.assertEqual('OK', self.sys_processor.status.health)
|
||||
self.assertEqual('OK', self.sys_processor.status.health_rollup)
|
||||
self.assertEqual(res_cons.STATE_ENABLED,
|
||||
self.sys_processor.status.state)
|
||||
self.assertEqual(res_cons.HEALTH_OK, self.sys_processor.status.health)
|
||||
self.assertEqual(res_cons.HEALTH_OK,
|
||||
self.sys_processor.status.health_rollup)
|
||||
|
||||
|
||||
class ProcessorCollectionTestCase(base.TestCase):
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
# 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
|
||||
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources.system import simple_storage
|
||||
from sushy.tests.unit import base
|
||||
|
||||
|
||||
class SimpleStorageTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SimpleStorageTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'simple_storage.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
|
||||
self.simpl_stor = simple_storage.SimpleStorage(
|
||||
self.conn, '/redfish/v1/Systems/437XR1138R2/SimpleStorage/1',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
def test__parse_attributes(self):
|
||||
self.simpl_stor._parse_attributes()
|
||||
self.assertEqual('1.0.2', self.simpl_stor.redfish_version)
|
||||
self.assertEqual('1', self.simpl_stor.identity)
|
||||
self.assertEqual('Simple Storage Controller', self.simpl_stor.name)
|
||||
self.assertEqual(8000000000000,
|
||||
self.simpl_stor.devices[0].capacity_bytes)
|
||||
self.assertEqual(4000000000000,
|
||||
self.simpl_stor.devices[1].capacity_bytes)
|
||||
self.assertEqual(res_cons.STATE_ENABLED,
|
||||
self.simpl_stor.devices[0].status.state)
|
||||
self.assertEqual(res_cons.STATE_ABSENT,
|
||||
self.simpl_stor.devices[2].status.state)
|
||||
self.assertEqual(res_cons.HEALTH_OK,
|
||||
self.simpl_stor.devices[0].status.health)
|
||||
|
||||
|
||||
class SimpleStorageCollectionTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SimpleStorageCollectionTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'simple_storage_collection.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
self.simpl_stor_col = simple_storage.SimpleStorageCollection(
|
||||
self.conn, '/redfish/v1/Systems/437XR1138R2/SimpleStorage',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
def test__parse_attributes(self):
|
||||
self.simpl_stor_col._parse_attributes()
|
||||
self.assertEqual((
|
||||
'/redfish/v1/Systems/437XR1138R2/SimpleStorage/1',),
|
||||
self.simpl_stor_col.members_identities)
|
||||
|
||||
@mock.patch.object(simple_storage, 'SimpleStorage', autospec=True)
|
||||
def test_get_member(self, SimpleStorage_mock):
|
||||
self.simpl_stor_col.get_member(
|
||||
'/redfish/v1/Systems/437XR1138R2/SimpleStorage/1')
|
||||
SimpleStorage_mock.assert_called_once_with(
|
||||
self.simpl_stor_col._conn,
|
||||
'/redfish/v1/Systems/437XR1138R2/SimpleStorage/1',
|
||||
redfish_version=self.simpl_stor_col.redfish_version)
|
||||
|
||||
@mock.patch.object(simple_storage, 'SimpleStorage', autospec=True)
|
||||
def test_get_members(self, SimpleStorage_mock):
|
||||
members = self.simpl_stor_col.get_members()
|
||||
calls = [
|
||||
mock.call(self.simpl_stor_col._conn,
|
||||
'/redfish/v1/Systems/437XR1138R2/SimpleStorage/1',
|
||||
redfish_version=self.simpl_stor_col.redfish_version),
|
||||
]
|
||||
SimpleStorage_mock.assert_has_calls(calls)
|
||||
self.assertIsInstance(members, list)
|
||||
self.assertEqual(1, len(members))
|
||||
|
||||
def test_max_size_bytes(self):
|
||||
self.assertIsNone(self.simpl_stor_col._max_size_bytes)
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'simple_storage.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
|
||||
self.assertEqual(8000000000000, self.simpl_stor_col.max_size_bytes)
|
||||
|
||||
# for any subsequent fetching it gets it from the cached value
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
self.assertEqual(8000000000000, self.simpl_stor_col.max_size_bytes)
|
||||
self.conn.get.return_value.json.assert_not_called()
|
||||
|
||||
def test_max_size_bytes_after_refresh(self):
|
||||
self.simpl_stor_col.refresh()
|
||||
self.assertIsNone(self.simpl_stor_col._max_size_bytes)
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'simple_storage.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
|
||||
self.assertEqual(8000000000000, self.simpl_stor_col.max_size_bytes)
|
|
@ -19,8 +19,8 @@ import mock
|
|||
|
||||
import sushy
|
||||
from sushy import exceptions
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources.system import bios
|
||||
from sushy.resources.system import constants as sys_cons
|
||||
from sushy.resources.system import ethernet_interface
|
||||
from sushy.resources.system import mappings as sys_map
|
||||
from sushy.resources.system import processor
|
||||
|
@ -58,9 +58,10 @@ class SystemTestCase(base.TestCase):
|
|||
self.assertEqual('Physical', self.sys_inst.system_type)
|
||||
self.assertEqual('38947555-7742-3448-3784-823347823834',
|
||||
self.sys_inst.uuid)
|
||||
self.assertEqual('Enabled', self.sys_inst.status.state)
|
||||
self.assertEqual('OK', self.sys_inst.status.health)
|
||||
self.assertEqual('OK', self.sys_inst.status.health_rollup)
|
||||
self.assertEqual(res_cons.STATE_ENABLED, self.sys_inst.status.state)
|
||||
self.assertEqual(res_cons.HEALTH_OK, self.sys_inst.status.health)
|
||||
self.assertEqual(res_cons.HEALTH_OK,
|
||||
self.sys_inst.status.health_rollup)
|
||||
self.assertEqual(sushy.SYSTEM_POWER_STATE_ON,
|
||||
self.sys_inst.power_state)
|
||||
self.assertEqual(96, self.sys_inst.memory_summary.size_gib)
|
||||
|
@ -374,9 +375,7 @@ class SystemTestCase(base.TestCase):
|
|||
self.assertIsNone(self.sys_inst._ethernet_interfaces)
|
||||
actual_macs = self.sys_inst.ethernet_interfaces.summary
|
||||
expected_macs = (
|
||||
{'12:44:6A:3B:04:11':
|
||||
sys_map.HEALTH_STATE_VALUE_MAP_REV.get(
|
||||
sys_cons.HEALTH_STATE_ENABLED)})
|
||||
{'12:44:6A:3B:04:11': res_cons.STATE_ENABLED})
|
||||
self.assertEqual(expected_macs, actual_macs)
|
||||
self.assertIsInstance(self.sys_inst._ethernet_interfaces,
|
||||
ethernet_interface.EthernetInterfaceCollection)
|
||||
|
|
Loading…
Reference in New Issue