mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-22 13:34:07 +03:00
details: Add OS name view/edit, + oslist rework
This is just a big nasty commit. Turn the OS inspection page into an always available page that shows the libosinfo name from the domain metadata XML. Use oslist.py and have it absorb more of the common behavior needed by create.py and details.py. Add UI tests for it all
This commit is contained in:
parent
75c64151b1
commit
b19f94299b
@ -58,7 +58,7 @@
|
||||
<name>test-many-devices</name>
|
||||
<metadata>
|
||||
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
|
||||
<libosinfo:os id="http://fedoraproject.org/fedora/27"/>
|
||||
<libosinfo:os id="http://fedoraproject.org/fedora/unknown"/>
|
||||
</libosinfo:libosinfo>
|
||||
</metadata>
|
||||
<currentMemory>204800</currentMemory>
|
||||
@ -550,14 +550,18 @@ Foo bar baz & yeah boii < > yeahfoo
|
||||
</domain>
|
||||
|
||||
|
||||
|
||||
<domain type='test'>
|
||||
<name>test-alternate-devs</name>
|
||||
<uuid>4a64cc71-19c4-2fd0-2323-00aa941ea3c3</uuid>
|
||||
<description>Test alternate devices that can't be crammed in
|
||||
test-many-devices, like an alternate RNG.
|
||||
test-many-devices, like an alternate RNG, EOL OS ID, title field
|
||||
</description>
|
||||
<title>test alternate devs (title)</title>
|
||||
<metadata>
|
||||
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
|
||||
<libosinfo:os id="http://microsoft.com/msdos/6.22"/>
|
||||
</libosinfo:libosinfo>
|
||||
</metadata>
|
||||
<memory>8388608</memory>
|
||||
<currentMemory>2097152</currentMemory>
|
||||
<vcpu>2</vcpu>
|
||||
@ -578,6 +582,7 @@ test-many-devices, like an alternate RNG.
|
||||
</devices>
|
||||
</domain>
|
||||
|
||||
|
||||
<domain type='test' xmlns:test='http://libvirt.org/schemas/domain/test/1.0'>
|
||||
<test:runstate>5</test:runstate>
|
||||
<name>test-clone-simple</name>
|
||||
|
@ -83,10 +83,12 @@ class Details(uiutils.UITestCase):
|
||||
|
||||
self._testRename(origname, "test-new-name")
|
||||
|
||||
def testDetailsEdits(self):
|
||||
def testDetailsEditDomain1(self):
|
||||
"""
|
||||
Test overview, memory, cpu pages
|
||||
"""
|
||||
win = self._open_details_window(vmname="test-many-devices")
|
||||
appl = win.find("config-apply", "push button")
|
||||
hwlist = win.find("hw-list")
|
||||
|
||||
# Overview description
|
||||
tab = self._select_hw(win, "Overview", "overview-tab")
|
||||
@ -141,6 +143,33 @@ class Details(uiutils.UITestCase):
|
||||
self.assertTrue(tab.find_fuzzy("Maximum", "spin").text == "32")
|
||||
|
||||
|
||||
def testDetailsEditDomain2(self):
|
||||
"""
|
||||
Test boot and OS pages
|
||||
"""
|
||||
win = self._open_details_window(vmname="test-many-devices")
|
||||
appl = win.find("config-apply", "push button")
|
||||
self._stop_vm(win)
|
||||
|
||||
|
||||
# OS edits
|
||||
tab = self._select_hw(win, "OS information", "os-tab")
|
||||
entry = tab.find("oslist-entry")
|
||||
self.assertEqual(entry.text, "Fedora")
|
||||
entry.click()
|
||||
self.pressKey("Down")
|
||||
popover = win.find("oslist-popover")
|
||||
popover.find("include-eol").click()
|
||||
entry.text = "fedora12"
|
||||
popover.find_fuzzy("fedora12").bring_on_screen().click()
|
||||
uiutils.check_in_loop(lambda: not popover.visible)
|
||||
self.assertEqual(entry.text, "Fedora 12")
|
||||
appl.click()
|
||||
uiutils.check_in_loop(lambda: not appl.sensitive)
|
||||
self.assertEqual(entry.text, "Fedora 12")
|
||||
|
||||
|
||||
# Boot tweaks
|
||||
def check_bootorder(c):
|
||||
# Click the bootlist checkbox, which is hard to find in the tree
|
||||
import dogtail.rawinput
|
||||
@ -149,7 +178,6 @@ class Details(uiutils.UITestCase):
|
||||
button = 1
|
||||
dogtail.rawinput.click(x, y, button)
|
||||
|
||||
# Boot tweaks
|
||||
tab = self._select_hw(win, "Boot Options", "boot-tab")
|
||||
self._stop_vm(win)
|
||||
tab.find_fuzzy("Start virtual machine on host", "check box").click()
|
||||
@ -178,6 +206,16 @@ class Details(uiutils.UITestCase):
|
||||
uiutils.check_in_loop(lambda: not appl.sensitive)
|
||||
|
||||
|
||||
def testDetailsEditDiskNet(self):
|
||||
"""
|
||||
Test disk and network devices
|
||||
"""
|
||||
win = self._open_details_window(vmname="test-many-devices")
|
||||
appl = win.find("config-apply", "push button")
|
||||
hwlist = win.find("hw-list")
|
||||
self._stop_vm(win)
|
||||
|
||||
|
||||
# Disk options
|
||||
tab = self._select_hw(win, "IDE Disk 1", "disk-tab")
|
||||
tab.find("Shareable:", "check box").click()
|
||||
@ -254,6 +292,15 @@ class Details(uiutils.UITestCase):
|
||||
uiutils.check_in_loop(lambda: not appl.sensitive)
|
||||
|
||||
|
||||
def testDetailsEditDevices(self):
|
||||
"""
|
||||
Test all other devices
|
||||
"""
|
||||
win = self._open_details_window(vmname="test-many-devices")
|
||||
appl = win.find("config-apply", "push button")
|
||||
self._stop_vm(win)
|
||||
|
||||
|
||||
# Graphics
|
||||
tab = self._select_hw(win, "Display VNC", "graphics-tab")
|
||||
tab.find("Type:", "combo box").click_combo_entry()
|
||||
|
@ -49,8 +49,28 @@ class NewVM(uiutils.UITestCase):
|
||||
# Create default PXE VM
|
||||
newvm.find_fuzzy("PXE", "radio").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
newvm.find("oslist-entry").text = "generic"
|
||||
newvm.find("oslist-popover").find_fuzzy("generic").click()
|
||||
osentry = newvm.find("oslist-entry")
|
||||
uiutils.check_in_loop(lambda: not osentry.text)
|
||||
|
||||
# Make sure we throw an error if no OS selected
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
alert = self.app.root.find("vmm dialog", "alert")
|
||||
alert.find("You must select", "label")
|
||||
alert.find("OK", "push button").click()
|
||||
|
||||
# Test activating the osentry to grab the popover selection
|
||||
osentry.click()
|
||||
osentry.typeText("generic")
|
||||
newvm.find("oslist-popover")
|
||||
osentry.click()
|
||||
self.pressKey("Enter")
|
||||
uiutils.check_in_loop(lambda: osentry.text == "Generic default")
|
||||
|
||||
# Verify back+forward still keeps Generic selected
|
||||
newvm.find_fuzzy("Back", "button").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
uiutils.check_in_loop(lambda: "Generic" in osentry.text)
|
||||
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
@ -88,15 +108,15 @@ class NewVM(uiutils.UITestCase):
|
||||
browser.find_fuzzy("iso-vol", "table cell").click()
|
||||
browser.find_fuzzy("Choose Volume", "button").click()
|
||||
|
||||
label = newvm.find("oslist-entry")
|
||||
osentry = newvm.find("oslist-entry")
|
||||
uiutils.check_in_loop(lambda: browser.showing is False)
|
||||
uiutils.check_in_loop(lambda: label.text == "None detected")
|
||||
uiutils.check_in_loop(lambda: osentry.text == "None detected")
|
||||
|
||||
# Change distro to win8
|
||||
newvm.find_fuzzy("Automatically detect", "check").click()
|
||||
label.text = "windows 8"
|
||||
osentry.text = "windows 8"
|
||||
popover = newvm.find("oslist-popover")
|
||||
popover.find_fuzzy("Include end of life").click()
|
||||
popover.find_fuzzy("include-eol").click()
|
||||
popover.find_fuzzy(r"\(win8\)").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
|
||||
@ -141,15 +161,40 @@ class NewVM(uiutils.UITestCase):
|
||||
|
||||
newvm.find_fuzzy("Network Install", "radio").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
osentry = newvm.find("oslist-entry")
|
||||
uiutils.check_in_loop(lambda: osentry.text.startswith("Waiting"))
|
||||
|
||||
newvm.find("URL", "text").text = (
|
||||
"https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/14/Fedora/x86_64/os/")
|
||||
url = "https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/10/Fedora/x86_64/os/"
|
||||
oslabel = "Fedora 10"
|
||||
newvm.find("URL", "text").text = url
|
||||
|
||||
version = newvm.find("oslist-entry")
|
||||
uiutils.check_in_loop(
|
||||
lambda: version.text == "Fedora 14",
|
||||
timeout=10)
|
||||
uiutils.check_in_loop(lambda: osentry.text == oslabel, timeout=10)
|
||||
|
||||
# Move forward, then back, ensure OS stays selected
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
newvm.find_fuzzy("Back", "button").click()
|
||||
uiutils.check_in_loop(lambda: osentry.text == oslabel)
|
||||
|
||||
# Disable autodetect, make sure OS still selected
|
||||
newvm.find_fuzzy("Automatically detect", "check").click()
|
||||
uiutils.check_in_loop(lambda: osentry.text == oslabel)
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
newvm.find_fuzzy("Back", "button").click()
|
||||
|
||||
# Ensure the EOL field was selected
|
||||
osentry.click()
|
||||
self.pressKey("Down")
|
||||
popover = newvm.find("oslist-popover")
|
||||
uiutils.check_in_loop(lambda: popover.showing)
|
||||
self.assertTrue(newvm.find("include-eol", "check").isChecked)
|
||||
|
||||
# Re-enable autodetect, check for detecting text
|
||||
newvm.find_fuzzy("Automatically detect", "check").click()
|
||||
uiutils.check_in_loop(lambda: not popover.showing)
|
||||
uiutils.check_in_loop(lambda: "Detecting" in osentry.text)
|
||||
uiutils.check_in_loop(lambda: osentry.text == oslabel, timeout=10)
|
||||
|
||||
# Progress the install
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
newvm.find_fuzzy("Forward", "button").click()
|
||||
@ -159,7 +204,7 @@ class NewVM(uiutils.UITestCase):
|
||||
"Creating Virtual Machine", "frame")
|
||||
uiutils.check_in_loop(lambda: not progress.showing, timeout=120)
|
||||
|
||||
self.app.root.find_fuzzy("fedora14 on", "frame")
|
||||
self.app.root.find_fuzzy("fedora10 on", "frame")
|
||||
self.assertFalse(newvm.showing)
|
||||
|
||||
|
||||
|
@ -269,6 +269,7 @@ class VMMDogtailNode(dogtail.tree.Node):
|
||||
raise RuntimeError("Could not bring widget on screen")
|
||||
return self
|
||||
|
||||
|
||||
#########################
|
||||
# Widget search helpers #
|
||||
#########################
|
||||
|
@ -116,7 +116,10 @@ class XMLParseTest(unittest.TestCase):
|
||||
check("on_lockfailure", "poweroff", "restart")
|
||||
|
||||
check = self._make_checker(guest._metadata.libosinfo) # pylint: disable=protected-access
|
||||
check("os_id", "http://fedoraproject.org/fedora/17", "frib")
|
||||
check("os_id", "http://fedoraproject.org/fedora/17")
|
||||
guest.set_os_name("generic")
|
||||
check("os_id", None, "frib")
|
||||
self.assertEqual(guest.osinfo.name, "generic")
|
||||
|
||||
check = self._make_checker(guest.clock)
|
||||
check("offset", "utc", "localtime")
|
||||
|
20
ui/create.ui
20
ui/create.ui
@ -2024,18 +2024,11 @@ connections is not yet supported.</small></property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="install-os-name">
|
||||
<object class="GtkAlignment" id="install-os-align">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">edit-find-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<signal name="search-changed" handler="on_install_os_name_search_changed" swapped="no"/>
|
||||
<signal name="stop-search" handler="on_install_os_name_stop_search" swapped="no"/>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="install-os-name-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">oslist-entry</property>
|
||||
</object>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
@ -2088,10 +2081,11 @@ connections is not yet supported.</small></property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<object class="GtkLabel" id="os-label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Operating system distribution:</property>
|
||||
<property name="label" translatable="yes">C_hoose the operating system you are installing:</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -1389,89 +1389,30 @@
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label72">
|
||||
<object class="GtkAlignment" id="details-os-align">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Product name:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="inspection-hostname">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label">foo</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label71">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Hostname:</property>
|
||||
<property name="hexpand">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Operating system:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="inspection-product-name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label">foo</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="inspection-type">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">foo</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label70">
|
||||
<object class="GtkLabel" id="details-os-label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes"><b>Operating System</b></property>
|
||||
<property name="label" translatable="yes"><b>Operating Sys_tem</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@ -1531,7 +1472,7 @@
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="details-overview-error-box">
|
||||
<object class="GtkGrid" id="details-inspection-error-box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
@ -1597,6 +1538,11 @@
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="box14-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">os-tab</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
@ -1606,7 +1552,7 @@
|
||||
<object class="GtkLabel" id="label14">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">inspect</property>
|
||||
<property name="label">osinfo</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
|
35
ui/oslist.ui
35
ui/oslist.ui
@ -2,10 +2,27 @@
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.14"/>
|
||||
<object class="GtkSearchEntry" id="os-name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">edit-find-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<signal name="activate" handler="on_os_name_activate" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_os_name_key_press_event" swapped="no"/>
|
||||
<signal name="search-changed" handler="on_os_name_search_changed" swapped="no"/>
|
||||
<signal name="stop-search" handler="on_os_name_stop_search" swapped="no"/>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="os-name-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">oslist-entry</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkPopover" id="vmm-oslist">
|
||||
<property name="width_request">400</property>
|
||||
<property name="height_request">300</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="modal">False</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
@ -25,6 +42,7 @@
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="stock">gtk-info</property>
|
||||
<property name="icon_size">3</property>
|
||||
</object>
|
||||
@ -35,10 +53,10 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<object class="GtkLabel" id="eol-warn">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Can't find the operating system you are looking for ?
|
||||
<property name="label">Can't find the operating system you are looking for?
|
||||
Try selecting the next most recent version displayed,
|
||||
or use the "Generic" entry.</property>
|
||||
</object>
|
||||
@ -56,7 +74,7 @@ or use the "Generic" entry.</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<object class="GtkScrolledWindow" id="os-scroll">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="vscrollbar_policy">always</property>
|
||||
@ -70,9 +88,15 @@ or use the "Generic" entry.</property>
|
||||
<property name="hover_selection">True</property>
|
||||
<property name="enable_grid_lines">horizontal</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="row-activated" handler="on_os_list_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="os-list-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">os-list</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@ -91,6 +115,11 @@ or use the "Generic" entry.</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_include_eol_toggled" swapped="no"/>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="include-eol-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">include-eol</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -28,8 +28,8 @@ from .domain import vmmDomainVirtinst
|
||||
from .engine import vmmEngine
|
||||
from .mediacombo import vmmMediaCombo
|
||||
from .netlist import vmmNetworkList
|
||||
from .storagebrowse import vmmStorageBrowser
|
||||
from .oslist import vmmOSList
|
||||
from .storagebrowse import vmmStorageBrowser
|
||||
|
||||
# Number of seconds to wait for media detection
|
||||
DETECT_TIMEOUT = 20
|
||||
@ -125,12 +125,10 @@ class vmmCreate(vmmGObjectUI):
|
||||
|
||||
self._guest = None
|
||||
self._failed_guest = None
|
||||
self._os = None
|
||||
|
||||
# Distro detection state variables
|
||||
self._detect_os_in_progress = False
|
||||
self._os_already_detected_for_media = False
|
||||
self._show_all_os_was_selected = False
|
||||
|
||||
self._customize_window = None
|
||||
|
||||
@ -172,7 +170,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
"on_install_oscontainer_browse_clicked": self._browse_oscontainer,
|
||||
"on_install_container_source_toggle": self._container_source_toggle,
|
||||
|
||||
"on_install_detect_os_toggled": self._toggle_detect_os,
|
||||
"on_install_detect_os_toggled": self._detect_os_toggled_cb,
|
||||
|
||||
"on_kernel_browse_clicked": self._browse_kernel,
|
||||
"on_initrd_browse_clicked": self._browse_initrd,
|
||||
@ -181,9 +179,6 @@ class vmmCreate(vmmGObjectUI):
|
||||
"on_enable_storage_toggled": self._toggle_enable_storage,
|
||||
|
||||
"on_create_vm_name_changed": self._name_changed,
|
||||
|
||||
"on_install_os_name_search_changed": self._os_name_search_changed,
|
||||
"on_install_os_name_stop_search": self._os_name_stop_search,
|
||||
})
|
||||
self.bind_escape_key_close()
|
||||
|
||||
@ -326,16 +321,10 @@ class vmmCreate(vmmGObjectUI):
|
||||
lst.set_model(model)
|
||||
uiutil.init_combo_text_column(lst, 0)
|
||||
|
||||
# OS distro list
|
||||
self._os_list = vmmOSList()
|
||||
self._os_list.connect("os-selected", self._os_name_selected)
|
||||
|
||||
def _os_name_selected(self, ignore, osobj):
|
||||
name = self.widget("install-os-name")
|
||||
self._os = osobj
|
||||
self._os_list.hide()
|
||||
|
||||
if self._os is not None:
|
||||
name.set_text(self._os.label)
|
||||
self.widget("install-os-align").add(self._os_list.search_entry)
|
||||
self.widget("os-label").set_mnemonic_widget(self._os_list.search_entry)
|
||||
|
||||
def _reset_state(self, urihint=None):
|
||||
"""
|
||||
@ -344,7 +333,6 @@ class vmmCreate(vmmGObjectUI):
|
||||
"""
|
||||
self._failed_guest = None
|
||||
self._guest = None
|
||||
self._show_all_os_was_selected = False
|
||||
self.reset_finish_cursor()
|
||||
|
||||
self.widget("create-pages").set_current_page(PAGE_NAME)
|
||||
@ -365,7 +353,8 @@ class vmmCreate(vmmGObjectUI):
|
||||
# Everything from this point forward should be connection independent
|
||||
|
||||
# Distro/Variant
|
||||
self._toggle_detect_os(self.widget("install-detect-os"))
|
||||
self._os_list.reset_state()
|
||||
self._os_already_detected_for_media = False
|
||||
|
||||
def _populate_media_model(media_model, urls):
|
||||
media_model.clear()
|
||||
@ -937,32 +926,6 @@ class vmmCreate(vmmGObjectUI):
|
||||
return activeconn
|
||||
|
||||
|
||||
#############################################
|
||||
# Helpers for populating OS type/variant UI #
|
||||
#############################################
|
||||
|
||||
def _set_distro_selection(self, variant):
|
||||
"""
|
||||
Update the UI with the distro that was detected from the detection
|
||||
thread.
|
||||
"""
|
||||
if not self._is_os_detect_active():
|
||||
# If the user changed the OS detect checkbox in the meantime,
|
||||
# don't update the UI
|
||||
return
|
||||
|
||||
name = self.widget("install-os-name")
|
||||
if variant:
|
||||
self._os = virtinst.OSDB.lookup_os(variant)
|
||||
else:
|
||||
self._os = None
|
||||
|
||||
if self._os is None:
|
||||
name.set_text(_("None detected"))
|
||||
else:
|
||||
name.set_text(self._os.label)
|
||||
|
||||
|
||||
###############################
|
||||
# Misc UI populating routines #
|
||||
###############################
|
||||
@ -1012,7 +975,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
elif instmethod == INSTALL_PAGE_VZ_TEMPLATE:
|
||||
install = _("Virtuozzo container")
|
||||
|
||||
self.widget("summary-os").set_text(self._os and self._os.label or _("Unknown"))
|
||||
self.widget("summary-os").set_text(self._guest.osinfo.label)
|
||||
self.widget("summary-install").set_text(install)
|
||||
self.widget("summary-mem").set_text(mem)
|
||||
self.widget("summary-cpu").set_text(cpu)
|
||||
@ -1251,34 +1214,17 @@ class vmmCreate(vmmGObjectUI):
|
||||
def _cdrom_changed(self, src):
|
||||
self._detectable_media_widget_changed(src)
|
||||
|
||||
def _toggle_detect_os(self, src):
|
||||
def _detect_os_toggled_cb(self, src):
|
||||
if not src.is_visible():
|
||||
return
|
||||
|
||||
# We are only here if the user explicitly changed detection UI
|
||||
dodetect = src.get_active()
|
||||
|
||||
self.widget("install-os-name").set_sensitive(not dodetect)
|
||||
self.widget("install-os-name").set_text("")
|
||||
self._os = None
|
||||
|
||||
self._change_os_detect(not dodetect)
|
||||
if dodetect:
|
||||
self._os_already_detected_for_media = False
|
||||
self._start_detect_os_if_needed()
|
||||
|
||||
def _os_name_search_changed(self, src):
|
||||
searchname = src.get_text().strip()
|
||||
if self._os is None:
|
||||
if src.get_sensitive() and searchname != "":
|
||||
self._os_list.filter_name(searchname)
|
||||
self._os_list.show(src)
|
||||
else:
|
||||
self._os_list.hide()
|
||||
else:
|
||||
if self._os.label != searchname:
|
||||
self._os = None
|
||||
self._os_list.hide()
|
||||
|
||||
def _os_name_stop_search(self, src):
|
||||
src.set_text("")
|
||||
self._os_list.hide()
|
||||
|
||||
def _local_media_toggled(self, src):
|
||||
usecdrom = src.get_active()
|
||||
self.widget("install-cdrom-align").set_sensitive(usecdrom)
|
||||
@ -1442,43 +1388,34 @@ class vmmCreate(vmmGObjectUI):
|
||||
|
||||
self.widget("header-pagenum").set_markup(page_lbl)
|
||||
|
||||
def _change_os_detect(self, sensitive):
|
||||
self._os_list.set_sensitive(sensitive)
|
||||
if not sensitive and not self._os_list.get_selected_os():
|
||||
self._os_list.search_entry.set_text(
|
||||
_("Waiting for install media / source"))
|
||||
|
||||
def _set_install_page(self):
|
||||
instnotebook = self.widget("install-method-pages")
|
||||
detectbox = self.widget("install-detect-os-box")
|
||||
detect = self.widget("install-detect-os")
|
||||
osbox = self.widget("install-os-distro-box")
|
||||
name = self.widget("install-os-name")
|
||||
instpage = self._get_config_install_page()
|
||||
|
||||
# Setting OS value for a container guest doesn't really matter
|
||||
# at the moment
|
||||
iscontainer = instpage in [INSTALL_PAGE_CONTAINER_APP,
|
||||
INSTALL_PAGE_CONTAINER_OS]
|
||||
osbox.set_visible(iscontainer)
|
||||
# Setting OS value for container doesn't matter presently
|
||||
self.widget("install-os-distro-box").set_visible(
|
||||
not self._is_container_install())
|
||||
|
||||
enabledetect = (instpage == INSTALL_PAGE_ISO and
|
||||
self.conn and
|
||||
not self.conn.is_remote() or
|
||||
self._get_config_install_page() == INSTALL_PAGE_URL)
|
||||
enabledetect = False
|
||||
if instpage == INSTALL_PAGE_URL:
|
||||
enabledetect = True
|
||||
elif instpage == INSTALL_PAGE_ISO and not self.conn.is_remote():
|
||||
enabledetect = True
|
||||
|
||||
detectbox.set_visible(enabledetect)
|
||||
autodetect = detectbox.get_visible() and detect.get_active()
|
||||
name.set_sensitive(not autodetect)
|
||||
if enabledetect:
|
||||
self._os = None
|
||||
else:
|
||||
if self._os is None:
|
||||
name.set_text("")
|
||||
self.widget("install-detect-os-box").set_visible(enabledetect)
|
||||
dodetect = (enabledetect and
|
||||
self.widget("install-detect-os").get_active())
|
||||
self._change_os_detect(not dodetect)
|
||||
|
||||
if instpage == INSTALL_PAGE_PXE:
|
||||
# Hide the install notebook for pxe, since there isn't anything
|
||||
# to ask for
|
||||
instnotebook.hide()
|
||||
else:
|
||||
instnotebook.show()
|
||||
|
||||
|
||||
instnotebook.set_current_page(instpage)
|
||||
# PXE installs have nothing to ask for
|
||||
self.widget("install-method-pages").set_visible(
|
||||
instpage != INSTALL_PAGE_PXE)
|
||||
self.widget("install-method-pages").set_current_page(instpage)
|
||||
|
||||
def _back_clicked(self, src_ignore):
|
||||
notebook = self.widget("create-pages")
|
||||
@ -1524,15 +1461,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
|
||||
|
||||
def _page_changed(self, ignore1, ignore2, pagenum):
|
||||
if pagenum == PAGE_INSTALL:
|
||||
self.widget("install-os-distro-box").set_visible(
|
||||
not self._is_container_install())
|
||||
|
||||
# Kick off distro detection when we switch to the install
|
||||
# page, to detect the default selected media
|
||||
self._start_detect_os_if_needed(check_install_page=False)
|
||||
|
||||
elif pagenum == PAGE_FINISH:
|
||||
if pagenum == PAGE_FINISH:
|
||||
try:
|
||||
self._populate_summary()
|
||||
except Exception as e:
|
||||
@ -1643,9 +1572,11 @@ class vmmCreate(vmmGObjectUI):
|
||||
init = None
|
||||
fs = None
|
||||
template = None
|
||||
osobj = self._os_list.get_selected_os()
|
||||
|
||||
if not self._is_container_install() and self._os is None:
|
||||
return self.err.val_err(_("Please specify a valid OS variant."))
|
||||
if not self._is_container_install() and not osobj:
|
||||
return self.err.val_err(_("You must select an OS.")
|
||||
+ "\n\n" + self._os_list.eol_text)
|
||||
|
||||
if instmethod == INSTALL_PAGE_ISO:
|
||||
instclass = virtinst.DistroInstaller
|
||||
@ -1743,7 +1674,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
try:
|
||||
# Overwrite the guest
|
||||
installer = instclass(self.conn.get_backend())
|
||||
variant = self._os and self._os.name or None
|
||||
variant = osobj and osobj.name or None
|
||||
self._guest = self._build_guest(variant)
|
||||
if not self._guest:
|
||||
return False
|
||||
@ -1801,7 +1732,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
self._guest.os.kernel_args = kargs
|
||||
|
||||
try:
|
||||
name = self._generate_default_name(self._os)
|
||||
name = self._generate_default_name(self._guest.osinfo)
|
||||
self.widget("create-vm-name").set_text(name)
|
||||
self._guest.validate_name(self._guest.conn, name)
|
||||
self._guest.name = name
|
||||
@ -1825,11 +1756,9 @@ class vmmCreate(vmmGObjectUI):
|
||||
self._addstorage.check_path_search(
|
||||
self, self.conn, path)
|
||||
|
||||
res = None
|
||||
if self._os is not None:
|
||||
res = self._os.get_recommended_resources(self._guest)
|
||||
logging.debug("Recommended resources for os=%s: %s",
|
||||
self._os.label, res)
|
||||
res = self._guest.osinfo.get_recommended_resources(self._guest)
|
||||
logging.debug("Recommended resources for os=%s: %s",
|
||||
self._guest.osinfo.name, res)
|
||||
|
||||
# Change the default values suggested to the user.
|
||||
ram_size = DEFAULT_MEM
|
||||
@ -1987,8 +1916,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
# Distro detection handling #
|
||||
#############################
|
||||
|
||||
def _start_detect_os_if_needed(self,
|
||||
forward_after_finish=False, check_install_page=True):
|
||||
def _start_detect_os_if_needed(self, forward_after_finish=False):
|
||||
"""
|
||||
Will kick off the OS detection thread if all conditions are met,
|
||||
like we actually have media to detect, detection isn't already
|
||||
@ -2002,12 +1930,9 @@ class vmmCreate(vmmGObjectUI):
|
||||
|
||||
if self._detect_os_in_progress:
|
||||
return
|
||||
if check_install_page and not is_install_page:
|
||||
if not is_install_page:
|
||||
return
|
||||
if not media:
|
||||
name = self.widget("install-os-name")
|
||||
if not name.get_sensitive():
|
||||
name.set_text(_("Waiting for install media / source"))
|
||||
return
|
||||
if not self._is_os_detect_active():
|
||||
return
|
||||
@ -2052,6 +1977,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
detectThread.setDaemon(True)
|
||||
detectThread.start()
|
||||
|
||||
self._os_list.search_entry.set_text(_("Detecting..."))
|
||||
spin = self.widget("install-detect-os-spinner")
|
||||
spin.start()
|
||||
|
||||
@ -2103,7 +2029,17 @@ class vmmCreate(vmmGObjectUI):
|
||||
self.widget("create-forward").set_sensitive(True)
|
||||
self._os_already_detected_for_media = True
|
||||
self._detect_os_in_progress = False
|
||||
self._set_distro_selection(distro)
|
||||
|
||||
if not self._is_os_detect_active():
|
||||
# If the user changed the OS detect checkbox in the meantime,
|
||||
# don't update the UI
|
||||
return
|
||||
|
||||
if distro:
|
||||
self._os_list.select_os(virtinst.OSDB.lookup_os(distro))
|
||||
else:
|
||||
self._os_list.reset_state()
|
||||
self._os_list.search_entry.set_text(_("None detected"))
|
||||
|
||||
if forward_after_finish:
|
||||
self.idle_add(self._forward_clicked, ())
|
||||
|
@ -17,16 +17,17 @@ from virtinst import util
|
||||
|
||||
from . import vmmenu
|
||||
from . import uiutil
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .addhardware import vmmAddHardware
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .choosecd import vmmChooseCD
|
||||
from .engine import vmmEngine
|
||||
from .fsdetails import vmmFSDetails
|
||||
from .gfxdetails import vmmGraphicsDetails
|
||||
from .graphwidgets import Sparkline
|
||||
from .netlist import vmmNetworkList
|
||||
from .oslist import vmmOSList
|
||||
from .snapshots import vmmSnapshotPage
|
||||
from .storagebrowse import vmmStorageBrowser
|
||||
from .graphwidgets import Sparkline
|
||||
|
||||
|
||||
# Parameters that can be edited in the details window
|
||||
@ -37,6 +38,8 @@ from .graphwidgets import Sparkline
|
||||
EDIT_DESC,
|
||||
EDIT_IDMAP,
|
||||
|
||||
EDIT_OS_NAME,
|
||||
|
||||
EDIT_VCPUS,
|
||||
EDIT_MAXVCPUS,
|
||||
EDIT_CPU,
|
||||
@ -95,7 +98,7 @@ from .graphwidgets import Sparkline
|
||||
|
||||
EDIT_FS,
|
||||
|
||||
EDIT_HOSTDEV_ROMBAR) = range(1, 53)
|
||||
EDIT_HOSTDEV_ROMBAR) = range(1, 54)
|
||||
|
||||
|
||||
# Columns in hw list model
|
||||
@ -107,7 +110,7 @@ from .graphwidgets import Sparkline
|
||||
|
||||
# Types for the hw list model: numbers specify what order they will be listed
|
||||
(HW_LIST_TYPE_GENERAL,
|
||||
HW_LIST_TYPE_INSPECTION,
|
||||
HW_LIST_TYPE_OS,
|
||||
HW_LIST_TYPE_STATS,
|
||||
HW_LIST_TYPE_CPU,
|
||||
HW_LIST_TYPE_MEMORY,
|
||||
@ -452,6 +455,7 @@ class vmmDetails(vmmGObjectUI):
|
||||
self._addhwmenuitems = None
|
||||
self._shutdownmenu = None
|
||||
self._vmmenu = None
|
||||
self._os_list = None
|
||||
self.init_menus()
|
||||
self.init_details()
|
||||
|
||||
@ -607,7 +611,8 @@ class vmmDetails(vmmGObjectUI):
|
||||
# Deliberately keep all this after signal connection
|
||||
self.vm.connect("state-changed", self.refresh_vm_state)
|
||||
self.vm.connect("resources-sampled", self.refresh_resources)
|
||||
self.vm.connect("inspection-changed", lambda *x: self.refresh_inspection_page())
|
||||
self.vm.connect("inspection-changed",
|
||||
lambda *x: self.refresh_os_page())
|
||||
|
||||
self.populate_hw_list()
|
||||
|
||||
@ -927,7 +932,13 @@ class vmmDetails(vmmGObjectUI):
|
||||
uiutil.set_grid_row_visible(
|
||||
self.widget("overview-chipset-title"), show_chipset)
|
||||
|
||||
# Inspection page
|
||||
# OS/Inspection page
|
||||
self._os_list = vmmOSList()
|
||||
self.widget("details-os-align").add(self._os_list.search_entry)
|
||||
self.widget("details-os-label").set_mnemonic_widget(
|
||||
self._os_list.search_entry)
|
||||
self._os_list.connect("os-selected", self._os_list_name_selected_cb)
|
||||
|
||||
apps_list = self.widget("inspection-apps")
|
||||
apps_model = Gtk.ListStore(str, str, str)
|
||||
apps_list.set_model(apps_model)
|
||||
@ -1196,8 +1207,8 @@ class vmmDetails(vmmGObjectUI):
|
||||
|
||||
if pagetype == HW_LIST_TYPE_GENERAL:
|
||||
self.refresh_overview_page()
|
||||
elif pagetype == HW_LIST_TYPE_INSPECTION:
|
||||
self.refresh_inspection_page()
|
||||
elif pagetype == HW_LIST_TYPE_OS:
|
||||
self.refresh_os_page()
|
||||
elif pagetype == HW_LIST_TYPE_STATS:
|
||||
self.refresh_stats_page()
|
||||
elif pagetype == HW_LIST_TYPE_CPU:
|
||||
@ -1577,6 +1588,9 @@ class vmmDetails(vmmGObjectUI):
|
||||
if inspection:
|
||||
inspection.vm_refresh(self.vm)
|
||||
|
||||
def _os_list_name_selected_cb(self, src, osobj):
|
||||
self.enable_apply(EDIT_OS_NAME)
|
||||
|
||||
|
||||
##############################
|
||||
# Details/Hardware listeners #
|
||||
@ -1891,6 +1905,8 @@ class vmmDetails(vmmGObjectUI):
|
||||
try:
|
||||
if pagetype is HW_LIST_TYPE_GENERAL:
|
||||
ret = self.config_overview_apply()
|
||||
elif pagetype is HW_LIST_TYPE_OS:
|
||||
ret = self.config_os_apply()
|
||||
elif pagetype is HW_LIST_TYPE_CPU:
|
||||
ret = self.config_vcpus_apply()
|
||||
elif pagetype is HW_LIST_TYPE_MEMORY:
|
||||
@ -1994,6 +2010,16 @@ class vmmDetails(vmmGObjectUI):
|
||||
kwargs, self.vm, self.err,
|
||||
hotplug_args=hotplug_args)
|
||||
|
||||
def config_os_apply(self):
|
||||
kwargs = {}
|
||||
|
||||
if self.edited(EDIT_OS_NAME):
|
||||
osobj = self._os_list.get_selected_os()
|
||||
kwargs["os_name"] = osobj and osobj.name or "generic"
|
||||
|
||||
return vmmAddHardware.change_config_helper(self.vm.define_os,
|
||||
kwargs, self.vm, self.err)
|
||||
|
||||
def config_vcpus_apply(self):
|
||||
kwargs = {}
|
||||
hotplug_args = {}
|
||||
@ -2448,7 +2474,9 @@ class vmmDetails(vmmGObjectUI):
|
||||
IdMap_proper = getattr(IdMap, name.replace("-", "_"))
|
||||
self.widget(name).set_value(int(IdMap_proper))
|
||||
|
||||
def refresh_inspection_page(self):
|
||||
def refresh_os_page(self):
|
||||
self._os_list.select_os(self.vm.xmlobj.osinfo)
|
||||
|
||||
inspection_supported = self.config.inspection_supported()
|
||||
uiutil.set_grid_row_visible(self.widget("details-overview-error"),
|
||||
bool(self.vm.inspection.errorstr))
|
||||
@ -2457,25 +2485,12 @@ class vmmDetails(vmmGObjectUI):
|
||||
self.vm.inspection.errorstr)
|
||||
inspection_supported = False
|
||||
|
||||
self.widget("details-inspection-os").set_visible(inspection_supported)
|
||||
self.widget("details-inspection-apps").set_visible(inspection_supported)
|
||||
self.widget("details-inspection-refresh").set_visible(
|
||||
inspection_supported)
|
||||
if not inspection_supported:
|
||||
return
|
||||
|
||||
# Operating System (ie. inspection data)
|
||||
hostname = self.vm.inspection.hostname
|
||||
if not hostname:
|
||||
hostname = _("unknown")
|
||||
self.widget("inspection-hostname").set_text(hostname)
|
||||
os_type = self.vm.inspection.os_type
|
||||
if not os_type:
|
||||
os_type = "unknown"
|
||||
self.widget("inspection-type").set_text(_label_for_os_type(os_type))
|
||||
product_name = self.vm.inspection.product_name
|
||||
if not product_name:
|
||||
product_name = _("unknown")
|
||||
self.widget("inspection-product-name").set_text(product_name)
|
||||
|
||||
# Applications (also inspection data)
|
||||
apps = self.vm.inspection.applications or []
|
||||
apps_list = self.widget("inspection-apps")
|
||||
@ -3073,10 +3088,8 @@ class vmmDetails(vmmGObjectUI):
|
||||
page_id, title])
|
||||
|
||||
add_hw_list_option(_("Overview"), HW_LIST_TYPE_GENERAL, "computer")
|
||||
add_hw_list_option(_("OS information"), HW_LIST_TYPE_OS, "computer")
|
||||
if not self.is_customize_dialog:
|
||||
if self.config.inspection_supported():
|
||||
add_hw_list_option(_("OS information"),
|
||||
HW_LIST_TYPE_INSPECTION, "computer")
|
||||
add_hw_list_option(_("Performance"), HW_LIST_TYPE_STATS,
|
||||
"utilities-system-monitor")
|
||||
add_hw_list_option(_("CPUs"), HW_LIST_TYPE_CPU, "device_cpu")
|
||||
|
@ -612,6 +612,14 @@ class vmmDomain(vmmLibvirtObject):
|
||||
|
||||
self._redefine_xmlobj(guest)
|
||||
|
||||
def define_os(self, os_name=_SENTINEL):
|
||||
guest = self._make_xmlobj_to_define()
|
||||
|
||||
if os_name != _SENTINEL:
|
||||
guest.set_os_name(os_name)
|
||||
|
||||
self._redefine_xmlobj(guest)
|
||||
|
||||
def define_boot(self, boot_order=_SENTINEL, boot_menu=_SENTINEL,
|
||||
kernel=_SENTINEL, initrd=_SENTINEL, dtb=_SENTINEL,
|
||||
kernel_args=_SENTINEL, init=_SENTINEL, initargs=_SENTINEL):
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This work is licensed under the GNU GPLv2 or later.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk, Gtk
|
||||
|
||||
import virtinst
|
||||
|
||||
@ -21,16 +21,32 @@ class vmmOSList(vmmGObjectUI):
|
||||
|
||||
self._filter_name = None
|
||||
self._filter_eol = True
|
||||
self._selected_os = None
|
||||
self.search_entry = self.widget("os-name")
|
||||
self.search_entry.set_placeholder_text(_("Type to start searching..."))
|
||||
self.eol_text = self.widget("eol-warn").get_text()
|
||||
|
||||
self.builder.connect_signals({
|
||||
"on_include_eol_toggled": self._eol_toggled,
|
||||
"on_include_eol_toggled": self._eol_toggled_cb,
|
||||
|
||||
"on_os_name_activate": self._entry_activate_cb,
|
||||
"on_os_name_key_press_event": self._key_press_cb,
|
||||
"on_os_name_search_changed": self._search_changed_cb,
|
||||
"on_os_name_stop_search": self._stop_search_cb,
|
||||
"on_os_list_row_activated": self._os_selected_cb,
|
||||
})
|
||||
|
||||
self._init_state()
|
||||
|
||||
def _init_state(self):
|
||||
def _cleanup(self):
|
||||
pass
|
||||
|
||||
self.topwin.set_modal(False)
|
||||
|
||||
###########
|
||||
# UI init #
|
||||
###########
|
||||
|
||||
def _init_state(self):
|
||||
os_list = self.widget("os-list")
|
||||
|
||||
# (os object, label)
|
||||
@ -41,10 +57,10 @@ class vmmOSList(vmmGObjectUI):
|
||||
for os in all_os:
|
||||
os_list_model.append([os, "%s (%s)" % (os.label, os.name)])
|
||||
|
||||
self._os_list_model = Gtk.TreeModelFilter(child_model=os_list_model)
|
||||
self._os_list_model.set_visible_func(self._filter_os)
|
||||
model_filter = Gtk.TreeModelFilter(child_model=os_list_model)
|
||||
model_filter.set_visible_func(self._filter_os_cb)
|
||||
|
||||
os_list.set_model(self._os_list_model)
|
||||
os_list.set_model(model_filter)
|
||||
|
||||
nameCol = Gtk.TreeViewColumn(_("Name"))
|
||||
nameCol.set_spacing(6)
|
||||
@ -54,50 +70,159 @@ class vmmOSList(vmmGObjectUI):
|
||||
nameCol.add_attribute(text, 'text', 1)
|
||||
os_list.append_column(nameCol)
|
||||
|
||||
os_list.connect("row_activated", self._os_selected_cb)
|
||||
|
||||
def _eol_toggled(self, src):
|
||||
###################
|
||||
# Private helpers #
|
||||
###################
|
||||
|
||||
def _set_default_selection(self):
|
||||
os_list = self.widget("os-list")
|
||||
sel = os_list.get_selection()
|
||||
if not self.topwin.get_visible():
|
||||
return
|
||||
if not len(os_list.get_model()):
|
||||
return
|
||||
sel.select_iter(os_list.get_model()[0].iter)
|
||||
|
||||
def _refilter(self):
|
||||
os_list = self.widget("os-list")
|
||||
sel = os_list.get_selection()
|
||||
sel.unselect_all()
|
||||
os_list.get_model().refilter()
|
||||
self._set_default_selection()
|
||||
|
||||
def _filter_by_name(self, partial_name):
|
||||
self._filter_name = partial_name.lower()
|
||||
self._refilter()
|
||||
|
||||
def _clear_filter(self):
|
||||
self._filter_by_name("")
|
||||
self.widget("os-scroll").get_vadjustment().set_value(0)
|
||||
|
||||
def _sync_os_selection(self):
|
||||
model, titer = self.widget("os-list").get_selection().get_selected()
|
||||
self._selected_os = None
|
||||
if titer:
|
||||
self._selected_os = model[titer][0]
|
||||
self.search_entry.set_text(self._selected_os.label)
|
||||
|
||||
self.emit("os-selected", self._selected_os)
|
||||
|
||||
def _show_popover(self):
|
||||
self.topwin.set_relative_to(self.search_entry)
|
||||
self.topwin.popup()
|
||||
self._set_default_selection()
|
||||
|
||||
|
||||
################
|
||||
# UI Callbacks #
|
||||
################
|
||||
|
||||
def _entry_activate_cb(self, src):
|
||||
os_list = self.widget("os-list")
|
||||
sel = os_list.get_selection()
|
||||
model, rows = sel.get_selected_rows()
|
||||
if rows:
|
||||
self.select_os(model[rows[0]][0])
|
||||
|
||||
def _key_press_cb(self, src, event):
|
||||
if Gdk.keyval_name(event.keyval) != "Down":
|
||||
return
|
||||
self._show_popover()
|
||||
self.widget("os-list").grab_focus()
|
||||
|
||||
def _eol_toggled_cb(self, src):
|
||||
self._filter_eol = not src.get_active()
|
||||
self._refilter()
|
||||
|
||||
def _os_selected_cb(self, tree_view, path, column):
|
||||
model, titer = tree_view.get_selection().get_selected()
|
||||
if titer is None:
|
||||
self.emit("os-selected", None)
|
||||
else:
|
||||
self.emit("os-selected", model[titer][0])
|
||||
def _search_changed_cb(self, src):
|
||||
"""
|
||||
Called text in search_entry is changed
|
||||
"""
|
||||
searchname = src.get_text().strip()
|
||||
selected_label = None
|
||||
if self._selected_os:
|
||||
selected_label = self._selected_os.label
|
||||
|
||||
if (not src.get_sensitive() or
|
||||
selected_label == searchname):
|
||||
self.topwin.popdown()
|
||||
self._clear_filter()
|
||||
return
|
||||
|
||||
self._filter_by_name(searchname)
|
||||
self._show_popover()
|
||||
|
||||
def _stop_search_cb(self, src):
|
||||
"""
|
||||
Called when the search window is closed, like with Escape key
|
||||
"""
|
||||
if self._selected_os:
|
||||
self.search_entry.set_text(self._selected_os.label)
|
||||
else:
|
||||
self.search_entry.set_text("")
|
||||
|
||||
def _os_selected_cb(self, src, path, column):
|
||||
self._sync_os_selection()
|
||||
|
||||
def _filter_os_cb(self, model, titer, ignore1):
|
||||
osobj = model.get(titer, 0)[0]
|
||||
if osobj.is_generic():
|
||||
return True
|
||||
|
||||
def _filter_os(self, model, titer, ignore1):
|
||||
os = model.get(titer, 0)[0]
|
||||
if self._filter_eol:
|
||||
if os.eol:
|
||||
if osobj.eol:
|
||||
return False
|
||||
|
||||
if self._filter_name is not None and self._filter_name != "":
|
||||
label = os.label.lower()
|
||||
name = os.name.lower()
|
||||
label = osobj.label.lower()
|
||||
name = osobj.name.lower()
|
||||
if (label.find(self._filter_name) == -1 and
|
||||
name.find(self._filter_name) == -1):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _refilter(self):
|
||||
|
||||
###############
|
||||
# Public APIs #
|
||||
###############
|
||||
|
||||
def reset_state(self):
|
||||
self._selected_os = None
|
||||
self.search_entry.set_text("")
|
||||
self._clear_filter()
|
||||
self._sync_os_selection()
|
||||
|
||||
def select_os(self, vmosobj):
|
||||
self._clear_filter()
|
||||
|
||||
os_list = self.widget("os-list")
|
||||
sel = os_list.get_selection()
|
||||
sel.unselect_all()
|
||||
self._os_list_model.refilter()
|
||||
if vmosobj.eol and not self.widget("include-eol").get_active():
|
||||
self.widget("include-eol").set_active(True)
|
||||
|
||||
def filter_name(self, partial_name):
|
||||
self._filter_name = partial_name.lower()
|
||||
self._refilter()
|
||||
for row in os_list.get_model():
|
||||
osobj = row[0]
|
||||
if osobj.name != vmosobj.name:
|
||||
continue
|
||||
|
||||
def show(self, parent):
|
||||
self.topwin.set_relative_to(parent)
|
||||
self.topwin.popup()
|
||||
os_list.get_selection().select_iter(row.iter)
|
||||
self._sync_os_selection()
|
||||
return
|
||||
|
||||
def hide(self):
|
||||
self.topwin.popdown()
|
||||
def get_selected_os(self):
|
||||
return self._selected_os
|
||||
|
||||
def _cleanup(self):
|
||||
pass
|
||||
def set_sensitive(self, sensitive):
|
||||
if sensitive == self.search_entry.get_sensitive():
|
||||
return
|
||||
|
||||
if not sensitive:
|
||||
self.search_entry.set_sensitive(False)
|
||||
self.reset_state()
|
||||
else:
|
||||
if self._selected_os:
|
||||
self.select_os(self._selected_os)
|
||||
else:
|
||||
self.reset_state()
|
||||
self.search_entry.set_sensitive(True)
|
||||
|
@ -264,8 +264,7 @@ class Guest(XMLBuilder):
|
||||
|
||||
logging.debug("Setting Guest os_name=%s", name)
|
||||
self.__osinfo = obj
|
||||
if self.__osinfo.full_id:
|
||||
self._metadata.libosinfo.os_id = self.__osinfo.full_id
|
||||
self._metadata.libosinfo.os_id = self.__osinfo.full_id
|
||||
|
||||
def _supports_virtio(self, os_support):
|
||||
if not self.conn.is_qemu():
|
||||
|
@ -20,6 +20,31 @@ from gi.repository import Libosinfo as libosinfo
|
||||
# Sorting helpers #
|
||||
###################
|
||||
|
||||
def _sortby(osobj):
|
||||
"""
|
||||
Combines distro+version to make a more sort friendly string. Examples
|
||||
|
||||
fedora25 -> fedora-0025000000000000
|
||||
ubuntu17.04 -> ubuntu-0017000400000000
|
||||
win2k8r2 -> win-0006000100000000
|
||||
"""
|
||||
if osobj.is_generic():
|
||||
# Sort generic at the end of the list
|
||||
return "zzzzzz-000000000000"
|
||||
|
||||
version = osobj.version
|
||||
try:
|
||||
t = version.split(".")
|
||||
t = t[:min(4, len(t))] + [0] * (4 - min(4, len(t)))
|
||||
new_version = ""
|
||||
for n in t:
|
||||
new_version = new_version + ("%.4i" % int(n))
|
||||
version = new_version
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return "%s-%s" % (osobj.distro, version)
|
||||
|
||||
def _sort(tosort):
|
||||
sortby_mappings = {}
|
||||
distro_mappings = {}
|
||||
@ -28,13 +53,15 @@ def _sort(tosort):
|
||||
for key, osinfo in tosort.items():
|
||||
# Libosinfo has some duplicate version numbers here, so append .1
|
||||
# if there's a collision
|
||||
sortby = osinfo.sortby
|
||||
sortby = _sortby(osinfo)
|
||||
while sortby_mappings.get(sortby):
|
||||
sortby = sortby + ".1"
|
||||
sortby_mappings[sortby] = key
|
||||
|
||||
# Group by distro first, so debian is clumped together, fedora, etc.
|
||||
distro = osinfo.distro
|
||||
if osinfo.is_generic():
|
||||
distro = "zzzzzz"
|
||||
if distro not in distro_mappings:
|
||||
distro_mappings[distro] = []
|
||||
distro_mappings[distro].append(sortby)
|
||||
@ -217,12 +244,12 @@ class _OsVariant(object):
|
||||
|
||||
self.full_id = self._os and self._os.get_id() or None
|
||||
self.name = self._os and self._os.get_short_id() or "generic"
|
||||
self.label = self._os and self._os.get_name() or "Generic"
|
||||
self.label = self._os and self._os.get_name() or "Generic default"
|
||||
self.codename = self._os and self._os.get_codename() or ""
|
||||
self.distro = self._os and self._os.get_distro() or ""
|
||||
self.version = self._os and self._os.get_version() or None
|
||||
|
||||
self.eol = self._get_eol()
|
||||
self.sortby = self._get_sortby()
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s name=%s>" % (self.__class__.__name__, self.name)
|
||||
@ -308,28 +335,14 @@ class _OsVariant(object):
|
||||
return now > rel5
|
||||
return False
|
||||
|
||||
def _get_sortby(self):
|
||||
if not self._os:
|
||||
return "1"
|
||||
|
||||
version = self._os.get_version()
|
||||
try:
|
||||
t = version.split(".")
|
||||
t = t[:min(4, len(t))] + [0] * (4 - min(4, len(t)))
|
||||
new_version = ""
|
||||
for n in t:
|
||||
new_version = new_version + ("%.4i" % int(n))
|
||||
version = new_version
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return "%s-%s" % (self.distro, version)
|
||||
|
||||
|
||||
###############
|
||||
# Public APIs #
|
||||
###############
|
||||
|
||||
def is_generic(self):
|
||||
return self._os is None
|
||||
|
||||
def is_windows(self):
|
||||
return self._family in ['win9x', 'winnt', 'win16']
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user