diff --git a/src/virtManager/details.py b/src/virtManager/details.py
index 4c9e89ecf..d53dad2b4 100644
--- a/src/virtManager/details.py
+++ b/src/virtManager/details.py
@@ -64,6 +64,12 @@ remove_pages = [ HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT,
HW_LIST_TYPE_GRAPHICS, HW_LIST_TYPE_SOUND, HW_LIST_TYPE_CHAR,
HW_LIST_TYPE_HOSTDEV, HW_LIST_TYPE_DISK, HW_LIST_TYPE_VIDEO]
+# Boot device columns
+BOOT_DEV_TYPE = 0
+BOOT_LABEL = 1
+BOOT_ICON = 2
+BOOT_ACTIVE = 3
+
# Main tab pages
PAGE_CONSOLE = 0
PAGE_DETAILS = 1
@@ -230,13 +236,23 @@ class vmmDetails(gobject.GObject):
"on_overview_acpi_changed": self.config_enable_apply,
"on_overview_apic_changed": self.config_enable_apply,
"on_overview_clock_changed": self.config_enable_apply,
+ "on_security_label_changed": self.security_label_changed,
+ "on_security_type_changed": self.security_type_changed,
+ "on_security_model_changed": self.security_model_changed,
+
"on_config_vcpus_changed": self.config_enable_apply,
+
"on_config_memory_changed": self.config_memory_changed,
"on_config_maxmem_changed": self.config_maxmem_changed,
- "on_config_boot_device_changed": self.config_boot_options_changed,
- "on_config_autostart_changed": self.config_boot_options_changed,
+
+ "on_config_boot_moveup_clicked" : (self.config_boot_move, True),
+ "on_config_boot_movedown_clicked" : (self.config_boot_move,
+ False),
+ "on_config_autostart_changed": self.config_enable_apply,
+
"on_disk_readonly_changed": self.config_enable_apply,
"on_disk_shareable_changed": self.config_enable_apply,
+
"on_video_model_combo_changed": self.config_enable_apply,
"on_config_apply_clicked": self.config_apply,
@@ -247,10 +263,6 @@ class vmmDetails(gobject.GObject):
"on_config_remove_clicked": self.remove_xml_dev,
"on_add_hardware_button_clicked": self.add_hardware,
- "on_security_label_changed": self.security_label_changed,
- "on_security_type_changed": self.security_type_changed,
- "on_security_model_changed": self.security_model_changed,
-
"on_hw_list_button_press_event": self.popup_addhw_menu,
# Listeners stored in vmmConsolePages
@@ -284,7 +296,12 @@ class vmmDetails(gobject.GObject):
self.vm.connect("status-changed", self.update_widget_states)
self.vm.connect("resources-sampled", self.refresh_resources)
self.vm.connect("config-changed", self.refresh_vm_info)
- self.window.get_widget("hw-list").get_selection().connect("changed", self.hw_selected)
+ self.window.get_widget("hw-list").get_selection().connect(
+ "changed",
+ self.hw_selected)
+ self.window.get_widget("config-boot-list").get_selection().connect(
+ "changed",
+ self.config_bootdev_selected)
finish_img = gtk.image_new_from_stock(gtk.STOCK_ADD,
gtk.ICON_SIZE_BUTTON)
@@ -499,17 +516,30 @@ class vmmDetails(gobject.GObject):
pinCol.add_attribute(pin_text, 'text', 2)
# Boot device list
- boot_list = self.window.get_widget("config-boot-device")
- # model = [ display name, icon name, boot type (hd, fd, etc) ]
- boot_list_model = gtk.ListStore(str, str, str)
+ boot_list = self.window.get_widget("config-boot-list")
+ # model = [ XML boot type, display name, icon name, enabled ]
+ boot_list_model = gtk.ListStore(str, str, str, bool)
boot_list.set_model(boot_list_model)
+ chkCol = gtk.TreeViewColumn()
+ txtCol = gtk.TreeViewColumn()
+
+ boot_list.append_column(chkCol)
+ boot_list.append_column(txtCol)
+
+ chk = gtk.CellRendererToggle()
+ chk.connect("toggled", self.config_boot_toggled)
+ chkCol.pack_start(chk, False)
+ chkCol.add_attribute(chk, 'active', BOOT_ACTIVE)
+
icon = gtk.CellRendererPixbuf()
- boot_list.pack_start(icon, False)
- boot_list.add_attribute(icon, 'icon-name', 1)
+ txtCol.pack_start(icon, False)
+ txtCol.add_attribute(icon, 'icon-name', BOOT_ICON)
+
text = gtk.CellRendererText()
- boot_list.pack_start(text, True)
- boot_list.add_attribute(text, 'text', 0)
+ txtCol.pack_start(text, True)
+ txtCol.add_attribute(text, 'text', BOOT_LABEL)
+ txtCol.add_attribute(text, 'sensitive', BOOT_ACTIVE)
# Video model combo
video_dev = self.window.get_widget("video-model-combo")
@@ -616,6 +646,14 @@ class vmmDetails(gobject.GObject):
else:
self.window.get_widget("toolbar-box").hide()
+ def get_boot_selection(self):
+ widget = self.window.get_widget("config-boot-list")
+ selection = widget.get_selection()
+ model, treepath = selection.get_selected()
+ if treepath == None:
+ return None
+ return model[treepath]
+
def get_hw_selection(self, field):
vmlist = self.window.get_widget("hw-list")
selection = vmlist.get_selection()
@@ -936,11 +974,23 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("details-pages").remove_page(page_idx)
self.serial_tabs.remove(name)
+ ############################
+ # Details/Hardware getters #
+ ############################
+
+ def get_config_boot_devs(self):
+ boot_model = self.window.get_widget("config-boot-list").get_model()
+ devs = []
+
+ for row in boot_model:
+ if row[BOOT_ACTIVE]:
+ devs.append(row[BOOT_DEV_TYPE])
+
+ return devs
##############################
# Details/Hardware listeners #
##############################
-
def config_enable_apply(self, ignore1=None, ignore2=None):
self.window.get_widget("config-apply").set_sensitive(True)
@@ -996,8 +1046,55 @@ class vmmDetails(gobject.GObject):
maxadj.lower = mem
# Boot device / Autostart
- def config_boot_options_changed(self, src):
- self.window.get_widget("config-apply").set_sensitive(True)
+ def config_bootdev_selected(self, ignore):
+ boot_row = self.get_boot_selection()
+ boot_selection = boot_row and boot_row[BOOT_DEV_TYPE]
+ boot_devs = self.get_config_boot_devs()
+ up_widget = self.window.get_widget("config-boot-moveup")
+ down_widget = self.window.get_widget("config-boot-movedown")
+
+ down_widget.set_sensitive(bool(boot_devs and
+ boot_selection and
+ boot_selection in boot_devs and
+ boot_selection != boot_devs[-1]))
+ up_widget.set_sensitive(bool(boot_devs and boot_selection and
+ boot_selection in boot_devs and
+ boot_selection != boot_devs[0]))
+
+ def config_boot_toggled(self, ignore, index):
+ boot_model = self.window.get_widget("config-boot-list").get_model()
+ boot_row = boot_model[index]
+ is_active = boot_row[BOOT_ACTIVE]
+
+ boot_row[BOOT_ACTIVE] = not is_active
+
+ self.repopulate_boot_list(self.get_config_boot_devs(),
+ boot_row[BOOT_DEV_TYPE])
+ self.config_enable_apply()
+
+ def config_boot_move(self, src, move_up):
+ boot_row = self.get_boot_selection()
+ if not boot_row:
+ return
+
+ boot_selection = boot_row[BOOT_DEV_TYPE]
+ boot_devs = self.get_config_boot_devs()
+ boot_idx = boot_devs.index(boot_selection)
+ if move_up:
+ new_idx = boot_idx - 1
+ else:
+ new_idx = boot_idx + 1
+
+ if new_idx < 0 or new_idx >= len(boot_devs):
+ # Somehow we got out of bounds
+ return
+
+ swap_dev = boot_devs[new_idx]
+ boot_devs[new_idx] = boot_selection
+ boot_devs[boot_idx] = swap_dev
+
+ self.repopulate_boot_list(boot_devs, boot_selection)
+ self.config_enable_apply()
# CDROM Eject/Connect
def toggle_storage_media(self, src):
@@ -1160,7 +1257,6 @@ class vmmDetails(gobject.GObject):
# Boot device / Autostart
def config_boot_options_apply(self):
- boot = self.window.get_widget("config-boot-device")
auto = self.window.get_widget("config-autostart")
if auto.get_property("sensitive"):
@@ -1171,10 +1267,9 @@ class vmmDetails(gobject.GObject):
str(e)), "".join(traceback.format_exc()))
return False
- if boot.get_property("sensitive") and boot.get_active() > -1:
- bootdev = boot.get_model()[boot.get_active()][2]
- return self._change_config_helper(self.vm.set_boot_device,
- (bootdev,))
+ bootdevs = self.get_config_boot_devs()
+ return self._change_config_helper(self.vm.set_boot_device,
+ (bootdevs,))
# CDROM
def change_storage_media(self, dev_id_info, newpath, _type=None):
@@ -1797,26 +1892,8 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("config-autostart").set_active(False)
self.window.get_widget("config-autostart").set_sensitive(False)
- # Refresh Boot Device list and correct selection
- boot_combo = self.window.get_widget("config-boot-device")
- if not self.vm.is_hvm():
- # Boot dev selection not supported for PV guest
- boot_combo.set_sensitive(False)
- boot_combo.set_active(-1)
- return
-
+ # Refresh Boot Device list
self.repopulate_boot_list()
- bootdev = self.vm.get_boot_device()
- boot_combo = self.window.get_widget("config-boot-device")
- boot_model = boot_combo.get_model()
- for i in range(0, len(boot_model)):
- if bootdev == boot_model[i][2]:
- boot_combo.set_active(i)
- break
-
- if boot_model[0][2] == None:
- # If no boot devices, select the 'No Device' entry
- boot_combo.set_active(0)
############################
@@ -1994,39 +2071,63 @@ class vmmDetails(gobject.GObject):
# Now actually remove it
hw_list_model.remove(_iter)
- def repopulate_boot_list(self):
- hw_list_model = self.window.get_widget("hw-list").get_model()
- boot_combo = self.window.get_widget("config-boot-device")
- boot_model = boot_combo.get_model()
+ def repopulate_boot_list(self, bootdevs=None, dev_select=None):
+ boot_list = self.window.get_widget("config-boot-list")
+ boot_model = boot_list.get_model()
+ old_order = map(lambda x: x[BOOT_DEV_TYPE], boot_model)
boot_model.clear()
- found_dev = {}
- for row in hw_list_model:
- hwtype = row[HW_LIST_COL_TYPE]
- if hwtype == HW_LIST_TYPE_DISK:
- diskinfo = row[HW_LIST_COL_DEVICE]
+ if bootdevs == None:
+ bootdevs = self.vm.get_boot_device()
- if diskinfo[4] == virtinst.VirtualDisk.DEVICE_DISK and not \
- found_dev.get(virtinst.VirtualDisk.DEVICE_DISK, False):
- boot_model.append(["Hard Disk", "drive-harddisk", "hd"])
- found_dev[virtinst.VirtualDisk.DEVICE_DISK] = True
- elif diskinfo[4] == virtinst.VirtualDisk.DEVICE_CDROM and not \
- found_dev.get(virtinst.VirtualDisk.DEVICE_CDROM, False):
- boot_model.append(["CDROM", "media-optical", "cdrom"])
- found_dev[virtinst.VirtualDisk.DEVICE_CDROM] = True
- elif diskinfo[4] == virtinst.VirtualDisk.DEVICE_FLOPPY and not \
- found_dev.get(virtinst.VirtualDisk.DEVICE_FLOPPY, False):
- boot_model.append(["Floppy", "media-floppy", "fd"])
- found_dev[virtinst.VirtualDisk.DEVICE_FLOPPY] = True
+ boot_rows = {
+ "hd" : ["hd", "Hard Disk", "drive-harddisk", False],
+ "cdrom" : ["cdrom", "CDROM", "media-optical", False],
+ "network" : ["network", "Network (PXE)", "network-idle", False],
+ "fd" : ["fd", "Floppy", "media-floppy", False],
+ }
- elif (hwtype == HW_LIST_TYPE_NIC and not
- found_dev.get(HW_LIST_TYPE_NIC, False)):
- boot_model.append(["Network (PXE)", "network-idle", "network"])
- found_dev[HW_LIST_TYPE_NIC] = True
+ for dev in bootdevs:
+ foundrow = None
- if len(boot_model) <= 0:
- boot_model.append([_("No Boot Device"), None, None])
+ for key, row in boot_rows.items():
+ if key == dev:
+ foundrow = row
+ del(boot_rows[key])
+ break
+
+ if not foundrow:
+ # Some boot device listed that we don't know about.
+ foundrow = [dev, "Boot type '%s'" % dev,
+ "drive-harddisk", True]
+
+ foundrow[BOOT_ACTIVE] = True
+ boot_model.append(foundrow)
+
+ # Append all remaining boot_rows that aren't enabled
+ for dev in old_order:
+ if boot_rows.has_key(dev):
+ boot_model.append(boot_rows[dev])
+ del(boot_rows[dev])
+
+ for row in boot_rows.values():
+ boot_model.append(row)
+
+ boot_list.set_model(boot_model)
+ selection = boot_list.get_selection()
+
+ if dev_select:
+ idx = 0
+ for row in boot_model:
+ if row[BOOT_DEV_TYPE] == dev_select:
+ break
+ idx += 1
+
+ boot_list.get_selection().select_path(str(idx))
+
+ elif not selection.get_selected()[1]:
+ # Set a default selection
+ selection.select_path("0")
- boot_combo.set_model(boot_model)
gobject.type_register(vmmDetails)
diff --git a/src/virtManager/domain.py b/src/virtManager/domain.py
index de6f1b022..04374d2d9 100644
--- a/src/virtManager/domain.py
+++ b/src/virtManager/domain.py
@@ -164,7 +164,7 @@ class vmmDomainBase(gobject.GObject):
def define_seclabel(self, model, t, label):
raise NotImplementedError()
- def set_boot_device(self, boot_type):
+ def set_boot_device(self, boot_list):
raise NotImplementedError()
def define_acpi(self, newvalue):
@@ -275,10 +275,13 @@ class vmmDomainBase(gobject.GObject):
xml = self.get_xml()
def get_boot_xml(doc, ctx):
- ret = ctx.xpathEval("/domain/os/boot[1]")
+ ret = ctx.xpathEval("/domain/os/boot")
+ devs = []
for node in ret:
dev = node.prop("dev")
- return dev
+ if dev:
+ devs.append(dev)
+ return devs
return util.xml_parse_wrapper(xml, get_boot_xml)
@@ -1672,15 +1675,25 @@ class vmmDomain(vmmDomainBase):
self._redefine(change_mem_xml, memory, maxmem)
# Boot device
- def set_boot_device(self, boot_type):
- logging.debug("Setting boot device to type: %s" % boot_type)
+ def set_boot_device(self, boot_list):
+ logging.debug("Setting boot devices to: %s" % boot_list)
def set_boot_xml(doc, ctx):
- node = ctx.xpathEval("/domain/os/boot[1]")
- node = (node and node[0] or None)
+ nodes = ctx.xpathEval("/domain/os/boot")
+ os_node = ctx.xpathEval("/domain/os")[0]
+ mappings = map(lambda x, y: (x, y), nodes, boot_list)
- if node and node.prop("dev"):
- node.setProp("dev", boot_type)
+ for node, boot_dev in mappings:
+ if node:
+ if boot_dev:
+ node.setProp("dev", boot_dev)
+ else:
+ node.unlinkNode()
+ node.freeNode()
+ else:
+ if boot_dev:
+ node = os_node.newChild(None, "boot", None)
+ node.setProp("dev", boot_dev)
return doc.serialize()
@@ -1698,6 +1711,7 @@ class vmmDomain(vmmDomainBase):
if not model:
if secnode:
secnode.unlinkNode()
+ secnode.freeNode()
elif not secnode:
# Need to create new node
@@ -2057,8 +2071,8 @@ class vmmDomainVirtinst(vmmDomainBase):
self._redefine(change_seclabel)
- def set_boot_device(self, boot_type):
- if not boot_type or boot_type == self.get_boot_device():
+ def set_boot_device(self, boot_list):
+ if not boot_list or boot_list == self.get_boot_device():
return
raise RuntimeError("Boot device is determined by the install media.")
diff --git a/src/vmm-details.glade b/src/vmm-details.glade
index da73c318f..01f8f0135 100644
--- a/src/vmm-details.glade
+++ b/src/vmm-details.glade
@@ -2395,32 +2395,107 @@ I/O:
3
12
-
+
True
- 2
- 3
+ 6
-
+
True
- 0
- Device virtual machine will _boot from:
- True
- config-boot-device
+ 3
+ 2
+ 6
+ 6
+
+
+ 20
+ True
+ True
+ never
+ never
+ in
+
+
+ True
+ True
+ False
+
+
+
+
+ 3
+
+
+
+
+ True
+ True
+ True
+
+
+
+ True
+ gtk-go-up
+
+
+
+
+ 1
+ 2
+ GTK_FILL
+ GTK_FILL
+
+
+
+
+ True
+ True
+ True
+
+
+
+ True
+ gtk-go-down
+
+
+
+
+ 1
+ 2
+ 1
+ 2
+ GTK_FILL
+ GTK_FILL
+
+
+
+
+ True
+
+
+
+
+
+ 1
+ 2
+ 2
+ 3
+ GTK_FILL
+
+
-
-
+ 0
-
+
True
-
+
+
+
- 1
- 2
- GTK_FILL
+ 1
@@ -2430,7 +2505,7 @@ I/O:
True
- <b>Boot Device</b>
+ <b>Boot device order</b>
True