diff --git a/ui/details.ui b/ui/details.ui index d615a87e..66134e58 100644 --- a/ui/details.ui +++ b/ui/details.ui @@ -2398,193 +2398,200 @@ - + True False + 24 - + True False + 0 + none - + True False - 0 - none + 3 + 12 - + + Start virt_ual machine on host boot up True - False - 3 - 12 - - - Start virt_ual machine on host boot up - True - True - False - True - 0.5 - True - - - - - - - - True - False - <b>Autostart</b> - True + True + False + True + 0.5 + True + - - False - False - 0 - - - + + True False - 24 + <b>Autostart</b> + True + + + + + 0 + 0 + 1 + 1 + + + + + True + False + 0 + none + + + True + False + 3 + 12 - + True False - 0 - none + 12 - + True False - 3 - 12 + Init path: + + + False + True + 0 + + + + + True + True + + 25 + + + + False + True + 1 + + + + + + + + + True + False + <b>Container init</b> + True + + + + + 0 + 3 + 1 + 1 + + + + + True + True + start + False + + + True + False + 3 + 12 + + + True + False + vertical + 6 + + + Enable direct kernel boot + True + True + False + 0 + True + + + + False + True + 0 + + + + + True + False + 6 - + True False - 6 + 4 + 2 + 6 - - Enable boot me_nu + True - True - False - True - 0.5 - True - + False + 0 + 8 + Kernel path: - False - True - 0 + GTK_FILL + GTK_FILL - + + True + False + 0 + 8 + Initrd path: + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + True False 6 - + True - False - 3 - 2 - 6 - 6 - - - 20 - True - True - never - never - in - - - True - True - False - - - - - - - - - - 3 - GTK_FILL - - - - - True - True - True - - - - True - False - gtk-go-up - - - - - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - True - True - - - - True - False - gtk-go-down - - - - - 1 - 2 - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - False - - - - - - 1 - 2 - 2 - 3 - GTK_FILL - GTK_FILL - - + True + + True @@ -2593,77 +2600,293 @@ - + + Browse True - False - - - + True + True + - True + False True 1 - False - True - 1 + 1 + 2 + 1 + 2 + + + + + + True + False + 6 + + + True + True + + + + + True + True + 0 + + + + + Browse + True + True + True + + + + False + True + 1 + + + + + 1 + 2 + + + + + + True + False + 0 + 8 + Kernel args: + + + 3 + 4 + GTK_FILL + GTK_FILL + + + + + True + True + + 40 + + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + True + False + 0 + 8 + DTB Path: + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + False + 6 + + + True + True + + + + + True + True + 0 + + + + + Browse + True + True + True + + + + False + True + 1 + + + + + 1 + 2 + 2 + 3 + + + False + True + 0 + + + + + True + False + + + + + + True + True + 1 + - - - - True - False - <b>Boot device order</b> - True - + + False + True + 1 + - - False - True - 1 - - - + + True False - 24 + <b>Direct kernel boot</b> + True + + + + + 0 + 2 + 1 + 1 + + + + + True + False + 0 + none + + + True + False + 3 + 12 - + True - True + False + 6 - + + Enable boot me_nu + True + True + False + True + 0 + True + + + + False + True + 0 + + + + True False - 3 - 12 + start + False + 6 + 6 - + + 125 + 125 + True + True + never + in + + + True + True + False + + + + + + + + + + 0 + 0 + 1 + 1 + + + + True False vertical 6 - - Enable direct kernel boot + True + False True - False - 0 - True - + True + start + + + + True + False + gtk-go-up + + False @@ -2672,232 +2895,20 @@ - + True - False - 6 + False + True + True + start + False + - + True False - 4 - 2 - 6 - - - True - False - 0 - 8 - Kernel path: - - - GTK_FILL - GTK_FILL - - - - - True - False - 0 - 8 - Initrd path: - - - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - False - 6 - - - True - True - - - - - True - True - 0 - - - - - Browse - True - True - True - - - - False - True - 1 - - - - - 1 - 2 - 1 - 2 - - - - - - True - False - 6 - - - True - True - - - - - True - True - 0 - - - - - Browse - True - True - True - - - - False - True - 1 - - - - - 1 - 2 - - - - - - True - False - 0 - 8 - Kernel args: - - - 3 - 4 - GTK_FILL - GTK_FILL - - - - - True - True - - 40 - - - - 1 - 2 - 3 - 4 - GTK_FILL - - - - - True - False - 0 - 8 - DTB Path: - - - 2 - 3 - GTK_FILL - GTK_FILL - - - - - True - False - 6 - - - True - True - - - - - True - True - 0 - - - - - Browse - True - True - True - - - - False - True - 1 - - - - - 1 - 2 - 2 - 3 - - - + gtk-go-down - - False - True - 0 - - - - - True - False - - - - - - True - True - 1 - @@ -2907,100 +2918,38 @@ + + 1 + 0 + 1 + 1 + - - - - True - False - <b>Direct kernel boot</b> - True - + + True + True + 1 + - - False - True - 2 - - - + + True False - 24 - - - True - False - 0 - none - - - True - False - 3 - 12 - - - True - False - 12 - - - True - False - Init path: - - - False - True - 0 - - - - - True - True - - 25 - - - - False - True - 1 - - - - - - - - - True - False - <b>Container init</b> - True - - - - + <b>Boot device order</b> + True - - False - False - 3 - - True - True - 0 + 0 + 1 + 1 + 1 diff --git a/virtManager/details.py b/virtManager/details.py index ff8aeec9..c126eda3 100644 --- a/virtManager/details.py +++ b/virtManager/details.py @@ -143,10 +143,11 @@ remove_pages = [HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT, HW_LIST_TYPE_RNG, HW_LIST_TYPE_PANIC] # Boot device columns -(BOOT_DEV_TYPE, +(BOOT_KEY, BOOT_LABEL, BOOT_ICON, - BOOT_ACTIVE) = range(4) + BOOT_ACTIVE, + BOOT_CAN_SELECT) = range(5) # Main tab pages (DETAILS_PAGE_DETAILS, @@ -198,7 +199,8 @@ def _build_redir_label(redirdev): else: raise RuntimeError("unhandled redirection kind: %s" % redirdev.type) - hwlabel = _("%s Redirector") % redirdev.bus.upper() + hwlabel = _("%s Redirector %s") % (redirdev.bus.upper(), + redirdev.vmmindex + 1) return addrlabel, hwlabel @@ -660,7 +662,6 @@ class vmmDetails(vmmGObjectUI): self.vm.connect("resources-sampled", self.refresh_resources) self.populate_hw_list() - self.repopulate_boot_list() self.hw_selected() self.refresh_vm_state() @@ -930,8 +931,8 @@ class vmmDetails(vmmGObjectUI): # Boot device list boot_list = self.widget("config-boot-list") - # model = [ XML boot type, display name, icon name, enabled ] - boot_list_model = Gtk.ListStore(str, str, str, bool) + # [XML boot type, display name, icon name, enabled, can select] + boot_list_model = Gtk.ListStore(str, str, str, bool, bool) boot_list.set_model(boot_list_model) chkCol = Gtk.TreeViewColumn() @@ -944,6 +945,7 @@ class vmmDetails(vmmGObjectUI): chk.connect("toggled", self.config_boot_toggled) chkCol.pack_start(chk, False) chkCol.add_attribute(chk, 'active', BOOT_ACTIVE) + chkCol.add_attribute(chk, 'visible', BOOT_CAN_SELECT) icon = Gtk.CellRendererPixbuf() txtCol.pack_start(icon, False) @@ -1515,13 +1517,13 @@ class vmmDetails(vmmGObjectUI): # Details/Hardware getters # ############################ - def get_config_boot_devs(self): + def get_config_boot_order(self): boot_model = self.widget("config-boot-list").get_model() devs = [] for row in boot_model: if row[BOOT_ACTIVE]: - devs.append(row[BOOT_DEV_TYPE]) + devs.append(row[BOOT_KEY]) return devs @@ -1694,8 +1696,8 @@ class vmmDetails(vmmGObjectUI): # Boot device / Autostart 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() + boot_selection = boot_row and boot_row[BOOT_KEY] + boot_devs = self.get_config_boot_order() up_widget = self.widget("config-boot-moveup") down_widget = self.widget("config-boot-movedown") @@ -1708,39 +1710,45 @@ class vmmDetails(vmmGObjectUI): boot_selection != boot_devs[0])) def config_boot_toggled(self, ignore, index): - boot_model = self.widget("config-boot-list").get_model() - boot_row = boot_model[index] - is_active = boot_row[BOOT_ACTIVE] + model = self.widget("config-boot-list").get_model() + row = model[index] - boot_row[BOOT_ACTIVE] = not is_active - - self.repopulate_boot_list(self.get_config_boot_devs(), - boot_row[BOOT_DEV_TYPE]) + row[BOOT_ACTIVE] = not row[BOOT_ACTIVE] self.enable_apply(EDIT_BOOTORDER) def config_boot_move(self, src, move_up): ignore = src - boot_row = self.get_boot_selection() - if not boot_row: + row = self.get_boot_selection() + if not row: return - boot_selection = boot_row[BOOT_DEV_TYPE] - boot_devs = self.get_config_boot_devs() - boot_idx = boot_devs.index(boot_selection) + row_key = row[BOOT_KEY] + boot_order = self.get_config_boot_order() + key_idx = boot_order.index(row_key) if move_up: - new_idx = boot_idx - 1 + new_idx = key_idx - 1 else: - new_idx = boot_idx + 1 + new_idx = key_idx + 1 - if new_idx < 0 or new_idx >= len(boot_devs): - # Somehow we got out of bounds + if new_idx < 0 or new_idx >= len(boot_order): + # Somehow we went out of bounds return - swap_dev = boot_devs[new_idx] - boot_devs[new_idx] = boot_selection - boot_devs[boot_idx] = swap_dev + boot_list = self.widget("config-boot-list") + model = boot_list.get_model() + prev_row = None + for row in model: + if prev_row and prev_row[BOOT_KEY] == row_key: + model.swap(prev_row.iter, row.iter) + break - self.repopulate_boot_list(boot_devs, boot_selection) + if row[BOOT_KEY] == row_key and prev_row and move_up: + model.swap(prev_row.iter, row.iter) + break + + prev_row = row + + boot_list.get_selection().emit("changed") self.enable_apply(EDIT_BOOTORDER) # IO Tuning @@ -2008,8 +2016,8 @@ class vmmDetails(vmmGObjectUI): return False if self.edited(EDIT_BOOTORDER): - bootdevs = self.get_config_boot_devs() - add_define(self.vm.set_boot_device, bootdevs) + bootdevs = self.get_config_boot_order() + add_define(self.vm.set_boot_order, bootdevs) if self.edited(EDIT_BOOTMENU): bootmenu = self.widget("boot-menu").get_active() @@ -3023,9 +3031,12 @@ class vmmDetails(vmmGObjectUI): show_init = self.vm.is_container() show_boot = (not self.vm.is_container() and not self.vm.is_xenpv()) - self.widget("boot-order-align").set_visible(show_boot) - self.widget("boot-kernel-align").set_visible(show_kernel) - self.widget("boot-init-align").set_visible(show_init) + uiutil.set_grid_row_visible( + self.widget("boot-order-frame"), show_boot) + uiutil.set_grid_row_visible( + self.widget("boot-kernel-expander"), show_kernel) + uiutil.set_grid_row_visible( + self.widget("boot-init-frame"), show_init) # Kernel/initrd boot kernel, initrd, dtb, args = self.vm.get_boot_kernel_info() @@ -3066,7 +3077,7 @@ class vmmDetails(vmmGObjectUI): # Boot menu populate menu = self.vm.get_boot_menu() or False self.widget("boot-menu").set_active(menu) - self.repopulate_boot_list() + self.repopulate_boot_order() ############################ @@ -3228,64 +3239,46 @@ class vmmDetails(vmmGObjectUI): hw_list_model.remove(_iter) - def repopulate_boot_list(self, bootdevs=None, dev_select=None): + def _make_boot_rows(self): + if not self.vm.can_use_device_boot_order(): + return [ + ["hd", "Hard Disk", "drive-harddisk", False, True], + ["cdrom", "CDROM", "media-optical", False, True], + ["network", "Network (PXE)", "network-idle", False, True], + ["fd", "Floppy", "media-floppy", False, True], + ] + + ret = [] + for dev in self.vm.get_bootable_devices(): + icon = _icon_for_device(dev) + label = _label_for_device(dev) + + ret.append([dev.vmmidstr, label, icon, False, True]) + + if not ret: + ret.append([None, _("No bootable devices"), None, False, False]) + return ret + + def repopulate_boot_order(self): boot_list = self.widget("config-boot-list") boot_model = boot_list.get_model() - old_order = [x[BOOT_DEV_TYPE] for x in boot_model] boot_model.clear() + boot_rows = self._make_boot_rows() + boot_order = self.vm.get_boot_order() - if bootdevs is None: - bootdevs = self.vm.get_boot_device() + for key in boot_order: + for row in boot_rows[:]: + if key != row[BOOT_KEY]: + continue - 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], - } + row[BOOT_ACTIVE] = True + boot_model.append(row) + boot_rows.remove(row) + break - for dev in bootdevs: - foundrow = 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 dev in boot_rows: - boot_model.append(boot_rows[dev]) - del(boot_rows[dev]) - - for row in boot_rows.values(): + for row in boot_rows: 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") - def show_pair(self, basename, show): combo = self.widget(basename) label = self.widget(basename + "-title") diff --git a/virtManager/domain.py b/virtManager/domain.py index ab545c65..c8cfdbe1 100644 --- a/virtManager/domain.py +++ b/virtManager/domain.py @@ -93,7 +93,7 @@ def compare_device(origdev, newdev, idx): return True -def find_device(guest, origdev): +def _find_device(guest, origdev): devlist = guest.get_devices(origdev.virtual_device_type) for idx in range(len(devlist)): dev = devlist[idx] @@ -458,22 +458,29 @@ class vmmDomain(vmmLibvirtObject): self._name = None self._id = None - def _redefine_device(self, cb, origdev): - defguest = self._get_xmlobj_to_define() - dev = find_device(defguest, origdev) + def _lookup_device_to_define(self, origdev, guest=None): + if guest is None: + guest = self._get_xmlobj_to_define() + + dev = _find_device(guest, origdev) if dev: - return cb(dev) + return dev # If we are removing multiple dev from an active VM, a double # attempt may result in a lookup failure. If device is present # in the active XML, assume all is good. - if find_device(self.get_xmlobj(), origdev): + if _find_device(self.get_xmlobj(), origdev): logging.debug("Device in active config but not inactive config.") return raise RuntimeError(_("Could not find specified device in the " "inactive VM configuration: %s") % repr(origdev)) + def _redefine_device(self, cb, origdev): + dev = self._lookup_device_to_define(origdev) + if dev: + return cb(dev) + ############################## # Persistent XML change APIs # @@ -508,7 +515,7 @@ class vmmDomain(vmmLibvirtObject): def change(guest): def rmdev(editdev): if con: - rmcon = find_device(guest, con) + rmcon = _find_device(guest, con) if rmcon: guest.remove_device(rmcon) @@ -569,7 +576,50 @@ class vmmDomain(vmmLibvirtObject): return self._redefine(change) # Boot define methods - def set_boot_device(self, boot_list): + def can_use_device_boot_order(self): + # Return 'True' if guest can use new style boot device ordering + return self.conn.check_support( + self.conn.SUPPORT_CONN_DEVICE_BOOTORDER) + + def get_bootable_devices(self): + devs = self.get_disk_devices() + devs += self.get_network_devices() + devs += self.get_hostdev_devices() + + # redirdev can also be marked bootable, but it should be rarely + # used and clutters the UI + return devs + + def _set_device_boot_order(self, boot_list): + boot_dev_order = [] + devmap = dict((dev.vmmidstr, dev) for dev in + self.get_bootable_devices()) + for b in boot_list: + if b in devmap: + boot_dev_order.append(devmap[b]) + + def change(guest): + # Unset the traditional boot order + guest.os.bootorder = [] + + # Unset standard boot order + for dev in guest.get_all_devices(): + dev.boot.order = None + + count = 1 + for origdev in boot_dev_order: + dev = self._lookup_device_to_define(origdev, guest=guest) + if not dev: + continue + dev.boot.order = count + count += 1 + + return self._redefine(change) + + def set_boot_order(self, boot_list): + if self.can_use_device_boot_order(): + return self._set_device_boot_order(boot_list) + def change(guest): guest.os.bootorder = boot_list return self._redefine(change) @@ -1036,8 +1086,46 @@ class vmmDomain(vmmLibvirtObject): def get_cpu_config(self): return self.get_xmlobj().cpu - def get_boot_device(self): + def _convert_old_boot_order(self): + boot_order = self._get_old_boot_order() + ret = [] + disks = self.get_disk_devices() + nets = self.get_network_devices() + + for b in boot_order: + if b == "network": + ret += [n.vmmidstr for n in nets] + if b == "hd": + ret += [d.vmmidstr for d in disks if + d.device not in ["cdrom", "floppy"]] + if b == "cdrom": + ret += [d.vmmidstr for d in disks if d.device == "cdrom"] + if b == "floppy": + ret += [d.vmmidstr for d in disks if d.device == "floppy"] + return ret + + def _get_device_boot_order(self): + devs = self.get_bootable_devices() + order = [] + for dev in devs: + if not dev.boot.order: + continue + order.append((dev.vmmidstr, dev.boot.order)) + + if not order: + # No devices individually marked bootable, convert traditional + # boot XML to fine grained, for the UI. + return self._convert_old_boot_order() + + order.sort(key=lambda p: p[1]) + return [p[0] for p in order] + + def _get_old_boot_order(self): return self.get_xmlobj().os.bootorder + def get_boot_order(self): + if self.can_use_device_boot_order(): + return self._get_device_boot_order() + return self._get_old_boot_order() def get_boot_menu(self): guest = self.get_xmlobj() return bool(guest.os.enable_bootmenu) @@ -1062,10 +1150,9 @@ class vmmDomain(vmmLibvirtObject): inactive=inactive) devs = guest.get_devices(device_type) - count = 0 - for dev in devs: - dev.vmmindex = count - count += 1 + for idx in range(len(devs)): + devs[idx].vmmindex = idx + devs[idx].vmmidstr = devs[idx].virtual_device_type + ("%.3d" % idx) return devs diff --git a/virtinst/support.py b/virtinst/support.py index 3e33ace4..755d1083 100644 --- a/virtinst/support.py +++ b/virtinst/support.py @@ -298,6 +298,8 @@ SUPPORT_CONN_QCOW2_LAZY_REFCOUNTS = _make(version="1001000", drv_version=[("qemu", 1002000), ("test", 0)]) SUPPORT_CONN_USBREDIR = _make(version="9005", drv_version=[("qemu", 1003000), ("test", 0)]) +SUPPORT_CONN_DEVICE_BOOTORDER = _make(version="8008", + drv_version=[("qemu", 0), ("test", 0)]) # Domain checks