Merge "Redfish: Adds APIs to support boot from http url"
This commit is contained in:
commit
c57437dc85
|
@ -119,7 +119,9 @@ SUPPORTED_REDFISH_METHODS = [
|
||||||
'set_iscsi_info',
|
'set_iscsi_info',
|
||||||
'unset_iscsi_info',
|
'unset_iscsi_info',
|
||||||
'get_iscsi_initiator_info',
|
'get_iscsi_initiator_info',
|
||||||
'set_iscsi_initiator_info'
|
'set_iscsi_initiator_info',
|
||||||
|
'set_http_boot_url',
|
||||||
|
'get_http_boot_url'
|
||||||
]
|
]
|
||||||
|
|
||||||
LOG = log.get_logger(__name__)
|
LOG = log.get_logger(__name__)
|
||||||
|
|
|
@ -55,6 +55,7 @@ DEVICE_COMMON_TO_REDFISH = {
|
||||||
'HDD': sushy.BOOT_SOURCE_TARGET_HDD,
|
'HDD': sushy.BOOT_SOURCE_TARGET_HDD,
|
||||||
'CDROM': sushy.BOOT_SOURCE_TARGET_CD,
|
'CDROM': sushy.BOOT_SOURCE_TARGET_CD,
|
||||||
'ISCSI': sushy.BOOT_SOURCE_TARGET_UEFI_TARGET,
|
'ISCSI': sushy.BOOT_SOURCE_TARGET_UEFI_TARGET,
|
||||||
|
'UEFIHTTP': sushy.BOOT_SOURCE_TARGET_UEFI_HTTP,
|
||||||
'NONE': sushy.BOOT_SOURCE_TARGET_NONE
|
'NONE': sushy.BOOT_SOURCE_TARGET_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +74,7 @@ PERSISTENT_BOOT_MAP = {
|
||||||
sushy.BOOT_SOURCE_TARGET_HDD: 'HDD',
|
sushy.BOOT_SOURCE_TARGET_HDD: 'HDD',
|
||||||
sushy.BOOT_SOURCE_TARGET_CD: 'CDROM',
|
sushy.BOOT_SOURCE_TARGET_CD: 'CDROM',
|
||||||
sushy.BOOT_SOURCE_TARGET_UEFI_TARGET: 'NETWORK',
|
sushy.BOOT_SOURCE_TARGET_UEFI_TARGET: 'NETWORK',
|
||||||
|
sushy.BOOT_SOURCE_TARGET_UEFI_HTTP: 'UEFIHTTP',
|
||||||
sushy.BOOT_SOURCE_TARGET_NONE: 'NONE'
|
sushy.BOOT_SOURCE_TARGET_NONE: 'NONE'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +570,7 @@ class RedfishOperations(operations.IloOperations):
|
||||||
for item in devices:
|
for item in devices:
|
||||||
if item.upper() not in DEVICE_COMMON_TO_REDFISH:
|
if item.upper() not in DEVICE_COMMON_TO_REDFISH:
|
||||||
msg = (self._('Invalid input "%(device)s". Valid devices: '
|
msg = (self._('Invalid input "%(device)s". Valid devices: '
|
||||||
'NETWORK, HDD, ISCSI or CDROM.') %
|
'NETWORK, HDD, ISCSI, UEFIHTTP or CDROM.') %
|
||||||
{'device': item})
|
{'device': item})
|
||||||
raise exception.IloInvalidInputError(msg)
|
raise exception.IloInvalidInputError(msg)
|
||||||
|
|
||||||
|
@ -594,7 +596,7 @@ class RedfishOperations(operations.IloOperations):
|
||||||
# Check if the input is valid
|
# Check if the input is valid
|
||||||
if device.upper() not in DEVICE_COMMON_TO_REDFISH:
|
if device.upper() not in DEVICE_COMMON_TO_REDFISH:
|
||||||
msg = (self._('Invalid input "%(device)s". Valid devices: '
|
msg = (self._('Invalid input "%(device)s". Valid devices: '
|
||||||
'NETWORK, HDD, ISCSI or CDROM.') %
|
'NETWORK, HDD, ISCSI, UEFIHTTP or CDROM.') %
|
||||||
{'device': device})
|
{'device': device})
|
||||||
raise exception.IloInvalidInputError(msg)
|
raise exception.IloInvalidInputError(msg)
|
||||||
|
|
||||||
|
@ -1276,3 +1278,52 @@ class RedfishOperations(operations.IloOperations):
|
||||||
"""
|
"""
|
||||||
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||||
return sushy_system.get_disk_types()
|
return sushy_system.get_disk_types()
|
||||||
|
|
||||||
|
def get_http_boot_url(self):
|
||||||
|
"""Sets current BIOS settings to the provided data.
|
||||||
|
|
||||||
|
:raises: IloError, on an error from iLO.
|
||||||
|
:return: Returns the setting 'UrlBootFile' if set previously.
|
||||||
|
"""
|
||||||
|
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||||
|
url = None
|
||||||
|
try:
|
||||||
|
settings = sushy_system.bios_settings.json
|
||||||
|
attributes = settings.get('Attributes')
|
||||||
|
url = attributes.get('UrlBootFile')
|
||||||
|
except sushy.exceptions.SushyError as e:
|
||||||
|
msg = (self._('The attribute "UrlBootFile" not found.'
|
||||||
|
' Error %(error)s') %
|
||||||
|
{'error': str(e)})
|
||||||
|
LOG.debug(msg)
|
||||||
|
raise exception.IloError(msg)
|
||||||
|
return url
|
||||||
|
|
||||||
|
def set_http_boot_url(self, url, is_dhcp_enabled=True):
|
||||||
|
"""Sets HTTP boot URL to boot from it.
|
||||||
|
|
||||||
|
:param: url: HTTP URL of the image to be booted on the iLO.
|
||||||
|
:param: is_dhcp_enabled: True if no static IP is set on the node and
|
||||||
|
preferred to use DHCP service running in the network.
|
||||||
|
If False, the MAC is expected to be configured with static IP.
|
||||||
|
:raises: IloError, on an error from iLO.
|
||||||
|
"""
|
||||||
|
if not url:
|
||||||
|
raise exception.IloError("Could not set http url with"
|
||||||
|
" empty URL")
|
||||||
|
data = {
|
||||||
|
'PreBootNetwork': 'Auto',
|
||||||
|
'UrlBootFile': url,
|
||||||
|
'Dhcpv4': 'Enabled' if is_dhcp_enabled else 'Disabled'
|
||||||
|
}
|
||||||
|
|
||||||
|
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||||
|
try:
|
||||||
|
settings_required = sushy_system.bios_settings.pending_settings
|
||||||
|
settings_required.update_bios_data_by_post(data)
|
||||||
|
except sushy.exceptions.SushyError as e:
|
||||||
|
msg = (self._('Could not set HTTPS URL on the iLO.'
|
||||||
|
' Error %(error)s') %
|
||||||
|
{'error': str(e)})
|
||||||
|
LOG.debug(msg)
|
||||||
|
raise exception.IloError(msg)
|
||||||
|
|
|
@ -48,7 +48,8 @@ PERSISTENT_BOOT_DEVICE_MAP = {
|
||||||
'CDROM': sushy.BOOT_SOURCE_TARGET_CD,
|
'CDROM': sushy.BOOT_SOURCE_TARGET_CD,
|
||||||
'NETWORK': sushy.BOOT_SOURCE_TARGET_PXE,
|
'NETWORK': sushy.BOOT_SOURCE_TARGET_PXE,
|
||||||
'ISCSI': sushy.BOOT_SOURCE_TARGET_UEFI_TARGET,
|
'ISCSI': sushy.BOOT_SOURCE_TARGET_UEFI_TARGET,
|
||||||
'HDD': sushy.BOOT_SOURCE_TARGET_HDD
|
'HDD': sushy.BOOT_SOURCE_TARGET_HDD,
|
||||||
|
'UEFIHTTP': sushy.BOOT_SOURCE_TARGET_UEFI_HTTP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,7 @@
|
||||||
"UefiShellStartupUrlFromDhcp": "Disabled",
|
"UefiShellStartupUrlFromDhcp": "Disabled",
|
||||||
"UncoreFreqScaling": "Auto",
|
"UncoreFreqScaling": "Auto",
|
||||||
"UpiPrefetcher": "Enabled",
|
"UpiPrefetcher": "Enabled",
|
||||||
"UrlBootFile": "",
|
"UrlBootFile": "http://w.x.y.z/ironic.iso",
|
||||||
"UrlBootFile2": "",
|
"UrlBootFile2": "",
|
||||||
"UrlBootFile3": "",
|
"UrlBootFile3": "",
|
||||||
"UrlBootFile4": "",
|
"UrlBootFile4": "",
|
||||||
|
|
|
@ -555,7 +555,7 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
exception.IloInvalidInputError,
|
exception.IloInvalidInputError,
|
||||||
('Invalid input "test". Valid devices: NETWORK, '
|
('Invalid input "test". Valid devices: NETWORK, '
|
||||||
'HDD, ISCSI or CDROM.'),
|
'HDD, ISCSI, UEFIHTTP or CDROM.'),
|
||||||
self.rf_client.update_persistent_boot, ['test'])
|
self.rf_client.update_persistent_boot, ['test'])
|
||||||
|
|
||||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
@ -578,7 +578,7 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
exception.IloInvalidInputError,
|
exception.IloInvalidInputError,
|
||||||
('Invalid input "test". Valid devices: NETWORK, '
|
('Invalid input "test". Valid devices: NETWORK, '
|
||||||
'HDD, ISCSI or CDROM.'),
|
'HDD, ISCSI, UEFIHTTP or CDROM.'),
|
||||||
self.rf_client.set_one_time_boot, 'test')
|
self.rf_client.set_one_time_boot, 'test')
|
||||||
|
|
||||||
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
@ -1913,3 +1913,98 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
||||||
'SSD']
|
'SSD']
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
['HDD', 'SSD'], self.rf_client.get_available_disk_types())
|
['HDD', 'SSD'], self.rf_client.get_available_disk_types())
|
||||||
|
|
||||||
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
def test_get_http_boot_url(self, get_system_mock):
|
||||||
|
with open('proliantutils/tests/redfish/'
|
||||||
|
'json_samples/bios.json', 'r') as f:
|
||||||
|
jsonval = json.loads(f.read()).get("Default")
|
||||||
|
type(
|
||||||
|
get_system_mock.return_value.bios_settings).json = (
|
||||||
|
mock.PropertyMock(return_value=jsonval))
|
||||||
|
settings = jsonval.get('Attributes')
|
||||||
|
expected_url_boot_file = settings.get('UrlBootFile')
|
||||||
|
|
||||||
|
actual_url_boot_file = self.rf_client.get_http_boot_url()
|
||||||
|
self.assertEqual(expected_url_boot_file, actual_url_boot_file)
|
||||||
|
|
||||||
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
def test_get_http_boot_url_fail(self, get_system_mock):
|
||||||
|
bios_mock = mock.PropertyMock(
|
||||||
|
side_effect=sushy.exceptions.SushyError)
|
||||||
|
type(get_system_mock.return_value).bios_settings = bios_mock
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
exception.IloError,
|
||||||
|
'The attribute "UrlBootFile" not found.',
|
||||||
|
self.rf_client.get_http_boot_url)
|
||||||
|
|
||||||
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
def test_set_http_boot_url_dhcp_default(self, system_mock):
|
||||||
|
bios_ps_mock = mock.MagicMock(spec=bios.BIOSPendingSettings)
|
||||||
|
pending_settings_mock = mock.PropertyMock(return_value=bios_ps_mock)
|
||||||
|
type(system_mock.return_value.bios_settings).pending_settings = (
|
||||||
|
pending_settings_mock)
|
||||||
|
|
||||||
|
url = 'a.b.c'
|
||||||
|
expected_parameter = {
|
||||||
|
'PreBootNetwork': 'Auto',
|
||||||
|
'UrlBootFile': 'a.b.c',
|
||||||
|
'Dhcpv4': 'Enabled'
|
||||||
|
}
|
||||||
|
self.rf_client.set_http_boot_url(url)
|
||||||
|
|
||||||
|
bios_ps_mock.update_bios_data_by_post.assert_called_once_with(
|
||||||
|
expected_parameter)
|
||||||
|
|
||||||
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
def test_set_http_boot_url_dhcp_enabled(self, system_mock):
|
||||||
|
bios_ps_mock = mock.MagicMock(spec=bios.BIOSPendingSettings)
|
||||||
|
pending_settings_mock = mock.PropertyMock(return_value=bios_ps_mock)
|
||||||
|
type(system_mock.return_value.bios_settings).pending_settings = (
|
||||||
|
pending_settings_mock)
|
||||||
|
|
||||||
|
dhcp_enabled = True
|
||||||
|
url = 'a.b.c'
|
||||||
|
expected_parameter = {
|
||||||
|
'PreBootNetwork': 'Auto',
|
||||||
|
'UrlBootFile': 'a.b.c',
|
||||||
|
'Dhcpv4': 'Enabled'
|
||||||
|
}
|
||||||
|
self.rf_client.set_http_boot_url(url, dhcp_enabled)
|
||||||
|
|
||||||
|
bios_ps_mock.update_bios_data_by_post.assert_called_once_with(
|
||||||
|
expected_parameter)
|
||||||
|
|
||||||
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
def test_set_http_boot_url_dhcp_disabled(self, system_mock):
|
||||||
|
bios_ps_mock = mock.MagicMock(spec=bios.BIOSPendingSettings)
|
||||||
|
pending_settings_mock = mock.PropertyMock(return_value=bios_ps_mock)
|
||||||
|
type(system_mock.return_value.bios_settings).pending_settings = (
|
||||||
|
pending_settings_mock)
|
||||||
|
|
||||||
|
dhcp_enabled = False
|
||||||
|
url = 'a.b.c'
|
||||||
|
expected_parameter = {
|
||||||
|
'PreBootNetwork': 'Auto',
|
||||||
|
'UrlBootFile': 'a.b.c',
|
||||||
|
'Dhcpv4': 'Disabled'
|
||||||
|
}
|
||||||
|
self.rf_client.set_http_boot_url(url, dhcp_enabled)
|
||||||
|
|
||||||
|
bios_ps_mock.update_bios_data_by_post.assert_called_once_with(
|
||||||
|
expected_parameter)
|
||||||
|
|
||||||
|
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
|
||||||
|
def test_set_http_boot_url_raises_exception(self, system_mock):
|
||||||
|
pending_settings_mock = mock.PropertyMock(
|
||||||
|
side_effect=sushy.exceptions.SushyError)
|
||||||
|
|
||||||
|
type(system_mock.return_value.bios_settings).pending_settings = (
|
||||||
|
pending_settings_mock)
|
||||||
|
|
||||||
|
dhcp_enabled = True
|
||||||
|
url = 'a.b.c'
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
exception.IloError,
|
||||||
|
'Could not set HTTPS URL on the iLO.',
|
||||||
|
self.rf_client.set_http_boot_url, url, dhcp_enabled)
|
||||||
|
|
Loading…
Reference in New Issue