[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[virt-tools-list] [PATCH virt-manager] details: Add UI for enabling UEFI via 'customize before install'



We expose a simple combobox with two entries: BIOS, and UEFI. The
UEFI option is only selectable if 1) libvirt supports the necessary
domcapabilities bits, 2) it detects that qemu supports the necessary
command line options, and 3) libvirt detects a UEFI binary on the
host that maps to a known template via qemu.conf

If those conditions aren't met, we disable the UEFI option, and show
a small warning icon with an explanatory tooltip.

The option can only be changed via New VM->Customize Before Install.
For existing x86 VMs, it's a readonly label.
---
 ui/details.ui          | 84 ++++++++++++++++++++++++++++++++++++++++++++++----
 virtManager/details.py | 72 ++++++++++++++++++++++++++++++++++++++++++-
 virtManager/domain.py  | 32 ++++++++++++++++++-
 virtinst/support.py    |  3 ++
 4 files changed, 183 insertions(+), 8 deletions(-)

diff --git a/ui/details.ui b/ui/details.ui
index cd2c9c5..4080a14 100644
--- a/ui/details.ui
+++ b/ui/details.ui
@@ -957,6 +957,7 @@
                                           <object class="GtkLabel" id="overview-machine-label">
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
+                                            <property name="valign">start</property>
                                             <property name="xalign">1</property>
                                             <property name="label" translatable="yes">Machine _Type: </property>
                                             <property name="use_underline">True</property>
@@ -982,16 +983,15 @@
                                           <object class="GtkLabel" id="overview-chipset-title">
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
-                                            <property name="margin_top">2</property>
+                                            <property name="valign">start</property>
                                             <property name="xalign">1</property>
-                                            <property name="yalign">0</property>
                                             <property name="label" translatable="yes">Chipse_t:</property>
                                             <property name="use_underline">True</property>
                                             <property name="ellipsize">middle</property>
                                           </object>
                                           <packing>
                                             <property name="left_attach">0</property>
-                                            <property name="top_attach">4</property>
+                                            <property name="top_attach">5</property>
                                           </packing>
                                         </child>
                                         <child>
@@ -1061,6 +1061,8 @@ if you know what you are doing.&lt;/small&gt;</property>
                                                   <object class="GtkLabel" id="overview-chipset-label">
                                                     <property name="visible">True</property>
                                                     <property name="can_focus">False</property>
+                                                    <property name="valign">start</property>
+                                                    <property name="vexpand">True</property>
                                                     <property name="xalign">0</property>
                                                     <property name="label">label</property>
                                                   </object>
@@ -1079,6 +1081,79 @@ if you know what you are doing.&lt;/small&gt;</property>
                                           </object>
                                           <packing>
                                             <property name="left_attach">1</property>
+                                            <property name="top_attach">5</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkLabel" id="overview-firmware-title">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="valign">start</property>
+                                            <property name="xalign">1</property>
+                                            <property name="label" translatable="yes">Firmware:</property>
+                                          </object>
+                                          <packing>
+                                            <property name="left_attach">0</property>
+                                            <property name="top_attach">4</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkBox" id="box11">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="spacing">3</property>
+                                            <child>
+                                              <object class="GtkBox" id="box16">
+                                                <property name="visible">True</property>
+                                                <property name="can_focus">False</property>
+                                                <child>
+                                                  <object class="GtkComboBox" id="overview-firmware">
+                                                    <property name="visible">True</property>
+                                                    <property name="can_focus">False</property>
+                                                    <signal name="changed" handler="on_overview_firmware_changed" swapped="no"/>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">0</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkLabel" id="overview-firmware-label">
+                                                    <property name="visible">True</property>
+                                                    <property name="can_focus">False</property>
+                                                    <property name="valign">start</property>
+                                                    <property name="vexpand">False</property>
+                                                    <property name="label">label</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">1</property>
+                                                  </packing>
+                                                </child>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <object class="GtkImage" id="overview-firmware-warn">
+                                                <property name="visible">True</property>
+                                                <property name="can_focus">False</property>
+                                                <property name="stock">gtk-dialog-warning</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">1</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                          <packing>
+                                            <property name="left_attach">1</property>
                                             <property name="top_attach">4</property>
                                           </packing>
                                         </child>
