Add SecureBoot support to the emulator, all drivers
This change adds a basic SecureBoot endpoint to the emulator which returns the actual secure boot enabled status. The fake and libvirt drivers supports setting the secure boot. OVMF paths have aligned on /usr/share/OVMF/ since this is supported by Red Hat and Debian family distros. Default and example firmware loader is now OVMF_CODE.secboot.fd since this exists on all distros and is known to work with both secure boot enabled and disabled on some distros. Change-Id: Icb85710d81c656da6853d67f5e1c6a8609abc1cf
This commit is contained in:
parent
8427349985
commit
41628d3546
@ -32,18 +32,23 @@ SUSHY_EMULATOR_IGNORE_BOOT_DEVICE = False
|
|||||||
|
|
||||||
|
|
||||||
# The map of firmware loaders dependant on the boot mode and
|
# The map of firmware loaders dependant on the boot mode and
|
||||||
# system architecture
|
# system architecture. Ideally the x86_64 loader will be capable
|
||||||
|
# of secure boot or not based on the chosen nvram.
|
||||||
SUSHY_EMULATOR_BOOT_LOADER_MAP = {
|
SUSHY_EMULATOR_BOOT_LOADER_MAP = {
|
||||||
u'UEFI': {
|
'UEFI': {
|
||||||
u'x86_64': u'/usr/share/OVMF/OVMF_CODE.fd',
|
'x86_64': u'/usr/share/OVMF/OVMF_CODE.secboot.fd',
|
||||||
u'aarch64': u'/usr/share/AAVMF/AAVMF_CODE.fd'
|
'aarch64': u'/usr/share/AAVMF/AAVMF_CODE.fd'
|
||||||
},
|
},
|
||||||
u'Legacy': {
|
'Legacy': {
|
||||||
u'x86_64': None,
|
'x86_64': None,
|
||||||
u'aarch64': None
|
'aarch64': None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# nvram templates to use on x86_64 to enable or disable secure boot
|
||||||
|
SUSHY_EMULATOR_SECURE_BOOT_ENABLED_NVRAM = '/usr/share/OVMF/OVMF_VARS.secboot.fd'
|
||||||
|
SUSHY_EMULATOR_SECURE_BOOT_DISABLED_NVRAM = '/usr/share/OVMF/OVMF_VARS.fd'
|
||||||
|
|
||||||
# This map contains statically configured Redfish Chassis linked
|
# This map contains statically configured Redfish Chassis linked
|
||||||
# up with the Systems and Managers enclosed into this Chassis.
|
# up with the Systems and Managers enclosed into this Chassis.
|
||||||
#
|
#
|
||||||
|
@ -152,8 +152,8 @@ On the host you need to have OVMF firmware binaries installed. Fedora users
|
|||||||
could pull them as `edk2-ovmf` RPM. On Ubuntu, `apt-get install ovmf` should
|
could pull them as `edk2-ovmf` RPM. On Ubuntu, `apt-get install ovmf` should
|
||||||
do the job.
|
do the job.
|
||||||
|
|
||||||
Then you need to create a VM by running `virt-install` with the `--boot uefi`
|
Then you need to create a VM by running `virt-install` with the UEFI specific
|
||||||
option:
|
`--boot` options:
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -163,7 +163,11 @@ Example:
|
|||||||
virt-install \
|
virt-install \
|
||||||
--name vbmc-node \
|
--name vbmc-node \
|
||||||
--ram 1024 \
|
--ram 1024 \
|
||||||
--boot uefi \
|
--boot loader.readonly=yes \
|
||||||
|
--boot loader.type=pflash \
|
||||||
|
--boot loader.secure=no \
|
||||||
|
--boot loader=/usr/share/OVMF/OVMF_CODE.secboot.fd \
|
||||||
|
--boot nvram.template=/usr/share/OVMF/OVMF_VARS.fd \
|
||||||
--disk size=1 \
|
--disk size=1 \
|
||||||
--vcpus 2 \
|
--vcpus 2 \
|
||||||
--os-type linux \
|
--os-type linux \
|
||||||
@ -174,16 +178,27 @@ Example:
|
|||||||
rm $tmpfile
|
rm $tmpfile
|
||||||
|
|
||||||
This will create a new `libvirt` domain with path to OVMF images properly
|
This will create a new `libvirt` domain with path to OVMF images properly
|
||||||
configured. Let's take a note on the path to the blob:
|
configured. Let's take a note on the path to the blob by running `virsh dumpxml vbmc-node`:
|
||||||
|
|
||||||
.. code-block:: bash
|
Example:
|
||||||
|
|
||||||
$ virsh dumpxml vbmc-node | grep loader
|
.. code-block:: xml
|
||||||
<loader readonly='yes' type='pflash'>/usr/share/edk2/ovmf/OVMF_CODE.fd</loader>
|
|
||||||
|
|
||||||
Because now we need to add this path to emulator's configuration matching
|
<domain type="kvm">
|
||||||
VM architecture we are running. Make a copy of stock configuration file
|
...
|
||||||
and edit it accordingly:
|
<os>
|
||||||
|
<type arch="x86_64" machine="q35">hvm</type>
|
||||||
|
<loader readonly="yes" type="pflash" secure="no">/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</loader>
|
||||||
|
<nvram template="/usr/share/edk2/ovmf/OVMF_VARS.fd"/>
|
||||||
|
<boot dev="hd"/>
|
||||||
|
</os>
|
||||||
|
...
|
||||||
|
</domain>
|
||||||
|
|
||||||
|
Because now we need to add this path to emulator's configuration matching VM
|
||||||
|
architecture we are running. It is also possible to make Redfish calls to enable
|
||||||
|
or disable Secure Boot by specifying which nvram template to load in each case.
|
||||||
|
Make a copy of stock configuration file and edit it accordingly:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@ -191,9 +206,11 @@ and edit it accordingly:
|
|||||||
...
|
...
|
||||||
SUSHY_EMULATOR_BOOT_LOADER_MAP = {
|
SUSHY_EMULATOR_BOOT_LOADER_MAP = {
|
||||||
'Uefi': {
|
'Uefi': {
|
||||||
'x86_64': '/usr/share/edk2/ovmf/OVMF_CODE.fd',
|
'x86_64': '/usr/share/OVMF/OVMF_CODE.secboot.fd',
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
SUSHY_EMULATOR_SECURE_BOOT_ENABLED_NVRAM = '/usr/share/OVMF/OVMF_VARS.secboot.fd'
|
||||||
|
SUSHY_EMULATOR_SECURE_BOOT_DISABLED_NVRAM = '/usr/share/OVMF/OVMF_VARS.fd'
|
||||||
...
|
...
|
||||||
|
|
||||||
Now you can run `sushy-emulator` with the updated configuration file:
|
Now you can run `sushy-emulator` with the updated configuration file:
|
||||||
|
12
releasenotes/notes/secure-boot-de663109ced9b266.yaml
Normal file
12
releasenotes/notes/secure-boot-de663109ced9b266.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
It is now possible to enable and disable UEFI Secure Boot mode via Redfish
|
||||||
|
requests for the libvirt driver. This is possible by configuring domains to
|
||||||
|
use a secure boot capable firmware loader, and setting configuration values
|
||||||
|
`SUSHY_EMULATOR_SECURE_BOOT_ENABLED_NVRAM` and
|
||||||
|
`SUSHY_EMULATOR_SECURE_BOOT_DISABLED_NVRAM` to nvram template paths which
|
||||||
|
enable or disable secure boot.
|
||||||
|
|
||||||
|
The fake driver supports getting and setting secure boot, the nova driver
|
||||||
|
only supports getting.
|
@ -558,6 +558,33 @@ def system_reset_bios(identity):
|
|||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/redfish/v1/Systems/<identity>/SecureBoot',
|
||||||
|
methods=['GET', 'PATCH'])
|
||||||
|
@api_utils.ensure_instance_access
|
||||||
|
@api_utils.returns_json
|
||||||
|
def secure_boot(identity):
|
||||||
|
|
||||||
|
if flask.request.method == 'GET':
|
||||||
|
secure = app.systems.get_secure_boot(identity)
|
||||||
|
|
||||||
|
app.logger.debug('Serving secure boot for system "%s"', identity)
|
||||||
|
|
||||||
|
return flask.render_template(
|
||||||
|
'secure_boot.json',
|
||||||
|
identity=identity,
|
||||||
|
secure_boot_enable=secure,
|
||||||
|
secure_boot_current_boot=secure and 'Enabled' or 'Disabled')
|
||||||
|
|
||||||
|
elif flask.request.method == 'PATCH':
|
||||||
|
secure = flask.request.json.get('SecureBootEnable')
|
||||||
|
|
||||||
|
app.systems.set_secure_boot(identity, secure)
|
||||||
|
|
||||||
|
app.logger.info('System "%s" secure boot updated to "%s"',
|
||||||
|
identity, secure)
|
||||||
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
@app.route('/redfish/v1/Systems/<identity>/SimpleStorage',
|
@app.route('/redfish/v1/Systems/<identity>/SimpleStorage',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@api_utils.ensure_instance_access
|
@api_utils.ensure_instance_access
|
||||||
|
@ -124,6 +124,24 @@ class AbstractSystemsDriver(metaclass=abc.ABCMeta):
|
|||||||
"""
|
"""
|
||||||
raise error.NotSupportedError('Not implemented')
|
raise error.NotSupportedError('Not implemented')
|
||||||
|
|
||||||
|
def get_secure_boot(self, identity):
|
||||||
|
"""Get computer system secure boot state for UEFI boot mode.
|
||||||
|
|
||||||
|
:returns: boolean of the current secure boot state
|
||||||
|
|
||||||
|
:raises: `FishyError` if the state can't be fetched
|
||||||
|
"""
|
||||||
|
raise error.NotSupportedError('Not implemented')
|
||||||
|
|
||||||
|
def set_secure_boot(self, identity, secure):
|
||||||
|
"""Set computer system secure boot state for UEFI boot mode.
|
||||||
|
|
||||||
|
:param secure: boolean requesting the secure boot state
|
||||||
|
|
||||||
|
:raises: `FishyError` if the can't be set
|
||||||
|
"""
|
||||||
|
raise error.NotSupportedError('Not implemented')
|
||||||
|
|
||||||
def get_total_memory(self, identity):
|
def get_total_memory(self, identity):
|
||||||
"""Get computer system total memory
|
"""Get computer system total memory
|
||||||
|
|
||||||
|
@ -143,6 +143,12 @@ class FakeDriver(AbstractSystemsDriver):
|
|||||||
def set_boot_mode(self, identity, boot_mode):
|
def set_boot_mode(self, identity, boot_mode):
|
||||||
self._update(identity, boot_mode=boot_mode)
|
self._update(identity, boot_mode=boot_mode)
|
||||||
|
|
||||||
|
def get_secure_boot(self, identity):
|
||||||
|
return self._get(identity).get('secure_boot', False)
|
||||||
|
|
||||||
|
def set_secure_boot(self, identity, secure):
|
||||||
|
self._update(identity, secure_boot=secure)
|
||||||
|
|
||||||
def get_boot_image(self, identity, device):
|
def get_boot_image(self, identity, device):
|
||||||
devinfo = self._get(identity).get('boot_image') or {}
|
devinfo = self._get(identity).get('boot_image') or {}
|
||||||
return devinfo.get(device) or (None, False, False)
|
return devinfo.get(device) or (None, False, False)
|
||||||
|
@ -101,7 +101,7 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||||||
|
|
||||||
BOOT_LOADER_MAP = {
|
BOOT_LOADER_MAP = {
|
||||||
'UEFI': {
|
'UEFI': {
|
||||||
'x86_64': '/usr/share/OVMF/OVMF_CODE.fd',
|
'x86_64': '/usr/share/OVMF/OVMF_CODE.secboot.fd',
|
||||||
'aarch64': '/usr/share/AAVMF/AAVMF_CODE.fd'
|
'aarch64': '/usr/share/AAVMF/AAVMF_CODE.fd'
|
||||||
},
|
},
|
||||||
'Legacy': {
|
'Legacy': {
|
||||||
@ -111,6 +111,9 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECURE_BOOT_ENABLED_NVRAM = '/usr/share/OVMF/OVMF_VARS.secboot.fd'
|
||||||
|
SECURE_BOOT_DISABLED_NVRAM = '/usr/share/OVMF/OVMF_VARS.fd'
|
||||||
|
|
||||||
DEVICE_TYPE_MAP = {
|
DEVICE_TYPE_MAP = {
|
||||||
constants.DEVICE_TYPE_CD: 'cdrom',
|
constants.DEVICE_TYPE_CD: 'cdrom',
|
||||||
constants.DEVICE_TYPE_FLOPPY: 'floppy',
|
constants.DEVICE_TYPE_FLOPPY: 'floppy',
|
||||||
@ -161,6 +164,12 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||||||
'SUSHY_EMULATOR_BOOT_LOADER_MAP', cls.BOOT_LOADER_MAP)
|
'SUSHY_EMULATOR_BOOT_LOADER_MAP', cls.BOOT_LOADER_MAP)
|
||||||
cls.KNOWN_BOOT_LOADERS = set(y for x in cls.BOOT_LOADER_MAP.values()
|
cls.KNOWN_BOOT_LOADERS = set(y for x in cls.BOOT_LOADER_MAP.values()
|
||||||
for y in x.values())
|
for y in x.values())
|
||||||
|
cls.SECURE_BOOT_ENABLED_NVRAM = cls._config.get(
|
||||||
|
'SUSHY_EMULATOR_SECURE_BOOT_ENABLED_NVRAM',
|
||||||
|
cls.SECURE_BOOT_ENABLED_NVRAM)
|
||||||
|
cls.SECURE_BOOT_DISABLED_NVRAM = cls._config.get(
|
||||||
|
'SUSHY_EMULATOR_SECURE_BOOT_DISABLED_NVRAM',
|
||||||
|
cls.SECURE_BOOT_DISABLED_NVRAM)
|
||||||
cls.SUSHY_EMULATOR_IGNORE_BOOT_DEVICE = \
|
cls.SUSHY_EMULATOR_IGNORE_BOOT_DEVICE = \
|
||||||
cls._config.get('SUSHY_EMULATOR_IGNORE_BOOT_DEVICE', False)
|
cls._config.get('SUSHY_EMULATOR_IGNORE_BOOT_DEVICE', False)
|
||||||
return cls
|
return cls
|
||||||
@ -503,11 +512,34 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||||||
|
|
||||||
:raises: `error.FishyError` if boot mode can't be set
|
:raises: `error.FishyError` if boot mode can't be set
|
||||||
"""
|
"""
|
||||||
|
|
||||||
domain = self._get_domain(identity, readonly=True)
|
domain = self._get_domain(identity, readonly=True)
|
||||||
|
|
||||||
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
|
# XML schema:
|
||||||
|
# https://libvirt.org/formatdomain.html#operating-system-booting
|
||||||
tree = ET.fromstring(self.get_xml_desc(domain))
|
tree = ET.fromstring(self.get_xml_desc(domain))
|
||||||
|
self._build_os_element(identity, tree, boot_mode)
|
||||||
|
|
||||||
|
with libvirt_open(self._uri) as conn:
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn.defineXML(ET.tostring(tree).decode('utf-8'))
|
||||||
|
|
||||||
|
except libvirt.libvirtError as e:
|
||||||
|
msg = ('Error changing boot mode at libvirt URI '
|
||||||
|
'"%(uri)s": %(error)s' % {'uri': self._uri,
|
||||||
|
'error': e})
|
||||||
|
|
||||||
|
raise error.FishyError(msg)
|
||||||
|
|
||||||
|
def _build_os_element(self, identity, tree, boot_mode, secure=None):
|
||||||
|
"""Set the boot mode and secure boot on the os element
|
||||||
|
|
||||||
|
This also converts from the previous manual layout to the automatic
|
||||||
|
approach.
|
||||||
|
|
||||||
|
:raises: `error.FishyError` if boot mode can't be set
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
loader_type = self.BOOT_MODE_MAP[boot_mode]
|
loader_type = self.BOOT_MODE_MAP[boot_mode]
|
||||||
|
|
||||||
@ -529,7 +561,6 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||||||
type_element = os_element.find('type')
|
type_element = os_element.find('type')
|
||||||
if type_element is None:
|
if type_element is None:
|
||||||
os_arch = None
|
os_arch = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
os_arch = type_element.get('arch')
|
os_arch = type_element.get('arch')
|
||||||
|
|
||||||
@ -544,41 +575,83 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||||||
boot_mode, os_arch)
|
boot_mode, os_arch)
|
||||||
loader_path = None
|
loader_path = None
|
||||||
|
|
||||||
loader_elements = os_element.findall('loader')
|
# delete loader and nvram elements to rebuild from stratch
|
||||||
if len(loader_elements) > 1:
|
for element in os_element.findall('loader'):
|
||||||
msg = ('Can\'t set boot mode because "loader" element must be '
|
os_element.remove(element)
|
||||||
'present exactly once in domain "%(identity)s" '
|
for element in os_element.findall('nvram'):
|
||||||
|
os_element.remove(element)
|
||||||
|
|
||||||
|
loader_element = ET.SubElement(os_element, 'loader')
|
||||||
|
loader_element.set('type', loader_type)
|
||||||
|
if loader_path:
|
||||||
|
loader_element.text = loader_path
|
||||||
|
loader_element.set('readonly', 'yes')
|
||||||
|
|
||||||
|
if boot_mode == 'UEFI':
|
||||||
|
nvram_element = ET.SubElement(os_element, 'nvram')
|
||||||
|
if secure:
|
||||||
|
nvram_suffix = '.secboot.fd'
|
||||||
|
loader_element.set('secure', 'yes')
|
||||||
|
nvram_element.set('template', self.SECURE_BOOT_ENABLED_NVRAM)
|
||||||
|
else:
|
||||||
|
nvram_suffix = '.fd'
|
||||||
|
loader_element.set('secure', 'no')
|
||||||
|
nvram_element.set('template', self.SECURE_BOOT_DISABLED_NVRAM)
|
||||||
|
|
||||||
|
# force a different nvram path for secure vs not. This will ensure
|
||||||
|
# it gets regenerated from the template when secure boot mode
|
||||||
|
# changes
|
||||||
|
nvram_path = "/var/lib/libvirt/nvram-%s%s" % (identity,
|
||||||
|
nvram_suffix)
|
||||||
|
nvram_element.text = nvram_path
|
||||||
|
|
||||||
|
def get_secure_boot(self, identity):
|
||||||
|
"""Get computer system secure boot state for UEFI boot mode.
|
||||||
|
|
||||||
|
:returns: boolean of the current secure boot state
|
||||||
|
|
||||||
|
:raises: `FishyError` if the state can't be fetched
|
||||||
|
"""
|
||||||
|
if self.get_boot_mode(identity) == 'Legacy':
|
||||||
|
msg = 'Legacy boot mode does not support secure boot'
|
||||||
|
raise error.NotSupportedError(msg)
|
||||||
|
|
||||||
|
domain = self._get_domain(identity, readonly=True)
|
||||||
|
|
||||||
|
# XML schema:
|
||||||
|
# https://libvirt.org/formatdomain.html#operating-system-booting
|
||||||
|
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
|
||||||
|
|
||||||
|
os_element = tree.find('os')
|
||||||
|
|
||||||
|
nvram = os_element.findall('nvram')
|
||||||
|
if len(nvram) > 1:
|
||||||
|
msg = ('Can\'t get secure boot state because "nvram" element '
|
||||||
|
'must be present exactly once in domain "%(identity)s" '
|
||||||
'configuration' % {'identity': identity})
|
'configuration' % {'identity': identity})
|
||||||
raise error.FishyError(msg)
|
raise error.FishyError(msg)
|
||||||
|
|
||||||
if loader_elements:
|
if not nvram:
|
||||||
loader_element = loader_elements[0]
|
return False
|
||||||
|
nvram_template = nvram[0].get('template')
|
||||||
|
return nvram_template == self.SECURE_BOOT_ENABLED_NVRAM
|
||||||
|
|
||||||
if loader_element.text not in self.KNOWN_BOOT_LOADERS:
|
def set_secure_boot(self, identity, secure):
|
||||||
msg = ('Unknown boot loader path "%(path)s" in domain '
|
"""Set computer system secure boot state for UEFI boot mode.
|
||||||
'"%(identity)s" configuration encountered while '
|
|
||||||
'setting boot mode "%(mode)s", system architecture '
|
|
||||||
'"%(arch)s". Consider adding this loader path to '
|
|
||||||
'emulator config.' % {'identity': identity,
|
|
||||||
'mode': boot_mode,
|
|
||||||
'arch': os_arch,
|
|
||||||
'path': loader_element.text})
|
|
||||||
raise error.FishyError(msg)
|
|
||||||
|
|
||||||
if loader_path:
|
:param secure: boolean requesting the secure boot state
|
||||||
loader_element.set('type', loader_type)
|
|
||||||
loader_element.set('readonly', 'yes')
|
|
||||||
loader_element.text = loader_path
|
|
||||||
|
|
||||||
else:
|
:raises: `FishyError` if the can't be set
|
||||||
# NOTE(etingof): path must be present or element must be absent
|
"""
|
||||||
os_element.remove(loader_element)
|
if self.get_boot_mode(identity) == 'Legacy':
|
||||||
|
msg = 'Legacy boot mode does not support secure boot'
|
||||||
|
raise error.NotSupportedError(msg)
|
||||||
|
|
||||||
elif loader_path:
|
domain = self._get_domain(identity, readonly=True)
|
||||||
loader_element = ET.SubElement(os_element, 'loader')
|
|
||||||
loader_element.set('type', loader_type)
|
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
|
||||||
loader_element.set('readonly', 'yes')
|
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
|
||||||
loader_element.text = loader_path
|
self._build_os_element(identity, tree, 'UEFI', secure)
|
||||||
|
|
||||||
with libvirt_open(self._uri) as conn:
|
with libvirt_open(self._uri) as conn:
|
||||||
|
|
||||||
@ -586,7 +659,7 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||||||
conn.defineXML(ET.tostring(tree).decode('utf-8'))
|
conn.defineXML(ET.tostring(tree).decode('utf-8'))
|
||||||
|
|
||||||
except libvirt.libvirtError as e:
|
except libvirt.libvirtError as e:
|
||||||
msg = ('Error changing boot mode at libvirt URI '
|
msg = ('Error changing secure boot at libvirt URI '
|
||||||
'"%(uri)s": %(error)s' % {'uri': self._uri,
|
'"%(uri)s": %(error)s' % {'uri': self._uri,
|
||||||
'error': e})
|
'error': e})
|
||||||
|
|
||||||
|
@ -273,6 +273,34 @@ class OpenStackDriver(AbstractSystemsDriver):
|
|||||||
|
|
||||||
raise error.NotSupportedError(msg)
|
raise error.NotSupportedError(msg)
|
||||||
|
|
||||||
|
def get_secure_boot(self, identity):
|
||||||
|
"""Get computer system secure boot state for UEFI boot mode.
|
||||||
|
|
||||||
|
:returns: boolean of the current secure boot state
|
||||||
|
|
||||||
|
:raises: `FishyError` if the state can't be fetched
|
||||||
|
"""
|
||||||
|
if self.get_boot_mode(identity) == 'Legacy':
|
||||||
|
msg = 'Legacy boot mode does not support secure boot'
|
||||||
|
raise error.NotSupportedError(msg)
|
||||||
|
|
||||||
|
instance = self._get_instance(identity)
|
||||||
|
|
||||||
|
image = self._get_image_info(instance.image['id'])
|
||||||
|
|
||||||
|
return getattr(image, 'os_secure_boot', None) == 'required'
|
||||||
|
|
||||||
|
def set_secure_boot(self, identity, secure):
|
||||||
|
"""Set computer system secure boot state for UEFI boot mode.
|
||||||
|
|
||||||
|
:param secure: boolean requesting the secure boot state
|
||||||
|
|
||||||
|
:raises: `FishyError` if the can't be set
|
||||||
|
"""
|
||||||
|
msg = ('The cloud driver %(driver)s does not support changing secure '
|
||||||
|
'boot mode through Redfish' % {'driver': self.driver})
|
||||||
|
raise error.NotSupportedError(msg)
|
||||||
|
|
||||||
def get_total_memory(self, identity):
|
def get_total_memory(self, identity):
|
||||||
"""Get computer system total memory
|
"""Get computer system total memory
|
||||||
|
|
||||||
|
12
sushy_tools/emulator/templates/secure_boot.json
Normal file
12
sushy_tools/emulator/templates/secure_boot.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"@odata.type": "#SecureBoot.v1_1_0.SecureBoot",
|
||||||
|
"Id": "SecureBoot",
|
||||||
|
"Name": "UEFI Secure Boot",
|
||||||
|
"Actions": {},
|
||||||
|
"SecureBootEnable": {{ 'true' if secure_boot_enable else 'false' }},
|
||||||
|
"SecureBootCurrentBoot": {{ secure_boot_current_boot|string|tojson }},
|
||||||
|
"SecureBootMode": "DeployedMode",
|
||||||
|
"@odata.id": {{ "/redfish/v1/Systems/%s/SecureBoot"|format(identity)|tojson }},
|
||||||
|
"@odata.context": "/redfish/v1/$metadata#SecureBoot.SecureBoot",
|
||||||
|
"@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
|
||||||
|
}
|
@ -67,6 +67,9 @@
|
|||||||
"EthernetInterfaces": {
|
"EthernetInterfaces": {
|
||||||
"@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces"|format(identity)|tojson }}
|
"@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces"|format(identity)|tojson }}
|
||||||
},
|
},
|
||||||
|
"SecureBoot": {
|
||||||
|
"@odata.id": {{ "/redfish/v1/Systems/%s/SecureBoot"|format(identity)|tojson }}
|
||||||
|
},
|
||||||
"SimpleStorage": {
|
"SimpleStorage": {
|
||||||
"@odata.id": {{ "/redfish/v1/Systems/%s/SimpleStorage"|format(identity)|tojson }}
|
"@odata.id": {{ "/redfish/v1/Systems/%s/SimpleStorage"|format(identity)|tojson }}
|
||||||
},
|
},
|
||||||
|
@ -29,6 +29,9 @@ class AliasAccessError(FishyError):
|
|||||||
class NotSupportedError(FishyError):
|
class NotSupportedError(FishyError):
|
||||||
"""Feature not supported by resource driver"""
|
"""Feature not supported by resource driver"""
|
||||||
|
|
||||||
|
def __init__(self, msg='Unsupported'):
|
||||||
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class NotFound(FishyError):
|
class NotFound(FishyError):
|
||||||
"""Entity not found."""
|
"""Entity not found."""
|
||||||
|
29
sushy_tools/tests/unit/emulator/domain-q35_uefi.xml
Normal file
29
sushy_tools/tests/unit/emulator/domain-q35_uefi.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<domain type='qemu'>
|
||||||
|
<name>QEmu-fedora-i686</name>
|
||||||
|
<uuid>c7a5fdbd-cdaf-9455-926a-d65c16db1809</uuid>
|
||||||
|
<memory>219200</memory>
|
||||||
|
<currentMemory>219200</currentMemory>
|
||||||
|
<vcpu>2</vcpu>
|
||||||
|
<os>
|
||||||
|
<type arch="x86_64" machine="q35">hvm</type>
|
||||||
|
<loader readonly="yes" type="pflash">/usr/share/OVMF/OVMF_CODE.fd</loader>
|
||||||
|
<nvram template="/usr/share/OVMF/OVMF_VARS.fd"/>
|
||||||
|
<boot dev="disk"/>
|
||||||
|
</os>
|
||||||
|
<devices>
|
||||||
|
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||||
|
<disk type='file' device='cdrom'>
|
||||||
|
<source file='/home/user/boot.iso'/>
|
||||||
|
<target dev='hdc'/>
|
||||||
|
<readonly/>
|
||||||
|
</disk>
|
||||||
|
<disk type='file' device='disk'>
|
||||||
|
<source file='/home/user/fedora.img'/>
|
||||||
|
<target dev='hda'/>
|
||||||
|
</disk>
|
||||||
|
<interface type='network'>
|
||||||
|
<source network='default'/>
|
||||||
|
</interface>
|
||||||
|
<graphics type='vnc' port='-1'/>
|
||||||
|
</devices>
|
||||||
|
</domain>
|
29
sushy_tools/tests/unit/emulator/domain-q35_uefi_secure.xml
Normal file
29
sushy_tools/tests/unit/emulator/domain-q35_uefi_secure.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<domain type='qemu'>
|
||||||
|
<name>QEmu-fedora-i686</name>
|
||||||
|
<uuid>c7a5fdbd-cdaf-9455-926a-d65c16db1809</uuid>
|
||||||
|
<memory>219200</memory>
|
||||||
|
<currentMemory>219200</currentMemory>
|
||||||
|
<vcpu>2</vcpu>
|
||||||
|
<os>
|
||||||
|
<type arch="x86_64" machine="q35">hvm</type>
|
||||||
|
<loader secure="yes" readonly="yes" type="pflash">/usr/share/OVMF/OVMF_CODE.secboot.fd</loader>
|
||||||
|
<nvram template="/usr/share/OVMF/OVMF_VARS.secboot.fd"/>
|
||||||
|
<boot dev="disk"/>
|
||||||
|
</os>
|
||||||
|
<devices>
|
||||||
|
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||||
|
<disk type='file' device='cdrom'>
|
||||||
|
<source file='/home/user/boot.iso'/>
|
||||||
|
<target dev='hdc'/>
|
||||||
|
<readonly/>
|
||||||
|
</disk>
|
||||||
|
<disk type='file' device='disk'>
|
||||||
|
<source file='/home/user/fedora.img'/>
|
||||||
|
<target dev='hda'/>
|
||||||
|
</disk>
|
||||||
|
<interface type='network'>
|
||||||
|
<source network='default'/>
|
||||||
|
</interface>
|
||||||
|
<graphics type='vnc' port='-1'/>
|
||||||
|
</devices>
|
||||||
|
</domain>
|
@ -92,3 +92,8 @@ class FakeDriverTestCase(base.BaseTestCase):
|
|||||||
self.test_driver.get_boot_image(UUID, 'Cd'))
|
self.test_driver.get_boot_image(UUID, 'Cd'))
|
||||||
self.assertEqual((None, False, False),
|
self.assertEqual((None, False, False),
|
||||||
self.test_driver.get_boot_image(UUID, 'Hdd'))
|
self.test_driver.get_boot_image(UUID, 'Hdd'))
|
||||||
|
|
||||||
|
def test_secure_boot(self):
|
||||||
|
self.assertFalse(self.test_driver.get_secure_boot(UUID))
|
||||||
|
self.test_driver.set_secure_boot(UUID, True)
|
||||||
|
self.assertTrue(self.test_driver.get_secure_boot(UUID))
|
||||||
|
@ -328,7 +328,7 @@ class LibvirtDriverTestCase(base.BaseTestCase):
|
|||||||
self.assertIn(expected, conn_mock.defineXML.call_args[0][0])
|
self.assertIn(expected, conn_mock.defineXML.call_args[0][0])
|
||||||
|
|
||||||
@mock.patch('libvirt.openReadOnly', autospec=True)
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
def test_get_boot_mode(self, libvirt_mock):
|
def test_get_boot_mode_legacy(self, libvirt_mock):
|
||||||
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
|
|
||||||
@ -340,6 +340,20 @@ class LibvirtDriverTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
self.assertEqual('Legacy', boot_mode)
|
self.assertEqual('Legacy', boot_mode)
|
||||||
|
|
||||||
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
|
def test_get_boot_mode_uefi(self, libvirt_mock):
|
||||||
|
with open('sushy_tools/tests/unit/emulator/domain-q35_uefi.xml',
|
||||||
|
'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
conn_mock = libvirt_mock.return_value
|
||||||
|
domain_mock = conn_mock.lookupByUUID.return_value
|
||||||
|
domain_mock.XMLDesc.return_value = data
|
||||||
|
|
||||||
|
boot_mode = self.test_driver.get_boot_mode(self.uuid)
|
||||||
|
|
||||||
|
self.assertEqual('UEFI', boot_mode)
|
||||||
|
|
||||||
@mock.patch('libvirt.open', autospec=True)
|
@mock.patch('libvirt.open', autospec=True)
|
||||||
@mock.patch('libvirt.openReadOnly', autospec=True)
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
def test_set_boot_mode(self, libvirt_mock, libvirt_rw_mock):
|
def test_set_boot_mode(self, libvirt_mock, libvirt_rw_mock):
|
||||||
@ -359,8 +373,9 @@ class LibvirtDriverTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
@mock.patch('libvirt.open', autospec=True)
|
@mock.patch('libvirt.open', autospec=True)
|
||||||
@mock.patch('libvirt.openReadOnly', autospec=True)
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
def test_set_boot_mode_known_loader(self, libvirt_mock, libvirt_rw_mock):
|
def test_set_boot_mode_legacy(self, libvirt_mock, libvirt_rw_mock):
|
||||||
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
with open('sushy_tools/tests/unit/emulator/domain-q35_uefi.xml',
|
||||||
|
'r') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
|
|
||||||
conn_mock = libvirt_mock.return_value
|
conn_mock = libvirt_mock.return_value
|
||||||
@ -369,37 +384,15 @@ class LibvirtDriverTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.test_driver, 'get_power_state', return_value='Off'):
|
self.test_driver, 'get_power_state', return_value='Off'):
|
||||||
self.test_driver.set_boot_mode(self.uuid, 'UEFI')
|
self.test_driver.set_boot_mode(self.uuid, 'Legacy')
|
||||||
|
|
||||||
conn_mock = libvirt_rw_mock.return_value
|
conn_mock = libvirt_rw_mock.return_value
|
||||||
xml_document = conn_mock.defineXML.call_args[0][0]
|
xml_document = conn_mock.defineXML.call_args[0][0]
|
||||||
tree = ET.fromstring(xml_document)
|
tree = ET.fromstring(xml_document)
|
||||||
os_element = tree.find('os')
|
os_element = tree.find('os')
|
||||||
loader_element = os_element.find('loader')
|
loader_element = os_element.find('loader')
|
||||||
self.assertEqual(
|
self.assertIsNone(loader_element.text)
|
||||||
'pflash', loader_element.get('type'))
|
self.assertNotIn('readonly', loader_element.attrib)
|
||||||
self.assertEqual(
|
|
||||||
'/usr/share/OVMF/OVMF_CODE.fd',
|
|
||||||
loader_element.text)
|
|
||||||
|
|
||||||
@mock.patch('libvirt.open', autospec=True)
|
|
||||||
@mock.patch('libvirt.openReadOnly', autospec=True)
|
|
||||||
def test_set_boot_mode_unknown_loader_path(
|
|
||||||
self, libvirt_mock, libvirt_rw_mock):
|
|
||||||
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
conn_mock = libvirt_mock.return_value
|
|
||||||
domain_mock = conn_mock.lookupByUUID.return_value
|
|
||||||
domain_mock.XMLDesc.return_value = data
|
|
||||||
|
|
||||||
with mock.patch.dict(
|
|
||||||
self.test_driver.KNOWN_BOOT_LOADERS, {}, clear=True):
|
|
||||||
with mock.patch.object(
|
|
||||||
self.test_driver, 'get_power_state', return_value='Off'):
|
|
||||||
self.assertRaises(
|
|
||||||
error.FishyError, self.test_driver.set_boot_mode,
|
|
||||||
self.uuid, 'Uefi')
|
|
||||||
|
|
||||||
@mock.patch('libvirt.open', autospec=True)
|
@mock.patch('libvirt.open', autospec=True)
|
||||||
@mock.patch('libvirt.openReadOnly', autospec=True)
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
@ -467,34 +460,6 @@ class LibvirtDriverTestCase(base.BaseTestCase):
|
|||||||
error.FishyError, self.test_driver.set_boot_mode,
|
error.FishyError, self.test_driver.set_boot_mode,
|
||||||
self.uuid, 'Uefi')
|
self.uuid, 'Uefi')
|
||||||
|
|
||||||
@mock.patch('libvirt.open', autospec=True)
|
|
||||||
@mock.patch('libvirt.openReadOnly', autospec=True)
|
|
||||||
def test_set_boot_mode_no_type(self, libvirt_mock, libvirt_rw_mock):
|
|
||||||
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
tree = ET.fromstring(data)
|
|
||||||
os_element = tree.find('os')
|
|
||||||
type_element = os_element.find('type')
|
|
||||||
os_element.remove(type_element)
|
|
||||||
|
|
||||||
data = ET.tostring(tree)
|
|
||||||
|
|
||||||
conn_mock = libvirt_mock.return_value
|
|
||||||
domain_mock = conn_mock.lookupByUUID.return_value
|
|
||||||
domain_mock.XMLDesc.return_value = data
|
|
||||||
|
|
||||||
with mock.patch.object(
|
|
||||||
self.test_driver, 'get_power_state', return_value='Off'):
|
|
||||||
self.test_driver.set_boot_mode(self.uuid, 'UEFI')
|
|
||||||
|
|
||||||
conn_mock = libvirt_rw_mock.return_value
|
|
||||||
xml_document = conn_mock.defineXML.call_args[0][0]
|
|
||||||
tree = ET.fromstring(xml_document)
|
|
||||||
os_element = tree.find('os')
|
|
||||||
# NOTE(etingof): should enforce default loader
|
|
||||||
self.assertIsNone(os_element.find('loader'))
|
|
||||||
|
|
||||||
@mock.patch('libvirt.openReadOnly', autospec=True)
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
def test_get_boot_image(self, libvirt_mock):
|
def test_get_boot_image(self, libvirt_mock):
|
||||||
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
||||||
@ -1117,3 +1082,106 @@ class LibvirtDriverTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
self.test_driver.find_or_create_storage_volume(vol_data)
|
self.test_driver.find_or_create_storage_volume(vol_data)
|
||||||
pool_mock.createXML.assert_called_once_with(mock.ANY)
|
pool_mock.createXML.assert_called_once_with(mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
|
def test_get_secure_boot_off(self, libvirt_mock):
|
||||||
|
with open('sushy_tools/tests/unit/emulator/domain-q35_uefi.xml',
|
||||||
|
'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
conn_mock = libvirt_mock.return_value
|
||||||
|
domain_mock = conn_mock.lookupByUUID.return_value
|
||||||
|
domain_mock.XMLDesc.return_value = data
|
||||||
|
|
||||||
|
self.assertFalse(self.test_driver.get_secure_boot(self.uuid))
|
||||||
|
|
||||||
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
|
def test_get_secure_boot_on(self, libvirt_mock):
|
||||||
|
with open('sushy_tools/tests/unit/emulator/domain-q35_uefi_secure.xml',
|
||||||
|
'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
conn_mock = libvirt_mock.return_value
|
||||||
|
domain_mock = conn_mock.lookupByUUID.return_value
|
||||||
|
domain_mock.XMLDesc.return_value = data
|
||||||
|
|
||||||
|
self.assertTrue(self.test_driver.get_secure_boot(self.uuid))
|
||||||
|
|
||||||
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
|
def test_get_secure_boot_not_uefi(self, libvirt_mock):
|
||||||
|
with open('sushy_tools/tests/unit/emulator/domain-q35.xml', 'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
conn_mock = libvirt_mock.return_value
|
||||||
|
domain_mock = conn_mock.lookupByUUID.return_value
|
||||||
|
domain_mock.XMLDesc.return_value = data
|
||||||
|
|
||||||
|
self.assertRaises(error.NotSupportedError,
|
||||||
|
self.test_driver.get_secure_boot, self.uuid)
|
||||||
|
|
||||||
|
@mock.patch('libvirt.open', autospec=True)
|
||||||
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
|
def test_set_secure_boot_on(self, libvirt_mock, libvirt_rw_mock):
|
||||||
|
with open('sushy_tools/tests/unit/emulator/domain-q35_uefi.xml',
|
||||||
|
'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
conn_mock = libvirt_mock.return_value
|
||||||
|
domain_mock = conn_mock.lookupByUUID.return_value
|
||||||
|
domain_mock.XMLDesc.return_value = data
|
||||||
|
|
||||||
|
self.test_driver.set_secure_boot(self.uuid, True)
|
||||||
|
|
||||||
|
conn_mock = libvirt_rw_mock.return_value
|
||||||
|
xml_document = conn_mock.defineXML.call_args[0][0]
|
||||||
|
tree = ET.fromstring(xml_document)
|
||||||
|
os_element = tree.find('os')
|
||||||
|
loader_element = os_element.find('loader')
|
||||||
|
nvram_element = os_element.find('nvram')
|
||||||
|
self.assertEqual('yes', loader_element.get('secure'))
|
||||||
|
self.assertEqual('/usr/share/OVMF/OVMF_CODE.secboot.fd',
|
||||||
|
loader_element.text)
|
||||||
|
self.assertEqual('/usr/share/OVMF/OVMF_VARS.secboot.fd',
|
||||||
|
nvram_element.get('template'))
|
||||||
|
self.assertEqual('/var/lib/libvirt/nvram-%s.secboot.fd' %
|
||||||
|
self.uuid, nvram_element.text)
|
||||||
|
|
||||||
|
@mock.patch('libvirt.open', autospec=True)
|
||||||
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
|
def test_set_secure_boot_off(self, libvirt_mock, libvirt_rw_mock):
|
||||||
|
with open('sushy_tools/tests/unit/emulator/domain-q35_uefi_secure.xml',
|
||||||
|
'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
conn_mock = libvirt_mock.return_value
|
||||||
|
domain_mock = conn_mock.lookupByUUID.return_value
|
||||||
|
domain_mock.XMLDesc.return_value = data
|
||||||
|
|
||||||
|
self.test_driver.set_secure_boot(self.uuid, False)
|
||||||
|
|
||||||
|
conn_mock = libvirt_rw_mock.return_value
|
||||||
|
xml_document = conn_mock.defineXML.call_args[0][0]
|
||||||
|
tree = ET.fromstring(xml_document)
|
||||||
|
os_element = tree.find('os')
|
||||||
|
loader_element = os_element.find('loader')
|
||||||
|
nvram_element = os_element.find('nvram')
|
||||||
|
self.assertEqual('no', loader_element.get('secure'))
|
||||||
|
self.assertEqual('/usr/share/OVMF/OVMF_CODE.secboot.fd',
|
||||||
|
loader_element.text)
|
||||||
|
self.assertEqual('/usr/share/OVMF/OVMF_VARS.fd',
|
||||||
|
nvram_element.get('template'))
|
||||||
|
self.assertEqual('/var/lib/libvirt/nvram-%s.fd' %
|
||||||
|
self.uuid, nvram_element.text)
|
||||||
|
|
||||||
|
@mock.patch('libvirt.open', autospec=True)
|
||||||
|
@mock.patch('libvirt.openReadOnly', autospec=True)
|
||||||
|
def test_set_secure_boot_not_uefi(self, libvirt_mock, libvirt_rw_mock):
|
||||||
|
with open('sushy_tools/tests/unit/emulator/domain-q35.xml', 'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
conn_mock = libvirt_mock.return_value
|
||||||
|
domain_mock = conn_mock.lookupByUUID.return_value
|
||||||
|
domain_mock.XMLDesc.return_value = data
|
||||||
|
|
||||||
|
self.assertRaises(error.NotSupportedError,
|
||||||
|
self.test_driver.set_secure_boot, self.uuid, True)
|
||||||
|
@ -262,3 +262,28 @@ class NovaDriverTestCase(base.BaseTestCase):
|
|||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
error.FishyError,
|
error.FishyError,
|
||||||
self.test_driver.get_simple_storage_collection, self.uuid)
|
self.test_driver.get_simple_storage_collection, self.uuid)
|
||||||
|
|
||||||
|
def test_get_secure_boot_off(self):
|
||||||
|
server = mock.Mock(id=self.uuid, image=dict(id=self.uuid))
|
||||||
|
self.nova_mock.return_value.get_server.return_value = server
|
||||||
|
|
||||||
|
image = mock.Mock()
|
||||||
|
|
||||||
|
self.nova_mock.return_value.image.find_image.return_value = image
|
||||||
|
|
||||||
|
self.assertFalse(self.test_driver.get_secure_boot(self.uuid))
|
||||||
|
|
||||||
|
def test_get_secure_boot_on(self):
|
||||||
|
server = mock.Mock(id=self.uuid, image=dict(id=self.uuid))
|
||||||
|
self.nova_mock.return_value.get_server.return_value = server
|
||||||
|
|
||||||
|
image = mock.Mock(os_secure_boot='required')
|
||||||
|
|
||||||
|
self.nova_mock.return_value.image.find_image.return_value = image
|
||||||
|
|
||||||
|
self.assertTrue(self.test_driver.get_secure_boot(self.uuid))
|
||||||
|
|
||||||
|
def test_set_secure_boot(self):
|
||||||
|
self.assertRaises(
|
||||||
|
error.NotSupportedError, self.test_driver.set_secure_boot,
|
||||||
|
self.uuid, True)
|
||||||
|
@ -428,6 +428,40 @@ class EthernetInterfacesTestCase(EmulatorTestCase):
|
|||||||
self.assertEqual(404, response.status_code)
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
||||||
|
|
||||||
|
@patch_resource('systems')
|
||||||
|
class SecureBootTestCase(EmulatorTestCase):
|
||||||
|
|
||||||
|
def test_secure_boot_get(self, systems_mock):
|
||||||
|
systems_mock = systems_mock.return_value
|
||||||
|
systems_mock.get_secure_boot.return_value = True
|
||||||
|
response = self.app.get('redfish/v1/Systems/%s/SecureBoot' % self.uuid)
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual('UEFI Secure Boot',
|
||||||
|
response.json['Name'])
|
||||||
|
self.assertTrue(response.json['SecureBootEnable'])
|
||||||
|
|
||||||
|
systems_mock.get_secure_boot.return_value = False
|
||||||
|
response = self.app.get('redfish/v1/Systems/%s/SecureBoot' % self.uuid)
|
||||||
|
self.assertFalse(response.json['SecureBootEnable'])
|
||||||
|
|
||||||
|
def test_secure_boot_patch_on(self, systems_mock):
|
||||||
|
systems_mock = systems_mock.return_value
|
||||||
|
data = {'SecureBootEnable': True}
|
||||||
|
response = self.app.patch('redfish/v1/Systems/%s/SecureBoot'
|
||||||
|
% self.uuid, json=data)
|
||||||
|
self.assertEqual(204, response.status_code)
|
||||||
|
systems_mock.set_secure_boot.assert_called_once_with(self.uuid, True)
|
||||||
|
|
||||||
|
def test_secure_boot_patch_off(self, systems_mock):
|
||||||
|
systems_mock = systems_mock.return_value
|
||||||
|
data = {'SecureBootEnable': False}
|
||||||
|
response = self.app.patch('redfish/v1/Systems/%s/SecureBoot'
|
||||||
|
% self.uuid, json=data)
|
||||||
|
self.assertEqual(204, response.status_code)
|
||||||
|
systems_mock.set_secure_boot.assert_called_once_with(self.uuid, False)
|
||||||
|
|
||||||
|
|
||||||
@patch_resource('systems')
|
@patch_resource('systems')
|
||||||
class StorageTestCase(EmulatorTestCase):
|
class StorageTestCase(EmulatorTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user