Merge "Add system boot mode support"

This commit is contained in:
Zuul 2018-06-08 12:37:29 +00:00 committed by Gerrit Code Review
commit 0260a11155
7 changed files with 169 additions and 17 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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": [
{

View File

@ -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>

View File

@ -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):