Merge "Add system boot mode support"
This commit is contained in:
commit
0260a11155
@ -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