1# Copyright (C) 2006-2008, 2013, 2014 Red Hat, Inc. 2# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com> 3# 4# This work is licensed under the GNU GPLv2 or later. 5# See the COPYING file in the top-level directory. 6 7import re 8 9from gi.repository import Gtk 10 11import libvirt 12 13import virtinst 14from virtinst import log 15 16from ..lib import uiutil 17from ..addhardware import vmmAddHardware 18from ..baseclass import vmmGObjectUI 19from ..device.addstorage import vmmAddStorage 20from ..device.fsdetails import vmmFSDetails 21from ..device.gfxdetails import vmmGraphicsDetails 22from ..device.mediacombo import vmmMediaCombo 23from ..device.netlist import vmmNetworkList 24from ..device.vsockdetails import vmmVsockDetails 25from ..lib.graphwidgets import Sparkline 26from ..oslist import vmmOSList 27from ..storagebrowse import vmmStorageBrowser 28from ..xmleditor import vmmXMLEditor 29from ..delete import vmmDeleteStorage 30 31 32# Parameters that can be edited in the details window 33(EDIT_XML, 34 35 EDIT_NAME, 36 EDIT_TITLE, 37 EDIT_MACHTYPE, 38 EDIT_FIRMWARE, 39 EDIT_DESC, 40 41 EDIT_OS_NAME, 42 43 EDIT_VCPUS, 44 EDIT_CPU, 45 EDIT_TOPOLOGY, 46 47 EDIT_MEM, 48 49 EDIT_AUTOSTART, 50 EDIT_BOOTORDER, 51 EDIT_BOOTMENU, 52 EDIT_KERNEL, 53 EDIT_INIT, 54 55 EDIT_DISK_BUS, 56 EDIT_DISK_PATH, 57 EDIT_DISK, 58 59 EDIT_SOUND_MODEL, 60 61 EDIT_SMARTCARD_MODE, 62 63 EDIT_NET_MODEL, 64 EDIT_NET_SOURCE, 65 EDIT_NET_MAC, 66 EDIT_NET_LINKSTATE, 67 68 EDIT_GFX, 69 70 EDIT_VIDEO_MODEL, 71 EDIT_VIDEO_3D, 72 73 EDIT_WATCHDOG_MODEL, 74 EDIT_WATCHDOG_ACTION, 75 76 EDIT_CONTROLLER_MODEL, 77 78 EDIT_TPM_TYPE, 79 EDIT_TPM_MODEL, 80 81 EDIT_VSOCK_AUTO, 82 EDIT_VSOCK_CID, 83 84 EDIT_FS, 85 86 EDIT_HOSTDEV_ROMBAR) = range(1, 38) 87 88 89# Columns in hw list model 90(HW_LIST_COL_LABEL, 91 HW_LIST_COL_ICON_NAME, 92 HW_LIST_COL_TYPE, 93 HW_LIST_COL_DEVICE, 94 HW_LIST_COL_KEY) = range(5) 95 96# Types for the hw list model: numbers specify what order they will be listed 97(HW_LIST_TYPE_GENERAL, 98 HW_LIST_TYPE_OS, 99 HW_LIST_TYPE_STATS, 100 HW_LIST_TYPE_CPU, 101 HW_LIST_TYPE_MEMORY, 102 HW_LIST_TYPE_BOOT, 103 HW_LIST_TYPE_DISK, 104 HW_LIST_TYPE_NIC, 105 HW_LIST_TYPE_INPUT, 106 HW_LIST_TYPE_GRAPHICS, 107 HW_LIST_TYPE_SOUND, 108 HW_LIST_TYPE_CHAR, 109 HW_LIST_TYPE_HOSTDEV, 110 HW_LIST_TYPE_VIDEO, 111 HW_LIST_TYPE_WATCHDOG, 112 HW_LIST_TYPE_CONTROLLER, 113 HW_LIST_TYPE_FILESYSTEM, 114 HW_LIST_TYPE_SMARTCARD, 115 HW_LIST_TYPE_REDIRDEV, 116 HW_LIST_TYPE_TPM, 117 HW_LIST_TYPE_RNG, 118 HW_LIST_TYPE_PANIC, 119 HW_LIST_TYPE_VSOCK) = range(23) 120 121remove_pages = [HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT, 122 HW_LIST_TYPE_GRAPHICS, HW_LIST_TYPE_SOUND, HW_LIST_TYPE_CHAR, 123 HW_LIST_TYPE_HOSTDEV, HW_LIST_TYPE_DISK, HW_LIST_TYPE_VIDEO, 124 HW_LIST_TYPE_WATCHDOG, HW_LIST_TYPE_CONTROLLER, 125 HW_LIST_TYPE_FILESYSTEM, HW_LIST_TYPE_SMARTCARD, 126 HW_LIST_TYPE_REDIRDEV, HW_LIST_TYPE_TPM, 127 HW_LIST_TYPE_RNG, HW_LIST_TYPE_PANIC, HW_LIST_TYPE_VSOCK] 128 129# Boot device columns 130(BOOT_KEY, 131 BOOT_LABEL, 132 BOOT_ICON, 133 BOOT_ACTIVE, 134 BOOT_CAN_SELECT) = range(5) 135 136 137def _calculate_disk_bus_index(disklist): 138 # Iterate through all disks and calculate what number they are 139 # This sets disk.disk_bus_index which is not a standard property 140 idx_mapping = {} 141 ret = [] 142 for dev in disklist: 143 devtype = dev.device 144 bus = dev.bus 145 key = devtype + (bus or "") 146 147 if key not in idx_mapping: 148 idx_mapping[key] = 1 149 150 disk_bus_index = idx_mapping[key] 151 idx_mapping[key] += 1 152 ret.append((dev, disk_bus_index)) 153 154 return ret 155 156 157def _label_for_device(dev, disk_bus_index): 158 devtype = dev.DEVICE_TYPE 159 160 if devtype == "disk": 161 if dev.device == "floppy": 162 return _("Floppy %(index)d") % {"index": disk_bus_index} 163 164 busstr = "" 165 if dev.bus: 166 busstr = vmmAddHardware.disk_pretty_bus(dev.bus) 167 if dev.device == "cdrom": 168 return _("%(bus)s CDROM %(index)d") % { 169 "bus": busstr, 170 "index": disk_bus_index, 171 } 172 elif dev.device == "disk": 173 return _("%(bus)s Disk %(index)d") % { 174 "bus": busstr, 175 "index": disk_bus_index, 176 } 177 return _("%(bus)s %(device)s %(index)d") % { 178 "bus": busstr, 179 "device": dev.device.capitalize(), 180 "index": disk_bus_index, 181 } 182 183 if devtype == "interface": 184 mac = dev.macaddr[-9:] or "" 185 return _("NIC %(mac)s") % {"mac": mac} 186 187 if devtype == "input": 188 if dev.type == "tablet": 189 return _("Tablet") 190 elif dev.type == "mouse": 191 return _("Mouse") 192 elif dev.type == "keyboard": 193 return _("Keyboard") 194 return _("Input") # pragma: no cover 195 196 if devtype == "serial": 197 port = dev.target_port or 0 198 return _("Serial %(num)d") % {"num": port + 1} 199 200 if devtype == "parallel": 201 port = dev.target_port or 0 202 return _("Parallel %(num)d") % {"num": port + 1} 203 204 if devtype == "console": 205 port = dev.target_port or 0 206 return _("Console %(num)d") % {"num": port + 1} 207 208 if devtype == "channel": 209 name = vmmAddHardware.char_pretty_channel_name(dev.target_name) 210 if name: 211 return _("Channel %(name)s") % {"name": name} 212 pretty_type = vmmAddHardware.char_pretty_type(dev.type) 213 return _("Channel %(type)s") % {"type": pretty_type} 214 215 if devtype == "graphics": 216 pretty = vmmGraphicsDetails.graphics_pretty_type_simple(dev.type) 217 return _("Display %s") % pretty 218 if devtype == "redirdev": 219 return _("%(bus)s Redirector %(index)d") % { 220 "bus": vmmAddHardware.disk_pretty_bus(dev.bus), 221 "index": dev.get_xml_idx() + 1, 222 } 223 if devtype == "hostdev": 224 return vmmAddHardware.hostdev_pretty_name(dev) 225 if devtype == "sound": 226 return _("Sound %s") % dev.model 227 if devtype == "video": 228 return _("Video %s") % vmmAddHardware.video_pretty_model(dev.model) 229 if devtype == "filesystem": 230 return _("Filesystem %(path)s") % {"path": dev.target[:8]} 231 if devtype == "controller": 232 idx = dev.index 233 if idx is not None: 234 return _("Controller %(controller)s %(index)s") % { 235 "controller": vmmAddHardware.controller_pretty_desc(dev), 236 "index": idx, 237 } 238 return _("Controller %(controller)s") % { 239 "controller": vmmAddHardware.controller_pretty_desc(dev), 240 } 241 if devtype == "rng": 242 if dev.device: 243 return _("RNG %(device)s") % {"device": dev.device} 244 return _("RNG") 245 if devtype == "tpm": 246 if dev.device_path: 247 return _("TPM %(device)s") % {"device": dev.device_path} 248 return _("TPM v%(version)s") % {"version": dev.version} 249 250 devmap = { 251 "panic": _("Panic Notifier"), 252 "smartcard": _("Smartcard"), 253 "vsock": _("VirtIO VSOCK"), 254 "watchdog": _("Watchdog"), 255 } 256 return devmap[devtype] 257 258 259def _icon_for_device(dev): 260 devtype = dev.DEVICE_TYPE 261 262 if devtype == "disk": 263 if dev.device == "cdrom": 264 return "media-optical" 265 elif dev.device == "floppy": 266 return "media-floppy" 267 return "drive-harddisk" 268 269 if devtype == "input": 270 if dev.type == "keyboard": 271 return "input-keyboard" 272 if dev.type == "tablet": 273 return "input-tablet" 274 return "input-mouse" 275 276 if devtype == "redirdev": 277 return "device_usb" 278 279 if devtype == "hostdev": 280 if dev.type == "usb": 281 return "device_usb" 282 return "device_pci" 283 284 typemap = { 285 "interface": "network-idle", 286 "graphics": "video-display", 287 "serial": "device_serial", 288 "parallel": "device_serial", 289 "console": "device_serial", 290 "channel": "device_serial", 291 "video": "video-display", 292 "watchdog": "device_pci", 293 "sound": "audio-card", 294 "rng": "system-run", 295 "tpm": "device_cpu", 296 "smartcard": "device_serial", 297 "filesystem": "folder", 298 "controller": "device_pci", 299 "panic": "system-run", 300 "vsock": "network-idle", 301 } 302 return typemap[devtype] 303 304 305def _chipset_label_from_machine(machine): 306 if machine and "q35" in machine: 307 return "Q35" 308 return "i440FX" 309 310 311def _get_performance_icon_name(): 312 # This icon isn't in standard adwaita-icon-theme, so 313 # fallback to system-run if it is missing 314 icon = "utilities-system-monitor" 315 if not Gtk.IconTheme.get_default().has_icon(icon): 316 icon = "system-run" # pragma: no cover 317 return icon 318 319 320def _unindent_device_xml(xml): 321 """ 322 The device parsed from a domain will have no indent 323 for the first line, but then <domain> expected indent 324 from the remaining lines. Try to unindent the remaining 325 lines so it looks nice in the XML editor. 326 """ 327 lines = xml.splitlines() 328 if not xml.startswith("<") or len(lines) < 2: 329 return xml 330 331 ret = "" 332 unindent = 0 333 for c in lines[1]: 334 if c != " ": 335 break 336 unindent += 1 337 338 unindent = max(0, unindent - 2) 339 ret = lines[0] + "\n" 340 for line in lines[1:]: 341 if re.match(r"^%s *<.*$" % (unindent * " "), line): 342 line = line[unindent:] 343 ret += line + "\n" 344 return ret 345 346 347class vmmDetails(vmmGObjectUI): 348 def __init__(self, vm, builder, topwin, is_customize_dialog): 349 vmmGObjectUI.__init__(self, "details.ui", 350 None, builder=builder, topwin=topwin) 351 352 self.vm = vm 353 self._active_edits = [] 354 self.top_box = self.widget("details-top-box") 355 356 self.addhw = None 357 self.storage_browser = None 358 self._mediacombo = None 359 self.is_customize_dialog = is_customize_dialog 360 361 def _e(edittype): 362 def signal_cb(*args): 363 self._enable_apply(edittype) 364 return signal_cb 365 366 self._mediacombo = vmmMediaCombo(self.conn, self.builder, self.topwin) 367 self.widget("disk-source-align").add(self._mediacombo.top_box) 368 self._mediacombo.set_mnemonic_label( 369 self.widget("disk-source-mnemonic")) 370 self._mediacombo.connect("changed", _e(EDIT_DISK_PATH)) 371 self._mediacombo.show_clear_icon() 372 373 self.fsDetails = vmmFSDetails(self.vm, self.builder, self.topwin) 374 self.widget("fs-alignment").add(self.fsDetails.top_box) 375 self.fsDetails.connect("changed", _e(EDIT_FS)) 376 377 self.gfxdetails = vmmGraphicsDetails( 378 self.vm, self.builder, self.topwin) 379 self.widget("graphics-align").add(self.gfxdetails.top_box) 380 self.gfxdetails.connect("changed", _e(EDIT_GFX)) 381 382 self.netlist = vmmNetworkList(self.conn, self.builder, self.topwin) 383 self.widget("network-source-label-align").add(self.netlist.top_label) 384 self.widget("network-source-ui-align").add(self.netlist.top_box) 385 self.netlist.connect("changed", _e(EDIT_NET_SOURCE)) 386 387 self.vsockdetails = vmmVsockDetails(self.vm, self.builder, self.topwin) 388 self.widget("vsock-align").add(self.vsockdetails.top_box) 389 self.vsockdetails.connect("changed-auto-cid", _e(EDIT_VSOCK_AUTO)) 390 self.vsockdetails.connect("changed-cid", _e(EDIT_VSOCK_CID)) 391 392 self._addstorage = vmmAddStorage(self.conn, self.builder, self.topwin) 393 self.widget("storage-advanced-align").add( 394 self._addstorage.advanced_top_box) 395 self._addstorage.connect("changed", _e(EDIT_DISK)) 396 397 self._xmleditor = vmmXMLEditor(self.builder, self.topwin, 398 self.widget("hw-panel-align"), 399 self.widget("hw-panel")) 400 self._xmleditor.connect("changed", _e(EDIT_XML)) 401 self._xmleditor.connect("xml-requested", 402 self._xmleditor_xml_requested_cb) 403 self._xmleditor.connect("xml-reset", 404 self._xmleditor_xml_reset_cb) 405 406 self._oldhwkey = None 407 self._popupmenu = None 408 self._popupmenuitems = None 409 self._os_list = None 410 self._init_menus() 411 self._init_details() 412 413 self._graph_cpu = None 414 self._graph_memory = None 415 self._graph_disk = None 416 self._graph_network = None 417 self._init_graphs() 418 419 self.vm.connect("inspection-changed", self._vm_inspection_changed_cb) 420 421 self.builder.connect_signals({ 422 "on_hw_list_changed": self._hw_changed_cb, 423 424 "on_overview_name_changed": _e(EDIT_NAME), 425 "on_overview_title_changed": _e(EDIT_TITLE), 426 "on_machine_type_changed": _e(EDIT_MACHTYPE), 427 "on_overview_firmware_changed": _e(EDIT_FIRMWARE), 428 "on_overview_chipset_changed": _e(EDIT_MACHTYPE), 429 430 "on_details_inspection_refresh_clicked": self._inspection_refresh_clicked_cb, 431 432 "on_cpu_vcpus_changed": self._config_vcpus_changed_cb, 433 "on_cpu_model_changed": _e(EDIT_CPU), 434 "on_cpu_copy_host_clicked": self._cpu_copy_host_clicked_cb, 435 "on_cpu_secure_toggled": _e(EDIT_CPU), 436 "on_cpu_cores_changed": self._cpu_topology_changed_cb, 437 "on_cpu_sockets_changed": self._cpu_topology_changed_cb, 438 "on_cpu_threads_changed": self._cpu_topology_changed_cb, 439 "on_cpu_topology_enable_toggled": self._cpu_topology_enable_cb, 440 "on_mem_maxmem_changed": _e(EDIT_MEM), 441 "on_mem_memory_changed": self._curmem_changed_cb, 442 443 "on_boot_list_changed": self._boot_list_changed_cb, 444 "on_boot_moveup_clicked": self._boot_moveup_clicked_cb, 445 "on_boot_movedown_clicked": self._boot_movedown_clicked_cb, 446 "on_boot_autostart_changed": _e(EDIT_AUTOSTART), 447 "on_boot_menu_changed": _e(EDIT_BOOTMENU), 448 "on_boot_kernel_enable_toggled": self._boot_kernel_toggled_cb, 449 "on_boot_kernel_changed": _e(EDIT_KERNEL), 450 "on_boot_initrd_changed": _e(EDIT_KERNEL), 451 "on_boot_dtb_changed": _e(EDIT_KERNEL), 452 "on_boot_kernel_args_changed": _e(EDIT_KERNEL), 453 "on_boot_kernel_browse_clicked": self._browse_kernel_clicked_cb, 454 "on_boot_initrd_browse_clicked": self._browse_initrd_clicked_cb, 455 "on_boot_dtb_browse_clicked": self._browse_dtb_clicked_cb, 456 "on_boot_init_path_changed": _e(EDIT_INIT), 457 "on_boot_init_args_changed": _e(EDIT_INIT), 458 459 460 "on_disk_source_browse_clicked": self._disk_source_browse_clicked_cb, 461 "on_disk_bus_combo_changed": _e(EDIT_DISK_BUS), 462 463 "on_network_model_combo_changed": _e(EDIT_NET_MODEL), 464 "on_network_mac_entry_changed": _e(EDIT_NET_MAC), 465 "on_network_link_state_checkbox_toggled": _e(EDIT_NET_LINKSTATE), 466 "on_network_refresh_ip_clicked": self._refresh_ip_clicked_cb, 467 468 "on_sound_model_combo_changed": _e(EDIT_SOUND_MODEL), 469 470 "on_video_model_combo_changed": self._video_model_changed_cb, 471 "on_video_3d_toggled": self._video_3d_toggled_cb, 472 473 "on_watchdog_model_combo_changed": _e(EDIT_WATCHDOG_MODEL), 474 "on_watchdog_action_combo_changed": _e(EDIT_WATCHDOG_ACTION), 475 476 "on_smartcard_mode_combo_changed": _e(EDIT_SMARTCARD_MODE), 477 478 "on_hostdev_rombar_toggled": _e(EDIT_HOSTDEV_ROMBAR), 479 "on_controller_model_combo_changed": _e(EDIT_CONTROLLER_MODEL), 480 "on_tpm_model_combo_changed": _e(EDIT_TPM_MODEL), 481 482 "on_config_apply_clicked": self._config_apply_clicked_cb, 483 "on_config_cancel_clicked": self._config_cancel_clicked_cb, 484 "on_config_remove_clicked": self._config_remove_clicked_cb, 485 "on_add_hardware_button_clicked": self._addhw_clicked_cb, 486 487 "on_hw_list_button_press_event": self._popup_addhw_menu_cb, 488 }) 489 490 self._init_hw_list() 491 self._refresh_page() 492 493 494 @property 495 def conn(self): 496 return self.vm.conn 497 498 def _cleanup(self): 499 self._oldhwkey = None 500 501 if self.addhw: 502 self.addhw.cleanup() 503 self.addhw = None 504 if self.storage_browser: 505 self.storage_browser.cleanup() 506 self.storage_browser = None 507 508 self._mediacombo.cleanup() 509 self._mediacombo = None 510 511 self.conn.disconnect_by_obj(self) 512 self.vm = None 513 self._popupmenu = None 514 self._popupmenuitems = None 515 516 self.gfxdetails.cleanup() 517 self.gfxdetails = None 518 self.fsDetails.cleanup() 519 self.fsDetails = None 520 self.netlist.cleanup() 521 self.netlist = None 522 self.vsockdetails.cleanup() 523 self.vsockdetails = None 524 self._xmleditor.cleanup() 525 self._xmleditor = None 526 self._addstorage.cleanup() 527 self._addstorage = None 528 self._os_list.cleanup() 529 self._os_list = None 530 531 532 ########################## 533 # Initialization helpers # 534 ########################## 535 536 def _init_menus(self): 537 # Add HW popup menu 538 self._popupmenu = Gtk.Menu() 539 540 addHW = Gtk.ImageMenuItem.new_with_label(_("_Add Hardware")) 541 addHW.set_use_underline(True) 542 addHWImg = Gtk.Image() 543 addHWImg.set_from_stock(Gtk.STOCK_ADD, Gtk.IconSize.MENU) 544 addHW.set_image(addHWImg) 545 addHW.show() 546 def _addhw_clicked_cb(*args, **kwargs): 547 self._show_addhw() 548 addHW.connect("activate", _addhw_clicked_cb) 549 550 rmHW = Gtk.ImageMenuItem.new_with_label(_("_Remove Hardware")) 551 rmHW.set_use_underline(True) 552 rmHWImg = Gtk.Image() 553 rmHWImg.set_from_stock(Gtk.STOCK_REMOVE, Gtk.IconSize.MENU) 554 rmHW.set_image(rmHWImg) 555 rmHW.show() 556 def _remove_clicked_cb(*args, **kwargs): 557 self._config_remove() 558 rmHW.connect("activate", _remove_clicked_cb) 559 560 self._popupmenuitems = {"add": addHW, "remove": rmHW} 561 for i in list(self._popupmenuitems.values()): 562 self._popupmenu.add(i) 563 564 self.widget("hw-panel").set_show_tabs(False) 565 566 567 def _init_graphs(self): 568 def _make_graph(): 569 g = Sparkline() 570 g.set_property("reversed", True) 571 g.show() 572 return g 573 574 self._graph_cpu = _make_graph() 575 self.widget("overview-cpu-usage-align").add(self._graph_cpu) 576 577 self._graph_memory = _make_graph() 578 self.widget("overview-memory-usage-align").add(self._graph_memory) 579 580 self._graph_disk = _make_graph() 581 self._graph_disk.set_property("filled", False) 582 self._graph_disk.set_property("num_sets", 2) 583 self._graph_disk.set_property("rgb", [x / 255.0 for x in 584 [0x82, 0x00, 0x3B, 0x29, 0x5C, 0x45]]) 585 self.widget("overview-disk-usage-align").add(self._graph_disk) 586 587 self._graph_network = _make_graph() 588 self._graph_network.set_property("filled", False) 589 self._graph_network.set_property("num_sets", 2) 590 self._graph_network.set_property("rgb", [x / 255.0 for x in 591 [0x82, 0x00, 0x3B, 592 0x29, 0x5C, 0x45]]) 593 self.widget("overview-network-traffic-align").add( 594 self._graph_network) 595 596 def _init_details(self): 597 # Hardware list 598 # [ label, icon name, hw type, dev xmlobj, unique key (dev or title)] 599 hw_list_model = Gtk.ListStore(str, str, int, object, object) 600 self.widget("hw-list").set_model(hw_list_model) 601 602 hwCol = Gtk.TreeViewColumn(_("Hardware")) 603 hwCol.set_spacing(6) 604 hwCol.set_min_width(165) 605 hw_txt = Gtk.CellRendererText() 606 hw_img = Gtk.CellRendererPixbuf() 607 hw_img.set_property("stock-size", Gtk.IconSize.LARGE_TOOLBAR) 608 hwCol.pack_start(hw_img, False) 609 hwCol.pack_start(hw_txt, True) 610 hwCol.add_attribute(hw_txt, 'text', HW_LIST_COL_LABEL) 611 hwCol.add_attribute(hw_img, 'icon-name', HW_LIST_COL_ICON_NAME) 612 self.widget("hw-list").append_column(hwCol) 613 614 # Description text view 615 desc = self.widget("overview-description") 616 buf = Gtk.TextBuffer() 617 def _buf_changed_cb(*args): 618 self._enable_apply(EDIT_DESC) 619 buf.connect("changed", _buf_changed_cb) 620 desc.set_buffer(buf) 621 622 arch = self.vm.get_arch() 623 caps = self.vm.conn.caps 624 625 # Machine type 626 machtype_combo = self.widget("machine-type") 627 machtype_model = Gtk.ListStore(str) 628 machtype_combo.set_model(machtype_model) 629 uiutil.init_combo_text_column(machtype_combo, 0) 630 machtype_model.set_sort_column_id(0, Gtk.SortType.ASCENDING) 631 632 machines = [] 633 try: 634 capsinfo = caps.guest_lookup( 635 os_type=self.vm.get_abi_type(), 636 arch=self.vm.get_arch(), 637 typ=self.vm.get_hv_type(), 638 machine=self.vm.get_machtype()) 639 640 machines = capsinfo.machines[:] 641 except Exception: 642 log.exception("Error determining machine list") 643 644 show_machine = (arch not in ["i686", "x86_64"]) 645 uiutil.set_grid_row_visible(self.widget("machine-type-title"), 646 show_machine) 647 648 if show_machine: 649 for machine in machines: 650 machtype_model.append([machine]) 651 652 self.widget("machine-type").set_visible(self.is_customize_dialog) 653 self.widget("machine-type-label").set_visible( 654 not self.is_customize_dialog) 655 656 # Firmware 657 combo = self.widget("overview-firmware") 658 # [label, path, is_sensitive] 659 model = Gtk.ListStore(str, str, bool) 660 combo.set_model(model) 661 text = Gtk.CellRendererText() 662 combo.pack_start(text, True) 663 combo.add_attribute(text, "text", 0) 664 combo.add_attribute(text, "sensitive", 2) 665 666 domcaps = self.vm.get_domain_capabilities() 667 uefipaths = [v.value for v in domcaps.os.loader.values] 668 669 warn_icon = self.widget("overview-firmware-warn") 670 hv_supports_uefi = domcaps.supports_uefi_xml() 671 if not hv_supports_uefi: 672 warn_icon.set_tooltip_text( 673 _("Libvirt or hypervisor does not support UEFI.")) 674 elif not uefipaths: 675 warn_icon.set_tooltip_text( # pragma: no cover 676 _("Libvirt did not detect any UEFI/OVMF firmware image " 677 "installed on the host.")) 678 679 model.append([domcaps.label_for_firmware_path(None), None, True]) 680 if not uefipaths: 681 model.append([_("UEFI not found"), None, False]) 682 else: 683 for path in uefipaths: 684 model.append([domcaps.label_for_firmware_path(path), 685 path, True]) 686 687 combo.set_active(0) 688 689 self.widget("overview-firmware-warn").set_visible( 690 not (uefipaths and hv_supports_uefi) and self.is_customize_dialog) 691 self.widget("overview-firmware").set_visible(self.is_customize_dialog) 692 self.widget("overview-firmware-label").set_visible( 693 not self.is_customize_dialog) 694 show_firmware = ((self.conn.is_qemu() or 695 self.conn.is_test() or 696 self.conn.is_xen()) and 697 domcaps.arch_can_uefi()) 698 uiutil.set_grid_row_visible( 699 self.widget("overview-firmware-title"), show_firmware) 700 701 # Chipset 702 combo = self.widget("overview-chipset") 703 model = Gtk.ListStore(str, str) 704 combo.set_model(model) 705 model.append([_chipset_label_from_machine("pc"), "pc"]) 706 if "q35" in machines: 707 model.append([_chipset_label_from_machine("q35"), "q35"]) 708 combo.set_active(0) 709 710 self.widget("overview-chipset").set_visible(self.is_customize_dialog) 711 self.widget("overview-chipset-label").set_visible( 712 not self.is_customize_dialog) 713 show_chipset = ((self.conn.is_qemu() or self.conn.is_test()) and 714 arch in ["i686", "x86_64"]) 715 uiutil.set_grid_row_visible( 716 self.widget("overview-chipset-title"), show_chipset) 717 718 # OS/Inspection page 719 self._os_list = vmmOSList() 720 self.widget("details-os-align").add(self._os_list.search_entry) 721 self.widget("details-os-label").set_mnemonic_widget( 722 self._os_list.search_entry) 723 self._os_list.connect("os-selected", self._os_list_name_selected_cb) 724 725 apps_list = self.widget("inspection-apps") 726 apps_model = Gtk.ListStore(str, str, str) 727 apps_list.set_model(apps_model) 728 729 name_col = Gtk.TreeViewColumn(_("Name")) 730 version_col = Gtk.TreeViewColumn(_("Version")) 731 summary_col = Gtk.TreeViewColumn() 732 733 apps_list.append_column(name_col) 734 apps_list.append_column(version_col) 735 apps_list.append_column(summary_col) 736 737 name_text = Gtk.CellRendererText() 738 name_col.pack_start(name_text, True) 739 name_col.add_attribute(name_text, 'text', 0) 740 name_col.set_sort_column_id(0) 741 742 version_text = Gtk.CellRendererText() 743 version_col.pack_start(version_text, True) 744 version_col.add_attribute(version_text, 'text', 1) 745 version_col.set_sort_column_id(1) 746 747 summary_text = Gtk.CellRendererText() 748 summary_col.pack_start(summary_text, True) 749 summary_col.add_attribute(summary_text, 'text', 2) 750 summary_col.set_sort_column_id(2) 751 752 753 # Boot device list 754 boot_list = self.widget("boot-list") 755 # [XML boot type, display name, icon name, enabled, can select] 756 boot_list_model = Gtk.ListStore(str, str, str, bool, bool) 757 boot_list.set_model(boot_list_model) 758 759 chkCol = Gtk.TreeViewColumn() 760 txtCol = Gtk.TreeViewColumn() 761 762 boot_list.append_column(chkCol) 763 boot_list.append_column(txtCol) 764 765 chk = Gtk.CellRendererToggle() 766 chk.connect("toggled", self._config_boot_toggled_cb) 767 chkCol.pack_start(chk, False) 768 chkCol.add_attribute(chk, 'active', BOOT_ACTIVE) 769 chkCol.add_attribute(chk, 'visible', BOOT_CAN_SELECT) 770 771 icon = Gtk.CellRendererPixbuf() 772 txtCol.pack_start(icon, False) 773 txtCol.add_attribute(icon, 'icon-name', BOOT_ICON) 774 775 text = Gtk.CellRendererText() 776 txtCol.pack_start(text, True) 777 txtCol.add_attribute(text, 'text', BOOT_LABEL) 778 txtCol.add_attribute(text, 'sensitive', BOOT_ACTIVE) 779 780 # CPU model combo 781 cpu_model = self.widget("cpu-model") 782 783 def sep_func(model, it, ignore): 784 return model[it][3] 785 786 # [label, sortkey, idstring, is sep] 787 model = Gtk.ListStore(str, str, str, bool) 788 cpu_model.set_model(model) 789 cpu_model.set_entry_text_column(0) 790 cpu_model.set_row_separator_func(sep_func, None) 791 model.set_sort_column_id(1, Gtk.SortType.ASCENDING) 792 model.append([_("Application Default"), "01", 793 virtinst.DomainCpu.SPECIAL_MODE_APP_DEFAULT, False]) 794 model.append([_("Hypervisor Default"), "02", 795 virtinst.DomainCpu.SPECIAL_MODE_HV_DEFAULT, False]) 796 model.append([_("Clear CPU configuration"), "03", 797 virtinst.DomainCpu.SPECIAL_MODE_CLEAR, False]) 798 model.append(["host-model", "04", 799 virtinst.DomainCpu.SPECIAL_MODE_HOST_MODEL, False]) 800 model.append(["host-passthrough", "05", 801 virtinst.DomainCpu.SPECIAL_MODE_HOST_PASSTHROUGH, False]) 802 model.append([None, None, None, True]) 803 for name in domcaps.get_cpu_models(): 804 model.append([name, name, name, False]) 805 806 # Disk bus combo 807 disk_bus = self.widget("disk-bus") 808 vmmAddHardware.build_disk_bus_combo(self.vm, disk_bus) 809 self.widget("disk-bus-label").set_visible( 810 not self.is_customize_dialog) 811 self.widget("disk-bus").set_visible(self.is_customize_dialog) 812 if not self.is_customize_dialog: 813 # Remove the mnemonic 814 self.widget("disk-bus-labeller").set_text(_("Disk bus:")) 815 816 # Network model 817 net_model = self.widget("network-model") 818 vmmAddHardware.build_network_model_combo(self.vm, net_model) 819 820 # Network mac 821 self.widget("network-mac-label").set_visible( 822 not self.is_customize_dialog) 823 self.widget("network-mac-entry").set_visible(self.is_customize_dialog) 824 825 # Sound model 826 sound_dev = self.widget("sound-model") 827 vmmAddHardware.build_sound_combo(self.vm, sound_dev) 828 829 # Video model combo 830 video_dev = self.widget("video-model") 831 vmmAddHardware.build_video_combo(self.vm, video_dev) 832 833 # Watchdog model combo 834 combo = self.widget("watchdog-model") 835 vmmAddHardware.build_watchdogmodel_combo(self.vm, combo) 836 837 # Watchdog action combo 838 combo = self.widget("watchdog-action") 839 vmmAddHardware.build_watchdogaction_combo(self.vm, combo) 840 841 # Smartcard mode 842 sc_mode = self.widget("smartcard-mode") 843 vmmAddHardware.build_smartcard_mode_combo(self.vm, sc_mode) 844 845 # TPM model 846 tpm_model = self.widget("tpm-model") 847 vmmAddHardware.build_tpm_model_combo(self.vm, tpm_model, None) 848 849 # Controller model 850 combo = self.widget("controller-model") 851 model = Gtk.ListStore(str, str) 852 combo.set_model(model) 853 uiutil.init_combo_text_column(combo, 1) 854 combo.set_active(-1) 855 856 combo = self.widget("controller-device-list") 857 model = Gtk.ListStore(str) 858 combo.set_model(model) 859 combo.set_headers_visible(False) 860 col = Gtk.TreeViewColumn() 861 text = Gtk.CellRendererText() 862 col.pack_start(text, True) 863 col.add_attribute(text, 'text', 0) 864 combo.append_column(col) 865 866 867 ########################## 868 # Window state listeners # 869 ########################## 870 871 def _popup_addhw_menu_cb(self, widget, event): 872 if event.button != 3: 873 return 874 875 # force select the list entry before showing popup_menu 876 path_tuple = widget.get_path_at_pos(int(event.x), int(event.y)) 877 if path_tuple is None: 878 return False # pragma: no cover 879 path = path_tuple[0] 880 _iter = widget.get_model().get_iter(path) 881 widget.get_selection().select_iter(_iter) 882 883 rmdev = self._popupmenuitems["remove"] 884 rmdev.set_visible(self.widget("config-remove").get_visible()) 885 rmdev.set_sensitive(self.widget("config-remove").get_sensitive()) 886 887 self._popupmenu.popup_at_pointer(event) 888 889 def _set_hw_selection(self, page, _disable_apply=True): 890 if _disable_apply: 891 self._disable_apply() 892 uiutil.set_list_selection_by_number(self.widget("hw-list"), page) 893 894 def _get_hw_row(self): 895 return uiutil.get_list_selected_row(self.widget("hw-list")) 896 897 def _get_hw_row_for_device(self, dev): 898 for row in self.widget("hw-list").get_model(): 899 if row[HW_LIST_COL_DEVICE] is dev: 900 return row 901 902 def _get_hw_row_label_for_device(self, dev): 903 row = self._get_hw_row_for_device(dev) 904 return row and row[HW_LIST_COL_LABEL] or "" 905 906 def _has_unapplied_changes(self, row): 907 """ 908 This is a bit confusing. 909 910 * If there are now changes pending, we return False 911 * If there are changes pending, we prompt the user whether 912 they want to apply them. If they say no, return False 913 * If the applying the changes succeeds, return False 914 * Return True if applying the changes failed. In this 915 case the caller should attempt to abort the action they 916 are trying to perform, if possible 917 """ 918 if not row: 919 return False 920 921 if not self.widget("config-apply").get_sensitive(): 922 return False 923 924 if not self.err.confirm_unapplied_changes(): 925 return False 926 927 return not self._config_apply(row=row) 928 929 def _hw_changed_cb(self, src): 930 """ 931 When user changes the hw-list selection 932 """ 933 newrow = self._get_hw_row() 934 model = self.widget("hw-list").get_model() 935 936 if not newrow or newrow[HW_LIST_COL_KEY] == self._oldhwkey: 937 return 938 939 oldhwrow = None 940 for row in model: 941 if row[HW_LIST_COL_KEY] == self._oldhwkey: 942 oldhwrow = row 943 break 944 945 if self._has_unapplied_changes(oldhwrow): 946 # Unapplied changes, and syncing them failed 947 pageidx = 0 948 for idx, row in enumerate(model): 949 if row[HW_LIST_COL_KEY] == self._oldhwkey: 950 pageidx = idx 951 break 952 self._set_hw_selection(pageidx, _disable_apply=False) 953 else: 954 self._oldhwkey = newrow[HW_LIST_COL_KEY] 955 self._refresh_page() 956 957 def _disable_device_remove(self, tooltip): 958 self.widget("config-remove").set_sensitive(False) 959 self.widget("config-remove").set_tooltip_text(tooltip) 960 961 962 ####################### 963 # vmwindow Public API # 964 ####################### 965 966 def _refresh_vm_state(self): 967 active = self.vm.is_active() 968 self.widget("overview-name").set_editable(not active) 969 970 reason = self.vm.run_status_reason() 971 if reason: 972 status = "%s (%s)" % (self.vm.run_status(), reason) 973 else: 974 status = self.vm.run_status() 975 self.widget("overview-status-text").set_text(status) 976 self.widget("overview-status-icon").set_from_icon_name( 977 self.vm.run_status_icon_name(), 978 Gtk.IconSize.BUTTON) 979 980 def vmwindow_resources_refreshed(self): 981 row = self._get_hw_row() 982 if row and row[HW_LIST_COL_TYPE] == HW_LIST_TYPE_STATS: 983 self._refresh_stats_page() 984 985 def vmwindow_refresh_vm_state(self, is_current_page): 986 if not is_current_page: 987 self._disable_apply() 988 return 989 990 self._refresh_vm_state() 991 self._repopulate_hw_list() 992 993 if self.widget("config-apply").get_sensitive(): 994 # Apply button sensitive means user is making changes, don't 995 # erase them 996 return 997 998 self._refresh_page() 999 1000 def vmwindow_activate_performance_page(self): 1001 index = 0 1002 model = self.widget("hw-list").get_model() 1003 for idx, row in enumerate(model): 1004 if row[HW_LIST_COL_TYPE] == HW_LIST_TYPE_STATS: 1005 index = idx 1006 break 1007 self._set_hw_selection(index) 1008 1009 def vmwindow_has_unapplied_changes(self): 1010 return self._has_unapplied_changes(self._get_hw_row()) 1011 1012 def vmwindow_close(self): 1013 self._disable_apply() 1014 1015 1016 ############################## 1017 # Add/remove device handling # 1018 ############################## 1019 1020 def _show_addhw(self): 1021 try: 1022 if self.addhw is None: 1023 self.addhw = vmmAddHardware(self.vm) 1024 1025 self.addhw.show(self.topwin) 1026 except Exception as e: # pragma: no cover 1027 self.err.show_err((_("Error launching hardware dialog: %s") % 1028 str(e))) 1029 1030 def _remove_non_disk(self, devobj): 1031 if not self.err.chkbox_helper(self.config.get_confirm_removedev, 1032 self.config.set_confirm_removedev, 1033 text1=(_("Are you sure you want to remove this device?"))): 1034 return 1035 1036 success = vmmDeleteStorage.remove_devobj_internal( 1037 self.vm, self.err, devobj) 1038 if not success: 1039 return 1040 1041 # This call here means when the vm config changes and triggers 1042 # refresh event, the UI page will be updated, rather than leaving 1043 # it untouched because it thinks changes are in progress 1044 self._disable_apply() 1045 1046 def _remove_disk(self, disk): 1047 dialog = vmmDeleteStorage(disk) 1048 dialog.show(self.topwin, self.vm) 1049 1050 def _config_remove(self): 1051 devobj = self._get_hw_row()[HW_LIST_COL_DEVICE] 1052 if devobj.DEVICE_TYPE == "disk": 1053 self._remove_disk(devobj) 1054 else: 1055 self._remove_non_disk(devobj) 1056 1057 1058 ############################ 1059 # Details/Hardware getters # 1060 ############################ 1061 1062 def _get_config_boot_order(self): 1063 boot_model = self.widget("boot-list").get_model() 1064 devs = [] 1065 1066 for row in boot_model: 1067 if row[BOOT_ACTIVE]: 1068 devs.append(row[BOOT_KEY]) 1069 1070 return devs 1071 1072 def _get_config_boot_selection(self): 1073 return uiutil.get_list_selected_row(self.widget("boot-list")) 1074 1075 1076 def _get_config_cpu_model(self): 1077 cpu_list = self.widget("cpu-model") 1078 text = cpu_list.get_child().get_text() 1079 1080 if self.widget("cpu-copy-host").get_active(): 1081 return virtinst.DomainCpu.SPECIAL_MODE_HOST_MODEL 1082 1083 key = None 1084 for row in cpu_list.get_model(): 1085 if text == row[0]: 1086 key = row[2] 1087 break 1088 if not key: 1089 return text 1090 1091 if key == virtinst.DomainCpu.SPECIAL_MODE_APP_DEFAULT: 1092 return self.config.get_default_cpu_setting() 1093 return key 1094 1095 def _get_config_vcpus(self): 1096 return uiutil.spin_get_helper(self.widget("cpu-vcpus")) 1097 1098 def _get_text(self, widgetname, strip=True, checksens=False): 1099 """ 1100 Helper for reading widget text with a few options 1101 """ 1102 widget = self.widget(widgetname) 1103 if (checksens and 1104 (not widget.is_sensitive() or not widget.is_visible())): 1105 return "" 1106 1107 ret = widget.get_text() 1108 if strip: 1109 ret = ret.strip() 1110 return ret 1111 1112 1113 ############################## 1114 # Details/Hardware listeners # 1115 ############################## 1116 1117 def _browse_file(self, callback, reason=None): 1118 if not reason: 1119 reason = self.config.CONFIG_DIR_IMAGE 1120 1121 if self.storage_browser is None: 1122 self.storage_browser = vmmStorageBrowser(self.conn) 1123 1124 self.storage_browser.set_finish_cb(callback) 1125 self.storage_browser.set_browse_reason(reason) 1126 self.storage_browser.show(self.topwin) 1127 1128 def _inspection_refresh_clicked_cb(self, src): 1129 from ..lib.inspection import vmmInspection 1130 inspection = vmmInspection.get_instance() 1131 if inspection: 1132 inspection.vm_refresh(self.vm) 1133 1134 def _os_list_name_selected_cb(self, src, osobj): 1135 self._enable_apply(EDIT_OS_NAME) 1136 1137 def _curmem_changed_cb(self, src): 1138 self._enable_apply(EDIT_MEM) 1139 maxadj = self.widget("mem-maxmem") 1140 mem = uiutil.spin_get_helper(self.widget("mem-memory")) 1141 1142 if maxadj.get_value() < mem: 1143 maxadj.set_value(mem) 1144 1145 ignore, upper = maxadj.get_range() 1146 maxadj.set_range(mem, upper) 1147 1148 def _config_vcpus_changed_cb(self, src): 1149 self._enable_apply(EDIT_VCPUS) 1150 1151 conn = self.vm.conn 1152 host_active_count = conn.host_active_processor_count() 1153 cur = self._get_config_vcpus() 1154 1155 # Warn about overcommit 1156 warn = bool(cur > host_active_count) 1157 self.widget("cpu-vcpus-warn-box").set_visible(warn) 1158 1159 def _cpu_copy_host_clicked_cb(self, src): 1160 uiutil.set_grid_row_visible( 1161 self.widget("cpu-model"), not src.get_active()) 1162 uiutil.set_grid_row_visible( 1163 self.widget("cpu-secure"), not src.get_active()) 1164 self._enable_apply(EDIT_CPU) 1165 1166 def _sync_cpu_topology_ui(self): 1167 manual_top = self.widget("cpu-topology-table").is_sensitive() 1168 self.widget("cpu-vcpus").set_sensitive(not manual_top) 1169 1170 if manual_top: 1171 cores = uiutil.spin_get_helper(self.widget("cpu-cores")) or 1 1172 sockets = uiutil.spin_get_helper(self.widget("cpu-sockets")) or 1 1173 threads = uiutil.spin_get_helper(self.widget("cpu-threads")) or 1 1174 total = cores * sockets * threads 1175 if uiutil.spin_get_helper(self.widget("cpu-vcpus")) > total: 1176 self.widget("cpu-vcpus").set_value(total) 1177 self.widget("cpu-vcpus").set_value(total) 1178 else: 1179 vcpus = uiutil.spin_get_helper(self.widget("cpu-vcpus")) 1180 self.widget("cpu-sockets").set_value(vcpus or 1) 1181 self.widget("cpu-cores").set_value(1) 1182 self.widget("cpu-threads").set_value(1) 1183 1184 self._enable_apply(EDIT_TOPOLOGY) 1185 1186 def _cpu_topology_enable_cb(self, src): 1187 do_enable = src.get_active() 1188 self.widget("cpu-topology-table").set_sensitive(do_enable) 1189 self._sync_cpu_topology_ui() 1190 1191 def _cpu_topology_changed_cb(self, src): 1192 self._sync_cpu_topology_ui() 1193 1194 def _video_model_changed_cb(self, src): 1195 model = uiutil.get_list_selection(self.widget("video-model")) 1196 uiutil.set_grid_row_visible( 1197 self.widget("video-3d"), model == "virtio") 1198 self._enable_apply(EDIT_VIDEO_MODEL) 1199 1200 def _video_3d_toggled_cb(self, src): 1201 self.widget("video-3d").set_inconsistent(False) 1202 self._enable_apply(EDIT_VIDEO_3D) 1203 1204 def _config_bootdev_selected(self): 1205 boot_row = self._get_config_boot_selection() 1206 boot_selection = boot_row and boot_row[BOOT_KEY] 1207 boot_devs = self._get_config_boot_order() 1208 up_widget = self.widget("boot-moveup") 1209 down_widget = self.widget("boot-movedown") 1210 1211 down_widget.set_sensitive(bool(boot_devs and 1212 boot_selection and 1213 boot_selection in boot_devs and 1214 boot_selection != boot_devs[-1])) 1215 up_widget.set_sensitive(bool(boot_devs and boot_selection and 1216 boot_selection in boot_devs and 1217 boot_selection != boot_devs[0])) 1218 1219 def _config_boot_toggled_cb(self, src, index): 1220 model = self.widget("boot-list").get_model() 1221 row = model[index] 1222 1223 row[BOOT_ACTIVE] = not row[BOOT_ACTIVE] 1224 self._config_bootdev_selected() 1225 self._enable_apply(EDIT_BOOTORDER) 1226 1227 def _config_boot_move(self, move_up): 1228 row = self._get_config_boot_selection() 1229 if not row: 1230 return # pragma: no cover 1231 1232 row_key = row[BOOT_KEY] 1233 boot_order = self._get_config_boot_order() 1234 key_idx = boot_order.index(row_key) 1235 if move_up: 1236 new_idx = key_idx - 1 1237 else: 1238 new_idx = key_idx + 1 1239 1240 if new_idx < 0 or new_idx >= len(boot_order): 1241 # Somehow we went out of bounds 1242 return # pragma: no cover 1243 1244 boot_list = self.widget("boot-list") 1245 model = boot_list.get_model() 1246 prev_row = None 1247 for row in model: 1248 # pylint: disable=unsubscriptable-object 1249 if prev_row and prev_row[BOOT_KEY] == row_key: 1250 model.swap(prev_row.iter, row.iter) 1251 break 1252 1253 if row[BOOT_KEY] == row_key and prev_row and move_up: 1254 model.swap(prev_row.iter, row.iter) 1255 break 1256 1257 prev_row = row 1258 1259 boot_list.get_selection().emit("changed") 1260 self._enable_apply(EDIT_BOOTORDER) 1261 1262 def _disk_source_browse_clicked_cb(self, src): 1263 disk = self._get_hw_row()[HW_LIST_COL_DEVICE] 1264 if disk.is_floppy(): 1265 reason = self.config.CONFIG_DIR_FLOPPY_MEDIA 1266 else: 1267 reason = self.config.CONFIG_DIR_ISO_MEDIA 1268 1269 def cb(ignore, path): 1270 self._mediacombo.set_path(path) 1271 self._browse_file(cb, reason=reason) 1272 1273 def _set_network_ip_details(self, net): 1274 ipv4, ipv6 = self.vm.get_ips(net) 1275 label = ipv4 or "" 1276 if ipv6: 1277 if label: 1278 label += "\n" 1279 label += ipv6 1280 self.widget("network-ip").set_text(label or _("Unknown")) 1281 1282 def _refresh_ip(self): 1283 net = self._get_hw_row()[HW_LIST_COL_DEVICE] 1284 self.vm.refresh_ips(net) 1285 self._set_network_ip_details(net) 1286 1287 1288 ################################################## 1289 # Details/Hardware config changes (apply button) # 1290 ################################################## 1291 1292 def _disable_apply(self): 1293 self._active_edits = [] 1294 self.widget("config-apply").set_sensitive(False) 1295 self.widget("config-cancel").set_sensitive(False) 1296 self._xmleditor.details_changed = False 1297 1298 def _enable_apply(self, edittype): 1299 self.widget("config-apply").set_sensitive(True) 1300 self.widget("config-cancel").set_sensitive(True) 1301 if edittype not in self._active_edits: 1302 self._active_edits.append(edittype) 1303 if edittype != EDIT_XML: 1304 self._xmleditor.details_changed = True 1305 1306 def _config_cancel(self, ignore=None): 1307 # Remove current changes and deactivate 'apply' button 1308 self._refresh_page() 1309 1310 def _config_apply(self, row=None): 1311 pagetype = None 1312 dev = None 1313 1314 if not row: 1315 row = self._get_hw_row() 1316 if row: 1317 pagetype = row[HW_LIST_COL_TYPE] 1318 dev = row[HW_LIST_COL_DEVICE] 1319 1320 success = False 1321 try: 1322 if self._edited(EDIT_XML): 1323 if dev: 1324 success = self._apply_xmleditor_device(dev) 1325 else: 1326 success = self._apply_xmleditor_domain() 1327 elif pagetype is HW_LIST_TYPE_GENERAL: 1328 success = self._apply_overview() 1329 elif pagetype is HW_LIST_TYPE_OS: 1330 success = self._apply_os() 1331 elif pagetype is HW_LIST_TYPE_CPU: 1332 success = self._apply_vcpus() 1333 elif pagetype is HW_LIST_TYPE_MEMORY: 1334 success = self._apply_memory() 1335 elif pagetype is HW_LIST_TYPE_BOOT: 1336 success = self._apply_boot_options() 1337 elif pagetype is HW_LIST_TYPE_DISK: 1338 success = self._apply_disk(dev) 1339 elif pagetype is HW_LIST_TYPE_NIC: 1340 success = self._apply_network(dev) 1341 elif pagetype is HW_LIST_TYPE_GRAPHICS: 1342 success = self._apply_graphics(dev) 1343 elif pagetype is HW_LIST_TYPE_SOUND: 1344 success = self._apply_sound(dev) 1345 elif pagetype is HW_LIST_TYPE_VIDEO: 1346 success = self._apply_video(dev) 1347 elif pagetype is HW_LIST_TYPE_WATCHDOG: 1348 success = self._apply_watchdog(dev) 1349 elif pagetype is HW_LIST_TYPE_SMARTCARD: 1350 success = self._apply_smartcard(dev) 1351 elif pagetype is HW_LIST_TYPE_CONTROLLER: 1352 success = self._apply_controller(dev) 1353 elif pagetype is HW_LIST_TYPE_FILESYSTEM: 1354 success = self._apply_filesystem(dev) 1355 elif pagetype is HW_LIST_TYPE_HOSTDEV: 1356 success = self._apply_hostdev(dev) 1357 elif pagetype is HW_LIST_TYPE_TPM: 1358 success = self._apply_tpm(dev) 1359 elif pagetype is HW_LIST_TYPE_VSOCK: 1360 success = self._apply_vsock(dev) 1361 except Exception as e: 1362 self.err.show_err(_("Error applying changes: %s") % e) 1363 1364 if success is not False: 1365 self._disable_apply() 1366 success = True 1367 return success 1368 1369 def _edited(self, pagetype): 1370 return pagetype in self._active_edits 1371 1372 def _change_config(self, cb, cb_kwargs, hotplug_args=None, devobj=None): 1373 return vmmAddHardware.change_config_helper( 1374 cb, cb_kwargs, self.vm, self.err, 1375 hotplug_args=hotplug_args, devobj=devobj) 1376 1377 def _apply_xmleditor_domain(self): 1378 newxml = self._xmleditor.get_xml() 1379 def change_cb(): 1380 return self.vm.define_xml(newxml) 1381 return self._change_config(change_cb, {}) 1382 1383 def _apply_xmleditor_device(self, devobj): 1384 newxml = self._xmleditor.get_xml() 1385 def change_cb(): 1386 return self.vm.replace_device_xml(devobj, newxml) 1387 # By not passing devobj to change_config_helper we are 1388 # explicitly opting out of attempting device hotplug 1389 return self._change_config(change_cb, {}) 1390 1391 def _apply_overview(self): 1392 kwargs = {} 1393 hotplug_args = {} 1394 1395 if self._edited(EDIT_TITLE): 1396 kwargs["title"] = self.widget("overview-title").get_text() 1397 hotplug_args["title"] = kwargs["title"] 1398 1399 if self._edited(EDIT_FIRMWARE): 1400 kwargs["loader"] = uiutil.get_list_selection( 1401 self.widget("overview-firmware"), column=1) 1402 1403 if self._edited(EDIT_MACHTYPE): 1404 if self.widget("overview-chipset").is_visible(): 1405 kwargs["machine"] = uiutil.get_list_selection( 1406 self.widget("overview-chipset"), column=1) 1407 else: 1408 kwargs["machine"] = uiutil.get_list_selection( 1409 self.widget("machine-type")) 1410 1411 if self._edited(EDIT_DESC): 1412 desc_widget = self.widget("overview-description") 1413 kwargs["description"] = ( 1414 desc_widget.get_buffer().get_property("text") or "") 1415 hotplug_args["description"] = kwargs["description"] 1416 1417 # This needs to be last 1418 if self._edited(EDIT_NAME): 1419 # Renaming is pretty convoluted, so do it here synchronously 1420 self.vm.rename_domain(self.widget("overview-name").get_text()) 1421 1422 if not kwargs and not hotplug_args: 1423 # Saves some useless redefine attempts 1424 return 1425 1426 return self._change_config( 1427 self.vm.define_overview, kwargs, 1428 hotplug_args=hotplug_args) 1429 1430 def _apply_os(self): 1431 kwargs = {} 1432 1433 if self._edited(EDIT_OS_NAME): 1434 osobj = self._os_list.get_selected_os() 1435 kwargs["os_name"] = osobj and osobj.name or "generic" 1436 1437 return self._change_config(self.vm.define_os, kwargs) 1438 1439 def _apply_vcpus(self): 1440 kwargs = {} 1441 1442 if self._edited(EDIT_VCPUS): 1443 kwargs["vcpus"] = self._get_config_vcpus() 1444 1445 if self._edited(EDIT_CPU): 1446 kwargs["model"] = self._get_config_cpu_model() 1447 kwargs["secure"] = self.widget("cpu-secure").get_active() 1448 1449 if self._edited(EDIT_TOPOLOGY): 1450 do_top = self.widget("cpu-topology-enable").get_active() 1451 kwargs["clear_topology"] = not do_top 1452 kwargs["sockets"] = self.widget("cpu-sockets").get_value() 1453 kwargs["cores"] = self.widget("cpu-cores").get_value() 1454 kwargs["threads"] = self.widget("cpu-threads").get_value() 1455 1456 return self._change_config(self.vm.define_cpu, kwargs) 1457 1458 def _apply_memory(self): 1459 kwargs = {} 1460 hotplug_args = {} 1461 1462 if self._edited(EDIT_MEM): 1463 maxmem = uiutil.spin_get_helper(self.widget("mem-maxmem")) 1464 curmem = uiutil.spin_get_helper(self.widget("mem-memory")) 1465 curmem = int(curmem) * 1024 1466 maxmem = int(maxmem) * 1024 1467 1468 kwargs["memory"] = curmem 1469 kwargs["maxmem"] = maxmem 1470 hotplug_args["memory"] = kwargs["memory"] 1471 hotplug_args["maxmem"] = kwargs["maxmem"] 1472 1473 return self._change_config( 1474 self.vm.define_memory, kwargs, 1475 hotplug_args=hotplug_args) 1476 1477 def _apply_boot_options(self): 1478 kwargs = {} 1479 1480 if self._edited(EDIT_AUTOSTART): 1481 auto = self.widget("boot-autostart") 1482 try: 1483 self.vm.set_autostart(auto.get_active()) 1484 except Exception as e: # pragma: no cover 1485 self.err.show_err( 1486 (_("Error changing autostart value: %s") % str(e))) 1487 return False 1488 1489 if self._edited(EDIT_BOOTORDER): 1490 kwargs["boot_order"] = self._get_config_boot_order() 1491 1492 if self._edited(EDIT_BOOTMENU): 1493 kwargs["boot_menu"] = self.widget("boot-menu").get_active() 1494 1495 if self._edited(EDIT_KERNEL): 1496 kwargs["kernel"] = self._get_text("boot-kernel", checksens=True) 1497 kwargs["initrd"] = self._get_text("boot-initrd", checksens=True) 1498 kwargs["dtb"] = self._get_text("boot-dtb", checksens=True) 1499 kwargs["kernel_args"] = self._get_text("boot-kernel-args", 1500 checksens=True) 1501 1502 if kwargs["initrd"] and not kwargs["kernel"]: 1503 msg = _("Cannot set initrd without specifying a kernel path") 1504 return self.err.val_err(msg) 1505 if kwargs["kernel_args"] and not kwargs["kernel"]: 1506 msg = _("Cannot set kernel arguments without specifying a kernel path") 1507 return self.err.val_err(msg) 1508 1509 if self._edited(EDIT_INIT): 1510 kwargs["init"] = self._get_text("boot-init-path") 1511 kwargs["initargs"] = self._get_text("boot-init-args") or "" 1512 if not kwargs["init"]: 1513 return self.err.val_err(_("An init path must be specified")) 1514 1515 return self._change_config(self.vm.define_boot, kwargs) 1516 1517 1518 def _apply_disk(self, devobj): 1519 kwargs = {} 1520 1521 if self._edited(EDIT_DISK_PATH): 1522 path = self._mediacombo.get_path() 1523 1524 names = virtinst.DeviceDisk.path_in_use_by(devobj.conn, path) 1525 if names: 1526 msg = (_("Disk '%(path)s' is already in use by other " 1527 "guests %(names)s") % 1528 {"path": path, "names": names}) 1529 res = self.err.yes_no(msg, 1530 _("Do you really want to use the disk?")) 1531 if not res: 1532 return False 1533 1534 vmmAddStorage.check_path_search(self, self.conn, path) 1535 kwargs["path"] = path or None 1536 1537 if self._edited(EDIT_DISK): 1538 vals = self._addstorage.get_values() 1539 kwargs.update(vals) 1540 1541 if self._edited(EDIT_DISK_BUS): 1542 kwargs["bus"] = uiutil.get_list_selection( 1543 self.widget("disk-bus")) 1544 1545 return self._change_config( 1546 self.vm.define_disk, kwargs, devobj=devobj) 1547 1548 def _apply_sound(self, devobj): 1549 kwargs = {} 1550 1551 if self._edited(EDIT_SOUND_MODEL): 1552 model = uiutil.get_list_selection(self.widget("sound-model")) 1553 if model: 1554 kwargs["model"] = model 1555 1556 return self._change_config( 1557 self.vm.define_sound, kwargs, devobj=devobj) 1558 1559 def _apply_smartcard(self, devobj): 1560 kwargs = {} 1561 1562 if self._edited(EDIT_SMARTCARD_MODE): 1563 model = uiutil.get_list_selection(self.widget("smartcard-mode")) 1564 if model: 1565 kwargs["model"] = model 1566 1567 return self._change_config( 1568 self.vm.define_smartcard, kwargs, devobj=devobj) 1569 1570 def _apply_network(self, devobj): 1571 kwargs = {} 1572 1573 if self._edited(EDIT_NET_MODEL): 1574 model = uiutil.get_list_selection(self.widget("network-model")) 1575 kwargs["model"] = model 1576 1577 if self._edited(EDIT_NET_SOURCE): 1578 (kwargs["ntype"], kwargs["source"], kwargs["mode"]) = ( 1579 self.netlist.get_network_selection()) 1580 1581 if self._edited(EDIT_NET_MAC): 1582 kwargs["macaddr"] = self.widget("network-mac-entry").get_text() 1583 virtinst.DeviceInterface.check_mac_in_use( 1584 self.conn.get_backend(), kwargs["macaddr"]) 1585 1586 if self._edited(EDIT_NET_LINKSTATE): 1587 kwargs["linkstate"] = self.widget("network-link-state-checkbox").get_active() 1588 1589 return self._change_config( 1590 self.vm.define_network, kwargs, devobj=devobj) 1591 1592 def _apply_graphics(self, devobj): 1593 kwargs = {} 1594 if self._edited(EDIT_GFX): 1595 kwargs = self.gfxdetails.get_values() 1596 1597 return self._change_config( 1598 self.vm.define_graphics, kwargs, devobj=devobj) 1599 1600 def _apply_video(self, devobj): 1601 kwargs = {} 1602 1603 if self._edited(EDIT_VIDEO_MODEL): 1604 model = uiutil.get_list_selection(self.widget("video-model")) 1605 if model: 1606 kwargs["model"] = model 1607 1608 if self._edited(EDIT_VIDEO_3D): 1609 kwargs["accel3d"] = self.widget("video-3d").get_active() 1610 1611 return self._change_config( 1612 self.vm.define_video, kwargs, devobj=devobj) 1613 1614 def _apply_controller(self, devobj): 1615 kwargs = {} 1616 1617 if self._edited(EDIT_CONTROLLER_MODEL): 1618 model = uiutil.get_list_selection(self.widget("controller-model")) 1619 kwargs["model"] = model 1620 1621 return self._change_config( 1622 self.vm.define_controller, kwargs, devobj=devobj) 1623 1624 def _apply_watchdog(self, devobj): 1625 kwargs = {} 1626 1627 if self._edited(EDIT_WATCHDOG_MODEL): 1628 kwargs["model"] = uiutil.get_list_selection( 1629 self.widget("watchdog-model")) 1630 1631 if self._edited(EDIT_WATCHDOG_ACTION): 1632 kwargs["action"] = uiutil.get_list_selection( 1633 self.widget("watchdog-action")) 1634 1635 return self._change_config( 1636 self.vm.define_watchdog, kwargs, devobj=devobj) 1637 1638 def _apply_filesystem(self, devobj): 1639 kwargs = {} 1640 1641 if self._edited(EDIT_FS): 1642 kwargs["newdev"] = self.fsDetails.update_device(devobj) 1643 1644 return self._change_config( 1645 self.vm.define_filesystem, kwargs, devobj=devobj) 1646 1647 def _apply_hostdev(self, devobj): 1648 kwargs = {} 1649 1650 if self._edited(EDIT_HOSTDEV_ROMBAR): 1651 kwargs["rom_bar"] = self.widget("hostdev-rombar").get_active() 1652 1653 return self._change_config( 1654 self.vm.define_hostdev, kwargs, devobj=devobj) 1655 1656 def _apply_tpm(self, devobj): 1657 kwargs = {} 1658 1659 if self._edited(EDIT_TPM_MODEL): 1660 model = uiutil.get_list_selection(self.widget("tpm-model")) 1661 kwargs["model"] = model 1662 1663 return self._change_config( 1664 self.vm.define_tpm, kwargs, devobj=devobj) 1665 1666 def _apply_vsock(self, devobj): 1667 auto_cid, cid = self.vsockdetails.get_values() 1668 1669 kwargs = {} 1670 1671 if self._edited(EDIT_VSOCK_AUTO): 1672 kwargs["auto_cid"] = auto_cid 1673 if self._edited(EDIT_VSOCK_CID): 1674 kwargs["cid"] = cid 1675 1676 return self._change_config( 1677 self.vm.define_vsock, kwargs, devobj=devobj) 1678 1679 ########################### 1680 # Details page refreshers # 1681 ########################### 1682 1683 def _refresh_page(self): 1684 row = self._get_hw_row() 1685 if not row: 1686 return # pragma: no cover 1687 1688 pagetype = row[HW_LIST_COL_TYPE] 1689 1690 self.widget("config-remove").set_sensitive(True) 1691 self.widget("config-remove").set_tooltip_text( 1692 _("Remove this device from the virtual machine")) 1693 1694 try: 1695 dev = row[HW_LIST_COL_DEVICE] 1696 if dev: 1697 self._xmleditor.set_xml(_unindent_device_xml(dev.get_xml())) 1698 else: 1699 self._xmleditor.set_xml_from_libvirtobject(self.vm) 1700 1701 if pagetype == HW_LIST_TYPE_GENERAL: 1702 self._refresh_overview_page() 1703 elif pagetype == HW_LIST_TYPE_OS: 1704 self._refresh_os_page() 1705 elif pagetype == HW_LIST_TYPE_STATS: 1706 self._refresh_stats_page() 1707 elif pagetype == HW_LIST_TYPE_CPU: 1708 self._refresh_config_cpu() 1709 elif pagetype == HW_LIST_TYPE_MEMORY: 1710 self._refresh_config_memory() 1711 elif pagetype == HW_LIST_TYPE_BOOT: 1712 self._refresh_boot_page() 1713 elif pagetype == HW_LIST_TYPE_DISK: 1714 self._refresh_disk_page(dev) 1715 elif pagetype == HW_LIST_TYPE_NIC: 1716 self._refresh_network_page(dev) 1717 elif pagetype == HW_LIST_TYPE_INPUT: 1718 self._refresh_input_page(dev) 1719 elif pagetype == HW_LIST_TYPE_GRAPHICS: 1720 self._refresh_graphics_page(dev) 1721 elif pagetype == HW_LIST_TYPE_SOUND: 1722 self._refresh_sound_page(dev) 1723 elif pagetype == HW_LIST_TYPE_CHAR: 1724 self._refresh_char_page(dev) 1725 elif pagetype == HW_LIST_TYPE_HOSTDEV: 1726 self._refresh_hostdev_page(dev) 1727 elif pagetype == HW_LIST_TYPE_VIDEO: 1728 self._refresh_video_page(dev) 1729 elif pagetype == HW_LIST_TYPE_WATCHDOG: 1730 self._refresh_watchdog_page(dev) 1731 elif pagetype == HW_LIST_TYPE_CONTROLLER: 1732 self._refresh_controller_page(dev) 1733 elif pagetype == HW_LIST_TYPE_FILESYSTEM: 1734 self._refresh_filesystem_page(dev) 1735 elif pagetype == HW_LIST_TYPE_SMARTCARD: 1736 self._refresh_smartcard_page(dev) 1737 elif pagetype == HW_LIST_TYPE_REDIRDEV: 1738 self._refresh_redir_page(dev) 1739 elif pagetype == HW_LIST_TYPE_TPM: 1740 self._refresh_tpm_page(dev) 1741 elif pagetype == HW_LIST_TYPE_RNG: 1742 self._refresh_rng_page(dev) 1743 elif pagetype == HW_LIST_TYPE_PANIC: 1744 self._refresh_panic_page(dev) 1745 elif pagetype == HW_LIST_TYPE_VSOCK: 1746 self._refresh_vsock_page(dev) 1747 except Exception as e: # pragma: no cover 1748 self.err.show_err(_("Error refreshing hardware page: %s") % str(e)) 1749 # Don't return, we want the rest of the bits to run regardless 1750 1751 self._disable_apply() 1752 rem = pagetype in remove_pages 1753 self.widget("config-remove").set_visible(rem) 1754 self.widget("hw-panel").set_current_page(pagetype) 1755 1756 def _refresh_overview_page(self): 1757 # Basic details 1758 self.widget("overview-name").set_text(self.vm.get_name()) 1759 self.widget("overview-uuid").set_text(self.vm.get_uuid()) 1760 desc = self.vm.get_description() or "" 1761 desc_widget = self.widget("overview-description") 1762 desc_widget.get_buffer().set_text(desc) 1763 1764 title = self.vm.get_title() 1765 self.widget("overview-title").set_text(title or "") 1766 1767 # Hypervisor Details 1768 self.widget("overview-hv").set_text(self.vm.get_pretty_hv_type()) 1769 arch = self.vm.get_arch() or _("Unknown") 1770 emu = self.vm.get_emulator() or _("None") 1771 self.widget("overview-arch").set_text(arch) 1772 self.widget("overview-emulator").set_text(emu) 1773 1774 # Firmware 1775 domcaps = self.vm.get_domain_capabilities() 1776 if self.vm.get_xmlobj().os.firmware == "efi": 1777 firmware = 'UEFI' 1778 else: 1779 firmware = domcaps.label_for_firmware_path( 1780 self.vm.get_xmlobj().os.loader) 1781 if self.widget("overview-firmware").is_visible(): 1782 uiutil.set_list_selection( 1783 self.widget("overview-firmware"), firmware) 1784 elif self.widget("overview-firmware-label").is_visible(): 1785 self.widget("overview-firmware-label").set_text(firmware) 1786 1787 # Machine settings 1788 machtype = self.vm.get_machtype() or _("Unknown") 1789 self.widget("machine-type-label").set_text(machtype) 1790 if self.widget("machine-type").is_visible(): 1791 uiutil.set_list_selection( 1792 self.widget("machine-type"), machtype) 1793 1794 # Chipset 1795 chipset = _chipset_label_from_machine(machtype) 1796 self.widget("overview-chipset-label").set_text(chipset) 1797 if self.widget("overview-chipset").is_visible(): 1798 uiutil.set_list_selection( 1799 self.widget("overview-chipset"), chipset) 1800 1801 def _refresh_os_page(self): 1802 self._os_list.select_os(self.vm.xmlobj.osinfo) 1803 1804 inspection_supported = self.config.inspection_supported() 1805 uiutil.set_grid_row_visible(self.widget("details-overview-error"), 1806 bool(self.vm.inspection.errorstr)) 1807 if self.vm.inspection.errorstr: 1808 self.widget("details-overview-error").set_text( 1809 self.vm.inspection.errorstr) 1810 inspection_supported = False 1811 1812 self.widget("details-inspection-apps").set_visible(inspection_supported) 1813 self.widget("details-inspection-refresh").set_visible( 1814 inspection_supported) 1815 if not inspection_supported: 1816 return 1817 1818 # Applications (also inspection data) 1819 apps = self.vm.inspection.applications or [] 1820 apps_list = self.widget("inspection-apps") 1821 apps_model = apps_list.get_model() 1822 apps_model.clear() 1823 for app in apps: 1824 name = "" 1825 if app.display_name: 1826 name = app.display_name 1827 elif app.name: 1828 name = app.name 1829 version = "" 1830 if app.epoch > 0: 1831 version += str(app.epoch) + ":" 1832 if app.version: 1833 version += app.version 1834 if app.release: 1835 version += "-" + app.release 1836 summary = "" 1837 if app.summary: 1838 summary = app.summary 1839 elif app.description: 1840 summary = app.description 1841 pos = summary.find("\n") 1842 if pos > -1: 1843 summary = _("%(summary)s ...") % { 1844 "summary": summary[0:pos] 1845 } 1846 1847 apps_model.append([name, version, summary]) 1848 1849 def _refresh_stats_page(self): 1850 def _multi_color(text1, text2): 1851 return ('<span color="#82003B">%s</span> ' 1852 '<span color="#295C45">%s</span>' % (text1, text2)) 1853 def _dsk_rx_tx_text(rx, tx, unit): 1854 opts = {"received": rx, "transferred": tx, "units": unit} 1855 return _multi_color(_("%(received)d %(units)s read") % opts, 1856 _("%(transferred)d %(units)s write") % opts) 1857 def _net_rx_tx_text(rx, tx, unit): 1858 opts = {"received": rx, "transferred": tx, "units": unit} 1859 return _multi_color(_("%(received)d %(units)s in") % opts, 1860 _("%(transferred)d %(units)s out") % opts) 1861 1862 cpu_txt = _("Disabled") 1863 mem_txt = _("Disabled") 1864 dsk_txt = _("Disabled") 1865 net_txt = _("Disabled") 1866 1867 if self.config.get_stats_enable_cpu_poll(): 1868 cpu_txt = "%d %%" % self.vm.guest_cpu_time_percentage() 1869 1870 if self.config.get_stats_enable_memory_poll(): 1871 cur_vm_memory = self.vm.stats_memory() 1872 vm_memory = self.vm.xmlobj.memory 1873 mem_txt = _("%(current-memory)s of %(total-memory)s") % { 1874 "current-memory": uiutil.pretty_mem(cur_vm_memory), 1875 "total-memory": uiutil.pretty_mem(vm_memory) 1876 } 1877 1878 if self.config.get_stats_enable_disk_poll(): 1879 dsk_txt = _dsk_rx_tx_text(self.vm.disk_read_rate(), 1880 self.vm.disk_write_rate(), "KiB/s") 1881 1882 if self.config.get_stats_enable_net_poll(): 1883 net_txt = _net_rx_tx_text(self.vm.network_rx_rate(), 1884 self.vm.network_tx_rate(), "KiB/s") 1885 1886 self.widget("overview-cpu-usage-text").set_text(cpu_txt) 1887 self.widget("overview-memory-usage-text").set_text(mem_txt) 1888 self.widget("overview-network-traffic-text").set_markup(net_txt) 1889 self.widget("overview-disk-usage-text").set_markup(dsk_txt) 1890 1891 self._graph_cpu.set_property("data_array", 1892 self.vm.guest_cpu_time_vector()) 1893 self._graph_memory.set_property("data_array", 1894 self.vm.stats_memory_vector()) 1895 1896 d1, d2 = self.vm.disk_io_vectors() 1897 self._graph_disk.set_property("data_array", d1 + d2) 1898 1899 n1, n2 = self.vm.network_traffic_vectors() 1900 self._graph_network.set_property("data_array", n1 + n2) 1901 1902 def _cpu_secure_is_available(self): 1903 domcaps = self.vm.get_domain_capabilities() 1904 features = domcaps.get_cpu_security_features() 1905 return self.vm.get_xmlobj().os.is_x86() and len(features) > 0 1906 1907 def _refresh_config_cpu(self): 1908 # Set topology first, because it impacts vcpus values 1909 cpu = self.vm.xmlobj.cpu 1910 show_top = cpu.has_topology() 1911 self.widget("cpu-topology-enable").set_active(show_top) 1912 1913 sockets = cpu.topology.sockets or 1 1914 cores = cpu.topology.cores or 1 1915 threads = cpu.topology.threads or 1 1916 1917 self.widget("cpu-sockets").set_value(sockets) 1918 self.widget("cpu-cores").set_value(cores) 1919 self.widget("cpu-threads").set_value(threads) 1920 if show_top: 1921 self.widget("cpu-topology-expander").set_expanded(True) 1922 1923 host_active_count = self.vm.conn.host_active_processor_count() 1924 vcpus = self.vm.xmlobj.vcpus 1925 1926 self.widget("cpu-vcpus").set_value(int(vcpus)) 1927 self.widget("state-host-cpus").set_text(str(host_active_count)) 1928 1929 # Trigger this again to make sure vcpus is correct 1930 self._sync_cpu_topology_ui() 1931 1932 # Warn about overcommit 1933 warn = bool(self._get_config_vcpus() > host_active_count) 1934 self.widget("cpu-vcpus-warn-box").set_visible(warn) 1935 1936 # CPU model config 1937 model = cpu.model or None 1938 if not model: 1939 if cpu.mode == "host-model" or cpu.mode == "host-passthrough": 1940 model = cpu.mode 1941 1942 if model: 1943 self.widget("cpu-model").get_child().set_text(model) 1944 else: 1945 uiutil.set_list_selection( 1946 self.widget("cpu-model"), 1947 virtinst.DomainCpu.SPECIAL_MODE_HV_DEFAULT, column=2) 1948 1949 is_host = (cpu.mode == "host-model") 1950 self.widget("cpu-copy-host").set_active(bool(is_host)) 1951 self._cpu_copy_host_clicked_cb(self.widget("cpu-copy-host")) 1952 1953 if not self._cpu_secure_is_available(): 1954 self.widget("cpu-secure").set_sensitive(False) 1955 self.widget("cpu-secure").set_tooltip_text( 1956 "No security features to copy, the host is missing " 1957 "security patches or the host CPU is not vulnerable.") 1958 1959 cpu.check_security_features(self.vm.get_xmlobj()) 1960 self.widget("cpu-secure").set_active(cpu.secure) 1961 1962 def _refresh_config_memory(self): 1963 host_mem_widget = self.widget("state-host-memory") 1964 host_mem = self.vm.conn.host_memory_size() // 1024 1965 vm_cur_mem = self.vm.xmlobj.currentMemory / 1024.0 1966 vm_max_mem = self.vm.xmlobj.memory / 1024.0 1967 1968 host_mem_widget.set_text("%d MiB" % (int(round(host_mem)))) 1969 1970 curmem = self.widget("mem-memory") 1971 maxmem = self.widget("mem-maxmem") 1972 curmem.set_value(int(round(vm_cur_mem))) 1973 maxmem.set_value(int(round(vm_max_mem))) 1974 1975 def _refresh_disk_page(self, disk): 1976 path = disk.get_source_path() 1977 devtype = disk.device 1978 bus = disk.bus 1979 1980 size = "-" 1981 if path: 1982 size = _("Unknown") 1983 vol = self.conn.get_vol_by_path(path) 1984 if vol: 1985 size = vol.get_pretty_capacity() 1986 1987 pretty_name = self._get_hw_row_label_for_device(disk) 1988 1989 self.widget("disk-target-type").set_text(pretty_name) 1990 self.widget("disk-size").set_text(size) 1991 1992 vmmAddHardware.populate_disk_bus_combo(self.vm, devtype, 1993 self.widget("disk-bus").get_model()) 1994 uiutil.set_list_selection(self.widget("disk-bus"), bus) 1995 self.widget("disk-bus-label").set_text( 1996 vmmAddHardware.disk_pretty_bus(bus) or "-") 1997 1998 is_removable = disk.is_cdrom() or disk.is_floppy() 1999 self.widget("disk-source-box").set_visible(is_removable) 2000 self.widget("disk-source-label").set_visible(not is_removable) 2001 2002 self.widget("disk-source-label").set_text(path or "-") 2003 if is_removable: 2004 self._mediacombo.reset_state(is_floppy=disk.is_floppy()) 2005 self._mediacombo.set_path(path or "") 2006 2007 self._addstorage.set_dev(disk) 2008 2009 def _refresh_network_page(self, net): 2010 vmmAddHardware.populate_network_model_combo( 2011 self.vm, self.widget("network-model")) 2012 uiutil.set_list_selection(self.widget("network-model"), net.model) 2013 2014 macaddr = net.macaddr or "" 2015 if self.widget("network-mac-label").is_visible(): 2016 self.widget("network-mac-label").set_text(macaddr) 2017 else: 2018 self.widget("network-mac-entry").set_text(macaddr) 2019 2020 state = net.link_state == "up" or net.link_state is None 2021 self.widget("network-link-state-checkbox").set_active(state) 2022 self._set_network_ip_details(net) 2023 2024 self.netlist.set_dev(net) 2025 2026 def _refresh_input_page(self, inp): 2027 dev = vmmAddHardware.input_pretty_name(inp.type, inp.bus) 2028 2029 mode = None 2030 if inp.type == "tablet": 2031 mode = _("Absolute Movement") 2032 elif inp.type == "mouse": 2033 mode = _("Relative Movement") 2034 2035 self.widget("input-dev-type").set_text(dev) 2036 self.widget("input-dev-mode").set_text(mode or "") 2037 uiutil.set_grid_row_visible(self.widget("input-dev-mode"), bool(mode)) 2038 2039 if ((inp.type == "mouse" and inp.bus in ("xen", "ps2")) or 2040 (inp.type == "keyboard" and inp.bus in ("xen", "ps2"))): 2041 self._disable_device_remove( 2042 _("Hypervisor does not support removing this device")) 2043 2044 def _refresh_graphics_page(self, gfx): 2045 pretty_type = vmmGraphicsDetails.graphics_pretty_type_simple(gfx.type) 2046 title = (_("%(graphicstype)s Server") % {"graphicstype": pretty_type}) 2047 self.gfxdetails.set_dev(gfx) 2048 self.widget("graphics-title").set_markup("<b>%s</b>" % title) 2049 2050 def _refresh_sound_page(self, sound): 2051 uiutil.set_list_selection(self.widget("sound-model"), sound.model) 2052 2053 def _refresh_smartcard_page(self, sc): 2054 uiutil.set_list_selection(self.widget("smartcard-mode"), sc.mode) 2055 2056 def _refresh_redir_page(self, rd): 2057 address = None 2058 if rd.type == 'tcp': 2059 address = "%s:%s" % (rd.source.host, rd.source.service) 2060 2061 title = self._get_hw_row_label_for_device(rd) 2062 self.widget("redir-title").set_markup(title) 2063 self.widget("redir-type").set_text( 2064 vmmAddHardware.redirdev_pretty_type(rd.type)) 2065 2066 self.widget("redir-address").set_text(address or "") 2067 uiutil.set_grid_row_visible( 2068 self.widget("redir-address"), bool(address)) 2069 2070 def _refresh_tpm_page(self, tpmdev): 2071 def show_ui(widgetname, val): 2072 doshow = bool(val) 2073 uiutil.set_grid_row_visible(self.widget(widgetname), doshow) 2074 self.widget(widgetname).set_text(val or "-") 2075 2076 dev_type = tpmdev.type 2077 self.widget("tpm-dev-type").set_text( 2078 vmmAddHardware.tpm_pretty_type(dev_type)) 2079 2080 vmmAddHardware.populate_tpm_model_combo( 2081 self.vm, self.widget("tpm-model"), tpmdev.version) 2082 uiutil.set_list_selection(self.widget("tpm-model"), tpmdev.model) 2083 2084 # Device type specific properties, only show if apply to the cur dev 2085 show_ui("tpm-device-path", tpmdev.device_path) 2086 show_ui("tpm-version", tpmdev.version) 2087 2088 def _refresh_panic_page(self, dev): 2089 model = dev.model or "isa" 2090 pmodel = vmmAddHardware.panic_pretty_model(model) 2091 self.widget("panic-model").set_text(pmodel) 2092 2093 def _refresh_rng_page(self, dev): 2094 is_random = dev.backend_model == "random" 2095 uiutil.set_grid_row_visible(self.widget("rng-device"), is_random) 2096 2097 self.widget("rng-type").set_text( 2098 vmmAddHardware.rng_pretty_type(dev.backend_model)) 2099 self.widget("rng-device").set_text(dev.device or "") 2100 2101 def _refresh_vsock_page(self, dev): 2102 self.vsockdetails.set_dev(dev) 2103 2104 def _refresh_char_page(self, chardev): 2105 char_type = chardev.DEVICE_TYPE 2106 target_port = chardev.target_port 2107 dev_type = chardev.type or "pty" 2108 primary = self.vm.serial_is_console_dup(chardev) 2109 show_target_type = not (char_type in ["serial", "parallel"]) 2110 2111 if char_type == "serial": 2112 typelabel = _("Serial Device") 2113 elif char_type == "parallel": 2114 typelabel = _("Parallel Device") 2115 elif char_type == "console": 2116 typelabel = _("Console Device") 2117 elif char_type == "channel": 2118 typelabel = _("Channel Device") 2119 else: # pragma: no cover 2120 typelabel = _("%s Device") % char_type.capitalize() 2121 2122 if (target_port is not None and 2123 chardev.DEVICE_TYPE == "console"): 2124 typelabel += " %s" % (int(target_port) + 1) 2125 if target_port is not None and not show_target_type: 2126 typelabel += " %s" % (int(target_port) + 1) 2127 if primary: 2128 typelabel += " (%s)" % _("Primary Console") 2129 typelabel = "<b>%s</b>" % typelabel 2130 2131 self.widget("char-type").set_markup(typelabel) 2132 self.widget("char-dev-type").set_text(dev_type) 2133 2134 def show_ui(widgetname, val): 2135 doshow = bool(val) 2136 uiutil.set_grid_row_visible(self.widget(widgetname), doshow) 2137 self.widget(widgetname).set_text(val or "-") 2138 2139 def build_host_str(host, port): 2140 ret = "" 2141 if host: 2142 ret += host 2143 if port: 2144 ret += ":%s" % str(port) 2145 return ret 2146 2147 connect_str = build_host_str( 2148 chardev.source.connect_host, chardev.source.connect_service) 2149 bind_str = build_host_str( 2150 chardev.source.bind_host, chardev.source.bind_service) 2151 target_type = show_target_type and chardev.target_type or None 2152 2153 # Device type specific properties, only show if apply to the cur dev 2154 show_ui("char-source-host", connect_str) 2155 show_ui("char-bind-host", bind_str) 2156 show_ui("char-source-path", chardev.source.path) 2157 show_ui("char-target-type", target_type) 2158 show_ui("char-target-name", chardev.target_name) 2159 show_ui("char-target-state", chardev.target_state) 2160 2161 def _refresh_hostdev_page(self, hostdev): 2162 rom_bar = hostdev.rom_bar 2163 if rom_bar is None: 2164 rom_bar = True 2165 2166 devtype = hostdev.type 2167 if hostdev.type == 'usb': 2168 devtype = 'usb_device' 2169 2170 nodedev = None 2171 for trydev in self.vm.conn.filter_nodedevs(devtype): 2172 if trydev.xmlobj.compare_to_hostdev(hostdev): 2173 nodedev = trydev 2174 2175 pretty_name = None 2176 if nodedev: 2177 pretty_name = nodedev.pretty_name() 2178 if not pretty_name: 2179 pretty_name = vmmAddHardware.hostdev_pretty_name(hostdev) 2180 2181 uiutil.set_grid_row_visible( 2182 self.widget("hostdev-rombar"), hostdev.type == "pci") 2183 2184 devlabel = "<b>" + _("Physical %s Device") % hostdev.type.upper() + "</b>" 2185 self.widget("hostdev-title").set_markup(devlabel) 2186 self.widget("hostdev-source").set_text(pretty_name) 2187 self.widget("hostdev-rombar").set_active(rom_bar) 2188 2189 def _refresh_video_page(self, vid): 2190 model = vid.model 2191 uiutil.set_list_selection(self.widget("video-model"), model) 2192 2193 if vid.accel3d is None: 2194 self.widget("video-3d").set_inconsistent(True) 2195 else: 2196 self.widget("video-3d").set_active(vid.accel3d) 2197 2198 if (self.vm.xmlobj.devices.graphics and 2199 len(self.vm.xmlobj.devices.video) <= 1): 2200 self._disable_device_remove( 2201 _("Cannot remove last video device while " 2202 "Graphics/Display is attached.")) 2203 2204 def _refresh_watchdog_page(self, watch): 2205 model = watch.model 2206 action = watch.action 2207 2208 uiutil.set_list_selection(self.widget("watchdog-model"), model) 2209 uiutil.set_list_selection(self.widget("watchdog-action"), action) 2210 2211 def _refresh_controller_page(self, controller): 2212 uiutil.set_grid_row_visible(self.widget("device-list-label"), False) 2213 uiutil.set_grid_row_visible(self.widget("controller-device-box"), False) 2214 2215 if self.vm.get_xmlobj().os.is_x86() and controller.type == "usb": 2216 self._disable_device_remove( 2217 _("Hypervisor does not support removing this device")) 2218 if controller.type == "pci": 2219 self._disable_device_remove( 2220 _("Hypervisor does not support removing this device")) 2221 elif controller.type in ["scsi", "sata", "ide", "fdc"]: 2222 model = self.widget("controller-device-list").get_model() 2223 model.clear() 2224 disks = controller.get_attached_devices(self.vm.xmlobj) 2225 for disk in disks: 2226 name = self._get_hw_row_label_for_device(disk) 2227 infoStr = _("%(device)s on %(address)s") % { 2228 "device": name, 2229 "address": disk.address.pretty_desc(), 2230 } 2231 model.append([infoStr]) 2232 self._disable_device_remove( 2233 _("Cannot remove controller while devices are attached.")) 2234 uiutil.set_grid_row_visible( 2235 self.widget("device-list-label"), True) 2236 uiutil.set_grid_row_visible( 2237 self.widget("controller-device-box"), True) 2238 2239 elif controller.type == "virtio-serial": 2240 devs = controller.get_attached_devices(self.vm.xmlobj) 2241 if devs: 2242 self._disable_device_remove( 2243 _("Cannot remove controller while devices are attached.")) 2244 2245 type_label = vmmAddHardware.controller_pretty_desc(controller) 2246 self.widget("controller-type").set_text(type_label) 2247 2248 combo = self.widget("controller-model") 2249 vmmAddHardware.populate_controller_model_combo(combo, controller.type) 2250 show_model = (controller.model or len(combo.get_model()) > 1) 2251 if controller.type == "pci": 2252 show_model = False 2253 uiutil.set_grid_row_visible(combo, show_model) 2254 2255 model = controller.model 2256 if controller.type == "usb" and "xhci" in str(model): 2257 model = "usb3" 2258 uiutil.set_list_selection(self.widget("controller-model"), model) 2259 2260 def _refresh_filesystem_page(self, dev): 2261 self.fsDetails.set_dev(dev) 2262 2263 def _refresh_boot_page(self): 2264 # Refresh autostart 2265 try: 2266 # Older libvirt versions return None if not supported 2267 autoval = self.vm.get_autostart() 2268 except libvirt.libvirtError: # pragma: no cover 2269 autoval = None 2270 2271 # Autostart 2272 autostart_chk = self.widget("boot-autostart") 2273 enable_autostart = (autoval is not None) 2274 autostart_chk.set_sensitive(enable_autostart) 2275 autostart_chk.set_active(enable_autostart and autoval or False) 2276 2277 show_kernel = not self.vm.is_container() 2278 show_init = self.vm.is_container() 2279 show_boot = (not self.vm.is_container() and not self.vm.is_xenpv()) 2280 2281 uiutil.set_grid_row_visible( 2282 self.widget("boot-order-frame"), show_boot) 2283 uiutil.set_grid_row_visible( 2284 self.widget("boot-kernel-expander"), show_kernel) 2285 uiutil.set_grid_row_visible( 2286 self.widget("boot-init-frame"), show_init) 2287 2288 # Kernel/initrd boot 2289 kernel, initrd, dtb, args = self.vm.get_boot_kernel_info() 2290 expand = bool(kernel or dtb or initrd or args) 2291 2292 def keep_text(wname, guestval): 2293 # If the user unsets kernel/initrd by unchecking the 2294 # 'enable kernel boot' box, we keep the previous values cached 2295 # in the text fields to allow easy switching back and forth. 2296 guestval = guestval or "" 2297 if self._get_text(wname) and not guestval: 2298 return 2299 self.widget(wname).set_text(guestval) 2300 2301 keep_text("boot-kernel", kernel) 2302 keep_text("boot-initrd", initrd) 2303 keep_text("boot-dtb", dtb) 2304 keep_text("boot-kernel-args", args) 2305 if expand: 2306 # Only 'expand' if requested, so a refresh doesn't 2307 # magically unexpand the UI the user just touched 2308 self.widget("boot-kernel-expander").set_expanded(True) 2309 self.widget("boot-kernel-enable").set_active(expand) 2310 self.widget("boot-kernel-enable").toggled() 2311 2312 # Only show dtb if it's supported 2313 arch = self.vm.get_arch() or "" 2314 show_dtb = (self._get_text("boot-dtb") or 2315 self.vm.get_hv_type() == "test" or 2316 "arm" in arch or "microblaze" in arch or "ppc" in arch) 2317 self.widget("boot-dtb-label").set_visible(show_dtb) 2318 self.widget("boot-dtb-box").set_visible(show_dtb) 2319 2320 # <init> populate 2321 init, initargs = self.vm.get_init() 2322 self.widget("boot-init-path").set_text(init or "") 2323 self.widget("boot-init-args").set_text(initargs or "") 2324 2325 # Boot menu populate 2326 menu = self.vm.get_boot_menu() or False 2327 self.widget("boot-menu").set_active(menu) 2328 self._refresh_boot_order() 2329 2330 def _make_boot_rows(self): 2331 if not self.vm.can_use_device_boot_order(): 2332 return [ 2333 ["hd", _("Hard Disk"), "drive-harddisk", False, True], 2334 ["cdrom", _("CDROM"), "media-optical", False, True], 2335 ["network", _("Network (PXE)"), "network-idle", False, True], 2336 ["fd", _("Floppy"), "media-floppy", False, True], 2337 ] 2338 2339 ret = [] 2340 for dev in self.vm.get_bootable_devices(): 2341 row = self._get_hw_row_for_device(dev) 2342 if not row: 2343 continue # pragma: no cover 2344 label = row[HW_LIST_COL_LABEL] 2345 icon = row[HW_LIST_COL_ICON_NAME] 2346 2347 ret.append([dev.get_xml_id(), label, icon, False, True]) 2348 2349 if not ret: 2350 ret.append([None, _("No bootable devices"), None, False, False]) 2351 return ret 2352 2353 def _refresh_boot_order(self): 2354 boot_list = self.widget("boot-list") 2355 boot_model = boot_list.get_model() 2356 boot_model.clear() 2357 boot_rows = self._make_boot_rows() 2358 boot_order = self.vm.get_boot_order() 2359 2360 for key in boot_order: 2361 for row in boot_rows[:]: 2362 if key != row[BOOT_KEY]: 2363 continue 2364 2365 row[BOOT_ACTIVE] = True 2366 boot_model.append(row) 2367 boot_rows.remove(row) 2368 break 2369 2370 for row in boot_rows: 2371 boot_model.append(row) 2372 2373 2374 ############################ 2375 # Hardware list population # 2376 ############################ 2377 2378 def _make_hw_list_entry(self, title, page_id, icon_name, devobj=None): 2379 hw_entry = [] 2380 hw_entry.insert(HW_LIST_COL_LABEL, title) 2381 hw_entry.insert(HW_LIST_COL_ICON_NAME, icon_name) 2382 hw_entry.insert(HW_LIST_COL_TYPE, page_id) 2383 hw_entry.insert(HW_LIST_COL_DEVICE, devobj) 2384 hw_entry.insert(HW_LIST_COL_KEY, devobj or title) 2385 return hw_entry 2386 2387 def _init_hw_list(self): 2388 """ 2389 Add the static entries to the hw list, like Overview 2390 """ 2391 hw_list_model = self.widget("hw-list").get_model() 2392 hw_list_model.clear() 2393 2394 def add_hw_list_option(*args, **kwargs): 2395 hw_list_model.append(self._make_hw_list_entry(*args, **kwargs)) 2396 2397 add_hw_list_option(_("Overview"), HW_LIST_TYPE_GENERAL, "computer") 2398 add_hw_list_option(_("OS information"), HW_LIST_TYPE_OS, "computer") 2399 if not self.is_customize_dialog: 2400 add_hw_list_option(_("Performance"), HW_LIST_TYPE_STATS, 2401 _get_performance_icon_name()) 2402 add_hw_list_option(_("CPUs"), HW_LIST_TYPE_CPU, "device_cpu") 2403 add_hw_list_option(_("Memory"), HW_LIST_TYPE_MEMORY, "device_mem") 2404 add_hw_list_option(_("Boot Options"), HW_LIST_TYPE_BOOT, "system-run") 2405 2406 self._repopulate_hw_list() 2407 self._set_hw_selection(0) 2408 2409 def _repopulate_hw_list(self): 2410 """ 2411 Refresh the hardware list entries with the latest VM config 2412 """ 2413 hw_list = self.widget("hw-list") 2414 hw_list_model = hw_list.get_model() 2415 2416 currentDevices = [] 2417 2418 def dev_cmp(origdev, newdev): 2419 if not origdev: 2420 return False 2421 2422 if origdev == newdev: 2423 return True 2424 2425 return origdev.get_xml_id() == newdev.get_xml_id() 2426 2427 def update_hwlist(hwtype, dev, disk_bus_index=None): 2428 """ 2429 See if passed hw is already in list, and if so, update info. 2430 If not in list, add it! 2431 """ 2432 label = _label_for_device(dev, disk_bus_index) 2433 icon = _icon_for_device(dev) 2434 2435 currentDevices.append(dev) 2436 2437 insertAt = 0 2438 for row in hw_list_model: 2439 rowdev = row[HW_LIST_COL_DEVICE] 2440 if dev_cmp(rowdev, dev): 2441 # Update existing HW info 2442 row[HW_LIST_COL_DEVICE] = dev 2443 row[HW_LIST_COL_LABEL] = label 2444 row[HW_LIST_COL_ICON_NAME] = icon 2445 return 2446 2447 if row[HW_LIST_COL_TYPE] <= hwtype: 2448 insertAt += 1 2449 2450 # Add the new HW row 2451 hw_entry = self._make_hw_list_entry(label, hwtype, icon, dev) 2452 hw_list_model.insert(insertAt, hw_entry) 2453 2454 2455 consoles = self.vm.xmlobj.devices.console 2456 serials = self.vm.xmlobj.devices.serial 2457 if serials and consoles and self.vm.serial_is_console_dup(serials[0]): 2458 consoles.pop(0) 2459 2460 disks = self.vm.xmlobj.devices.disk 2461 for dev, _disk_bus_index in _calculate_disk_bus_index(disks): 2462 update_hwlist(HW_LIST_TYPE_DISK, dev, _disk_bus_index) 2463 for dev in self.vm.xmlobj.devices.interface: 2464 update_hwlist(HW_LIST_TYPE_NIC, dev) 2465 for dev in self.vm.xmlobj.devices.input: 2466 update_hwlist(HW_LIST_TYPE_INPUT, dev) 2467 for dev in self.vm.xmlobj.devices.graphics: 2468 update_hwlist(HW_LIST_TYPE_GRAPHICS, dev) 2469 for dev in self.vm.xmlobj.devices.sound: 2470 update_hwlist(HW_LIST_TYPE_SOUND, dev) 2471 for dev in serials: 2472 update_hwlist(HW_LIST_TYPE_CHAR, dev) 2473 for dev in self.vm.xmlobj.devices.parallel: 2474 update_hwlist(HW_LIST_TYPE_CHAR, dev) 2475 for dev in consoles: 2476 update_hwlist(HW_LIST_TYPE_CHAR, dev) 2477 for dev in self.vm.xmlobj.devices.channel: 2478 update_hwlist(HW_LIST_TYPE_CHAR, dev) 2479 for dev in self.vm.xmlobj.devices.hostdev: 2480 update_hwlist(HW_LIST_TYPE_HOSTDEV, dev) 2481 for dev in self.vm.xmlobj.devices.redirdev: 2482 update_hwlist(HW_LIST_TYPE_REDIRDEV, dev) 2483 for dev in self.vm.xmlobj.devices.video: 2484 update_hwlist(HW_LIST_TYPE_VIDEO, dev) 2485 for dev in self.vm.xmlobj.devices.watchdog: 2486 update_hwlist(HW_LIST_TYPE_WATCHDOG, dev) 2487 2488 for dev in self.vm.xmlobj.devices.controller: 2489 # skip USB2 ICH9 companion controllers 2490 if dev.model in ["ich9-uhci1", "ich9-uhci2", "ich9-uhci3"]: 2491 continue 2492 2493 # These are all parts of a default PCIe setup, which we 2494 # condense down to one listing 2495 if dev.model in ["pcie-root-port", "dmi-to-pci-bridge", 2496 "pci-bridge"]: 2497 continue 2498 2499 update_hwlist(HW_LIST_TYPE_CONTROLLER, dev) 2500 2501 for dev in self.vm.xmlobj.devices.filesystem: 2502 update_hwlist(HW_LIST_TYPE_FILESYSTEM, dev) 2503 for dev in self.vm.xmlobj.devices.smartcard: 2504 update_hwlist(HW_LIST_TYPE_SMARTCARD, dev) 2505 for dev in self.vm.xmlobj.devices.tpm: 2506 update_hwlist(HW_LIST_TYPE_TPM, dev) 2507 for dev in self.vm.xmlobj.devices.rng: 2508 update_hwlist(HW_LIST_TYPE_RNG, dev) 2509 for dev in self.vm.xmlobj.devices.panic: 2510 update_hwlist(HW_LIST_TYPE_PANIC, dev) 2511 for dev in self.vm.xmlobj.devices.vsock: 2512 update_hwlist(HW_LIST_TYPE_VSOCK, dev) 2513 2514 devs = list(range(len(hw_list_model))) 2515 devs.reverse() 2516 for i in devs: 2517 _iter = hw_list_model.iter_nth_child(None, i) 2518 olddev = hw_list_model[i][HW_LIST_COL_DEVICE] 2519 2520 # Existing device, don't remove it 2521 if not olddev or olddev in currentDevices: 2522 continue 2523 2524 hw_list_model.remove(_iter) 2525 2526 2527 ################ 2528 # UI listeners # 2529 ################ 2530 2531 def _config_apply_clicked_cb(self, src): 2532 self._config_apply() 2533 def _config_cancel_clicked_cb(self, src): 2534 self._config_cancel() 2535 def _config_remove_clicked_cb(self, src): 2536 self._config_remove() 2537 2538 def _refresh_ip_clicked_cb(self, src): 2539 self._refresh_ip() 2540 2541 def _browse_kernel_clicked_cb(self, src): 2542 def cb(ignore, path): 2543 self.widget("boot-kernel").set_text(path) 2544 self._browse_file(cb) 2545 def _browse_initrd_clicked_cb(self, src): 2546 def cb(ignore, path): 2547 self.widget("boot-initrd").set_text(path) 2548 self._browse_file(cb) 2549 def _browse_dtb_clicked_cb(self, src): 2550 def cb(ignore, path): 2551 self.widget("boot-dtb").set_text(path) 2552 self._browse_file(cb) 2553 2554 def _xmleditor_xml_requested_cb(self, src): 2555 self._refresh_page() 2556 def _xmleditor_xml_reset_cb(self, src): 2557 self._refresh_page() 2558 2559 def _addhw_clicked_cb(self, src): 2560 self._show_addhw() 2561 2562 def _boot_kernel_toggled_cb(self, src): 2563 self.widget("boot-kernel-box").set_sensitive(src.get_active()) 2564 self._enable_apply(EDIT_KERNEL) 2565 def _boot_list_changed_cb(self, src): 2566 self._config_bootdev_selected() 2567 def _boot_moveup_clicked_cb(self, src): 2568 self._config_boot_move(True) 2569 def _boot_movedown_clicked_cb(self, src): 2570 self._config_boot_move(False) 2571 2572 def _vm_inspection_changed_cb(self, vm): 2573 self._refresh_os_page() 2574