details: Expose fine grained boot config
Basically put an explicit device list in the boot UI, if the hypervisor supports it. For unsupported hv, use the same old disk/cd/floppy/net UI.
This commit is contained in:
parent
8577bdf386
commit
4a006b5ff8
927
ui/details.ui
927
ui/details.ui
File diff suppressed because it is too large
Load Diff
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue