Redfish: Adding the ability to set boot mode and devices
This commits adds new function 'set_pending_boot_mode', 'update_persistent_boot' and 'set_one_time_boot' to the server Change-Id: I7609eac74143f4b529b011ed1cbab96b578770b8
This commit is contained in:
parent
7488ae2048
commit
289de798cc
|
@ -72,8 +72,10 @@ SUPPORTED_REDFISH_METHODS = [
|
|||
'insert_virtual_media',
|
||||
'set_vm_status'
|
||||
'update_firmware',
|
||||
'set_vm_status',
|
||||
'get_persistent_boot_device',
|
||||
'set_one_time_boot',
|
||||
'update_persistent_boot',
|
||||
'set_pending_boot_mode',
|
||||
]
|
||||
|
||||
LOG = log.get_logger(__name__)
|
||||
|
|
|
@ -485,7 +485,88 @@ class RedfishOperations(operations.IloOperations):
|
|||
return PERSISTENT_BOOT_MAP.get(boot_device)
|
||||
except sushy.exceptions.SushyError as e:
|
||||
msg = (self._("The Redfish controller is unable to get "
|
||||
"persistent boot device.' Error %(error)s") %
|
||||
"persistent boot device. Error %(error)s") %
|
||||
{'error': str(e)})
|
||||
LOG.debug(msg)
|
||||
raise exception.IloError(msg)
|
||||
|
||||
def set_pending_boot_mode(self, boot_mode):
|
||||
"""Sets the boot mode of the system for next boot.
|
||||
|
||||
:param boot_mode: either 'uefi' or 'legacy'.
|
||||
:raises: IloInvalidInputError, on an invalid input.
|
||||
:raises: IloError, on an error from iLO.
|
||||
"""
|
||||
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||
|
||||
if boot_mode.upper() not in BOOT_MODE_MAP_REV.keys():
|
||||
msg = (('Invalid Boot mode: "%(boot_mode)s" specified, valid boot '
|
||||
'modes are either "uefi" or "legacy"')
|
||||
% {'boot_mode': boot_mode})
|
||||
raise exception.IloInvalidInputError(msg)
|
||||
|
||||
try:
|
||||
sushy_system.bios_settings.pending_settings.set_pending_boot_mode(
|
||||
BOOT_MODE_MAP_REV.get(boot_mode.upper()))
|
||||
except sushy.exceptions.SushyError as e:
|
||||
msg = (self._('The Redfish controller failed to set '
|
||||
'pending boot mode to %(boot_mode)s. '
|
||||
'Error: %(error)s') %
|
||||
{'boot_mode': boot_mode, 'error': str(e)})
|
||||
LOG.debug(msg)
|
||||
raise exception.IloError(msg)
|
||||
|
||||
def update_persistent_boot(self, devices=[], mac=None):
|
||||
"""Changes the persistent boot device order for the host
|
||||
|
||||
:param devices: ordered list of boot devices
|
||||
:param mac: intiator mac address, mandatory for iSCSI uefi boot
|
||||
:raises: IloError, on an error from iLO.
|
||||
:raises: IloInvalidInputError, if the given input is not valid.
|
||||
"""
|
||||
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||
# Check if the input is valid
|
||||
for item in devices:
|
||||
if item.upper() not in DEVICE_COMMON_TO_REDFISH:
|
||||
msg = (self._('Invalid input "%(device)s". Valid devices: '
|
||||
'NETWORK, HDD, ISCSI or CDROM.') %
|
||||
{'device': item})
|
||||
raise exception.IloInvalidInputError(msg)
|
||||
|
||||
try:
|
||||
sushy_system.update_persistent_boot(
|
||||
devices, persistent=True, mac=mac)
|
||||
except sushy.exceptions.SushyError as e:
|
||||
msg = (self._('The Redfish controller failed to update '
|
||||
'persistent boot device %(devices)s.'
|
||||
'Error: %(error)s') %
|
||||
{'devices': devices, 'error': str(e)})
|
||||
LOG.debug(msg)
|
||||
raise exception.IloError(msg)
|
||||
|
||||
def set_one_time_boot(self, device, mac=None):
|
||||
"""Configures a single boot from a specific device.
|
||||
|
||||
:param device: Device to be set as a one time boot device
|
||||
:param mac: intiator mac address, optional parameter
|
||||
:raises: IloError, on an error from iLO.
|
||||
:raises: IloInvalidInputError, if the given input is not valid.
|
||||
"""
|
||||
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||
# Check if the input is valid
|
||||
if device.upper() not in DEVICE_COMMON_TO_REDFISH:
|
||||
msg = (self._('Invalid input "%(device)s". Valid devices: '
|
||||
'NETWORK, HDD, ISCSI or CDROM.') %
|
||||
{'device': device})
|
||||
raise exception.IloInvalidInputError(msg)
|
||||
|
||||
try:
|
||||
sushy_system.update_persistent_boot(
|
||||
[device], persistent=False, mac=mac)
|
||||
except sushy.exceptions.SushyError as e:
|
||||
msg = (self._('The Redfish controller failed to set '
|
||||
'one time boot device %(device)s. '
|
||||
'Error: %(error)s') %
|
||||
{'device': device, 'error': str(e)})
|
||||
LOG.debug(msg)
|
||||
raise exception.IloError(msg)
|
||||
|
|
|
@ -17,6 +17,7 @@ from sushy.resources import base
|
|||
|
||||
from proliantutils import exception
|
||||
from proliantutils import log
|
||||
from proliantutils.redfish.resources.system import constants as sys_cons
|
||||
from proliantutils.redfish.resources.system import mappings
|
||||
from proliantutils.redfish import utils
|
||||
|
||||
|
@ -75,6 +76,21 @@ class BIOSPendingSettings(base.ResourceBase):
|
|||
boot_mode = base.MappedField(["Attributes", "BootMode"],
|
||||
mappings.GET_BIOS_BOOT_MODE_MAP)
|
||||
|
||||
def set_pending_boot_mode(self, boot_mode):
|
||||
"""Sets the boot mode of the system for next boot.
|
||||
|
||||
:param boot_mode: either sys_cons.BIOS_BOOT_MODE_LEGACY_BIOS,
|
||||
sys_cons.BIOS_BOOT_MODE_UEFI.
|
||||
"""
|
||||
bios_properties = {}
|
||||
bios_properties['BootMode'] = (
|
||||
mappings.GET_BIOS_BOOT_MODE_MAP_REV.get(boot_mode))
|
||||
|
||||
if boot_mode == sys_cons.BIOS_BOOT_MODE_UEFI:
|
||||
bios_properties['UefiOptimizedBoot'] = 'Enabled'
|
||||
|
||||
self._conn.patch(self._path, bios_properties)
|
||||
|
||||
|
||||
class BIOSBootSettings(base.ResourceBase):
|
||||
|
||||
|
|
|
@ -23,3 +23,10 @@ PUSH_POWER_BUTTON_PRESS_AND_HOLD = 'press and hold'
|
|||
|
||||
BIOS_BOOT_MODE_LEGACY_BIOS = 'legacy bios'
|
||||
BIOS_BOOT_MODE_UEFI = 'uefi'
|
||||
|
||||
# Persistent boot device for set
|
||||
|
||||
BOOT_SOURCE_TARGET_CD = 'Cd'
|
||||
BOOT_SOURCE_TARGET_PXE = 'Pxe'
|
||||
BOOT_SOURCE_TARGET_UEFI_TARGET = 'UefiTarget'
|
||||
BOOT_SOURCE_TARGET_HDD = 'Hdd'
|
||||
|
|
|
@ -14,17 +14,26 @@
|
|||
|
||||
__author__ = 'HPE'
|
||||
|
||||
import sushy
|
||||
from sushy.resources import base
|
||||
from sushy.resources.system import system
|
||||
|
||||
from proliantutils import exception
|
||||
from proliantutils import log
|
||||
from proliantutils.redfish.resources.system import bios
|
||||
from proliantutils.redfish.resources.system import constants as sys_cons
|
||||
from proliantutils.redfish.resources.system import mappings
|
||||
from proliantutils.redfish import utils
|
||||
|
||||
LOG = log.get_logger(__name__)
|
||||
|
||||
PERSISTENT_BOOT_DEVICE_MAP = {
|
||||
'CDROM': sys_cons.BOOT_SOURCE_TARGET_CD,
|
||||
'NETWORK': sys_cons.BOOT_SOURCE_TARGET_PXE,
|
||||
'ISCSI': sys_cons.BOOT_SOURCE_TARGET_UEFI_TARGET,
|
||||
'HDD': sys_cons.BOOT_SOURCE_TARGET_HDD
|
||||
}
|
||||
|
||||
|
||||
class PowerButtonActionField(base.CompositeField):
|
||||
allowed_values = base.Field('PushType@Redfish.AllowableValues',
|
||||
|
@ -95,3 +104,54 @@ class HPESystem(system.System):
|
|||
redfish_version=self.redfish_version)
|
||||
|
||||
return self._bios_settings
|
||||
|
||||
def update_persistent_boot(self, devices=[], persistent=False,
|
||||
mac=None):
|
||||
"""Changes the persistent boot device order in BIOS boot mode for host
|
||||
|
||||
Note: It uses first boot device from the devices and ignores rest.
|
||||
|
||||
:param devices: ordered list of boot devices
|
||||
:param persistent: Boolean flag to indicate if the device to be set as
|
||||
a persistent boot device
|
||||
:param mac: intiator mac address, mandotory for iSCSI uefi boot
|
||||
:raises: IloError, on an error from iLO.
|
||||
:raises: IloInvalidInputError, if the given input is not valid.
|
||||
"""
|
||||
new_device = devices[0]
|
||||
tenure = 'Continuous' if persistent else 'Once'
|
||||
|
||||
try:
|
||||
boot_sources = self.bios_settings.boot_settings.boot_sources
|
||||
except sushy.exceptions.SushyError:
|
||||
msg = ('The BIOS Boot Settings was not found.')
|
||||
raise exception.IloError(msg)
|
||||
|
||||
if devices[0].upper() in PERSISTENT_BOOT_DEVICE_MAP:
|
||||
new_device = PERSISTENT_BOOT_DEVICE_MAP[devices[0].upper()]
|
||||
|
||||
new_boot_settings = {}
|
||||
if new_device is 'UefiTarget':
|
||||
if not mac:
|
||||
msg = ('Mac is needed for iscsi uefi boot')
|
||||
raise exception.IloInvalidInputError(msg)
|
||||
|
||||
boot_string = None
|
||||
for boot_source in boot_sources:
|
||||
if(mac.upper() in boot_source['UEFIDevicePath'] and
|
||||
'iSCSI' in boot_source['UEFIDevicePath']):
|
||||
boot_string = boot_source['StructuredBootString']
|
||||
break
|
||||
|
||||
if not boot_string:
|
||||
msg = ('MAC provided "%s" is Invalid' % mac)
|
||||
raise exception.IloInvalidInputError(msg)
|
||||
|
||||
uefi_boot_settings = {}
|
||||
uefi_boot_settings['Boot'] = (
|
||||
{'UefiTargetBootSourceOverride': boot_string})
|
||||
self._conn.patch(self._path, uefi_boot_settings)
|
||||
|
||||
new_boot_settings['Boot'] = {'BootSourceOverrideEnabled': tenure,
|
||||
'BootSourceOverrideTarget': new_device}
|
||||
self._conn.patch(self._path, new_boot_settings)
|
||||
|
|
|
@ -105,7 +105,14 @@
|
|||
"CorrelatableID": "PciRoot(0x0)/Pci(0x1C,0x4)/Pci(0x0,0x4)/USB(0x2,0x0)",
|
||||
"StructuredBootString": "FD.Virtual.4.1",
|
||||
"UEFIDevicePath": "PciRoot(0x0)/Pci(0x1C,0x4)/Pci(0x0,0x4)/USB(0x2,0x0)"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
"BootString": "Embedded LOM 1 Port 1 : HP Ethernet 1Gb 2-port 361i Adapter - NIC (iSCSI IPv4) ",
|
||||
"CorrelatableID": "PciRoot(0x0)/Pci(0x2,0x3)/Pci(0x0,0x0)",
|
||||
"StructuredBootString": "NIC.LOM.1.1.iSCSI",
|
||||
"UEFIDevicePath": "PciRoot(0x0)/Pci(0x2,0x3)/Pci(0x0,0x0)/MAC (C4346BB7EF30,0x1)/IPv4(0.0.0.0)/iSCSI(iqn.2016.org.de:,0x1,0x0,None,None,None,TCP)"
|
||||
}
|
||||
],
|
||||
"DefaultBootOrder":
|
||||
[
|
||||
|
@ -225,7 +232,7 @@
|
|||
"Id": "boot",
|
||||
"Name": "Boot Order Current Settings"
|
||||
},
|
||||
|
||||
|
||||
"BIOS_boot_without_boot_sources": {
|
||||
"@Redfish.Settings":
|
||||
{
|
||||
|
|
|
@ -97,6 +97,23 @@ class BIOSPendingSettingsTestCase(testtools.TestCase):
|
|||
self.assertEqual(sys_cons.BIOS_BOOT_MODE_UEFI,
|
||||
self.bios_settings_inst.boot_mode)
|
||||
|
||||
def test_set_pending_boot_mode_bios(self):
|
||||
self.bios_settings_inst.set_pending_boot_mode(
|
||||
sys_cons.BIOS_BOOT_MODE_LEGACY_BIOS)
|
||||
data = {}
|
||||
data['BootMode'] = 'LegacyBios'
|
||||
self.bios_settings_inst._conn.patch.assert_called_once_with(
|
||||
'/redfish/v1/Systems/1/bios/settings', data)
|
||||
|
||||
def test_set_pending_boot_mode_uefi(self):
|
||||
self.bios_settings_inst.set_pending_boot_mode(
|
||||
sys_cons.BIOS_BOOT_MODE_UEFI)
|
||||
data = {}
|
||||
data['UefiOptimizedBoot'] = 'Enabled'
|
||||
data['BootMode'] = 'Uefi'
|
||||
self.bios_settings_inst._conn.patch.assert_called_once_with(
|
||||
'/redfish/v1/Systems/1/bios/settings', data)
|
||||
|
||||
|
||||
class BIOSBootSettingsTestCase(testtools.TestCase):
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import json
|
||||
|
||||
import mock
|
||||
import sushy
|
||||
import testtools
|
||||
|
||||
from proliantutils import exception
|
||||
|
@ -79,3 +80,94 @@ class HPESystemTestCase(testtools.TestCase):
|
|||
self.assertIs(actual_bios,
|
||||
self.sys_inst.bios_settings)
|
||||
self.conn.get.return_value.json.assert_not_called()
|
||||
|
||||
def test_update_persistent_boot_persistent(self):
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios.json', 'r') as f:
|
||||
bios_mock = json.loads(f.read())
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios_boot.json', 'r') as g:
|
||||
boot_mock = json.loads(g.read())
|
||||
self.conn.get.return_value.json.side_effect = [bios_mock['Default'],
|
||||
boot_mock['Default']]
|
||||
self.sys_inst.update_persistent_boot(['CDROM'], True)
|
||||
data = {}
|
||||
data['Boot'] = {'BootSourceOverrideEnabled': 'Continuous',
|
||||
'BootSourceOverrideTarget': 'Cd'}
|
||||
self.sys_inst._conn.patch.assert_called_once_with(
|
||||
'/redfish/v1/Systems/1', data)
|
||||
|
||||
def test_update_persistent_boot_not_persistent(self):
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios.json', 'r') as f:
|
||||
bios_mock = json.loads(f.read())
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios_boot.json', 'r') as f:
|
||||
boot_mock = json.loads(f.read())
|
||||
self.conn.get.return_value.json.side_effect = [bios_mock['Default'],
|
||||
boot_mock['Default']]
|
||||
self.sys_inst.update_persistent_boot(['CDROM'], False)
|
||||
data = {}
|
||||
data['Boot'] = {'BootSourceOverrideEnabled': 'Once',
|
||||
'BootSourceOverrideTarget': 'Cd'}
|
||||
self.sys_inst._conn.patch.assert_called_once_with(
|
||||
'/redfish/v1/Systems/1', data)
|
||||
|
||||
def test_update_persistent_boot_uefi_target(self):
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios.json', 'r') as f:
|
||||
bios_mock = json.loads(f.read())
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios_boot.json', 'r') as f:
|
||||
boot_mock = json.loads(f.read())
|
||||
self.conn.get.return_value.json.side_effect = [bios_mock['Default'],
|
||||
boot_mock['Default']]
|
||||
self.sys_inst.update_persistent_boot(['ISCSI'], persistent=True,
|
||||
mac='C4346BB7EF30')
|
||||
data = {}
|
||||
data['Boot'] = {'UefiTargetBootSourceOverride': 'NIC.LOM.1.1.iSCSI'}
|
||||
new_data = {}
|
||||
new_data['Boot'] = {'BootSourceOverrideEnabled': 'Continuous',
|
||||
'BootSourceOverrideTarget': 'UefiTarget'}
|
||||
calls = [mock.call('/redfish/v1/Systems/1', data),
|
||||
mock.call('/redfish/v1/Systems/1', new_data)]
|
||||
self.sys_inst._conn.patch.assert_has_calls(calls)
|
||||
|
||||
def test_update_persistent_boot_uefi_target_without_mac(self):
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios.json', 'r') as f:
|
||||
bios_mock = json.loads(f.read())
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios_boot.json', 'r') as f:
|
||||
boot_mock = json.loads(f.read())
|
||||
self.conn.get.return_value.json.side_effect = [bios_mock['Default'],
|
||||
boot_mock['Default']]
|
||||
self.assertRaisesRegex(
|
||||
exception.IloInvalidInputError,
|
||||
'Mac is needed for iscsi uefi boot',
|
||||
self.sys_inst.update_persistent_boot, ['ISCSI'], True, None)
|
||||
|
||||
def test_update_persistent_boot_uefi_target_invalid_mac(self):
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios.json', 'r') as f:
|
||||
bios_mock = json.loads(f.read())
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios_boot.json', 'r') as f:
|
||||
boot_mock = json.loads(f.read())
|
||||
self.conn.get.return_value.json.side_effect = [bios_mock['Default'],
|
||||
boot_mock['Default']]
|
||||
self.assertRaisesRegex(
|
||||
exception.IloInvalidInputError,
|
||||
'MAC provided "12345678" is Invalid',
|
||||
self.sys_inst.update_persistent_boot, ['ISCSI'], True, '12345678')
|
||||
|
||||
def test_update_persistent_boot_fail(self):
|
||||
with open('proliantutils/tests/redfish/'
|
||||
'json_samples/bios.json', 'r') as f:
|
||||
bios_mock = json.loads(f.read())
|
||||
self.conn.get.return_value.json.side_effect = (
|
||||
[bios_mock['Default'], sushy.exceptions.SushyError])
|
||||
self.assertRaisesRegex(
|
||||
exception.IloError,
|
||||
'The BIOS Boot Settings was not found.',
|
||||
self.sys_inst.update_persistent_boot, ['CDROM'], True, None)
|
||||
|
|
|
@ -504,3 +504,72 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
|||
exception.IloError,
|
||||
'The Redfish controller is unable to get persistent boot device.',
|
||||
self.rf_client.get_persistent_boot_device)
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||
def test_set_pending_boot_mode(self, get_system_mock):
|
||||
self.rf_client.set_pending_boot_mode('uefi')
|
||||
(get_system_mock.return_value.
|
||||
bios_settings.pending_settings.set_pending_boot_mode.
|
||||
assert_called_once_with('uefi'))
|
||||
|
||||
def test_set_pending_boot_mode_invalid_input(self):
|
||||
self.assertRaisesRegex(
|
||||
exception.IloInvalidInputError,
|
||||
'Invalid Boot mode: "test" specified',
|
||||
self.rf_client.set_pending_boot_mode, 'test')
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||
def test_set_pending_boot_mode_fail(self, get_system_mock):
|
||||
(get_system_mock.return_value.bios_settings.
|
||||
pending_settings.set_pending_boot_mode.side_effect) = (
|
||||
sushy.exceptions.SushyError)
|
||||
self.assertRaisesRegex(
|
||||
exception.IloError,
|
||||
'The Redfish controller failed to set pending boot mode.',
|
||||
self.rf_client.set_pending_boot_mode, 'uefi')
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||
def test_update_persistent_boot(self, get_system_mock):
|
||||
self.rf_client.update_persistent_boot(['NETWORK'])
|
||||
(get_system_mock.return_value.update_persistent_boot.
|
||||
assert_called_once_with(['NETWORK'], mac=None, persistent=True))
|
||||
|
||||
def test_update_persistent_boot_invalid_input(self):
|
||||
self.assertRaisesRegex(
|
||||
exception.IloInvalidInputError,
|
||||
('Invalid input "test". Valid devices: NETWORK, '
|
||||
'HDD, ISCSI or CDROM.'),
|
||||
self.rf_client.update_persistent_boot, ['test'])
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||
def test_update_persistent_boot_fail(self, get_system_mock):
|
||||
get_system_mock.return_value.update_persistent_boot.side_effect = (
|
||||
sushy.exceptions.SushyError)
|
||||
self.assertRaisesRegex(
|
||||
exception.IloError,
|
||||
'The Redfish controller failed to update persistent boot.',
|
||||
self.rf_client.update_persistent_boot,
|
||||
['NETWORK'])
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||
def test_set_one_time_boot(self, get_system_mock):
|
||||
self.rf_client.set_one_time_boot('CDROM')
|
||||
(get_system_mock.return_value.update_persistent_boot.
|
||||
assert_called_once_with(['CDROM'], mac=None, persistent=False))
|
||||
|
||||
def test_set_one_time_boot_invalid_input(self):
|
||||
self.assertRaisesRegex(
|
||||
exception.IloInvalidInputError,
|
||||
('Invalid input "test". Valid devices: NETWORK, '
|
||||
'HDD, ISCSI or CDROM.'),
|
||||
self.rf_client.set_one_time_boot, 'test')
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||
def test_set_one_time_boot_fail(self, get_system_mock):
|
||||
get_system_mock.return_value.update_persistent_boot.side_effect = (
|
||||
sushy.exceptions.SushyError)
|
||||
self.assertRaisesRegex(
|
||||
exception.IloError,
|
||||
'The Redfish controller failed to set one time boot.',
|
||||
self.rf_client.set_one_time_boot,
|
||||
'CDROM')
|
||||
|
|
Loading…
Reference in New Issue