Add system boot mode support
This change adds the ability to read and (for some drivers) change system boot mode via Redfish call. At present just Legacy and Uefi boot modes are supported. Story: 1741062 Task: 12045 Change-Id: I077d31b37a34d90fc8bfd87d24b66b227088fd7c
This commit is contained in:
parent
4c73cbaa2b
commit
5b16e8c8e9
@ -64,6 +64,7 @@ class AbstractDriver(object):
|
||||
If not specified, current system power state is returned.
|
||||
Valid values are: *On*, *ForceOn*, *ForceOff*, *GracefulShutdown*,
|
||||
*GracefulRestart*, *ForceRestart*, *Nmi*.
|
||||
|
||||
:raises: `FishyError` if power state can't be set
|
||||
"""
|
||||
|
||||
@ -82,9 +83,27 @@ class AbstractDriver(object):
|
||||
:param boot_source: string literal requesting boot device change on the
|
||||
system. If not specified, current boot device is returned.
|
||||
Valid values are: *Pxe*, *Hdd*, *Cd*.
|
||||
|
||||
:raises: `FishyError` if boot device can't be set
|
||||
"""
|
||||
|
||||
def get_boot_mode(self, identity):
|
||||
"""Get computer system boot mode.
|
||||
|
||||
:returns: either *Uefi* or *Legacy* as `str` or `None` if
|
||||
current boot mode can't be determined
|
||||
"""
|
||||
|
||||
def set_boot_mode(self, identity, boot_mode):
|
||||
"""Set computer system boot mode.
|
||||
|
||||
:param boot_mode: optional string literal requesting boot mode
|
||||
change on the system. If not specified, current boot mode is
|
||||
returned. Valid values are: *Uefi*, *Legacy*.
|
||||
|
||||
:raises: `FishyError` if boot mode can't be set
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_total_memory(self, identity):
|
||||
"""Get computer system total memory
|
||||
|
@ -47,6 +47,8 @@ class libvirt_open(object):
|
||||
class LibvirtDriver(AbstractDriver):
|
||||
"""Libvirt driver"""
|
||||
|
||||
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
|
||||
|
||||
BOOT_DEVICE_MAP = {
|
||||
'Pxe': 'network',
|
||||
'Hdd': 'hd',
|
||||
@ -57,6 +59,13 @@ class LibvirtDriver(AbstractDriver):
|
||||
|
||||
LIBVIRT_URI = 'qemu:///system'
|
||||
|
||||
BOOT_MODE_MAP = {
|
||||
'Legacy': 'rom',
|
||||
'Uefi': 'pflash',
|
||||
}
|
||||
|
||||
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
|
||||
|
||||
def __init__(self, uri=None):
|
||||
self._uri = uri or self.LIBVIRT_URI
|
||||
|
||||
@ -173,6 +182,7 @@ class LibvirtDriver(AbstractDriver):
|
||||
|
||||
domain = conn.lookupByName(identity)
|
||||
|
||||
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
|
||||
tree = ET.fromstring(domain.XMLDesc())
|
||||
|
||||
try:
|
||||
@ -202,6 +212,71 @@ class LibvirtDriver(AbstractDriver):
|
||||
|
||||
raise FishyError(msg)
|
||||
|
||||
def get_boot_mode(self, identity):
|
||||
"""Get computer system boot mode.
|
||||
|
||||
:returns: either *Uefi* or *Legacy* as `str` or `None` if
|
||||
current boot mode can't be determined
|
||||
"""
|
||||
with libvirt_open(self._uri, readonly=True) as conn:
|
||||
|
||||
domain = conn.lookupByName(identity)
|
||||
|
||||
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
|
||||
tree = ET.fromstring(domain.XMLDesc())
|
||||
|
||||
loader_element = tree.find('.//loader')
|
||||
|
||||
if loader_element is not None:
|
||||
boot_mode = (
|
||||
self.BOOT_MODE_MAP_REV.get(loader_element.get('type'))
|
||||
)
|
||||
|
||||
return boot_mode
|
||||
|
||||
def set_boot_mode(self, identity, boot_mode):
|
||||
"""Set computer system boot mode.
|
||||
|
||||
:param boot_mode: optional string literal requesting boot mode
|
||||
change on the system. If not specified, current boot mode is
|
||||
returned. Valid values are: *Uefi*, *Legacy*.
|
||||
|
||||
:raises: `FishyError` if boot mode can't be set
|
||||
"""
|
||||
with libvirt_open(self._uri) as conn:
|
||||
|
||||
domain = conn.lookupByName(identity)
|
||||
|
||||
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
|
||||
tree = ET.fromstring(domain.XMLDesc())
|
||||
|
||||
try:
|
||||
target = self.BOOT_MODE_MAP[boot_mode]
|
||||
|
||||
except KeyError:
|
||||
msg = ('Unknown boot mode requested: '
|
||||
'%(boot_mode)s' % {'boot_mode': boot_mode})
|
||||
|
||||
raise FishyError(msg)
|
||||
|
||||
for os_element in tree.findall('os'):
|
||||
# Remove all "boot" elements
|
||||
for loader_element in os_element.findall('loader'):
|
||||
os_element.remove(loader_element)
|
||||
|
||||
# Add a new loader element with the request boot mode
|
||||
loader_element = ET.SubElement(os_element, 'loader')
|
||||
loader_element.set('type', target)
|
||||
|
||||
try:
|
||||
self._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 FishyError(msg)
|
||||
|
||||
def get_total_memory(self, identity):
|
||||
"""Get computer system total memory
|
||||
|
||||
|
@ -38,6 +38,13 @@ class OpenStackDriver(AbstractDriver):
|
||||
|
||||
BOOT_DEVICE_MAP_REV = {v: k for k, v in BOOT_DEVICE_MAP.items()}
|
||||
|
||||
BOOT_MODE_MAP = {
|
||||
'Legacy': 'bios',
|
||||
'Uefi': 'uefi',
|
||||
}
|
||||
|
||||
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
|
||||
|
||||
def __init__(self, os_cloud, readonly=False):
|
||||
self._cc = openstack.connect(cloud=os_cloud)
|
||||
self._os_cloud = os_cloud
|
||||
@ -188,6 +195,37 @@ class OpenStackDriver(AbstractDriver):
|
||||
if target == 'network' else ''}
|
||||
)
|
||||
|
||||
def get_boot_mode(self, identity):
|
||||
"""Get computer system boot mode.
|
||||
|
||||
:returns: either *Uefi* or *Legacy* as `str` or `None` if
|
||||
current boot mode can't be determined
|
||||
"""
|
||||
instance = self._get_instance(identity)
|
||||
|
||||
image = self._nc.glance.find_image(instance.image['id'])
|
||||
|
||||
hw_firmware_type = getattr(image, 'hw_firmware_type', None)
|
||||
|
||||
return self.BOOT_MODE_MAP_REV.get(hw_firmware_type)
|
||||
|
||||
def set_boot_mode(self, identity, boot_mode):
|
||||
"""Set computer system boot mode.
|
||||
|
||||
:param boot_mode: optional string literal requesting boot mode
|
||||
change on the system. If not specified, current boot mode is
|
||||
returned. Valid values are: *Uefi*, *Legacy*.
|
||||
|
||||
:raises: `FishyError` if boot mode can't be set
|
||||
"""
|
||||
# just to make sure passed identity exists
|
||||
self._get_instance(identity)
|
||||
|
||||
msg = ('The cloud driver %(driver)s does not allow changing boot '
|
||||
'mode through Redfish' % {'driver': self.driver})
|
||||
|
||||
raise FishyError(msg)
|
||||
|
||||
def get_total_memory(self, identity):
|
||||
"""Get computer system total memory
|
||||
|
||||
|
@ -125,7 +125,8 @@ def system_resource(identity):
|
||||
power_state=driver.get_power_state(identity),
|
||||
total_memory_gb=driver.get_total_memory(identity),
|
||||
total_cpus=driver.get_total_cpus(identity),
|
||||
boot_source_target=driver.get_boot_device(identity)
|
||||
boot_source_target=driver.get_boot_device(identity),
|
||||
boot_source_mode=driver.get_boot_mode(identity)
|
||||
)
|
||||
|
||||
elif flask.request.method == 'PATCH':
|
||||
@ -134,20 +135,28 @@ def system_resource(identity):
|
||||
return 'PATCH only works for the Boot element', 400
|
||||
|
||||
target = boot.get('BootSourceOverrideTarget')
|
||||
if not target:
|
||||
return 'Missing the BootSourceOverrideTarget element', 400
|
||||
|
||||
# NOTE(lucasagomes): In libvirt we always set the boot
|
||||
# device frequency to "continuous" so, we are ignoring the
|
||||
# BootSourceOverrideEnabled element here
|
||||
if target:
|
||||
# NOTE(lucasagomes): In libvirt we always set the boot
|
||||
# device frequency to "continuous" so, we are ignoring the
|
||||
# BootSourceOverrideEnabled element here
|
||||
|
||||
# TODO(lucasagomes): We should allow changing the boot mode from
|
||||
# BIOS to UEFI (and vice-versa)
|
||||
driver.set_boot_device(identity, target)
|
||||
|
||||
driver.set_boot_device(identity, target)
|
||||
app.logger.info('Set boot device to "%s" for system "%s"',
|
||||
target, identity)
|
||||
|
||||
app.logger.info('Set boot device to "%s" for system "%s"',
|
||||
target, identity)
|
||||
mode = boot.get('BootSourceOverrideMode')
|
||||
|
||||
if mode:
|
||||
driver.set_boot_mode(identity, mode)
|
||||
|
||||
app.logger.info('Set boot mode to "%s" for system "%s"',
|
||||
mode, identity)
|
||||
|
||||
if not target and not mode:
|
||||
return ('Missing the BootSourceOverrideTarget and/or '
|
||||
'BootSourceOverrideMode element', 400)
|
||||
|
||||
return '', 204
|
||||
|
||||
@ -205,14 +214,14 @@ def main():
|
||||
if args.os_cloud:
|
||||
if not novadriver:
|
||||
app.logger.error('Nova driver not loaded')
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
driver = novadriver.OpenStackDriver(args.os_cloud)
|
||||
|
||||
else:
|
||||
if not libvirtdriver:
|
||||
app.logger.error('libvirt driver not loaded')
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
driver = libvirtdriver.LibvirtDriver(args.libvirt_uri)
|
||||
|
||||
|
@ -22,17 +22,27 @@
|
||||
"PowerState": "{{ power_state }}",
|
||||
{%- endif %}
|
||||
"Boot": {
|
||||
"BootSourceOverrideEnabled": "Continuous",
|
||||
{%- if boot_source_target %}
|
||||
"BootSourceOverrideEnabled": "Continuous",
|
||||
"BootSourceOverrideTarget": "{{ boot_source_target }}",
|
||||
"BootSourceOverrideTarget@Redfish.AllowableValues": [
|
||||
"Pxe",
|
||||
"Cd",
|
||||
"Hdd"
|
||||
{%- if boot_source_mode %}
|
||||
],
|
||||
{%- endif %}
|
||||
"BootSourceOverrideMode": "UEFI",
|
||||
{%- if 'uefi' in boot_source_mode.lower() %}
|
||||
"BootSourceOverrideMode": "{{ boot_source_mode }}",
|
||||
"UefiTargetBootSourceOverride": "/0x31/0x33/0x01/0x01"
|
||||
{%- else %}
|
||||
"BootSourceOverrideMode": "{{ boot_source_mode }}"
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
]
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
"BootSourceOverrideEnabled": "Continuous"
|
||||
{%- endif %}
|
||||
},
|
||||
"TrustedModules": [
|
||||
{
|
||||
|
@ -7,6 +7,7 @@
|
||||
<os>
|
||||
<type arch='i686' machine='pc'>hvm</type>
|
||||
<boot dev='cdrom'/>
|
||||
<loader type='rom'/>
|
||||
</os>
|
||||
<devices>
|
||||
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||
|
@ -63,7 +63,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
||||
render_mock.assert_called_once_with(
|
||||
'system.json', identity='xxxx-yyyy-zzzz', uuid='zzzz-yyyy-xxxx',
|
||||
power_state='On', total_memory_gb=1, total_cpus=2,
|
||||
boot_source_target='Cd')
|
||||
boot_source_target='Cd', boot_source_mode='Legacy')
|
||||
|
||||
@mock.patch('libvirt.open', autospec=True)
|
||||
def test_system_resource_patch(self, libvirt_mock):
|
||||
|
Loading…
x
Reference in New Issue
Block a user