Add @Redfish.Settings update status and expose it for BIOS
Adds logic to determine status exposed to user based on message severities. This is initial logic and in future can be improved to determine, e.g., if settings update is pending. Change-Id: Ic5da181af128641c6eaa432be566cbe0a5f69de4 Story: 2001791 Task: 19767
This commit is contained in:
parent
86ffbcb6ff
commit
51032b62ca
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
``Bios`` resource introduces ``update_status`` property that exposes
|
||||
the status and any errors of last BIOS attribute update.
|
|
@ -180,7 +180,8 @@ class Sushy(base.ResourceBase):
|
|||
:returns: The System object
|
||||
"""
|
||||
return system.System(self._conn, identity,
|
||||
redfish_version=self.redfish_version)
|
||||
redfish_version=self.redfish_version,
|
||||
registries=self._get_message_registries())
|
||||
|
||||
def get_chassis_collection(self):
|
||||
"""Get the ChassisCollection object
|
||||
|
|
|
@ -18,7 +18,44 @@ import logging
|
|||
|
||||
from sushy.resources import base
|
||||
from sushy.resources import common
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources import mappings as res_maps
|
||||
from sushy.resources.registry import message_registry
|
||||
|
||||
# Settings update statuses
|
||||
|
||||
UPDATE_UNKNOWN = 0
|
||||
"""Update status unknown"""
|
||||
|
||||
UPDATE_SUCCESS = 1
|
||||
"""Update was successful"""
|
||||
|
||||
UPDATE_FAILURE = 2
|
||||
"""Update encountered errors"""
|
||||
|
||||
UPDATE_PENDING = 3
|
||||
"""Update waiting for being applied"""
|
||||
|
||||
NO_UPDATES = 4
|
||||
"""No updates made"""
|
||||
|
||||
|
||||
class SettingsUpdate(object):
|
||||
"""Contains Settings update status and details of the update"""
|
||||
|
||||
def __init__(self, status, messages):
|
||||
self._status = status
|
||||
self._messages = messages
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
"""The status of the update"""
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def messages(self):
|
||||
"""List of :class:`.MessageListField` with messages from the update"""
|
||||
return self._messages
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -161,3 +198,29 @@ class SettingsField(base.CompositeField):
|
|||
@property
|
||||
def resource_uri(self):
|
||||
return self._settings_object_idref.resource_uri
|
||||
|
||||
def get_status(self, registries):
|
||||
"""Determines the status of last update based
|
||||
|
||||
Uses message id-s and severity to determine the status.
|
||||
|
||||
:param registries: registries to use to parse message
|
||||
:returns: :class:`.SettingsUpdate` object containing status
|
||||
and any messages
|
||||
"""
|
||||
|
||||
if not self.time:
|
||||
return SettingsUpdate(NO_UPDATES, None)
|
||||
|
||||
parsed_msgs = []
|
||||
for m in self.messages:
|
||||
parsed_msgs.append(
|
||||
message_registry.parse_message(registries, m))
|
||||
any_errors = any(m for m in parsed_msgs
|
||||
if not m.severity == res_cons.SEVERITY_OK)
|
||||
|
||||
if any_errors:
|
||||
status = UPDATE_FAILURE
|
||||
else:
|
||||
status = UPDATE_SUCCESS
|
||||
return SettingsUpdate(status, parsed_msgs)
|
||||
|
|
|
@ -31,6 +31,17 @@ class ActionsField(base.CompositeField):
|
|||
|
||||
class Bios(base.ResourceBase):
|
||||
|
||||
def __init__(self, connector, path, registries, *args, **kwargs):
|
||||
super(Bios, self).__init__(connector, path, *args, **kwargs)
|
||||
self._registries = registries
|
||||
"""A class representing a Bios
|
||||
|
||||
:param connector: A Connector instance
|
||||
:param path: Sub-URI path to the Bios resource
|
||||
:param registries: Dict of message registries to be used when
|
||||
parsing messages of attribute update status
|
||||
"""
|
||||
|
||||
identity = base.Field('Id', required=True)
|
||||
"""The Bios resource identity string"""
|
||||
|
||||
|
@ -65,6 +76,7 @@ class Bios(base.ResourceBase):
|
|||
"""Pending BIOS settings resource"""
|
||||
return Bios(
|
||||
self._conn, self._settings.resource_uri,
|
||||
registries=None,
|
||||
redfish_version=self.redfish_version)
|
||||
|
||||
@property
|
||||
|
@ -153,3 +165,12 @@ class Bios(base.ResourceBase):
|
|||
'OldPassword': old_password,
|
||||
'PasswordName': password_name})
|
||||
LOG.info('BIOS password %s is being changed', self.identity)
|
||||
|
||||
@property
|
||||
def update_status(self):
|
||||
"""Status of the last attribute update
|
||||
|
||||
:returns: :class:`sushy.resources.settings.SettingsUpdate` object
|
||||
containing status and any messages
|
||||
"""
|
||||
return self._settings.get_status(self._registries)
|
||||
|
|
|
@ -135,15 +135,19 @@ class System(base.ResourceBase):
|
|||
|
||||
_actions = ActionsField('Actions', required=True)
|
||||
|
||||
def __init__(self, connector, identity, redfish_version=None):
|
||||
def __init__(self, connector, identity, redfish_version=None,
|
||||
registries=None):
|
||||
"""A class representing a ComputerSystem
|
||||
|
||||
:param connector: A Connector instance
|
||||
:param identity: The identity of the System resource
|
||||
:param redfish_version: The version of RedFish. Used to construct
|
||||
the object according to schema of the given version.
|
||||
:param registries: Dict of registries to be used in any resource
|
||||
that needs registries to parse messages
|
||||
"""
|
||||
super(System, self).__init__(connector, identity, redfish_version)
|
||||
self._registries = registries
|
||||
|
||||
def _get_reset_action_element(self):
|
||||
reset_action = self._actions.reset
|
||||
|
@ -315,7 +319,8 @@ class System(base.ResourceBase):
|
|||
return bios.Bios(
|
||||
self._conn,
|
||||
utils.get_sub_resource_path_by(self, 'Bios'),
|
||||
redfish_version=self.redfish_version)
|
||||
redfish_version=self.redfish_version,
|
||||
registries=self._registries)
|
||||
|
||||
@property
|
||||
@utils.cache_it
|
||||
|
|
|
@ -20,9 +20,12 @@
|
|||
"ETag": "9234ac83b9700123cc32",
|
||||
"Messages": [
|
||||
{
|
||||
"MessageId": "Base.1.0.SettingsFailed",
|
||||
"MessageId": "Test.1.0.Failed",
|
||||
"RelatedProperties": [
|
||||
"#/Attributes/ProcTurboMode"
|
||||
],
|
||||
"MessageArgs": [
|
||||
"arg1"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"@odata.type": "#Settings.v1_2_0.Settings",
|
||||
"ETag": "9234ac83b9700123cc32",
|
||||
"Messages": [{
|
||||
"MessageId": "Base.1.0.SettingsFailed",
|
||||
"Message": "Settings update failed due to invalid value",
|
||||
"MessageId": "Test.1.0.Failed",
|
||||
"Message": "Settings %1 update failed due to invalid value",
|
||||
"Severity": "Critical",
|
||||
"Resolution": "Fix the value and try again",
|
||||
"MessageArgs": [
|
||||
|
|
|
@ -18,6 +18,8 @@ import mock
|
|||
from dateutil import parser
|
||||
|
||||
from sushy import exceptions
|
||||
from sushy.resources.registry import message_registry
|
||||
from sushy.resources import settings
|
||||
from sushy.resources.system import bios
|
||||
from sushy.tests.unit import base
|
||||
|
||||
|
@ -37,8 +39,16 @@ class BiosTestCase(base.TestCase):
|
|||
bios_settings_json,
|
||||
bios_settings_json]
|
||||
|
||||
conn = mock.Mock()
|
||||
with open('sushy/tests/unit/json_samples/message_registry.json') as f:
|
||||
conn.get.return_value.json.return_value = json.load(f)
|
||||
registry = message_registry.MessageRegistry(
|
||||
conn, '/redfish/v1/Registries/Test',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
self.sys_bios = bios.Bios(
|
||||
self.conn, '/redfish/v1/Systems/437XR1138R2/BIOS',
|
||||
registries={'Test.1.0': registry},
|
||||
redfish_version='1.0.2')
|
||||
|
||||
def test__parse_attributes(self):
|
||||
|
@ -59,6 +69,8 @@ class BiosTestCase(base.TestCase):
|
|||
self.sys_bios._settings._etag)
|
||||
self.assertEqual('(404) 555-1212',
|
||||
self.sys_bios.pending_attributes['AdminPhone'])
|
||||
self.assertEqual(settings.UPDATE_FAILURE,
|
||||
self.sys_bios.update_status.status)
|
||||
|
||||
def test_set_attribute(self):
|
||||
self.sys_bios.set_attribute('ProcTurboMode', 'Disabled')
|
||||
|
|
|
@ -17,6 +17,7 @@ import json
|
|||
import mock
|
||||
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources.registry import message_registry
|
||||
from sushy.resources import settings
|
||||
from sushy.tests.unit import base
|
||||
|
||||
|
@ -30,6 +31,14 @@ class SettingsFieldTestCase(base.TestCase):
|
|||
|
||||
self.settings = settings.SettingsField()
|
||||
|
||||
conn = mock.Mock()
|
||||
with open('sushy/tests/unit/json_samples/message_registry.json') as f:
|
||||
conn.get.return_value.json.return_value = json.load(f)
|
||||
registry = message_registry.MessageRegistry(
|
||||
conn, '/redfish/v1/Registries/Test',
|
||||
redfish_version='1.0.2')
|
||||
self.registries = {'Test.1.0': registry}
|
||||
|
||||
@mock.patch.object(settings, 'LOG', autospec=True)
|
||||
def test__load(self, mock_LOG):
|
||||
instance = self.settings._load(self.json, mock.Mock())
|
||||
|
@ -40,9 +49,9 @@ class SettingsFieldTestCase(base.TestCase):
|
|||
instance.time)
|
||||
self.assertEqual('/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||
instance._settings_object_idref.resource_uri)
|
||||
self.assertEqual('Base.1.0.SettingsFailed',
|
||||
self.assertEqual('Test.1.0.Failed',
|
||||
instance.messages[0].message_id)
|
||||
self.assertEqual('Settings update failed due to invalid value',
|
||||
self.assertEqual('Settings %1 update failed due to invalid value',
|
||||
instance.messages[0].message)
|
||||
self.assertEqual(res_cons.SEVERITY_CRITICAL,
|
||||
instance.messages[0].severity)
|
||||
|
@ -67,3 +76,33 @@ class SettingsFieldTestCase(base.TestCase):
|
|||
conn.patch.assert_called_once_with(
|
||||
'/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||
data={'Attributes': {'key': 'value'}})
|
||||
|
||||
def test_get_status_failure(self):
|
||||
instance = self.settings._load(self.json, mock.Mock())
|
||||
|
||||
status = instance.get_status(self.registries)
|
||||
self.assertEqual(status.status,
|
||||
settings.UPDATE_FAILURE)
|
||||
self.assertEqual(status.messages[0].severity,
|
||||
res_cons.SEVERITY_CRITICAL)
|
||||
self.assertEqual(status.messages[0].message,
|
||||
'The property arg1 broke everything.')
|
||||
|
||||
def test_get_status_success(self):
|
||||
instance = self.settings._load(self.json, mock.Mock())
|
||||
instance.messages[0].message_id = 'Test.1.0.Success'
|
||||
instance.messages[0].severity = res_cons.SEVERITY_OK
|
||||
status = instance.get_status(self.registries)
|
||||
self.assertEqual(status.status,
|
||||
settings.UPDATE_SUCCESS)
|
||||
self.assertEqual(status.messages[0].severity, res_cons.SEVERITY_OK)
|
||||
self.assertEqual(status.messages[0].message,
|
||||
'Everything done successfully.')
|
||||
|
||||
def test_get_status_noupdates(self):
|
||||
instance = self.settings._load(self.json, mock.Mock())
|
||||
instance.time = None
|
||||
status = instance.get_status(self.registries)
|
||||
self.assertEqual(status.status,
|
||||
settings.NO_UPDATES)
|
||||
self.assertIsNone(status.messages)
|
||||
|
|
|
@ -100,11 +100,16 @@ class MainTestCase(base.TestCase):
|
|||
redfish_version=self.root.redfish_version)
|
||||
|
||||
@mock.patch.object(system, 'System', autospec=True)
|
||||
def test_get_system(self, mock_system):
|
||||
@mock.patch('sushy.Sushy._get_message_registries', autospec=True)
|
||||
def test_get_system(self, mock_registries, mock_system):
|
||||
mock_registry = mock.Mock()
|
||||
mock_registries.return_value = [mock_registry]
|
||||
self.root._standard_message_registries_path = None
|
||||
self.root.get_system('fake-system-id')
|
||||
mock_system.assert_called_once_with(
|
||||
self.root._conn, 'fake-system-id',
|
||||
redfish_version=self.root.redfish_version)
|
||||
redfish_version=self.root.redfish_version,
|
||||
registries=[mock_registry])
|
||||
|
||||
@mock.patch.object(chassis, 'Chassis', autospec=True)
|
||||
def test_get_chassis(self, mock_chassis):
|
||||
|
|
Loading…
Reference in New Issue