virt-install: Add --boot uefi
Will enable UEFI if we know libvirt + hypervisor support it, and libvirt is advertising a suitable UEFI binary.
This commit is contained in:
parent
760465fd2b
commit
a04307cd70
|
@ -451,28 +451,6 @@ configurations like ARM or PPC
|
|||
|
||||
Use BIOSPATH as the virtual machine BIOS.
|
||||
|
||||
=item B<--boot loader=/usr/share/OVMF/OVMF_CODE.fd,loader_ro=yes,loader_type=pflash>
|
||||
|
||||
Specify that the virtual machine use the system-wide installed OVMF binary as
|
||||
boot firmware, mapped as a virtual flash chip. This form requests that libvirt
|
||||
instantiate the VM-specific UEFI varstore from the varstore template that is
|
||||
assigned to "/usr/share/OVMF/OVMF_CODE.fd" in libvirtd's "qemu.conf", in the
|
||||
"nvram" stanza.
|
||||
|
||||
=item B<--boot loader=/.../OVMF_CODE.fd,loader_ro=yes,loader_type=pflash,nvram_template=/.../OVMF_VARS.fd>
|
||||
|
||||
Specify that the virtual machine use the custom OVMF binary as boot firmware,
|
||||
mapped as a virtual flash chip. In addition, request that libvirt instantiate
|
||||
the VM-specific UEFI varstore from the custom "/.../OVMF_VARS.fd" varstore
|
||||
template.
|
||||
|
||||
=item B<--boot loader=/.../OVMF_CODE.fd,loader_ro=yes,loader_type=pflash,nvram=/.../guest_VARS.fd>
|
||||
|
||||
Specify that the virtual machine use the custom OVMF binary as boot firmware,
|
||||
mapped as a virtual flash chip. In addition, the VM-specific UEFI varstore is
|
||||
directly the preexistent file "/.../guest_VARS.fd"; it is not instantiated from
|
||||
any template.
|
||||
|
||||
=item B<--boot menu=on,useserial=on>
|
||||
|
||||
Enable the bios boot menu, and enable sending bios text output over
|
||||
|
@ -484,6 +462,21 @@ Path to a binary that the container guest will init. If a root C<--filesystem>
|
|||
has been specified, virt-install will default to /sbin/init, otherwise
|
||||
will default to /bin/sh.
|
||||
|
||||
=item B<--boot uefi>
|
||||
|
||||
Configure the VM to boot from UEFI. In order for virt-install to know the
|
||||
correct UEFI parameters, libvirt needs to be advertising known UEFI binaries
|
||||
via domcapabilities XML, so this will likely only work if using properly
|
||||
configured distro packages.
|
||||
|
||||
=item B<--boot loader=/.../OVMF_CODE.fd,loader_ro=yes,loader_type=pflash,nvram_template=/.../OVMF_VARS.fd>
|
||||
|
||||
Specify that the virtual machine use the custom OVMF binary as boot firmware,
|
||||
mapped as a virtual flash chip. In addition, request that libvirt instantiate
|
||||
the VM-specific UEFI varstore from the custom "/.../OVMF_VARS.fd" varstore
|
||||
template. This is the recommended UEFI setup, and should be used if
|
||||
--boot uefi doesn't know about your UEFI binaries.
|
||||
|
||||
=back
|
||||
|
||||
Use --boot=? to see a list of all available sub options. Complete details at L<http://libvirt.org/formatdomain.html#elementsOS>
|
||||
|
|
|
@ -13,9 +13,8 @@
|
|||
</numatune>
|
||||
<os>
|
||||
<type arch="x86_64">hvm</type>
|
||||
<boot dev="network"/>
|
||||
<loader readonly="yes" type="pflash">/usr/share/OVMF/OVMF_CODE.fd</loader>
|
||||
<boot dev="hd"/>
|
||||
<bootmenu enable="yes"/>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
|
|
|
@ -460,7 +460,7 @@ c.add_compare(""" \
|
|||
--vcpus 4 --cpuset=1,3-5 \
|
||||
--cpu host \
|
||||
--description \"foobar & baz\" \
|
||||
--boot network,hd,menu=on \
|
||||
--boot uefi \
|
||||
--security type=dynamic \
|
||||
--numatune 1,2,3,5-7,^6 \
|
||||
--memorybacking hugepages=on \
|
||||
|
@ -675,6 +675,8 @@ c.add_invalid("--graphics spice,tlsport=5") # Invalid port
|
|||
c.add_invalid("--serial unix") # Unix with no path
|
||||
c.add_invalid("--serial null,path=/tmp/foo") # Path where it doesn't belong
|
||||
c.add_invalid("--channel pty,target_type=guestfwd") # --channel guestfwd without target_address
|
||||
c.add_invalid("--boot uefi") # URI doesn't support UEFI bits
|
||||
c.add_invalid("--connect %(KVMURI)s --boot uefi,arch=ppc64") # unsupported arch for UEFI
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -803,8 +803,7 @@ class vmmDetails(vmmGObjectUI):
|
|||
uefipath = domcaps.os.loader.values[0].value
|
||||
|
||||
warn_icon = self.widget("overview-firmware-warn")
|
||||
hv_supports_uefi = ("readonly" in domcaps.os.loader.enum_names() and
|
||||
"yes" in domcaps.os.loader.get_enum("readonly").get_values())
|
||||
hv_supports_uefi = domcaps.supports_uefi_xml()
|
||||
if not hv_supports_uefi:
|
||||
warn_icon.set_tooltip_text(
|
||||
_("Libvirt or hypervisor does not support UEFI."))
|
||||
|
@ -826,7 +825,7 @@ class vmmDetails(vmmGObjectUI):
|
|||
self.widget("overview-firmware-label").set_visible(
|
||||
not self.is_customize_dialog)
|
||||
show_firmware = ((self.conn.is_qemu() or self.conn.is_test_conn()) and
|
||||
arch in ["i686", "x86_64"] and
|
||||
domcaps.arch_can_uefi(arch) and
|
||||
not self.vm.is_management_domain())
|
||||
uiutil.set_grid_row_visible(
|
||||
self.widget("overview-firmware-title"), show_firmware)
|
||||
|
|
|
@ -508,17 +508,9 @@ class vmmDomain(vmmLibvirtObject):
|
|||
"image allocated to the guest.")
|
||||
|
||||
def get_domain_capabilities(self):
|
||||
if not self.conn.check_support(
|
||||
self.conn.SUPPORT_CONN_DOMAIN_CAPABILITIES):
|
||||
self._domain_caps = DomainCapabilities(self.conn.get_backend())
|
||||
|
||||
if not self._domain_caps:
|
||||
xml = self.conn.get_backend().getDomainCapabilities(
|
||||
self.get_xmlobj().emulator, self.get_xmlobj().os.arch,
|
||||
self.get_xmlobj().os.machine, self.get_xmlobj().type)
|
||||
self._domain_caps = DomainCapabilities(self.conn.get_backend(),
|
||||
parsexml=xml)
|
||||
|
||||
self._domain_caps = DomainCapabilities.build_from_guest(
|
||||
self.get_xmlobj())
|
||||
return self._domain_caps
|
||||
|
||||
|
||||
|
|
|
@ -696,7 +696,7 @@ class _VirtCLIArgument(object):
|
|||
setter_cb=None, ignore_default=False,
|
||||
can_comma=False, aliases=None,
|
||||
is_list=False, is_onoff=False,
|
||||
lookup_cb=None):
|
||||
lookup_cb=None, is_novalue=False):
|
||||
"""
|
||||
A single subargument passed to compound command lines like --disk,
|
||||
--network, etc.
|
||||
|
@ -722,6 +722,8 @@ class _VirtCLIArgument(object):
|
|||
it to true/false.
|
||||
@lookup_cb: If specified, use this function for performing match
|
||||
lookups.
|
||||
@is_novalue: If specified, the parameter is not expected in the
|
||||
form FOO=BAR, but just FOO.
|
||||
"""
|
||||
self.attrname = attrname
|
||||
self.cliname = cliname
|
||||
|
@ -733,6 +735,7 @@ class _VirtCLIArgument(object):
|
|||
self.is_list = is_list
|
||||
self.is_onoff = is_onoff
|
||||
self.lookup_cb = lookup_cb
|
||||
self.is_novalue = is_novalue
|
||||
|
||||
|
||||
def parse(self, opts, inst, support_cb=None, lookup=False):
|
||||
|
@ -740,7 +743,7 @@ class _VirtCLIArgument(object):
|
|||
for cliname in self.aliases + [self.cliname]:
|
||||
# We iterate over all values unconditionally, so they are
|
||||
# removed from opts
|
||||
foundval = opts.get_opt_param(cliname)
|
||||
foundval = opts.get_opt_param(cliname, self.is_novalue)
|
||||
if foundval is not None:
|
||||
val = foundval
|
||||
if val is None:
|
||||
|
@ -809,12 +812,16 @@ class VirtOptionString(object):
|
|||
self.opts, self.orderedopts = self._parse_optstr(
|
||||
virtargmap, remove_first)
|
||||
|
||||
def get_opt_param(self, key):
|
||||
def get_opt_param(self, key, is_novalue=False):
|
||||
if key not in self.opts:
|
||||
return None
|
||||
|
||||
ret = self.opts.pop(key)
|
||||
if ret is None:
|
||||
raise RuntimeError("Option '%s' had no value set." % key)
|
||||
if not is_novalue:
|
||||
raise RuntimeError("Option '%s' had no value set." % key)
|
||||
ret = ""
|
||||
|
||||
return ret
|
||||
|
||||
def check_leftover_opts(self):
|
||||
|
@ -1280,6 +1287,19 @@ class ParserBoot(VirtCLIParser):
|
|||
def _init_params(self):
|
||||
self.clear_attr = "os"
|
||||
|
||||
# UEFI depends on these bits, so set them first
|
||||
self.set_param("os.arch", "arch")
|
||||
self.set_param("type", "domain_type")
|
||||
self.set_param("os.os_type", "os_type")
|
||||
self.set_param("emulator", "emulator")
|
||||
|
||||
def set_uefi(opts, inst, cliname, val):
|
||||
ignore = opts
|
||||
ignore = cliname
|
||||
ignore = val
|
||||
inst.set_uefi_default()
|
||||
self.set_param(None, "uefi", setter_cb=set_uefi, is_novalue=True)
|
||||
|
||||
self.set_param("os.useserial", "useserial", is_onoff=True)
|
||||
self.set_param("os.enable_bootmenu", "menu", is_onoff=True)
|
||||
self.set_param("os.kernel", "kernel")
|
||||
|
@ -1293,11 +1313,7 @@ class ParserBoot(VirtCLIParser):
|
|||
self.set_param("os.kernel_args", "kernel_args",
|
||||
aliases=["extra_args"], can_comma=True)
|
||||
self.set_param("os.init", "init")
|
||||
self.set_param("os.arch", "arch")
|
||||
self.set_param("type", "domain_type")
|
||||
self.set_param("os.machine", "machine")
|
||||
self.set_param("os.os_type", "os_type")
|
||||
self.set_param("emulator", "emulator")
|
||||
|
||||
def set_initargs_cb(opts, inst, cliname, val):
|
||||
ignore = opts
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
# MA 02110-1301 USA.
|
||||
|
||||
import re
|
||||
|
||||
from .xmlbuilder import XMLBuilder, XMLChildProperty
|
||||
from .xmlbuilder import XMLProperty as _XMLProperty
|
||||
|
||||
|
@ -76,6 +78,58 @@ class _Devices(_CapsBlock):
|
|||
|
||||
|
||||
class DomainCapabilities(XMLBuilder):
|
||||
@staticmethod
|
||||
def build_from_guest(guest):
|
||||
if not guest.conn.check_support(
|
||||
guest.conn.SUPPORT_CONN_DOMAIN_CAPABILITIES):
|
||||
# If not supported, just use a stub object
|
||||
return DomainCapabilities(guest.conn)
|
||||
|
||||
xml = guest.conn.getDomainCapabilities(
|
||||
guest.emulator, guest.os.arch, guest.os.machine, guest.type)
|
||||
return DomainCapabilities(guest.conn, parsexml=xml)
|
||||
|
||||
# Mapping of UEFI binary names to their associated architectures. We
|
||||
# only use this info to do things automagically for the user, it shouldn't
|
||||
# validate anything the user explicitly enters.
|
||||
_uefi_arch_patterns = {
|
||||
"x86_64": [
|
||||
".*OVMF_CODE\.fd", # RHEL
|
||||
".*ovmf-x64/OVMF.*\.fd", # gerd's firmware repo
|
||||
],
|
||||
"aarch64": [
|
||||
".*AAVMF_CODE\.fd", # RHEL
|
||||
".*aarch64/QEMU_EFI.*", # gerd's firmware repo
|
||||
],
|
||||
}
|
||||
|
||||
def find_uefi_path_for_arch(self, arch):
|
||||
"""
|
||||
Search the loader paths for one that matches the passed arch
|
||||
"""
|
||||
if not self.arch_can_uefi(arch):
|
||||
return
|
||||
|
||||
patterns = self._uefi_arch_patterns.get(arch)
|
||||
for pattern in patterns:
|
||||
for path in [v.value for v in self.os.loader.values]:
|
||||
if re.match(pattern, path):
|
||||
return path
|
||||
|
||||
def arch_can_uefi(self, arch):
|
||||
"""
|
||||
Return True if we know how to setup UEFI for the passed arch
|
||||
"""
|
||||
return arch in self._uefi_arch_patterns.keys()
|
||||
|
||||
def supports_uefi_xml(self):
|
||||
"""
|
||||
Return True if libvirt advertises support for proper UEFI setup
|
||||
"""
|
||||
return ("readonly" in self.os.loader.enum_names() and
|
||||
"yes" in self.os.loader.get_enum("readonly").get_values())
|
||||
|
||||
|
||||
_XML_ROOT_NAME = "domainCapabilities"
|
||||
os = XMLChildProperty(_OS, is_single=True)
|
||||
devices = XMLChildProperty(_Devices, is_single=True)
|
||||
|
|
|
@ -46,6 +46,7 @@ from .domainmemorybacking import DomainMemorybacking
|
|||
from .domainmemorytune import DomainMemorytune
|
||||
from .domainnumatune import DomainNumatune
|
||||
from .domainresource import DomainResource
|
||||
from .domcapabilities import DomainCapabilities
|
||||
from .idmap import IdMap
|
||||
from .osxml import OSXML
|
||||
from .pm import PM
|
||||
|
@ -529,6 +530,35 @@ class Guest(XMLBuilder):
|
|||
return osdict.lookup_osdict_key(self.os_variant, key, default)
|
||||
|
||||
|
||||
###########################
|
||||
# XML convenience helpers #
|
||||
###########################
|
||||
|
||||
def set_uefi_default(self):
|
||||
"""
|
||||
Configure UEFI for the VM, but only if libvirt is advertising
|
||||
a known UEFI binary path.
|
||||
"""
|
||||
domcaps = DomainCapabilities.build_from_guest(self)
|
||||
|
||||
if not domcaps.supports_uefi_xml():
|
||||
raise RuntimeError(_("Libvirt version does not support UEFI."))
|
||||
|
||||
if not domcaps.arch_can_uefi(self.os.arch):
|
||||
raise RuntimeError(
|
||||
_("Don't know how to setup UEFI for arch '%s'") %
|
||||
self.os.arch)
|
||||
|
||||
path = domcaps.find_uefi_path_for_arch(self.os.arch)
|
||||
if not path:
|
||||
raise RuntimeError(_("Did not find any UEFI binary path for "
|
||||
"arch '%s'") % self.os.arch)
|
||||
|
||||
self.os.loader_ro = True
|
||||
self.os.loader_type = "pflash"
|
||||
self.os.loader = path
|
||||
|
||||
|
||||
###################
|
||||
# Device defaults #
|
||||
###################
|
||||
|
|
Loading…
Reference in New Issue