Redfish: Add local_gb attribute to get_essential_properties
This commit adds the local_gb attribute to the get_essential_properties() method. It also adds the utility function max_safe() which doesn't raise ValueError exception if max() fail with that exception. Change-Id: Ie1543e9f5653a025b484726ce278500ee8fdb261
This commit is contained in:
parent
f901f22145
commit
88294f20cf
|
@ -84,6 +84,7 @@ SUPPORTED_REDFISH_METHODS = [
|
|||
'clear_secure_boot_keys',
|
||||
'get_server_capabilities',
|
||||
'get_supported_boot_mode',
|
||||
'get_essential_properties',
|
||||
]
|
||||
|
||||
LOG = log.get_logger(__name__)
|
||||
|
|
|
@ -18,6 +18,7 @@ import json
|
|||
|
||||
from six.moves.urllib import parse
|
||||
import sushy
|
||||
from sushy.resources.system import mappings as sushy_map
|
||||
from sushy import utils
|
||||
|
||||
from proliantutils import exception
|
||||
|
@ -28,6 +29,8 @@ from proliantutils import log
|
|||
from proliantutils.redfish import main
|
||||
from proliantutils.redfish.resources.manager import constants as mgr_cons
|
||||
from proliantutils.redfish.resources.system import constants as sys_cons
|
||||
from proliantutils.redfish.resources.system.storage \
|
||||
import common as common_storage
|
||||
from proliantutils.redfish import utils as rf_utils
|
||||
|
||||
"""
|
||||
|
@ -828,7 +831,9 @@ class RedfishOperations(operations.IloOperations):
|
|||
# local_gb = sushy_system.storage_summary
|
||||
prop = {'memory_mb': (sushy_system.memory_summary.size_gib * 1024),
|
||||
'cpus': sushy_system.processors.summary.count,
|
||||
'cpu_arch': sushy_system.processors.summary.architecture}
|
||||
'cpu_arch': sushy_map.PROCESSOR_ARCH_VALUE_MAP_REV.get(
|
||||
sushy_system.processors.summary.architecture),
|
||||
'local_gb': common_storage.get_local_gb(sushy_system)}
|
||||
return {'properties': prop,
|
||||
'macs': sushy_system.ethernet_interfaces.summary}
|
||||
except sushy.exceptions.SushyError as e:
|
||||
|
|
|
@ -79,8 +79,8 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._logical_drives_maximum_size_mib is None:
|
||||
self._logical_drives_maximum_size_mib = (
|
||||
max([member.logical_drives.maximum_size_mib
|
||||
for member in self.get_members()]))
|
||||
utils.max_safe([member.logical_drives.maximum_size_mib
|
||||
for member in self.get_members()]))
|
||||
return self._logical_drives_maximum_size_mib
|
||||
|
||||
@property
|
||||
|
@ -91,8 +91,8 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._physical_drives_maximum_size_mib is None:
|
||||
self._physical_drives_maximum_size_mib = (
|
||||
max([member.physical_drives.maximum_size_mib
|
||||
for member in self.get_members()]))
|
||||
utils.max_safe([member.physical_drives.maximum_size_mib
|
||||
for member in self.get_members()]))
|
||||
return self._physical_drives_maximum_size_mib
|
||||
|
||||
def refresh(self):
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
# Copyright 2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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.
|
||||
|
||||
__author__ = 'HPE'
|
||||
|
||||
import sushy
|
||||
|
||||
from proliantutils import exception
|
||||
from proliantutils import log
|
||||
from proliantutils.redfish import utils
|
||||
|
||||
LOG = log.get_logger(__name__)
|
||||
|
||||
|
||||
def _get_attribute_value_of(resource, attribute_name, default=None):
|
||||
"""Gets the value of attribute_name from the resource
|
||||
|
||||
It catches the exception, if any, while retrieving the
|
||||
value of attribute_name from resource and returns default.
|
||||
|
||||
:param resource: The resource object
|
||||
:attribute_name: Property of the resource
|
||||
:returns the property value if no error encountered
|
||||
else return 0.
|
||||
"""
|
||||
try:
|
||||
return getattr(resource, attribute_name)
|
||||
except (sushy.exceptions.SushyError,
|
||||
exception.MissingAttributeError) as e:
|
||||
msg = (('The Redfish controller failed to get the '
|
||||
'attribute %(attribute)s from resource %(resource)s. '
|
||||
'Error %(error)s') % {'error': str(e),
|
||||
'attribute': attribute_name,
|
||||
'resource':
|
||||
resource.__class__.__name__})
|
||||
LOG.debug(msg)
|
||||
return default
|
||||
|
||||
|
||||
def get_local_gb(system_obj):
|
||||
"""Gets the largest volume or the largest disk
|
||||
|
||||
:param system_obj: The HPESystem object.
|
||||
:returns the size in GB
|
||||
"""
|
||||
local_max_bytes = 0
|
||||
logical_max_mib = 0
|
||||
volume_max_bytes = 0
|
||||
physical_max_mib = 0
|
||||
drives_max_bytes = 0
|
||||
simple_max_bytes = 0
|
||||
|
||||
# Gets the resources and properties
|
||||
# its quite possible for a system to lack the resource, hence its
|
||||
# URI may also be lacking.
|
||||
|
||||
# Check if smart_storage resource exist at the system
|
||||
smart_resource = _get_attribute_value_of(system_obj, 'smart_storage')
|
||||
# Check if storage resource exist at the system
|
||||
storage_resource = _get_attribute_value_of(system_obj, 'storages')
|
||||
|
||||
if smart_resource is not None:
|
||||
logical_max_mib = _get_attribute_value_of(
|
||||
smart_resource, 'logical_drives_maximum_size_mib', default=0)
|
||||
if storage_resource is not None:
|
||||
volume_max_bytes = _get_attribute_value_of(
|
||||
storage_resource, 'volumes_maximum_size_bytes', default=0)
|
||||
|
||||
# Get the largest volume from the system.
|
||||
local_max_bytes = utils.max_safe([(logical_max_mib * 1024 * 1024),
|
||||
volume_max_bytes])
|
||||
# if volume is not found, then traverse through the possible disk drives
|
||||
# and get the biggest disk.
|
||||
if local_max_bytes == 0:
|
||||
if smart_resource is not None:
|
||||
physical_max_mib = _get_attribute_value_of(
|
||||
smart_resource, 'physical_drives_maximum_size_mib', default=0)
|
||||
|
||||
if storage_resource is not None:
|
||||
drives_max_bytes = _get_attribute_value_of(
|
||||
storage_resource, 'drives_maximum_size_bytes', default=0)
|
||||
|
||||
# Check if the SimpleStorage resource exist at the system.
|
||||
simple_resource = _get_attribute_value_of(system_obj,
|
||||
'simple_storages')
|
||||
if simple_resource is not None:
|
||||
simple_max_bytes = _get_attribute_value_of(
|
||||
simple_resource, 'maximum_size_bytes', default=0)
|
||||
|
||||
local_max_bytes = utils.max_safe([(physical_max_mib * 1024 * 1024),
|
||||
drives_max_bytes, simple_max_bytes])
|
||||
# Convert the received size to GB and reduce the value by 1 Gb as
|
||||
# ironic requires the local_gb to be returned 1 less than actual size.
|
||||
local_gb = 0
|
||||
if local_max_bytes > 0:
|
||||
local_gb = int(local_max_bytes / (1024 * 1024 * 1024)) - 1
|
||||
else:
|
||||
msg = ('The maximum size for the hard disk or logical '
|
||||
'volume could not be determined.')
|
||||
LOG.debug(msg)
|
||||
return local_gb
|
|
@ -12,11 +12,9 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from sushy.resources import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
from proliantutils.redfish import utils
|
||||
|
||||
|
||||
class HPELogicalDrive(base.ResourceBase):
|
||||
|
@ -48,7 +46,8 @@ class HPELogicalDriveCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._maximum_size_mib is None:
|
||||
self._maximum_size_mib = (
|
||||
max([member.capacity_mib for member in self.get_members()]))
|
||||
utils.max_safe([member.capacity_mib
|
||||
for member in self.get_members()]))
|
||||
return self._maximum_size_mib
|
||||
|
||||
def refresh(self):
|
||||
|
|
|
@ -16,6 +16,8 @@ import logging
|
|||
|
||||
from sushy.resources import base
|
||||
|
||||
from proliantutils.redfish import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -50,7 +52,8 @@ class HPEPhysicalDriveCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._maximum_size_mib is None:
|
||||
self._maximum_size_mib = (
|
||||
max([member.capacity_mib for member in self.get_members()]))
|
||||
utils.max_safe([member.capacity_mib
|
||||
for member in self.get_members()]))
|
||||
return self._maximum_size_mib
|
||||
|
||||
def refresh(self):
|
||||
|
|
|
@ -12,12 +12,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from sushy.resources import base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
from proliantutils.redfish import utils
|
||||
|
||||
|
||||
class SimpleStorage(base.ResourceBase):
|
||||
|
@ -45,9 +43,9 @@ class SimpleStorage(base.ResourceBase):
|
|||
"""
|
||||
if self._maximum_size_bytes is None:
|
||||
self._maximum_size_bytes = (
|
||||
max([device.get('CapacityBytes')
|
||||
for device in self.devices
|
||||
if device.get('CapacityBytes') is not None]))
|
||||
utils.max_safe([device.get('CapacityBytes')
|
||||
for device in self.devices
|
||||
if device.get('CapacityBytes') is not None]))
|
||||
return self._maximum_size_bytes
|
||||
|
||||
def refresh(self):
|
||||
|
@ -71,8 +69,8 @@ class SimpleStorageCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._maximum_size_bytes is None:
|
||||
self._maximum_size_bytes = (
|
||||
max([member.maximum_size_bytes
|
||||
for member in self.get_members()]))
|
||||
utils.max_safe([member.maximum_size_bytes
|
||||
for member in self.get_members()]))
|
||||
return self._maximum_size_bytes
|
||||
|
||||
def refresh(self):
|
||||
|
|
|
@ -56,8 +56,9 @@ class HPESmartStorage(base.ResourceBase):
|
|||
"""
|
||||
if self._logical_drives_maximum_size_mib is None:
|
||||
self._logical_drives_maximum_size_mib = (
|
||||
max([member.logical_drives.maximum_size_mib
|
||||
for member in self.array_controllers.get_members()]))
|
||||
utils.max_safe(
|
||||
[member.logical_drives.maximum_size_mib
|
||||
for member in self.array_controllers.get_members()]))
|
||||
return self._logical_drives_maximum_size_mib
|
||||
|
||||
@property
|
||||
|
@ -68,8 +69,9 @@ class HPESmartStorage(base.ResourceBase):
|
|||
"""
|
||||
if self._physical_drives_maximum_size_mib is None:
|
||||
self._physical_drives_maximum_size_mib = (
|
||||
max([member.physical_drives.maximum_size_mib
|
||||
for member in self.array_controllers.get_members()]))
|
||||
utils.max_safe(
|
||||
[member.physical_drives.maximum_size_mib
|
||||
for member in self.array_controllers.get_members()]))
|
||||
return self._physical_drives_maximum_size_mib
|
||||
|
||||
def refresh(self):
|
||||
|
|
|
@ -74,8 +74,8 @@ class Storage(base.ResourceBase):
|
|||
"""
|
||||
if self._drives_maximum_size_bytes is None:
|
||||
self._drives_maximum_size_bytes = (
|
||||
max([member.capacity_bytes
|
||||
for member in self._drives_list()]))
|
||||
utils.max_safe([member.capacity_bytes
|
||||
for member in self._drives_list()]))
|
||||
return self._drives_maximum_size_bytes
|
||||
|
||||
def refresh(self):
|
||||
|
@ -101,8 +101,8 @@ class StorageCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._volumes_maximum_size_bytes is None:
|
||||
self._volumes_maximum_size_bytes = (
|
||||
max([member.volumes.maximum_size_bytes
|
||||
for member in self.get_members()]))
|
||||
utils.max_safe([member.volumes.maximum_size_bytes
|
||||
for member in self.get_members()]))
|
||||
return self._volumes_maximum_size_bytes
|
||||
|
||||
@property
|
||||
|
@ -113,8 +113,8 @@ class StorageCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._drives_maximum_size_bytes is None:
|
||||
self._drives_maximum_size_bytes = (
|
||||
max([member.drives_maximum_size_bytes
|
||||
for member in self.get_members()]))
|
||||
utils.max_safe([member.drives_maximum_size_bytes
|
||||
for member in self.get_members()]))
|
||||
return self._drives_maximum_size_bytes
|
||||
|
||||
def refresh(self):
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
from sushy.resources import base
|
||||
|
||||
from proliantutils.redfish import utils
|
||||
|
||||
|
||||
class Volume(base.ResourceBase):
|
||||
|
||||
|
@ -44,7 +46,8 @@ class VolumeCollection(base.ResourceCollectionBase):
|
|||
"""
|
||||
if self._maximum_size_bytes is None:
|
||||
self._maximum_size_bytes = (
|
||||
max([member.capacity_bytes for member in self.get_members()]))
|
||||
utils.max_safe([member.capacity_bytes
|
||||
for member in self.get_members()]))
|
||||
return self._maximum_size_bytes
|
||||
|
||||
def refresh(self):
|
||||
|
|
|
@ -79,13 +79,14 @@ class HPESystem(system.System):
|
|||
_bios_settings = None # ref to BIOSSettings instance
|
||||
_secure_boot = None # ref to SecureBoot instance
|
||||
|
||||
_smart_storage = None
|
||||
_storages = None
|
||||
_pci_devices = None
|
||||
_smart_storage = None # SmartStorage instance
|
||||
_simple_storages = None # SimpleStorage instance
|
||||
_storages = None # Storage instance
|
||||
_pci_devices = None # PCIDevice instance
|
||||
|
||||
_ethernet_interfaces = None
|
||||
_ethernet_interfaces = None # EthernetInterface instance
|
||||
|
||||
_memory = None
|
||||
_memory = None # Memory instance
|
||||
|
||||
def _get_hpe_push_power_button_action_element(self):
|
||||
push_action = self._hpe_actions.computer_system_ext_powerbutton
|
||||
|
|
|
@ -108,3 +108,17 @@ def is_operation_allowed(method, resource, subresouce_path):
|
|||
:returns: True if the operation is allowed else False
|
||||
"""
|
||||
return method in get_allowed_operations(resource, subresouce_path)
|
||||
|
||||
|
||||
def max_safe(iterable):
|
||||
"""Creates a wrapper over python max() function.
|
||||
|
||||
This function is just a wrapper over pthon max().
|
||||
It catches the exceptions and let max() return without any error.
|
||||
"""
|
||||
|
||||
try:
|
||||
return max(iterable)
|
||||
except ValueError:
|
||||
# The TypeError is not caught here as that should be thrown.
|
||||
return 0
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
# Copyright 2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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.
|
||||
__author__ = 'HPE'
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
import sushy
|
||||
import testtools
|
||||
|
||||
from proliantutils import exception
|
||||
from proliantutils.redfish.resources.system.storage import common
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class CommonMethodsTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(CommonMethodsTestCase, self).setUp()
|
||||
self.system_obj = mock.MagicMock()
|
||||
|
||||
@ddt.data((953837, 60000, 60000, 60000, 60000, 930),
|
||||
(sushy.exceptions.SushyError, 1000169537536, 60000, 60000,
|
||||
60000, 930),
|
||||
(953837, sushy.exceptions.SushyError, 60000, 60000, 60000,
|
||||
930),
|
||||
(sushy.exceptions.SushyError, sushy.exceptions.SushyError,
|
||||
953837, 60000, 40000, 930),
|
||||
(sushy.exceptions.SushyError, sushy.exceptions.SushyError,
|
||||
sushy.exceptions.SushyError, 1000169537536, 40000, 930),
|
||||
(sushy.exceptions.SushyError, sushy.exceptions.SushyError,
|
||||
sushy.exceptions.SushyError, sushy.exceptions.SushyError,
|
||||
1000169537536, 930),
|
||||
(sushy.exceptions.SushyError, sushy.exceptions.SushyError,
|
||||
sushy.exceptions.SushyError, sushy.exceptions.SushyError,
|
||||
sushy.exceptions.SushyError, 0),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_get_local_gb(self, logical_max, volume_max, physical_max,
|
||||
drive_max, simple_max, expected):
|
||||
|
||||
def _mock_property(value):
|
||||
if value is sushy.exceptions.SushyError:
|
||||
mock_value = mock.PropertyMock(side_effect=value)
|
||||
else:
|
||||
mock_value = mock.PropertyMock(return_value=value)
|
||||
return mock_value
|
||||
system_obj = self.system_obj
|
||||
type(system_obj.smart_storage).logical_drives_maximum_size_mib = (
|
||||
_mock_property(logical_max))
|
||||
type(system_obj.storages).volumes_maximum_size_bytes = (
|
||||
_mock_property(volume_max))
|
||||
type(system_obj.smart_storage).physical_drives_maximum_size_mib = (
|
||||
_mock_property(physical_max))
|
||||
type(system_obj.storages).drives_maximum_size_bytes = (
|
||||
_mock_property(drive_max))
|
||||
type(system_obj.simple_storages).maximum_size_bytes = (
|
||||
_mock_property(simple_max))
|
||||
actual = common.get_local_gb(system_obj)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test__get_attribute_value_of(self):
|
||||
system_obj = self.system_obj
|
||||
si_mock = mock.PropertyMock(return_value=1000169537536)
|
||||
type(system_obj.simple_storages).maximum_size_bytes = si_mock
|
||||
actual = common._get_attribute_value_of(system_obj.simple_storages,
|
||||
'maximum_size_bytes')
|
||||
self.assertEqual(1000169537536, actual)
|
||||
|
||||
def test__get_attribute_value_of_sushy_error(self):
|
||||
system_obj = self.system_obj
|
||||
si_mock = mock.PropertyMock(side_effect=sushy.exceptions.SushyError)
|
||||
type(system_obj.simple_storages).maximum_size_bytes = si_mock
|
||||
actual = common._get_attribute_value_of(system_obj.simple_storages,
|
||||
'maximum_size_bytes',
|
||||
default=0)
|
||||
self.assertEqual(0, actual)
|
||||
|
||||
def test__get_attribute_value_of_fail_missing_attribute(self):
|
||||
system_obj = self.system_obj
|
||||
si_mock = mock.PropertyMock(
|
||||
side_effect=exception.MissingAttributeError)
|
||||
type(system_obj.simple_storages).maximum_size_bytes = si_mock
|
||||
actual = common._get_attribute_value_of(system_obj.simple_storages,
|
||||
'maximum_size_bytes')
|
||||
self.assertIsNone(actual)
|
|
@ -35,6 +35,8 @@ from proliantutils.redfish.resources.system import iscsi
|
|||
from proliantutils.redfish.resources.system import memory
|
||||
from proliantutils.redfish.resources.system import pci_device
|
||||
from proliantutils.redfish.resources.system.storage import array_controller
|
||||
from proliantutils.redfish.resources.system.storage \
|
||||
import common as common_storage
|
||||
from proliantutils.redfish.resources.system import system as pro_sys
|
||||
from sushy.resources.system import system
|
||||
|
||||
|
@ -979,8 +981,9 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
|||
'on the server.',
|
||||
self.rf_client.clear_secure_boot_keys)
|
||||
|
||||
@mock.patch.object(common_storage, 'get_local_gb')
|
||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||
def test_get_essential_properties(self, get_system_mock):
|
||||
def test_get_essential_properties(self, get_system_mock, local_gb_mock):
|
||||
memory_mock = mock.PropertyMock(return_value=20)
|
||||
type(get_system_mock.return_value.memory_summary).size_gib = (
|
||||
memory_mock)
|
||||
|
@ -992,12 +995,13 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
|||
arch_mock)
|
||||
type(get_system_mock.return_value.ethernet_interfaces).summary = (
|
||||
{'1': '12:44:6A:3B:04:11'})
|
||||
# TODO(nisha): To add after local_gb changes merge.
|
||||
# type(get_system_mock.return_value).storage_summary = 600
|
||||
|
||||
local_gb_mock.return_value = 600
|
||||
actual = self.rf_client.get_essential_properties()
|
||||
expected = {'properties': {'cpus': 40,
|
||||
'cpu_arch': 'x86 or x86-64',
|
||||
'memory_mb': 20480},
|
||||
'cpu_arch': 'x86',
|
||||
'memory_mb': 20480,
|
||||
'local_gb': 600},
|
||||
'macs': {'1': '12:44:6A:3B:04:11'}}
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
|
|
@ -104,3 +104,10 @@ class UtilsTestCase(testtools.TestCase):
|
|||
ret_val = utils.is_operation_allowed(method, self.sys_inst,
|
||||
subresource_path)
|
||||
self.assertEqual(ret_val, expected)
|
||||
|
||||
@ddt.data(([2, 4, 6], 6),
|
||||
([], 0))
|
||||
@ddt.unpack
|
||||
def test_max_safe(self, iterable, expected):
|
||||
actual = utils.max_safe(iterable)
|
||||
self.assertEqual(expected, actual)
|
||||
|
|
Loading…
Reference in New Issue