Adds an option for setting the http boot uri

In order to utilize Http Boot from URL, we need to be
able to set the the parameter to the remote BMC.

This enables a greater level of security as the
URL and ultimately the instruction comes from an
authenticated source, i.e. the BMC connection, and
not DHCP which is the fallback if a target is set
to UefiHttp.

We now also unset the the http boot uri if it is set
and we're not setting an explicit URI to use. This
keeps the state of the data clean in order to prevent
any unexpected behavior.

Change-Id: I73ae614ffe735bd055090ded32926a5416a12fc0
This commit is contained in:
Julia Kreger 2020-04-07 15:44:54 -07:00
parent 82772d3424
commit 7340b340c1
4 changed files with 110 additions and 5 deletions

View File

@ -0,0 +1,9 @@
---
features:
- |
Adds functionality to the ``set_system_boot_options`` method
permitting an ``http_boot_uri`` option to be set.
- |
Adds an ``http_boot_uri`` option to the System boot object,
permitting an API client user to view what the BMC's
redfish ``HttpBootUri`` setting is.

View File

@ -56,6 +56,8 @@ class BootField(base.CompositeField):
target = base.MappedField('BootSourceOverrideTarget', sys_cons.BootSource)
http_boot_uri = base.Field('HttpBootUri')
class MemorySummaryField(base.CompositeField):
health = base.Field(['Status', 'HealthRollup'])
@ -212,7 +214,8 @@ class System(base.ResourceBase):
return {v for v in sys_cons.BootSource
if v.value in self.boot.allowed_values}
def set_system_boot_options(self, target=None, enabled=None, mode=None):
def set_system_boot_options(self, target=None, enabled=None, mode=None,
http_boot_uri=None):
"""Set boot source and/or boot frequency and/or boot mode.
Set the boot source and/or boot frequency and/or boot mode to use
@ -220,10 +223,19 @@ class System(base.ResourceBase):
:param target: The target boot source,
a :py:class:`sushy.BootSource` value. Optional.
:param enabled: How long the override be enabled,
:param enabled: How long the override is enabled,
a :py:class:`sushy.BootSourceOverrideEnabled` value. Optional.
:param mode: The boot mode,
a :py:class:`sushy.BootSourceOverrideMode` value. Optional.
:param http_boot_uri: The requested HTTP Boot URI to transmit to the
BMC. Only valid when BootSourceOverrideTarget is set to UefiHTTP,
when utilizing the ``target`` parameter. If no value is supplied,
and the target is set to UefiHTTP, then an empty value will be
sent to the BMC to remove any prior setting, allowing the host
to load configuration from DHCP.
If not explicitly set, any value will be removed from a BMC when
UefiHttp boot is not engaged.
:raises: InvalidParameterValueError, if any information passed is
invalid.
"""
@ -235,6 +247,7 @@ class System(base.ResourceBase):
settings_boot_section = settings_resp.json().get('Boot', {})
else:
settings_resp = None
settings_boot_section = {}
if target is not None:
valid_targets = self.get_allowed_system_boot_source_values()
@ -293,6 +306,25 @@ class System(base.ResourceBase):
else:
data['Boot']['BootSourceOverrideMode'] = fishy_mode
if target == sys_cons.BootSource.UEFI_HTTP:
# The http_boot_uri value *can* be set independently of the
# target, but the BMC will just ignore it unless the target
# is set. So we should only, and explicitly set it when we've
# been requested to boot from UefiHTTP.
if not http_boot_uri:
# This should clear out any old entries, as no URI translates
# to the intent of "use whatever the dhcp server says".
data['Boot']['HttpBootUri'] = None
else:
# Explicilty set the URI.
data['Boot']['HttpBootUri'] = http_boot_uri
elif not http_boot_uri:
# We're not doing boot from URL, we should cleanup any setting
# which may be from a prior step/call.
if settings_boot_section.get('HttpBootUri'):
# If the setting is present, and has any value, unset it.
data['Boot']['HttpBootUri'] = None
# TODO(lucasagomes): Check the return code and response body ?
# Probably we should call refresh() as well.
if settings_data.get('Boot'):