@@ -1310,9 +1385,6 @@ if you know what you are doing.&lt;/small&gt;</property>
                                 <property name="position">2</property>
                               </packing>
                             </child>
-                            <child>
-                              <placeholder/>
-                            </child>
                           </object>
                         </child>
                         <child type="tab">
diff --git a/virtManager/details.py b/virtManager/details.py
index bab87ee..e451191 100644
--- a/virtManager/details.py
+++ b/virtManager/details.py
@@ -48,6 +48,7 @@ from virtinst import VirtualRNGDevice
 (EDIT_NAME,
 EDIT_TITLE,
 EDIT_MACHTYPE,
+EDIT_FIRMWARE,
 EDIT_DESC,
 EDIT_IDMAP,
 
@@ -103,7 +104,7 @@ EDIT_FS,
 
 EDIT_HOSTDEV_ROMBAR,
 
-) = range(1, 44)
+) = range(1, 45)
 
 
 # Columns in hw list model
@@ -398,6 +399,18 @@ def _chipset_label_from_machine(machine):
     return "i440FX"
 
 
+def _firmware_label_from_loader(vm, loader, force_uefi=False):
+    domcaps = vm.get_domain_capabilities()
+    if (domcaps.os.loader.values and
+        loader == domcaps.os.loader.values[0].value) or force_uefi:
+        return "UEFI"
+
+    if loader is None:
+        return "BIOS"
+
+    return "Custom"
+
+
 class vmmDetails(vmmGObjectUI):
     __gsignals__ = {
         "action-save-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]),
@@ -548,6 +561,7 @@ class vmmDetails(vmmGObjectUI):
             "on_overview_name_changed": lambda *x: self.enable_apply(x, EDIT_NAME),
             "on_overview_title_changed": lambda *x: self.enable_apply(x, EDIT_TITLE),
             "on_machine_type_changed": lambda *x: self.enable_apply(x, EDIT_MACHTYPE),
+            "on_overview_firmware_changed": lambda *x: self.enable_apply(x, EDIT_FIRMWARE),
             "on_overview_chipset_changed": lambda *x: self.enable_apply(x, EDIT_MACHTYPE),
             "on_idmap_uid_target_changed": lambda *x: self.enable_apply(x, EDIT_IDMAP),
             "on_idmap_uid_count_changed": lambda *x: self.enable_apply(x, EDIT_IDMAP),
@@ -873,6 +887,49 @@ class vmmDetails(vmmGObjectUI):
                     continue
                 machtype_model.append([machine])
 
+        # Firmware
+        combo = self.widget("overview-firmware")
+        model = Gtk.ListStore(str, str, bool)
+        combo.set_model(model)
+        text = Gtk.CellRendererText()
+        combo.pack_start(text, True)
+        combo.add_attribute(text, "text", 0)
+        combo.add_attribute(text, "sensitive", 2)
+
+        domcaps = self.vm.get_domain_capabilities()
+        uefipath = None
+        if domcaps.os.loader.values:
+            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())
+        if not hv_supports_uefi:
+            warn_icon.set_tooltip_text(
+                _("Libvirt or hypervisor does not support UEFI."))
+        elif not uefipath:
+            warn_icon.set_tooltip_text(
+                _("Libvirt did not detect any UEFI/OVMF firmware image "
+                  "installed on the host."))
+
+        model.append([_firmware_label_from_loader(self.vm, None),
+            None, True])
+        model.append([_firmware_label_from_loader(
+            self.vm, uefipath, force_uefi=True), uefipath,
+            bool(uefipath and hv_supports_uefi)])
+        combo.set_active(0)
+
+        self.widget("overview-firmware-warn").set_visible(
+            not (uefipath and hv_supports_uefi) and self.is_customize_dialog)
+        self.widget("overview-firmware").set_visible(self.is_customize_dialog)
+        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
+            not self.vm.is_management_domain())
+        uiutil.set_grid_row_visible(
+            self.widget("overview-firmware-title"), show_firmware)
+
         # Chipset
         combo = self.widget("overview-chipset")
         model = Gtk.ListStore(str, str)
