Merge "Introduce BIOS API"
This commit is contained in:
commit
acc27a1b15
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds support for the BIOS resource to the library.
|
|
@ -20,3 +20,10 @@ class ActionField(base.CompositeField):
|
||||||
class ResetActionField(ActionField):
|
class ResetActionField(ActionField):
|
||||||
allowed_values = base.Field('ResetType@Redfish.AllowableValues',
|
allowed_values = base.Field('ResetType@Redfish.AllowableValues',
|
||||||
adapter=list)
|
adapter=list)
|
||||||
|
|
||||||
|
|
||||||
|
class IdRefField(base.CompositeField):
|
||||||
|
"""Reference to the resource for updating settings"""
|
||||||
|
|
||||||
|
resource_uri = base.Field('@odata.id')
|
||||||
|
"""The unique identifier for a resource"""
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
# 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/Settings.v1_0_0.json
|
||||||
|
|
||||||
|
|
||||||
|
from sushy.resources import base
|
||||||
|
from sushy.resources import common
|
||||||
|
|
||||||
|
|
||||||
|
class MessageListField(base.ListField):
|
||||||
|
"""List of messages with details of settings update status"""
|
||||||
|
|
||||||
|
message_id = base.Field('MessageId', required=True)
|
||||||
|
"""The key for this message which can be used
|
||||||
|
to look up the message in a message registry
|
||||||
|
"""
|
||||||
|
|
||||||
|
message = base.Field('Message')
|
||||||
|
"""Human readable message, if provided"""
|
||||||
|
|
||||||
|
severity = base.Field('Severity')
|
||||||
|
"""Severity of the error"""
|
||||||
|
|
||||||
|
resolution = base.Field('Resolution')
|
||||||
|
"""Used to provide suggestions on how to resolve
|
||||||
|
the situation that caused the error
|
||||||
|
"""
|
||||||
|
|
||||||
|
_related_properties = base.Field('RelatedProperties')
|
||||||
|
"""List of properties described by the message"""
|
||||||
|
|
||||||
|
message_args = base.Field('MessageArgs')
|
||||||
|
"""List of message substitution arguments for the message
|
||||||
|
referenced by `message_id` from the message registry
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsField(base.CompositeField):
|
||||||
|
"""The settings of a resource
|
||||||
|
|
||||||
|
Represents the future state and configuration of the resource. The
|
||||||
|
field is added to resources that support future state and
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
This field includes several properties to help clients monitor when
|
||||||
|
the resource is consumed by the service and determine the results of
|
||||||
|
applying the values, which may or may not have been successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(SettingsField, self).__init__(path="@Redfish.Settings")
|
||||||
|
|
||||||
|
time = base.Field('Time')
|
||||||
|
"""Indicates the time the settings were applied to the server"""
|
||||||
|
|
||||||
|
_etag = base.Field('ETag')
|
||||||
|
"""The ETag of the resource to which the settings were applied,
|
||||||
|
after the application
|
||||||
|
"""
|
||||||
|
|
||||||
|
_settings_object_idref = common.IdRefField("SettingsObject")
|
||||||
|
"""Reference to the resource the client may PUT/PATCH in order
|
||||||
|
to change this resource
|
||||||
|
"""
|
||||||
|
|
||||||
|
messages = MessageListField("Messages")
|
||||||
|
"""Represents the results of the last time the values of the Settings
|
||||||
|
resource were applied to the server"""
|
||||||
|
|
||||||
|
def commit(self, connector, value, etag=None):
|
||||||
|
"""Commits new settings values
|
||||||
|
|
||||||
|
The new values will be applied when the system or a service
|
||||||
|
restarts.
|
||||||
|
|
||||||
|
:param connector: A Connector instance
|
||||||
|
:param value: Value representing JSON whose structure is specific
|
||||||
|
to each resource and the caller must format it correctly
|
||||||
|
:param etag: Optional ETag of resource version to update. If
|
||||||
|
this ETag is provided and it does not match on server, then
|
||||||
|
the new values will not be committed
|
||||||
|
"""
|
||||||
|
|
||||||
|
connector.patch(self.resource_uri,
|
||||||
|
data=value,
|
||||||
|
headers={'If-Match': etag} if etag else None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def resource_uri(self):
|
||||||
|
return self._settings_object_idref.resource_uri
|
|
@ -0,0 +1,162 @@
|
||||||
|
# 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.
|
||||||
|
# https://redfish.dmtf.org/schemas/Bios.v1_0_3.json
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sushy import exceptions
|
||||||
|
from sushy.resources import base
|
||||||
|
from sushy.resources import common
|
||||||
|
from sushy.resources import settings
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ActionsField(base.CompositeField):
|
||||||
|
change_password = common.ActionField('#Bios.ChangePassword')
|
||||||
|
reset_bios = common.ActionField('#Bios.ResetBios')
|
||||||
|
|
||||||
|
|
||||||
|
class Bios(base.ResourceBase):
|
||||||
|
|
||||||
|
identity = base.Field('Id', required=True)
|
||||||
|
"""The Bios resource identity string"""
|
||||||
|
|
||||||
|
name = base.Field('Name')
|
||||||
|
"""The name of the resource"""
|
||||||
|
|
||||||
|
description = base.Field('Description')
|
||||||
|
"""Human-readable description of the BIOS resource"""
|
||||||
|
|
||||||
|
attribute_registry = base.Field('AttributeRegistry')
|
||||||
|
"""The Resource ID of the Attribute Registry
|
||||||
|
for the BIOS Attributes resource
|
||||||
|
"""
|
||||||
|
|
||||||
|
_settings = settings.SettingsField()
|
||||||
|
"""Results of last BIOS attribute update"""
|
||||||
|
|
||||||
|
attributes = base.Field('Attributes')
|
||||||
|
"""Vendor-specific key-value dict of effective BIOS attributes
|
||||||
|
|
||||||
|
Attributes cannot be updated directly.
|
||||||
|
To update use :py:func:`~set_attribute` or :py:func:`~set_attributes`
|
||||||
|
"""
|
||||||
|
|
||||||
|
_actions = ActionsField('Actions')
|
||||||
|
|
||||||
|
_etag = base.Field('@odata.etag')
|
||||||
|
|
||||||
|
_pending_settings_resource = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pending_attributes(self):
|
||||||
|
"""Pending BIOS attributes
|
||||||
|
|
||||||
|
BIOS attributes that have been comitted to the system,
|
||||||
|
but for them to take effect system restart is necessary
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._pending_settings_resource:
|
||||||
|
self._pending_settings_resource = Bios(
|
||||||
|
self._conn,
|
||||||
|
self._settings.resource_uri,
|
||||||
|
redfish_version=self.redfish_version)
|
||||||
|
self._pending_settings_resource.refresh(force=False)
|
||||||
|
return self._pending_settings_resource.attributes
|
||||||
|
|
||||||
|
def set_attribute(self, key, value):
|
||||||
|
"""Update an attribute
|
||||||
|
|
||||||
|
Attribute update is not immediate but requires system restart.
|
||||||
|
Committed attributes can be checked at :py:attr:`~pending_attributes`
|
||||||
|
property
|
||||||
|
|
||||||
|
:param key: Attribute name
|
||||||
|
:param value: Attribute value
|
||||||
|
"""
|
||||||
|
self.set_attributes({key: value})
|
||||||
|
|
||||||
|
def set_attributes(self, value):
|
||||||
|
"""Update many attributes at once
|
||||||
|
|
||||||
|
Attribute update is not immediate but requires system restart.
|
||||||
|
Committed attributes can be checked at :py:attr:`~pending_attributes`
|
||||||
|
property
|
||||||
|
|
||||||
|
:param value: Key-value pairs for attribute name and value
|
||||||
|
"""
|
||||||
|
self._settings.commit(self._conn,
|
||||||
|
{'Attributes': value},
|
||||||
|
self._etag)
|
||||||
|
if self._pending_settings_resource:
|
||||||
|
self._pending_settings_resource.invalidate()
|
||||||
|
|
||||||
|
def _get_reset_bios_action_element(self):
|
||||||
|
actions = self._actions
|
||||||
|
|
||||||
|
if not actions:
|
||||||
|
raise exceptions.MissingAttributeError(attribute="Actions",
|
||||||
|
resource=self._path)
|
||||||
|
|
||||||
|
reset_bios_action = actions.reset_bios
|
||||||
|
|
||||||
|
if not reset_bios_action:
|
||||||
|
raise exceptions.MissingActionError(action='#Bios.ResetBios',
|
||||||
|
resource=self._path)
|
||||||
|
return reset_bios_action
|
||||||
|
|
||||||
|
def _get_change_password_element(self):
|
||||||
|
actions = self._actions
|
||||||
|
|
||||||
|
if not actions:
|
||||||
|
raise exceptions.MissingAttributeError(attribute="Actions",
|
||||||
|
resource=self._path)
|
||||||
|
|
||||||
|
change_password_action = actions.change_password
|
||||||
|
|
||||||
|
if not change_password_action:
|
||||||
|
raise exceptions.MissingActionError(action='#Bios.ChangePassword',
|
||||||
|
resource=self._path)
|
||||||
|
return change_password_action
|
||||||
|
|
||||||
|
def reset_bios(self):
|
||||||
|
"""Reset the BIOS attributes to default"""
|
||||||
|
|
||||||
|
target_uri = self._get_reset_bios_action_element().target_uri
|
||||||
|
|
||||||
|
LOG.debug('Resetting BIOS attributes %s ...', self.identity)
|
||||||
|
self._conn.post(target_uri)
|
||||||
|
LOG.info('BIOS attributes %s is being reset', self.identity)
|
||||||
|
|
||||||
|
def change_password(self, new_password, old_password, password_name):
|
||||||
|
"""Change BIOS password"""
|
||||||
|
|
||||||
|
target_uri = self._get_change_password_element().target_uri
|
||||||
|
|
||||||
|
LOG.debug('Changing BIOS password %s ...', self.identity)
|
||||||
|
self._conn.post(target_uri, data={'NewPassword': new_password,
|
||||||
|
'OldPassword': old_password,
|
||||||
|
'PasswordName': password_name})
|
||||||
|
LOG.info('BIOS password %s is being changed', self.identity)
|
||||||
|
|
||||||
|
def _do_refresh(self, force=False):
|
||||||
|
"""Do custom resource specific refresh activities
|
||||||
|
|
||||||
|
On refresh, all sub-resources are marked as stale, i.e.
|
||||||
|
greedy-refresh not done for them unless forced by ``force``
|
||||||
|
argument.
|
||||||
|
"""
|
||||||
|
if self._pending_settings_resource is not None:
|
||||||
|
self._pending_settings_resource.invalidate(force)
|
|
@ -18,6 +18,7 @@ import logging
|
||||||
from sushy import exceptions
|
from sushy import exceptions
|
||||||
from sushy.resources import base
|
from sushy.resources import base
|
||||||
from sushy.resources import common
|
from sushy.resources import common
|
||||||
|
from sushy.resources.system import bios
|
||||||
from sushy.resources.system import constants as sys_cons
|
from sushy.resources.system import constants as sys_cons
|
||||||
from sushy.resources.system import ethernet_interface
|
from sushy.resources.system import ethernet_interface
|
||||||
from sushy.resources.system import mappings as sys_maps
|
from sushy.resources.system import mappings as sys_maps
|
||||||
|
@ -130,6 +131,8 @@ class System(base.ResourceBase):
|
||||||
|
|
||||||
_ethernet_interfaces = None
|
_ethernet_interfaces = None
|
||||||
|
|
||||||
|
_bios = None
|
||||||
|
|
||||||
def __init__(self, connector, identity, redfish_version=None):
|
def __init__(self, connector, identity, redfish_version=None):
|
||||||
"""A class representing a ComputerSystem
|
"""A class representing a ComputerSystem
|
||||||
|
|
||||||
|
@ -289,6 +292,23 @@ class System(base.ResourceBase):
|
||||||
self._ethernet_interfaces.refresh(force=False)
|
self._ethernet_interfaces.refresh(force=False)
|
||||||
return self._ethernet_interfaces
|
return self._ethernet_interfaces
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bios(self):
|
||||||
|
"""Property to reference `Bios` instance
|
||||||
|
|
||||||
|
It is set once when the first time it is queried. On refresh,
|
||||||
|
this property is marked as stale (greedy-refresh not done).
|
||||||
|
Here the actual refresh of the sub-resource happens, if stale.
|
||||||
|
"""
|
||||||
|
if self._bios is None:
|
||||||
|
self._bios = bios.Bios(
|
||||||
|
self._conn,
|
||||||
|
utils.get_sub_resource_path_by(self, 'Bios'),
|
||||||
|
redfish_version=self.redfish_version)
|
||||||
|
|
||||||
|
self._bios.refresh(force=False)
|
||||||
|
return self._bios
|
||||||
|
|
||||||
def _do_refresh(self, force=False):
|
def _do_refresh(self, force=False):
|
||||||
"""Do custom resource specific refresh activities
|
"""Do custom resource specific refresh activities
|
||||||
|
|
||||||
|
@ -300,6 +320,8 @@ class System(base.ResourceBase):
|
||||||
self._processors.invalidate(force)
|
self._processors.invalidate(force)
|
||||||
if self._ethernet_interfaces is not None:
|
if self._ethernet_interfaces is not None:
|
||||||
self._ethernet_interfaces.invalidate(force)
|
self._ethernet_interfaces.invalidate(force)
|
||||||
|
if self._bios is not None:
|
||||||
|
self._bios.invalidate(force)
|
||||||
|
|
||||||
|
|
||||||
class SystemCollection(base.ResourceCollectionBase):
|
class SystemCollection(base.ResourceCollectionBase):
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"@odata.type": "#Bios.v1_0_0.Bios",
|
||||||
|
"Id": "BIOS",
|
||||||
|
"Name": "BIOS Configuration Current Settings",
|
||||||
|
"AttributeRegistry": "BiosAttributeRegistryP89.v1_0_0",
|
||||||
|
"Attributes": {
|
||||||
|
"AdminPhone": "",
|
||||||
|
"BootMode": "Uefi",
|
||||||
|
"EmbeddedSata": "Raid",
|
||||||
|
"NicBoot1": "NetworkBoot",
|
||||||
|
"NicBoot2": "Disabled",
|
||||||
|
"PowerProfile": "MaxPerf",
|
||||||
|
"ProcCoreDisable": 0,
|
||||||
|
"ProcHyperthreading": "Enabled",
|
||||||
|
"ProcTurboMode": "Enabled",
|
||||||
|
"UsbControl": "UsbEnabled"
|
||||||
|
},
|
||||||
|
"@Redfish.Settings": {
|
||||||
|
"@odata.type": "#Settings.v1_0_0.Settings",
|
||||||
|
"ETag": "9234ac83b9700123cc32",
|
||||||
|
"Messages": [
|
||||||
|
{
|
||||||
|
"MessageId": "Base.1.0.SettingsFailed",
|
||||||
|
"RelatedProperties": [
|
||||||
|
"#/Attributes/ProcTurboMode"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"SettingsObject": {
|
||||||
|
"@odata.id": "/redfish/v1/Systems/437XR1138R2/BIOS/Settings"
|
||||||
|
},
|
||||||
|
"Time": "2016-03-07T14:44.30-05:00"
|
||||||
|
},
|
||||||
|
"Actions": {
|
||||||
|
"#Bios.ResetBios": {
|
||||||
|
"target": "/redfish/v1/Systems/437XR1138R2/BIOS/Actions/Bios.ResetBios"
|
||||||
|
},
|
||||||
|
"#Bios.ChangePassword": {
|
||||||
|
"target": "/redfish/v1/Systems/437XR1138R2/BIOS/Actions/Bios.ChangePassword"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@odata.etag": "123",
|
||||||
|
"@odata.context": "/redfish/v1/$metadata#Bios.Bios",
|
||||||
|
"@odata.id": "/redfish/v1/Systems/437XR1138R2/BIOS"
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"@odata.type": "#Bios.v1_0_0.Bios",
|
||||||
|
"Id": "Settings",
|
||||||
|
"Name": "BIOS Configuration Pending Settings",
|
||||||
|
"AttributeRegistry": "BiosAttributeRegistryP89.v1_0_0",
|
||||||
|
"Attributes": {
|
||||||
|
"AdminPhone": "(404) 555-1212",
|
||||||
|
"BootMode": "Uefi",
|
||||||
|
"EmbeddedSata": "Ahci",
|
||||||
|
"NicBoot1": "NetworkBoot",
|
||||||
|
"NicBoot2": "NetworkBoot",
|
||||||
|
"PowerProfile": "MaxPerf",
|
||||||
|
"ProcCoreDisable": 0,
|
||||||
|
"ProcHyperthreading": "Enabled",
|
||||||
|
"ProcTurboMode": "Disabled",
|
||||||
|
"UsbControl": "UsbEnabled"
|
||||||
|
},
|
||||||
|
"@odata.context": "/redfish/v1/$metadata#Bios.Bios",
|
||||||
|
"@odata.id": "/redfish/v1/Systems/437XR1138R2/BIOS/Settings",
|
||||||
|
"@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"@Redfish.Settings": {
|
||||||
|
"@odata.type": "#Settings.v1_0_0.Settings",
|
||||||
|
"ETag": "9234ac83b9700123cc32",
|
||||||
|
"Messages": [{
|
||||||
|
"MessageId": "Base.1.0.SettingsFailed",
|
||||||
|
"Message": "Settings update failed due to invalid value",
|
||||||
|
"Severity": "High",
|
||||||
|
"Resolution": "Fix the value and try again",
|
||||||
|
"MessageArgs": [
|
||||||
|
"arg1"
|
||||||
|
],
|
||||||
|
"RelatedProperties": [
|
||||||
|
"#/Attributes/ProcTurboMode"
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
"SettingsObject": {
|
||||||
|
"@odata.id": "/redfish/v1/Systems/437XR1138R2/BIOS/Settings"
|
||||||
|
},
|
||||||
|
"Time": "2016-03-07T14:44.30-05:00"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
from sushy import exceptions
|
||||||
|
from sushy.resources.system import bios
|
||||||
|
from sushy.tests.unit import base
|
||||||
|
|
||||||
|
|
||||||
|
class BiosTestCase(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(BiosTestCase, self).setUp()
|
||||||
|
self.conn = mock.Mock()
|
||||||
|
with open('sushy/tests/unit/json_samples/bios.json', 'r') as f:
|
||||||
|
bios_json = json.loads(f.read())
|
||||||
|
with open('sushy/tests/unit/json_samples/bios_settings.json',
|
||||||
|
'r') as f:
|
||||||
|
bios_settings_json = json.loads(f.read())
|
||||||
|
|
||||||
|
self.conn.get.return_value.json.side_effect = [
|
||||||
|
bios_json,
|
||||||
|
bios_settings_json,
|
||||||
|
bios_settings_json]
|
||||||
|
|
||||||
|
self.sys_bios = bios.Bios(
|
||||||
|
self.conn, '/redfish/v1/Systems/437XR1138R2/BIOS',
|
||||||
|
redfish_version='1.0.2')
|
||||||
|
|
||||||
|
def test__parse_attributes(self):
|
||||||
|
self.sys_bios._parse_attributes()
|
||||||
|
self.assertEqual('1.0.2', self.sys_bios.redfish_version)
|
||||||
|
self.assertEqual('BIOS', self.sys_bios.identity)
|
||||||
|
self.assertEqual('BIOS Configuration Current Settings',
|
||||||
|
self.sys_bios.name)
|
||||||
|
self.assertIsNone(self.sys_bios.description)
|
||||||
|
self.assertEqual('123', self.sys_bios._etag)
|
||||||
|
self.assertEqual('BiosAttributeRegistryP89.v1_0_0',
|
||||||
|
self.sys_bios.attribute_registry)
|
||||||
|
self.assertEqual('', self.sys_bios.attributes['AdminPhone'])
|
||||||
|
self.assertEqual('Uefi', self.sys_bios.attributes['BootMode'])
|
||||||
|
self.assertEqual(0, self.sys_bios.attributes['ProcCoreDisable'])
|
||||||
|
# testing here if settings subfield parsed by checking ETag,
|
||||||
|
# other settings fields tested in specific settings test
|
||||||
|
self.assertEqual('9234ac83b9700123cc32',
|
||||||
|
self.sys_bios._settings._etag)
|
||||||
|
self.assertEqual('(404) 555-1212',
|
||||||
|
self.sys_bios.pending_attributes['AdminPhone'])
|
||||||
|
|
||||||
|
def test_set_attribute(self):
|
||||||
|
self.sys_bios.set_attribute('ProcTurboMode', 'Disabled')
|
||||||
|
self.sys_bios._conn.patch.assert_called_once_with(
|
||||||
|
'/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||||
|
data={'Attributes': {'ProcTurboMode': 'Disabled'}},
|
||||||
|
headers={'If-Match': '123'})
|
||||||
|
|
||||||
|
def test_set_attribute_on_refresh(self):
|
||||||
|
# make it to instantiate pending attributes
|
||||||
|
self.sys_bios.pending_attributes
|
||||||
|
self.sys_bios.set_attribute('ProcTurboMode', 'Disabled')
|
||||||
|
self.assertTrue(self.sys_bios._pending_settings_resource._is_stale)
|
||||||
|
# make it to refresh pending attributes on next retrieval
|
||||||
|
self.sys_bios.pending_attributes
|
||||||
|
self.assertFalse(self.sys_bios._pending_settings_resource._is_stale)
|
||||||
|
|
||||||
|
def test_set_attributes(self):
|
||||||
|
self.sys_bios.set_attributes({'ProcTurboMode': 'Disabled',
|
||||||
|
'UsbControl': 'UsbDisabled'})
|
||||||
|
self.sys_bios._conn.patch.assert_called_once_with(
|
||||||
|
'/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||||
|
data={'Attributes': {'ProcTurboMode': 'Disabled',
|
||||||
|
'UsbControl': 'UsbDisabled'}},
|
||||||
|
headers={'If-Match': '123'})
|
||||||
|
|
||||||
|
def test_set_attributes_on_refresh(self):
|
||||||
|
# make it to instantiate pending attributes
|
||||||
|
self.sys_bios.pending_attributes
|
||||||
|
self.sys_bios.set_attributes({'ProcTurboMode': 'Disabled',
|
||||||
|
'UsbControl': 'UsbDisabled'})
|
||||||
|
self.assertTrue(self.sys_bios._pending_settings_resource._is_stale)
|
||||||
|
# make it to refresh pending attributes on next retrieval
|
||||||
|
self.sys_bios.pending_attributes
|
||||||
|
self.assertFalse(self.sys_bios._pending_settings_resource._is_stale)
|
||||||
|
|
||||||
|
def test__get_reset_bios_action_element(self):
|
||||||
|
value = self.sys_bios._get_reset_bios_action_element()
|
||||||
|
self.assertEqual('/redfish/v1/Systems/437XR1138R2/BIOS/Actions/'
|
||||||
|
'Bios.ResetBios',
|
||||||
|
value.target_uri)
|
||||||
|
|
||||||
|
def test_reset_bios_missing_action(self):
|
||||||
|
self.sys_bios._actions.reset_bios = None
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
exceptions.MissingActionError, '#Bios.ResetBios',
|
||||||
|
self.sys_bios.reset_bios)
|
||||||
|
|
||||||
|
def test__parse_attributes_missing_reset_bios_target(self):
|
||||||
|
self.sys_bios.json['Actions']['#Bios.ResetBios'].pop(
|
||||||
|
'target')
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
exceptions.MissingAttributeError,
|
||||||
|
'attribute Actions/#Bios.ResetBios/target',
|
||||||
|
self.sys_bios._parse_attributes)
|
||||||
|
|
||||||
|
def test_reset_bios(self):
|
||||||
|
self.sys_bios.reset_bios()
|
||||||
|
self.sys_bios._conn.post.assert_called_once_with(
|
||||||
|
'/redfish/v1/Systems/437XR1138R2/BIOS/Actions/Bios.ResetBios')
|
||||||
|
|
||||||
|
def test__get_change_password_element(self):
|
||||||
|
value = self.sys_bios._get_change_password_element()
|
||||||
|
self.assertEqual("/redfish/v1/Systems/437XR1138R2/BIOS/Actions/"
|
||||||
|
"Bios.ChangePassword",
|
||||||
|
value.target_uri)
|
||||||
|
|
||||||
|
def test_change_password_missing_action(self):
|
||||||
|
self.sys_bios._actions.change_password = None
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
exceptions.MissingActionError, '#Bios.ChangePassword',
|
||||||
|
self.sys_bios.change_password, 'newpassword',
|
||||||
|
'oldpassword',
|
||||||
|
'adminpassword')
|
||||||
|
|
||||||
|
def test__parse_attributes_missing_change_password_target(self):
|
||||||
|
self.sys_bios.json['Actions']['#Bios.ChangePassword'].pop(
|
||||||
|
'target')
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
exceptions.MissingAttributeError,
|
||||||
|
'attribute Actions/#Bios.ChangePassword/target',
|
||||||
|
self.sys_bios._parse_attributes)
|
||||||
|
|
||||||
|
def test_change_password(self):
|
||||||
|
self.sys_bios.change_password('newpassword',
|
||||||
|
'oldpassword',
|
||||||
|
'adminpassword')
|
||||||
|
self.sys_bios._conn.post.assert_called_once_with(
|
||||||
|
'/redfish/v1/Systems/437XR1138R2/BIOS/Actions/Bios.ChangePassword',
|
||||||
|
data={'OldPassword': 'oldpassword',
|
||||||
|
'NewPassword': 'newpassword',
|
||||||
|
'PasswordName': 'adminpassword'})
|
|
@ -19,6 +19,7 @@ import mock
|
||||||
|
|
||||||
import sushy
|
import sushy
|
||||||
from sushy import exceptions
|
from sushy import exceptions
|
||||||
|
from sushy.resources.system import bios
|
||||||
from sushy.resources.system import constants as sys_cons
|
from sushy.resources.system import constants as sys_cons
|
||||||
from sushy.resources.system import ethernet_interface
|
from sushy.resources.system import ethernet_interface
|
||||||
from sushy.resources.system import mappings as sys_map
|
from sushy.resources.system import mappings as sys_map
|
||||||
|
@ -66,6 +67,7 @@ class SystemTestCase(base.TestCase):
|
||||||
self.assertEqual("OK", self.sys_inst.memory_summary.health)
|
self.assertEqual("OK", self.sys_inst.memory_summary.health)
|
||||||
self.assertIsNone(self.sys_inst._processors)
|
self.assertIsNone(self.sys_inst._processors)
|
||||||
self.assertIsNone(self.sys_inst._ethernet_interfaces)
|
self.assertIsNone(self.sys_inst._ethernet_interfaces)
|
||||||
|
self.assertIsNone(self.sys_inst._bios)
|
||||||
|
|
||||||
def test__parse_attributes_missing_actions(self):
|
def test__parse_attributes_missing_actions(self):
|
||||||
self.sys_inst.json.pop('Actions')
|
self.sys_inst.json.pop('Actions')
|
||||||
|
@ -379,6 +381,18 @@ class SystemTestCase(base.TestCase):
|
||||||
self.assertIsInstance(self.sys_inst._ethernet_interfaces,
|
self.assertIsInstance(self.sys_inst._ethernet_interfaces,
|
||||||
ethernet_interface.EthernetInterfaceCollection)
|
ethernet_interface.EthernetInterfaceCollection)
|
||||||
|
|
||||||
|
def test_bios(self):
|
||||||
|
self.conn.get.return_value.json.reset_mock()
|
||||||
|
bios_return_value = None
|
||||||
|
with open('sushy/tests/unit/json_samples/bios.json', 'r') as f:
|
||||||
|
bios_return_value = json.loads(f.read())
|
||||||
|
self.conn.get.return_value.json.side_effect = [bios_return_value]
|
||||||
|
|
||||||
|
self.assertIsNone(self.sys_inst._bios)
|
||||||
|
self.assertIsInstance(self.sys_inst.bios, bios.Bios)
|
||||||
|
self.assertEqual('BIOS Configuration Current Settings',
|
||||||
|
self.sys_inst.bios.name)
|
||||||
|
|
||||||
|
|
||||||
class SystemCollectionTestCase(base.TestCase):
|
class SystemCollectionTestCase(base.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from sushy.resources import settings
|
||||||
|
from sushy.tests.unit import base
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsFieldTestCase(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(SettingsFieldTestCase, self).setUp()
|
||||||
|
with open('sushy/tests/unit/json_samples/settings.json',
|
||||||
|
'r') as f:
|
||||||
|
self.json = json.loads(f.read())
|
||||||
|
|
||||||
|
self.settings = settings.SettingsField()
|
||||||
|
|
||||||
|
def test__load(self):
|
||||||
|
instance = self.settings._load(self.json, mock.Mock())
|
||||||
|
|
||||||
|
self.assertEqual('9234ac83b9700123cc32',
|
||||||
|
instance._etag)
|
||||||
|
self.assertEqual('2016-03-07T14:44.30-05:00',
|
||||||
|
instance.time)
|
||||||
|
self.assertEqual('/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||||
|
instance._settings_object_idref.resource_uri)
|
||||||
|
self.assertEqual('Base.1.0.SettingsFailed',
|
||||||
|
instance.messages[0].message_id)
|
||||||
|
self.assertEqual('Settings update failed due to invalid value',
|
||||||
|
instance.messages[0].message)
|
||||||
|
self.assertEqual('High',
|
||||||
|
instance.messages[0].severity)
|
||||||
|
self.assertEqual('Fix the value and try again',
|
||||||
|
instance.messages[0].resolution)
|
||||||
|
self.assertEqual('arg1',
|
||||||
|
instance.messages[0].message_args[0])
|
||||||
|
self.assertEqual('#/Attributes/ProcTurboMode',
|
||||||
|
instance.messages[0]._related_properties[0])
|
||||||
|
self.assertEqual('/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||||
|
instance._settings_object_idref.resource_uri)
|
||||||
|
|
||||||
|
def test_commit(self):
|
||||||
|
conn = mock.Mock()
|
||||||
|
instance = self.settings._load(self.json, conn)
|
||||||
|
instance.commit(conn, {'Attributes': {'key': 'value'}})
|
||||||
|
conn.patch.assert_called_once_with(
|
||||||
|
'/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||||
|
data={'Attributes': {'key': 'value'}}, headers=None)
|
||||||
|
|
||||||
|
def test_commit_with_etag(self):
|
||||||
|
conn = mock.Mock()
|
||||||
|
instance = self.settings._load(self.json, conn)
|
||||||
|
instance.commit(conn,
|
||||||
|
{'Attributes': {'key': 'value'}},
|
||||||
|
'123')
|
||||||
|
conn.patch.assert_called_once_with(
|
||||||
|
'/redfish/v1/Systems/437XR1138R2/BIOS/Settings',
|
||||||
|
data={'Attributes': {'key': 'value'}},
|
||||||
|
headers={'If-Match': '123'})
|
Loading…
Reference in New Issue