View File

@ -36,10 +36,12 @@
"Utilities",
"Diags",
"SDCard",
"UefiTarget"
"UefiTarget",
"UefiHttp"
],
"BootSourceOverrideMode": "UEFI",
"UefiTargetBootSourceOverride": "/0x31/0x33/0x01/0x01"
"UefiTargetBootSourceOverride": "/0x31/0x33/0x01/0x01",
"HttpBootUri": "https://Contoso.lan/boot.iso"
},
"TrustedModules": [
{

View File

@ -243,7 +243,8 @@ class SystemTestCase(base.TestCase):
sushy.BootSource.UTILITIES,
sushy.BootSource.DIAGS,
sushy.BootSource.SD_CARD,
sushy.BootSource.UEFI_TARGET])
sushy.BootSource.UEFI_TARGET,
sushy.BootSource.UEFI_HTTP])
self.assertEqual(expected, values)
self.assertIsInstance(values, set)
@ -477,6 +478,57 @@ class SystemTestCase(base.TestCase):
data={'Boot': {'BootSourceOverrideMode': 'UEFI'}},
etag='"3d7b838291941d"')
def test_set_system_boot_options_httpbooturi(self):
self.sys_inst.set_system_boot_options(
sushy.BootSource.UEFI_HTTP,
enabled=sushy.BootSourceOverrideEnabled.ONCE,
mode=sushy.BootSourceOverrideMode.UEFI,
http_boot_uri='http://test.lan/test_image.iso'
)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideTarget': 'UefiHttp',
'BootSourceOverrideEnabled': 'Once',
'BootSourceOverrideMode': 'UEFI',
'HttpBootUri': 'http://test.lan/test_image.iso'}},
etag=mock.ANY)
def test_set_system_boot_options_httpboot(self):
self.sys_inst.set_system_boot_options(
sushy.BootSource.UEFI_HTTP,
enabled=sushy.BootSourceOverrideEnabled.ONCE,
mode=sushy.BootSourceOverrideMode.UEFI
)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideTarget': 'UefiHttp',
'BootSourceOverrideEnabled': 'Once',
'BootSourceOverrideMode': 'UEFI',
'HttpBootUri': None}},
etag=mock.ANY)
def test_set_system_boot_options_httpboot_unset(self):
self.sys_inst._settings = mock.Mock()
self.sys_inst._settings.resource_uri = 'meow'
settings_body = json.dumps(
{'Boot': {'HttpBootUri': 'http://foo.bar'}}
)
get_settings = mock.MagicMock()
get_settings.json.return_value = settings_body
self.conn.get.side_effect = get_settings
self.sys_inst.set_system_boot_options(
sushy.BootSource.HDD,
mode=sushy.BootSourceOverrideMode.UEFI
)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideTarget': 'Hdd',
'BootSourceOverrideMode': 'UEFI',
'HttpBootUri': None}},
etag=mock.ANY)
def test_set_system_boot_source(self):
self.sys_inst.set_system_boot_source(
sushy.BootSource.PXE,
@ -512,6 +564,16 @@ class SystemTestCase(base.TestCase):
'BootSourceOverrideTarget': 'Hdd'}},
etag=None)
def test_set_system_boot_unsets_http_boot_uri(self):
self.sys_inst.set_system_boot_source(
sushy.BootSource.HDD,
enabled=sushy.BootSourceOverrideEnabled.ONCE)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideEnabled': 'Once',
'BootSourceOverrideTarget': 'Hdd'}},
etag=mock.ANY)
def test_set_system_boot_source_invalid_target(self):
self.assertRaises(exceptions.InvalidParameterValueError,
self.sys_inst.set_system_boot_source,