@@ -1974,6 +2031,10 @@ class vmmDetails(vmmGObjectUI):
             kwargs["title"] = self.widget("overview-title").get_text()
             hotplug_args["title"] = kwargs["title"]
 
+        if self.edited(EDIT_FIRMWARE):
+            kwargs["loader"] = uiutil.get_list_selection(
+                self.widget("overview-firmware"), 1)
+
         if self.edited(EDIT_MACHTYPE):
             if self.widget("overview-chipset").is_visible():
                 kwargs["machine"] = uiutil.get_list_selection(
@@ -2402,6 +2463,15 @@ class vmmDetails(vmmGObjectUI):
         self.widget("overview-arch").set_text(arch)
         self.widget("overview-emulator").set_text(emu)
 
+        # Firmware
+        firmware = _firmware_label_from_loader(self.vm,
+            self.vm.get_xmlobj().os.loader)
+        if self.widget("overview-firmware").is_visible():
+            uiutil.set_combo_entry(
+                self.widget("overview-firmware"), firmware)
+        elif self.widget("overview-firmware-label").is_visible():
+            self.widget("overview-firmware-label").set_text(firmware)
+
         # Machine settings
         machtype = self.vm.get_machtype()
         if arch not in ["i686", "x86_64"]:
diff --git a/virtManager/domain.py b/virtManager/domain.py
index 3d279e8..29f3861 100644
--- a/virtManager/domain.py
+++ b/virtManager/domain.py
@@ -27,6 +27,7 @@ import threading
 
 import libvirt
 
+from virtinst import DomainCapabilities
 from virtinst import DomainSnapshot
 from virtinst import Guest
 from virtinst import util
@@ -301,6 +302,7 @@ class vmmDomain(vmmLibvirtObject):
         self._has_managed_save = None
         self._snapshot_list = None
         self._autostart = None
+        self._domain_caps = None
 
         self.lastStatus = libvirt.VIR_DOMAIN_SHUTOFF
         self._lastStatusReason = getattr(libvirt, "VIR_DOMAIN_SHUTOFF_SHUTDOWN",
@@ -505,6 +507,20 @@ class vmmDomain(vmmLibvirtObject):
             return _("Snapshots require at least one writeable qcow2 disk "
                      "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)
+
+        return self._domain_caps
+
 
     #############################
     # Internal XML handling API #
@@ -619,7 +635,7 @@ class vmmDomain(vmmLibvirtObject):
                                         newname)
 
     def define_overview(self, machine=_SENTINEL, description=_SENTINEL,
-        title=_SENTINEL, idmap_list=_SENTINEL):
+        title=_SENTINEL, idmap_list=_SENTINEL, loader=_SENTINEL):
         def change(guest):
             if machine != _SENTINEL:
                 guest.os.machine = machine
@@ -628,6 +644,20 @@ class vmmDomain(vmmLibvirtObject):
             if title != _SENTINEL:
                 guest.title = title or None
 
+            if loader != _SENTINEL:
+                if loader is None:
+                    # Implies seabios, aka the default, so clear everything
+                    guest.os.loader = None
+                    guest.os.loader_ro = None
+                    guest.os.loader_type = None
+                    guest.os.nvram = None
+                    guest.os.nvram_template = None
+                else:
+                    # Implies UEFI
+                    guest.os.loader = loader
+                    guest.os.loader_type = "pflash"
+                    guest.os.loader_ro = True
+
             if idmap_list != _SENTINEL:
                 if idmap_list is not None:
                     # pylint: disable=unpacking-non-sequence
diff --git a/virtinst/support.py b/virtinst/support.py
index 2df4fb1..d8c203b 100644
--- a/virtinst/support.py
+++ b/virtinst/support.py
@@ -298,6 +298,9 @@ SUPPORT_CONN_HYPERV_VAPIC = _make(
 SUPPORT_CONN_HYPERV_CLOCK = _make(
     version="1.2.2", hv_version={"qemu": "2.0.0", "test": 0})
 SUPPORT_CONN_LOADER_ROM = _make(version="1.2.9")
+SUPPORT_CONN_DOMAIN_CAPABILITIES = _make(
+    function="virConnect.getDomainCapabilities",
+    run_args=(None, None, None, None))
 
 
 # Domain checks
-- 
2.1.0


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]