2
# Copyright (C) 2006-2008 Red Hat, Inc.
3
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
28
import virtManager.uihelpers as uihelpers
29
from virtManager.storagebrowse import vmmStorageBrowser
30
from virtManager.baseclass import vmmGObjectUI
31
from virtManager.addhardware import vmmAddHardware
32
from virtManager.choosecd import vmmChooseCD
33
from virtManager.console import vmmConsolePages
34
from virtManager.serialcon import vmmSerialConsole
35
from virtManager.graphwidgets import Sparkline
36
from virtManager import util as util
40
# Columns in hw list model
42
HW_LIST_COL_ICON_NAME = 1
43
HW_LIST_COL_ICON_SIZE = 2
45
HW_LIST_COL_DEVICE = 4
47
# Types for the hw list model: numbers specify what order they will be listed
48
HW_LIST_TYPE_GENERAL = 0
49
HW_LIST_TYPE_STATS = 1
51
HW_LIST_TYPE_MEMORY = 3
55
HW_LIST_TYPE_INPUT = 7
56
HW_LIST_TYPE_GRAPHICS = 8
57
HW_LIST_TYPE_SOUND = 9
58
HW_LIST_TYPE_CHAR = 10
59
HW_LIST_TYPE_HOSTDEV = 11
60
HW_LIST_TYPE_VIDEO = 12
61
HW_LIST_TYPE_WATCHDOG = 13
62
HW_LIST_TYPE_CONTROLLER = 14
64
remove_pages = [HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT,
65
HW_LIST_TYPE_GRAPHICS, HW_LIST_TYPE_SOUND, HW_LIST_TYPE_CHAR,
66
HW_LIST_TYPE_HOSTDEV, HW_LIST_TYPE_DISK, HW_LIST_TYPE_VIDEO,
67
HW_LIST_TYPE_WATCHDOG, HW_LIST_TYPE_CONTROLLER]
78
PAGE_DYNAMIC_OFFSET = 2
80
def prettyify_disk_bus(bus):
81
if bus in ["ide", "scsi", "usb"]:
85
return bus.capitalize()
92
def prettyify_disk(devtype, bus, idx):
93
busstr = prettyify_disk_bus(bus) or ""
95
if devtype == "floppy":
98
elif devtype == "cdrom":
101
devstr = devtype.capitalize()
104
ret = "%s %s" % (busstr, devstr)
108
return "%s %s" % (ret, idx)
110
def safeint(val, fmt="%.3d"):
115
return fmt % int(val)
117
def prettyify_bytes(val):
118
if val > (1024 * 1024 * 1024):
119
return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0))
121
return "%2.2f MB" % (val / (1024.0 * 1024.0))
123
def build_hostdev_label(hostdev):
124
# String shown in the devices details section
126
# String shown in the VMs hardware list
130
vendor = hostdev.vendor
131
product = hostdev.product
132
addrbus = hostdev.bus
133
addrdev = hostdev.device
134
addrslt = hostdev.slot
135
addrfun = hostdev.function
136
addrdom = hostdev.domain
139
if val.startswith("0x"):
143
hwlabel = typ.upper()
144
srclabel = typ.upper()
146
if vendor and product:
147
# USB by vendor + product
148
devstr = " %s:%s" % (dehex(vendor), dehex(product))
152
elif addrbus and addrdev:
154
srclabel += (" Bus %s Device %s" %
155
(safeint(addrbus), safeint(addrdev)))
156
hwlabel += " %s:%s" % (safeint(addrbus), safeint(addrdev))
158
elif addrbus and addrslt and addrfun and addrdom:
159
# PCI by bus:slot:function
160
devstr = (" %s:%s:%s.%s" %
161
(dehex(addrdom), dehex(addrbus),
162
dehex(addrslt), dehex(addrfun)))
166
return srclabel, hwlabel
168
def lookup_nodedev(vmmconn, hostdev):
169
def intify(val, do_hex=False):
172
return int(val or '0x00', 16)
178
def attrVal(node, attr):
179
if not hasattr(node, attr):
181
return getattr(node, attr)
183
devtype = hostdev.type
184
vendor_id = hostdev.vendor or -1
185
product_id = hostdev.product or -1
186
device = intify(hostdev.device, True)
187
bus = intify(hostdev.bus, True)
188
domain = intify(hostdev.domain, True)
189
func = intify(hostdev.function, True)
190
slot = intify(hostdev.slot, True)
193
# For USB we want a device, not a bus
195
devtype = 'usb_device'
197
devs = vmmconn.get_devices(devtype, None)
199
# Try to get info from {product|vendor}_id
200
if (attrVal(dev, "product_id") == product_id and
201
attrVal(dev, "vendor_id") == vendor_id):
205
# Try to get info from bus/addr
206
dev_id = intify(attrVal(dev, "device"))
207
bus_id = intify(attrVal(dev, "bus"))
208
dom_id = intify(attrVal(dev, "domain"))
209
func_id = intify(attrVal(dev, "function"))
210
slot_id = intify(attrVal(dev, "slot"))
212
if ((dev_id == device and bus_id == bus) or
213
(dom_id == domain and func_id == func and
214
bus_id == bus and slot_id == slot)):
220
class vmmDetails(vmmGObjectUI):
222
"action-show-console": (gobject.SIGNAL_RUN_FIRST,
223
gobject.TYPE_NONE, (str, str)),
224
"action-save-domain": (gobject.SIGNAL_RUN_FIRST,
225
gobject.TYPE_NONE, (str, str)),
226
"action-destroy-domain": (gobject.SIGNAL_RUN_FIRST,
227
gobject.TYPE_NONE, (str, str)),
228
"action-suspend-domain": (gobject.SIGNAL_RUN_FIRST,
229
gobject.TYPE_NONE, (str, str)),
230
"action-resume-domain": (gobject.SIGNAL_RUN_FIRST,
231
gobject.TYPE_NONE, (str, str)),
232
"action-run-domain": (gobject.SIGNAL_RUN_FIRST,
233
gobject.TYPE_NONE, (str, str)),
234
"action-shutdown-domain": (gobject.SIGNAL_RUN_FIRST,
235
gobject.TYPE_NONE, (str, str)),
236
"action-reboot-domain": (gobject.SIGNAL_RUN_FIRST,
237
gobject.TYPE_NONE, (str, str)),
238
"action-show-help": (gobject.SIGNAL_RUN_FIRST,
239
gobject.TYPE_NONE, [str]),
240
"action-exit-app": (gobject.SIGNAL_RUN_FIRST,
241
gobject.TYPE_NONE, []),
242
"action-view-manager": (gobject.SIGNAL_RUN_FIRST,
243
gobject.TYPE_NONE, []),
244
"action-migrate-domain": (gobject.SIGNAL_RUN_FIRST,
245
gobject.TYPE_NONE, (str, str)),
246
"action-clone-domain": (gobject.SIGNAL_RUN_FIRST,
247
gobject.TYPE_NONE, (str, str)),
248
"details-closed": (gobject.SIGNAL_RUN_FIRST,
249
gobject.TYPE_NONE, ()),
253
def __init__(self, vm, engine, parent=None):
254
vmmGObjectUI.__init__(self, "vmm-details.glade", "vmm-details")
256
self.conn = self.vm.get_connection()
259
self.is_customize_dialog = False
261
self.is_customize_dialog = True
262
# Details window is being abused as a 'configure before install'
263
# dialog, set things as appropriate
264
self.topwin.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
265
self.topwin.set_transient_for(parent)
267
self.window.get_widget("customize-toolbar").show()
268
self.window.get_widget("details-toolbar").hide()
269
self.window.get_widget("details-menubar").hide()
270
pages = self.window.get_widget("details-pages")
271
pages.set_current_page(PAGE_DETAILS)
274
self.serial_tabs = []
275
self.last_console_page = PAGE_CONSOLE
277
self.media_choosers = {"cdrom": None, "floppy": None}
278
self.storage_browser = None
280
self.ignorePause = False
281
self.ignoreDetails = False
282
self._cpu_copy_host = False
284
self.console = vmmConsolePages(self.vm, self.window)
286
# Set default window size
287
w, h = self.vm.get_details_window_size()
288
self.topwin.set_default_size(w or 800, h or 600)
290
self.addhwmenu = None
294
self.serial_popup = None
295
self.serial_copy = None
296
self.serial_paste = None
297
self.serial_close = None
300
self.cpu_usage_graph = None
301
self.memory_usage_graph = None
302
self.disk_io_graph = None
303
self.network_traffic_graph = None
306
self.window.signal_autoconnect({
307
"on_close_details_clicked": self.close,
308
"on_details_menu_close_activate": self.close,
309
"on_vmm_details_delete_event": self.close,
310
"on_vmm_details_configure_event": self.window_resized,
311
"on_details_menu_quit_activate": self.exit_app,
313
"on_control_vm_details_toggled": self.details_console_changed,
314
"on_control_vm_console_toggled": self.details_console_changed,
315
"on_control_run_clicked": self.control_vm_run,
316
"on_control_shutdown_clicked": self.control_vm_shutdown,
317
"on_control_pause_toggled": self.control_vm_pause,
318
"on_control_fullscreen_toggled": self.control_fullscreen,
320
"on_details_customize_finish_clicked": self.close,
322
"on_details_menu_run_activate": self.control_vm_run,
323
"on_details_menu_poweroff_activate": self.control_vm_shutdown,
324
"on_details_menu_reboot_activate": self.control_vm_reboot,
325
"on_details_menu_save_activate": self.control_vm_save,
326
"on_details_menu_destroy_activate": self.control_vm_destroy,
327
"on_details_menu_pause_activate": self.control_vm_pause,
328
"on_details_menu_clone_activate": self.control_vm_clone,
329
"on_details_menu_migrate_activate": self.control_vm_migrate,
330
"on_details_menu_screenshot_activate": self.control_vm_screenshot,
331
"on_details_menu_graphics_activate": self.control_vm_console,
332
"on_details_menu_view_toolbar_activate": self.toggle_toolbar,
333
"on_details_menu_view_manager_activate": self.view_manager,
334
"on_details_menu_view_details_toggled": self.details_console_changed,
335
"on_details_menu_view_console_toggled": self.details_console_changed,
337
"on_details_pages_switch_page": self.switch_page,
339
"on_overview_acpi_changed": self.config_enable_apply,
340
"on_overview_apic_changed": self.config_enable_apply,
341
"on_overview_clock_changed": self.config_enable_apply,
342
"on_security_label_changed": self.security_label_changed,
343
"on_security_type_changed": self.security_type_changed,
345
"on_config_vcpus_changed": self.config_vcpus_changed,
346
"on_config_maxvcpus_changed": self.config_maxvcpus_changed,
347
"on_config_vcpupin_changed": self.config_vcpus_changed,
348
"on_config_vcpupin_generate_clicked": self.config_vcpupin_generate,
349
"on_cpu_model_changed": self.config_enable_apply,
350
"on_cpu_cores_changed": self.config_enable_apply,
351
"on_cpu_sockets_changed": self.config_enable_apply,
352
"on_cpu_threads_changed": self.config_enable_apply,
353
"on_cpu_copy_host_clicked": self.config_cpu_copy_host,
354
"on_cpu_topology_enable_toggled": self.config_cpu_topology_enable,
356
"on_config_memory_changed": self.config_memory_changed,
357
"on_config_maxmem_changed": self.config_maxmem_changed,
359
"on_config_boot_moveup_clicked" : (self.config_boot_move, True),
360
"on_config_boot_movedown_clicked" : (self.config_boot_move,
362
"on_config_autostart_changed": self.config_enable_apply,
363
"on_boot_menu_changed": self.config_enable_apply,
364
"on_boot_kernel_changed": self.config_enable_apply,
365
"on_boot_kernel_initrd_changed": self.config_enable_apply,
366
"on_boot_kernel_args_changed": self.config_enable_apply,
367
"on_boot_kernel_browse_clicked": self.browse_kernel,
368
"on_boot_kernel_initrd_browse_clicked": self.browse_initrd,
370
"on_disk_readonly_changed": self.config_enable_apply,
371
"on_disk_shareable_changed": self.config_enable_apply,
372
"on_disk_cache_combo_changed": self.config_enable_apply,
373
"on_disk_bus_combo_changed": self.config_enable_apply,
374
"on_disk_format_changed": self.config_enable_apply,
376
"on_network_model_combo_changed": self.config_enable_apply,
378
"on_vnc_keymap_combo_changed": self.config_enable_apply,
379
"on_vnc_password_changed": self.config_enable_apply,
381
"on_sound_model_combo_changed": self.config_enable_apply,
383
"on_video_model_combo_changed": self.config_enable_apply,
385
"on_watchdog_model_combo_changed": self.config_enable_apply,
386
"on_watchdog_action_combo_changed": self.config_enable_apply,
388
"on_config_apply_clicked": self.config_apply,
390
"on_details_help_activate": self.show_help,
392
"on_config_cdrom_connect_clicked": self.toggle_storage_media,
393
"on_config_remove_clicked": self.remove_xml_dev,
394
"on_add_hardware_button_clicked": self.add_hardware,
396
"on_hw_list_button_press_event": self.popup_addhw_menu,
398
# Listeners stored in vmmConsolePages
399
"on_details_menu_view_fullscreen_activate": self.console.toggle_fullscreen,
400
"on_details_menu_view_size_to_vm_activate": self.console.size_to_vm,
401
"on_details_menu_view_scale_always_toggled": self.console.set_scale_type,
402
"on_details_menu_view_scale_fullscreen_toggled": self.console.set_scale_type,
403
"on_details_menu_view_scale_never_toggled": self.console.set_scale_type,
405
"on_details_menu_send_cad_activate": self.console.send_key,
406
"on_details_menu_send_cab_activate": self.console.send_key,
407
"on_details_menu_send_caf1_activate": self.console.send_key,
408
"on_details_menu_send_caf2_activate": self.console.send_key,
409
"on_details_menu_send_caf3_activate": self.console.send_key,
410
"on_details_menu_send_caf4_activate": self.console.send_key,
411
"on_details_menu_send_caf5_activate": self.console.send_key,
412
"on_details_menu_send_caf6_activate": self.console.send_key,
413
"on_details_menu_send_caf7_activate": self.console.send_key,
414
"on_details_menu_send_caf8_activate": self.console.send_key,
415
"on_details_menu_send_caf9_activate": self.console.send_key,
416
"on_details_menu_send_caf10_activate": self.console.send_key,
417
"on_details_menu_send_caf11_activate": self.console.send_key,
418
"on_details_menu_send_caf12_activate": self.console.send_key,
419
"on_details_menu_send_printscreen_activate": self.console.send_key,
421
"on_console_auth_password_activate": self.console.auth_login,
422
"on_console_auth_login_clicked": self.console.auth_login,
425
# Deliberately keep all this after signal connection
426
self.vm.connect("status-changed", self.refresh_vm_state)
427
self.vm.connect("config-changed", self.refresh_vm_state)
428
self.vm.connect("resources-sampled", self.refresh_resources)
429
self.window.get_widget("hw-list").get_selection().connect(
432
self.window.get_widget("config-boot-list").get_selection().connect(
434
self.config_bootdev_selected)
436
finish_img = gtk.image_new_from_stock(gtk.STOCK_ADD,
437
gtk.ICON_SIZE_BUTTON)
438
self.window.get_widget("add-hardware-button").set_image(finish_img)
440
self.populate_hw_list()
441
self.repopulate_boot_list()
444
self.refresh_vm_state()
448
vis = self.is_visible()
449
self.topwin.present()
453
self.engine.increment_window_counter()
454
self.refresh_vm_state()
456
def close(self, ignore1=None, ignore2=None):
457
fs = self.window.get_widget("details-menu-view-fullscreen")
461
if not self.is_visible():
465
if self.console.viewer and self.console.viewer.get_widget() and \
466
self.console.viewer.get_widget().flags() & gtk.VISIBLE:
468
self.console.close_viewer()
470
logging.error("Failure when disconnecting from desktop server")
471
self.engine.decrement_window_counter()
473
self.emit("details-closed")
476
def is_visible(self):
477
return bool(self.topwin.flags() & gtk.VISIBLE)
480
##########################
481
# Initialization helpers #
482
##########################
484
def init_menus(self):
485
# Shutdown button menu
486
uihelpers.build_shutdown_button_menu(
487
self.window.get_widget("control-shutdown"),
488
self.control_vm_shutdown,
489
self.control_vm_reboot,
490
self.control_vm_destroy,
491
self.control_vm_save)
493
icon_name = self.config.get_shutdown_icon_name()
494
for name in ["details-menu-shutdown",
495
"details-menu-reboot",
496
"details-menu-poweroff",
497
"details-menu-destroy"]:
498
image = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
499
self.window.get_widget(name).set_image(image)
502
self.addhwmenu = gtk.Menu()
503
addHW = gtk.ImageMenuItem(_("Add Hardware"))
504
addHWImg = gtk.Image()
505
addHWImg.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
506
addHW.set_image(addHWImg)
508
addHW.connect("activate", self.add_hardware)
509
self.addhwmenu.add(addHW)
513
smenu.connect("show", self.populate_serial_menu)
514
self.window.get_widget("details-menu-view-serial-list").set_submenu(smenu)
516
# Don't allowing changing network/disks for Dom0
517
dom0 = self.vm.is_management_domain()
518
self.window.get_widget("add-hardware-button").set_sensitive(not dom0)
520
self.window.get_widget("hw-panel").set_show_tabs(False)
521
self.window.get_widget("details-pages").set_show_tabs(False)
522
self.window.get_widget("console-pages").set_show_tabs(False)
523
self.window.get_widget("details-menu-view-toolbar").set_active(self.config.get_details_show_toolbar())
525
# XXX: Help docs useless/out of date
526
self.window.get_widget("help_menuitem").hide()
528
def init_serial(self):
529
self.serial_popup = gtk.Menu()
531
self.serial_copy = gtk.ImageMenuItem(gtk.STOCK_COPY)
532
self.serial_popup.add(self.serial_copy)
534
self.serial_paste = gtk.ImageMenuItem(gtk.STOCK_PASTE)
535
self.serial_popup.add(self.serial_paste)
537
self.serial_popup.add(gtk.SeparatorMenuItem())
539
self.serial_close = gtk.ImageMenuItem(_("Close tab"))
540
close_image = gtk.Image()
541
close_image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
542
self.serial_close.set_image(close_image)
543
self.serial_popup.add(self.serial_close)
545
def init_graphs(self):
546
graph_table = self.window.get_widget("graph-table")
548
self.cpu_usage_graph = Sparkline()
549
self.cpu_usage_graph.set_property("reversed", True)
550
graph_table.attach(self.cpu_usage_graph, 1, 2, 0, 1)
552
self.memory_usage_graph = Sparkline()
553
self.memory_usage_graph.set_property("reversed", True)
554
graph_table.attach(self.memory_usage_graph, 1, 2, 1, 2)
556
self.disk_io_graph = Sparkline()
557
self.disk_io_graph.set_property("reversed", True)
558
self.disk_io_graph.set_property("filled", False)
559
self.disk_io_graph.set_property("num_sets", 2)
560
self.disk_io_graph.set_property("rgb", map(lambda x: x / 255.0,
561
[0x82, 0x00, 0x3B, 0x29, 0x5C, 0x45]))
562
graph_table.attach(self.disk_io_graph, 1, 2, 2, 3)
564
self.network_traffic_graph = Sparkline()
565
self.network_traffic_graph.set_property("reversed", True)
566
self.network_traffic_graph.set_property("filled", False)
567
self.network_traffic_graph.set_property("num_sets", 2)
568
self.network_traffic_graph.set_property("rgb",
569
map(lambda x: x / 255.0,
572
graph_table.attach(self.network_traffic_graph, 1, 2, 3, 4)
574
graph_table.show_all()
576
def init_details(self):
578
# [ label, icon name, icon size, hw type, hw data/class]
579
hw_list_model = gtk.ListStore(str, str, int, int,
580
gobject.TYPE_PYOBJECT)
581
self.window.get_widget("hw-list").set_model(hw_list_model)
583
hwCol = gtk.TreeViewColumn("Hardware")
585
hwCol.set_min_width(165)
586
hw_txt = gtk.CellRendererText()
587
hw_img = gtk.CellRendererPixbuf()
588
hwCol.pack_start(hw_img, False)
589
hwCol.pack_start(hw_txt, True)
590
hwCol.add_attribute(hw_txt, 'text', HW_LIST_COL_LABEL)
591
hwCol.add_attribute(hw_img, 'stock-size', HW_LIST_COL_ICON_SIZE)
592
hwCol.add_attribute(hw_img, 'icon-name', HW_LIST_COL_ICON_NAME)
593
self.window.get_widget("hw-list").append_column(hwCol)
595
# Description text view
596
desc = self.window.get_widget("overview-description")
597
buf = gtk.TextBuffer()
598
buf.connect("changed", self.config_enable_apply)
602
clock_combo = self.window.get_widget("overview-clock-combo")
603
clock_model = gtk.ListStore(str)
604
clock_combo.set_model(clock_model)
605
text = gtk.CellRendererText()
606
clock_combo.pack_start(text, True)
607
clock_combo.add_attribute(text, 'text', 0)
608
clock_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
609
for offset in ["localtime", "utc"]:
610
clock_model.append([offset])
612
# Security info tooltips
613
util.tooltip_wrapper(self.window.get_widget("security-static-info"),
614
_("Static SELinux security type tells libvirt to always start the guest process with the specified label. The administrator is responsible for making sure the images are labeled correctly on disk."))
615
util.tooltip_wrapper(self.window.get_widget("security-dynamic-info"),
616
_("The dynamic SELinux security type tells libvirt to automatically pick a unique label for the guest process and guest image, ensuring total isolation of the guest. (Default)"))
619
generate_cpuset = self.window.get_widget("config-vcpupin-generate")
620
generate_warn = self.window.get_widget("config-vcpupin-generate-err")
621
if not self.conn.get_capabilities().host.topology:
622
generate_cpuset.set_sensitive(False)
624
util.tooltip_wrapper(generate_warn,
625
_("Libvirt did not detect NUMA capabilities."))
628
# [ VCPU #, Currently running on Phys CPU #, CPU Pinning list ]
629
vcpu_list = self.window.get_widget("config-vcpu-list")
630
vcpu_model = gtk.ListStore(str, str, str)
631
vcpu_list.set_model(vcpu_model)
633
vcpuCol = gtk.TreeViewColumn(_("VCPU"))
634
physCol = gtk.TreeViewColumn(_("On CPU"))
635
pinCol = gtk.TreeViewColumn(_("Pinning"))
637
vcpu_list.append_column(vcpuCol)
638
vcpu_list.append_column(physCol)
639
vcpu_list.append_column(pinCol)
641
vcpu_text = gtk.CellRendererText()
642
vcpuCol.pack_start(vcpu_text, True)
643
vcpuCol.add_attribute(vcpu_text, 'text', 0)
644
vcpuCol.set_sort_column_id(0)
646
phys_text = gtk.CellRendererText()
647
physCol.pack_start(phys_text, True)
648
physCol.add_attribute(phys_text, 'text', 1)
649
physCol.set_sort_column_id(1)
651
pin_text = gtk.CellRendererText()
652
pin_text.set_property("editable", True)
653
pin_text.connect("edited", self.config_vcpu_pin)
654
pinCol.pack_start(pin_text, True)
655
pinCol.add_attribute(pin_text, 'text', 2)
658
boot_list = self.window.get_widget("config-boot-list")
659
# model = [ XML boot type, display name, icon name, enabled ]
660
boot_list_model = gtk.ListStore(str, str, str, bool)
661
boot_list.set_model(boot_list_model)
663
chkCol = gtk.TreeViewColumn()
664
txtCol = gtk.TreeViewColumn()
666
boot_list.append_column(chkCol)
667
boot_list.append_column(txtCol)
669
chk = gtk.CellRendererToggle()
670
chk.connect("toggled", self.config_boot_toggled)
671
chkCol.pack_start(chk, False)
672
chkCol.add_attribute(chk, 'active', BOOT_ACTIVE)
674
icon = gtk.CellRendererPixbuf()
675
txtCol.pack_start(icon, False)
676
txtCol.add_attribute(icon, 'icon-name', BOOT_ICON)
678
text = gtk.CellRendererText()
679
txtCol.pack_start(text, True)
680
txtCol.add_attribute(text, 'text', BOOT_LABEL)
681
txtCol.add_attribute(text, 'sensitive', BOOT_ACTIVE)
683
no_default = not self.is_customize_dialog
686
caps = self.vm.get_connection().get_capabilities()
691
cpu_values = caps.get_cpu_values(self.vm.get_arch())
692
cpu_names = sorted(map(lambda c: c.model, cpu_values.cpus),
695
logging.exception("Error populating CPU model list")
697
cpu_model = self.window.get_widget("cpu-model")
699
model = gtk.ListStore(str, object)
700
cpu_model.set_model(model)
701
cpu_model.set_text_column(0)
702
model.set_sort_column_id(0, gtk.SORT_ASCENDING)
703
for name in cpu_names:
704
model.append([name, cpu_values.get_cpu(name)])
707
disk_cache = self.window.get_widget("disk-cache-combo")
708
uihelpers.build_cache_combo(self.vm, disk_cache)
711
format_list = self.window.get_widget("disk-format")
712
uihelpers.build_storage_format_combo(self.vm, format_list)
715
disk_bus = self.window.get_widget("disk-bus-combo")
716
uihelpers.build_disk_bus_combo(self.vm, disk_bus)
719
net_model = self.window.get_widget("network-model-combo")
720
uihelpers.build_netmodel_combo(self.vm, net_model)
723
vnc_keymap = self.window.get_widget("gfx-keymap-combo")
724
uihelpers.build_vnc_keymap_combo(self.vm, vnc_keymap,
725
no_default=no_default)
728
sound_dev = self.window.get_widget("sound-model-combo")
729
uihelpers.build_sound_combo(self.vm, sound_dev, no_default=no_default)
732
video_dev = self.window.get_widget("video-model-combo")
733
uihelpers.build_video_combo(self.vm, video_dev, no_default=no_default)
735
# Watchdog model combo
736
combo = self.window.get_widget("watchdog-model-combo")
737
uihelpers.build_watchdogmodel_combo(self.vm, combo,
738
no_default=no_default)
740
# Watchdog action combo
741
combo = self.window.get_widget("watchdog-action-combo")
742
uihelpers.build_watchdogaction_combo(self.vm, combo,
743
no_default=no_default)
745
# Helper function to handle the combo/label pattern used for
746
# video model, sound model, network model, etc.
747
def set_combo_label(self, prefix, model_idx, value):
748
model_label = self.window.get_widget(prefix + "-label")
749
model_combo = self.window.get_widget(prefix + "-combo")
750
model_list = map(lambda x: x[model_idx], model_combo.get_model())
751
model_in_list = (value in model_list)
753
model_label.set_property("visible", not model_in_list)
754
model_combo.set_property("visible", model_in_list)
755
model_label.set_text(value or "")
758
model_combo.set_active(model_list.index(value))
760
##########################
761
# Window state listeners #
762
##########################
764
def window_resized(self, ignore, event):
765
# Sometimes dimensions change when window isn't visible
766
if not self.is_visible():
769
self.vm.set_details_window_size(event.width, event.height)
771
def popup_addhw_menu(self, widget, event):
773
if event.button != 3:
776
self.addhwmenu.popup(None, None, None, 0, event.time)
778
def populate_serial_menu(self, src):
782
devs = self.vm.get_serial_devs()
784
item = gtk.MenuItem(_("No text console available"))
785
item.set_sensitive(False)
788
on_serial = (self.last_console_page >= PAGE_DYNAMIC_OFFSET)
789
serial_page_dev = None
791
serial_idx = self.last_console_page - PAGE_DYNAMIC_OFFSET
792
if len(self.serial_tabs) >= serial_idx:
793
serial_page_dev = self.serial_tabs[serial_idx]
794
on_graphics = (self.last_console_page == PAGE_CONSOLE)
797
usable_types = ["pty"]
801
item = gtk.RadioMenuItem(group, dev[0])
805
if self.vm.get_connection().is_remote():
806
msg = _("Serial console not yet supported over remote "
808
elif not self.vm.is_active():
809
msg = _("Serial console not available for inactive guest.")
810
elif not dev[1] in usable_types:
811
msg = _("Console for device type '%s' not yet supported.") % \
813
elif dev[2] and not os.access(dev[2], os.R_OK | os.W_OK):
814
msg = _("Can not access console path '%s'.") % str(dev[2])
819
util.tooltip_wrapper(item, msg)
820
item.set_sensitive(sensitive)
822
if sensitive and on_serial and serial_page_dev == dev[0]:
823
# Tab is already open, make sure marked as such
824
item.set_active(True)
825
item.connect("toggled", self.control_serial_tab, dev[0], dev[3])
828
src.add(gtk.SeparatorMenuItem())
830
devs = self.vm.get_graphics_devices()
832
item = gtk.MenuItem(_("No graphical console available"))
833
item.set_sensitive(False)
837
item = gtk.RadioMenuItem(group, _("Graphical Console %s") %
843
item.set_active(True)
844
item.connect("toggled", self.control_serial_tab,
845
dev.virtual_device_type, dev.type)
850
def control_fullscreen(self, src):
851
menu = self.window.get_widget("details-menu-view-fullscreen")
852
if src.get_active() != menu.get_active():
853
menu.set_active(src.get_active())
855
def toggle_toolbar(self, src):
856
active = src.get_active()
857
self.config.set_details_show_toolbar(active)
859
self.window.get_widget("details-menu-view-fullscreen").get_active():
860
self.window.get_widget("toolbar-box").show()
862
self.window.get_widget("toolbar-box").hide()
864
def get_boot_selection(self):
865
widget = self.window.get_widget("config-boot-list")
866
selection = widget.get_selection()
867
model, treepath = selection.get_selected()
870
return model[treepath]
872
def set_hw_selection(self, page):
873
hwlist = self.window.get_widget("hw-list")
874
selection = hwlist.get_selection()
875
selection.select_path(str(page))
877
def get_hw_selection(self, field):
878
vmlist = self.window.get_widget("hw-list")
879
selection = vmlist.get_selection()
880
active = selection.get_selected()
881
if active[1] == None:
883
return active[0].get_value(active[1], field)
885
def force_get_hw_pagetype(self, page=None):
889
page = self.get_hw_selection(HW_LIST_COL_TYPE)
891
page = HW_LIST_TYPE_GENERAL
892
self.window.get_widget("hw-list").get_selection().select_path(0)
896
def hw_selected(self, ignore1=None, page=None, selected=True):
897
pagetype = self.force_get_hw_pagetype(page)
899
self.window.get_widget("config-remove").set_sensitive(True)
900
self.window.get_widget("hw-panel").set_sensitive(True)
901
self.window.get_widget("hw-panel").show()
904
if pagetype == HW_LIST_TYPE_GENERAL:
905
self.refresh_overview_page()
906
elif pagetype == HW_LIST_TYPE_STATS:
907
self.refresh_stats_page()
908
elif pagetype == HW_LIST_TYPE_CPU:
909
self.refresh_config_cpu()
910
elif pagetype == HW_LIST_TYPE_MEMORY:
911
self.refresh_config_memory()
912
elif pagetype == HW_LIST_TYPE_BOOT:
913
self.refresh_boot_page()
914
elif pagetype == HW_LIST_TYPE_DISK:
915
self.refresh_disk_page()
916
elif pagetype == HW_LIST_TYPE_NIC:
917
self.refresh_network_page()
918
elif pagetype == HW_LIST_TYPE_INPUT:
919
self.refresh_input_page()
920
elif pagetype == HW_LIST_TYPE_GRAPHICS:
921
self.refresh_graphics_page()
922
elif pagetype == HW_LIST_TYPE_SOUND:
923
self.refresh_sound_page()
924
elif pagetype == HW_LIST_TYPE_CHAR:
925
self.refresh_char_page()
926
elif pagetype == HW_LIST_TYPE_HOSTDEV:
927
self.refresh_hostdev_page()
928
elif pagetype == HW_LIST_TYPE_VIDEO:
929
self.refresh_video_page()
930
elif pagetype == HW_LIST_TYPE_WATCHDOG:
931
self.refresh_watchdog_page()
932
elif pagetype == HW_LIST_TYPE_CONTROLLER:
933
self.refresh_controller_page()
937
self.err.show_err(_("Error refreshing hardware page: %s") % str(e),
938
"".join(traceback.format_exc()))
941
rem = pagetype in remove_pages
943
self.window.get_widget("config-apply").set_sensitive(False)
944
self.window.get_widget("config-remove").set_property("visible", rem)
946
self.window.get_widget("hw-panel").set_current_page(pagetype)
948
def details_console_changed(self, src):
949
if self.ignoreDetails:
952
if not src.get_active():
956
if (src == self.window.get_widget("control-vm-details") or
957
src == self.window.get_widget("details-menu-view-details")):
960
pages = self.window.get_widget("details-pages")
962
pages.set_current_page(PAGE_DETAILS)
964
pages.set_current_page(self.last_console_page)
966
def sync_details_console_view(self, is_details):
967
details = self.window.get_widget("control-vm-details")
968
details_menu = self.window.get_widget("details-menu-view-details")
969
console = self.window.get_widget("control-vm-console")
970
console_menu = self.window.get_widget("details-menu-view-console")
973
self.ignoreDetails = True
975
details.set_active(is_details)
976
details_menu.set_active(is_details)
977
console.set_active(not is_details)
978
console_menu.set_active(not is_details)
980
self.ignoreDetails = False
982
def switch_page(self, ignore1=None, ignore2=None, newpage=None):
983
self.page_refresh(newpage)
985
self.sync_details_console_view(newpage == PAGE_DETAILS)
987
if newpage == PAGE_CONSOLE or newpage >= PAGE_DYNAMIC_OFFSET:
988
self.last_console_page = newpage
990
def change_run_text(self, can_restore):
995
strip_text = text.replace("_", "")
997
self.window.get_widget("details-menu-run").get_child().set_label(text)
998
self.window.get_widget("control-run").set_label(strip_text)
1000
def refresh_vm_state(self, ignore1=None, ignore2=None, ignore3=None):
1002
status = self.vm.status()
1004
self.toggle_toolbar(
1005
self.window.get_widget("details-menu-view-toolbar"))
1007
destroy = vm.is_destroyable()
1008
run = vm.is_runable()
1009
stop = vm.is_stoppable()
1010
paused = vm.is_paused()
1011
ro = vm.is_read_only()
1013
if vm.managedsave_supported:
1014
self.change_run_text(vm.hasSavedImage())
1016
self.window.get_widget("details-menu-destroy").set_sensitive(destroy)
1017
self.window.get_widget("control-run").set_sensitive(run)
1018
self.window.get_widget("details-menu-run").set_sensitive(run)
1020
self.window.get_widget("details-menu-migrate").set_sensitive(stop)
1021
self.window.get_widget("control-shutdown").set_sensitive(stop)
1022
self.window.get_widget("details-menu-shutdown").set_sensitive(stop)
1023
self.window.get_widget("details-menu-save").set_sensitive(stop)
1024
self.window.get_widget("control-pause").set_sensitive(stop)
1025
self.window.get_widget("details-menu-pause").set_sensitive(stop)
1027
# Set pause widget states
1029
self.ignorePause = True
1030
self.window.get_widget("control-pause").set_active(paused)
1031
self.window.get_widget("details-menu-pause").set_active(paused)
1033
self.ignorePause = False
1035
self.window.get_widget("config-vcpus").set_sensitive(not ro)
1036
self.window.get_widget("config-vcpupin").set_sensitive(not ro)
1037
self.window.get_widget("config-memory").set_sensitive(not ro)
1038
self.window.get_widget("config-maxmem").set_sensitive(not ro)
1040
# Disable send key menu entries for offline VM
1041
send_key = self.window.get_widget("details-menu-send-key")
1042
for c in send_key.get_submenu().get_children():
1043
c.set_sensitive(not (run or paused))
1045
self.console.update_widget_states(vm, status)
1047
self.window.get_widget("overview-status-text").set_text(
1048
self.vm.run_status())
1049
self.window.get_widget("overview-status-icon").set_from_pixbuf(
1050
self.vm.run_status_icon())
1052
details = self.window.get_widget("details-pages")
1053
self.page_refresh(details.get_current_page())
1055
# This is safe to refresh, and is dependent on domain state
1056
self._refresh_runtime_pinning()
1059
#############################
1060
# External action listeners #
1061
#############################
1063
def show_help(self, src_ignore):
1064
self.emit("action-show-help", "virt-manager-details-window")
1066
def view_manager(self, src_ignore):
1067
self.emit("action-view-manager")
1069
def exit_app(self, src_ignore):
1070
self.emit("action-exit-app")
1072
def activate_console_page(self):
1073
self.window.get_widget("details-pages").set_current_page(PAGE_CONSOLE)
1075
def activate_performance_page(self):
1076
self.window.get_widget("details-pages").set_current_page(PAGE_DETAILS)
1077
self.set_hw_selection(HW_LIST_TYPE_STATS)
1079
def activate_config_page(self):
1080
self.window.get_widget("details-pages").set_current_page(PAGE_DETAILS)
1082
def add_hardware(self, src_ignore):
1083
if self.addhw is None:
1084
self.addhw = vmmAddHardware(self.vm)
1088
def remove_xml_dev(self, src_ignore):
1089
info = self.get_hw_selection(HW_LIST_COL_DEVICE)
1093
devtype = info.virtual_device_type
1094
self.remove_device(devtype, info)
1096
def control_vm_pause(self, src):
1097
if self.ignorePause:
1100
if src.get_active():
1101
self.emit("action-suspend-domain",
1102
self.vm.get_connection().get_uri(),
1105
self.emit("action-resume-domain",
1106
self.vm.get_connection().get_uri(),
1109
def control_vm_run(self, src_ignore):
1110
self.emit("action-run-domain",
1111
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1113
def control_vm_shutdown(self, src_ignore):
1114
self.emit("action-shutdown-domain",
1115
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1117
def control_vm_reboot(self, src_ignore):
1118
self.emit("action-reboot-domain",
1119
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1121
def control_vm_console(self, src_ignore):
1122
self.emit("action-show-console",
1123
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1125
def control_vm_save(self, src_ignore):
1126
self.emit("action-save-domain",
1127
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1129
def control_vm_destroy(self, src_ignore):
1130
self.emit("action-destroy-domain",
1131
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1133
def control_vm_clone(self, src_ignore):
1134
self.emit("action-clone-domain",
1135
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1137
def control_vm_migrate(self, src_ignore):
1138
self.emit("action-migrate-domain",
1139
self.vm.get_connection().get_uri(), self.vm.get_uuid())
1141
def control_vm_screenshot(self, src_ignore):
1142
# If someone feels kind they could extend this code to allow
1143
# user to choose what image format they'd like to save in....
1144
path = util.browse_local(
1146
_("Save Virtual Machine Screenshot"),
1147
self.vm.get_connection(),
1148
_type=("png", "PNG files"),
1149
dialog_type=gtk.FILE_CHOOSER_ACTION_SAVE,
1150
browse_reason=self.config.CONFIG_DIR_SCREENSHOT)
1155
if not(filename.endswith(".png")):
1157
image = self.console.viewer.get_pixbuf()
1159
# Save along with a little metadata about us & the domain
1160
image.save(filename, 'png',
1161
{'tEXt::Hypervisor URI': self.vm.get_connection().get_uri(),
1162
'tEXt::Domain Name': self.vm.get_name(),
1163
'tEXt::Domain UUID': self.vm.get_uuid(),
1164
'tEXt::Generator App': self.config.get_appname(),
1165
'tEXt::Generator Version': self.config.get_appversion()})
1167
msg = gtk.MessageDialog(self.topwin,
1171
(_("The screenshot has been saved to:\n%s") %
1173
msg.set_title(_("Screenshot saved"))
1178
# ------------------------------
1179
# Serial Console pieces
1180
# ------------------------------
1182
def control_serial_tab(self, src_ignore, name, target_port):
1183
is_graphics = (name == "graphics")
1184
is_serial = not is_graphics
1187
self.window.get_widget("details-pages").set_current_page(PAGE_CONSOLE)
1189
self._show_serial_tab(name, target_port)
1191
def show_serial_rcpopup(self, src, event):
1192
if event.button != 3:
1195
self.serial_popup.show_all()
1196
self.serial_copy.connect("activate", self.serial_copy_text, src)
1197
self.serial_paste.connect("activate", self.serial_paste_text, src)
1198
self.serial_close.connect("activate", self.serial_close_tab,
1199
self.window.get_widget("details-pages").get_current_page())
1201
if src.get_has_selection():
1202
self.serial_copy.set_sensitive(True)
1204
self.serial_copy.set_sensitive(False)
1205
self.serial_popup.popup(None, None, None, 0, event.time)
1207
def serial_close_tab(self, src_ignore, pagenum):
1208
tab_idx = (pagenum - PAGE_DYNAMIC_OFFSET)
1209
if (tab_idx < 0) or (tab_idx > len(self.serial_tabs) - 1):
1211
return self._close_serial_tab(self.serial_tabs[tab_idx])
1213
def serial_copy_text(self, src_ignore, terminal):
1214
terminal.copy_clipboard()
1216
def serial_paste_text(self, src_ignore, terminal):
1217
terminal.paste_clipboard()
1219
def _show_serial_tab(self, name, target_port):
1220
if not self.serial_tabs.count(name):
1221
child = vmmSerialConsole(self.vm, target_port)
1222
child.terminal.connect("button-press-event",
1223
self.show_serial_rcpopup)
1224
title = gtk.Label(name)
1226
self.window.get_widget("details-pages").append_page(child, title)
1227
self.serial_tabs.append(name)
1229
page_idx = self.serial_tabs.index(name) + PAGE_DYNAMIC_OFFSET
1230
self.window.get_widget("details-pages").set_current_page(page_idx)
1232
def _close_serial_tab(self, name):
1233
if not self.serial_tabs.count(name):
1236
page_idx = self.serial_tabs.index(name) + PAGE_DYNAMIC_OFFSET
1237
self.window.get_widget("details-pages").remove_page(page_idx)
1238
self.serial_tabs.remove(name)
1240
############################
1241
# Details/Hardware getters #
1242
############################
1244
def get_config_boot_devs(self):
1245
boot_model = self.window.get_widget("config-boot-list").get_model()
1248
for row in boot_model:
1249
if row[BOOT_ACTIVE]:
1250
devs.append(row[BOOT_DEV_TYPE])
1254
def get_config_cpu_model(self):
1255
cpu_list = self.window.get_widget("cpu-model")
1256
model = cpu_list.child.get_text()
1258
for row in cpu_list.get_model():
1260
return model, row[1].vendor
1264
##############################
1265
# Details/Hardware listeners #
1266
##############################
1267
def _spin_get_helper(self, wname):
1268
widget = self.window.get_widget(wname)
1269
adj = widget.get_adjustment()
1270
txt = widget.get_text()
1278
def _browse_file(self, callback, is_media=False):
1280
reason = self.config.CONFIG_DIR_MEDIA
1282
reason = self.config.CONFIG_DIR_IMAGE
1284
if self.storage_browser == None:
1285
self.storage_browser = vmmStorageBrowser(self.conn)
1287
self.storage_browser.set_finish_cb(callback)
1288
self.storage_browser.set_browse_reason(reason)
1289
self.storage_browser.show(self.conn)
1291
def browse_kernel(self, src_ignore):
1292
def cb(ignore, path):
1293
self.window.get_widget("boot-kernel").set_text(path)
1294
self._browse_file(cb)
1295
def browse_initrd(self, src_ignore):
1296
def cb(ignore, path):
1297
self.window.get_widget("boot-kernel-initrd").set_text(path)
1298
self._browse_file(cb)
1300
def config_enable_apply(self, ignore1=None, ignore2=None):
1301
self.window.get_widget("config-apply").set_sensitive(True)
1303
# Overview -> Security
1304
def security_label_changed(self, label_ignore):
1305
self.config_enable_apply()
1307
def security_type_changed(self, button):
1308
self.config_enable_apply()
1309
self.window.get_widget("security-label").set_sensitive(
1310
not button.get_active())
1313
def config_get_maxmem(self):
1314
return self._spin_get_helper("config-maxmem")
1315
def config_get_memory(self):
1316
return self._spin_get_helper("config-memory")
1318
def config_maxmem_changed(self, src_ignore):
1319
self.config_enable_apply()
1321
def config_memory_changed(self, src_ignore):
1322
self.config_enable_apply()
1324
maxadj = self.window.get_widget("config-maxmem").get_adjustment()
1326
mem = self.config_get_memory()
1327
if maxadj.value < mem:
1331
def generate_cpuset(self):
1332
mem = int(self.vm.get_memory()) / 1024 / 1024
1333
return virtinst.Guest.generate_cpuset(self.conn.vmm, mem)
1336
def config_get_vcpus(self):
1337
return self._spin_get_helper("config-vcpus")
1338
def config_get_maxvcpus(self):
1339
return self._spin_get_helper("config-maxvcpus")
1341
def config_vcpupin_generate(self, ignore):
1343
pinstr = self.generate_cpuset()
1344
except Exception, e:
1345
return self.err.val_err(
1346
_("Error generating CPU configuration: %s") % str(e))
1348
self.window.get_widget("config-vcpupin").set_text("")
1349
self.window.get_widget("config-vcpupin").set_text(pinstr)
1351
def config_vcpus_changed(self, ignore):
1352
self.config_enable_apply()
1354
conn = self.vm.get_connection()
1355
host_active_count = conn.host_active_processor_count()
1356
cur = self.config_get_vcpus()
1358
# Warn about overcommit
1359
warn = bool(cur > host_active_count)
1360
self.window.get_widget("config-vcpus-warn-box").set_property(
1363
maxadj = self.window.get_widget("config-maxvcpus").get_adjustment()
1364
maxval = self.config_get_maxvcpus()
1369
def config_maxvcpus_changed(self, ignore):
1370
self.config_enable_apply()
1372
def config_cpu_copy_host(self, src_ignore):
1373
# Update UI with output copied from host
1375
CPU = virtinst.CPU(self.vm.get_connection().vmm)
1378
self._refresh_cpu_config(CPU)
1379
self._cpu_copy_host = True
1380
except Exception, e:
1381
self.err.show_err(_("Error copying host CPU: %s") % str(e),
1382
"".join(traceback.format_exc()))
1385
def config_cpu_topology_enable(self, src):
1386
do_enable = src.get_active()
1387
self.window.get_widget("cpu-topology-table").set_sensitive(do_enable)
1388
self.config_enable_apply()
1390
# Boot device / Autostart
1391
def config_bootdev_selected(self, ignore):
1392
boot_row = self.get_boot_selection()
1393
boot_selection = boot_row and boot_row[BOOT_DEV_TYPE]
1394
boot_devs = self.get_config_boot_devs()
1395
up_widget = self.window.get_widget("config-boot-moveup")
1396
down_widget = self.window.get_widget("config-boot-movedown")
1398
down_widget.set_sensitive(bool(boot_devs and
1400
boot_selection in boot_devs and
1401
boot_selection != boot_devs[-1]))
1402
up_widget.set_sensitive(bool(boot_devs and boot_selection and
1403
boot_selection in boot_devs and
1404
boot_selection != boot_devs[0]))
1406
def config_boot_toggled(self, ignore, index):
1407
boot_model = self.window.get_widget("config-boot-list").get_model()
1408
boot_row = boot_model[index]
1409
is_active = boot_row[BOOT_ACTIVE]
1411
boot_row[BOOT_ACTIVE] = not is_active
1413
self.repopulate_boot_list(self.get_config_boot_devs(),
1414
boot_row[BOOT_DEV_TYPE])
1415
self.config_enable_apply()
1417
def config_boot_move(self, src_ignore, move_up):
1418
boot_row = self.get_boot_selection()
1422
boot_selection = boot_row[BOOT_DEV_TYPE]
1423
boot_devs = self.get_config_boot_devs()
1424
boot_idx = boot_devs.index(boot_selection)
1426
new_idx = boot_idx - 1
1428
new_idx = boot_idx + 1
1430
if new_idx < 0 or new_idx >= len(boot_devs):
1431
# Somehow we got out of bounds
1434
swap_dev = boot_devs[new_idx]
1435
boot_devs[new_idx] = boot_selection
1436
boot_devs[boot_idx] = swap_dev
1438
self.repopulate_boot_list(boot_devs, boot_selection)
1439
self.config_enable_apply()
1441
# CDROM Eject/Connect
1442
def toggle_storage_media(self, src_ignore):
1443
disk = self.get_hw_selection(HW_LIST_COL_DEVICE)
1449
devtype = disk.device
1453
self.change_storage_media(dev_id_info, None)
1456
def change_cdrom_wrapper(src_ignore, dev_id_info, newpath):
1457
return self.change_storage_media(dev_id_info, newpath)
1459
# Launch 'Choose CD' dialog
1460
if self.media_choosers[devtype] is None:
1461
ret = vmmChooseCD(dev_id_info,
1462
self.vm.get_connection(),
1465
ret.connect("cdrom-chosen", change_cdrom_wrapper)
1466
self.media_choosers[devtype] = ret
1468
dialog = self.media_choosers[devtype]
1469
dialog.dev_id_info = dev_id_info
1472
##################################################
1473
# Details/Hardware config changes (apply button) #
1474
##################################################
1476
def config_apply(self, ignore):
1477
pagetype = self.get_hw_selection(HW_LIST_COL_TYPE)
1478
devobj = self.get_hw_selection(HW_LIST_COL_DEVICE)
1482
if pagetype is HW_LIST_TYPE_GENERAL:
1483
ret = self.config_overview_apply()
1484
elif pagetype is HW_LIST_TYPE_CPU:
1485
ret = self.config_vcpus_apply()
1486
elif pagetype is HW_LIST_TYPE_MEMORY:
1487
ret = self.config_memory_apply()
1488
elif pagetype is HW_LIST_TYPE_BOOT:
1489
ret = self.config_boot_options_apply()
1490
elif pagetype is HW_LIST_TYPE_DISK:
1491
ret = self.config_disk_apply(key)
1492
elif pagetype is HW_LIST_TYPE_NIC:
1493
ret = self.config_network_apply(key)
1494
elif pagetype is HW_LIST_TYPE_GRAPHICS:
1495
ret = self.config_graphics_apply(key)
1496
elif pagetype is HW_LIST_TYPE_SOUND:
1497
ret = self.config_sound_apply(key)
1498
elif pagetype is HW_LIST_TYPE_VIDEO:
1499
ret = self.config_video_apply(key)
1500
elif pagetype is HW_LIST_TYPE_WATCHDOG:
1501
ret = self.config_watchdog_apply(key)
1505
if ret is not False:
1506
self.window.get_widget("config-apply").set_sensitive(False)
1508
# Helper for accessing value of combo/label pattern
1509
def get_combo_label_value(self, prefix, model_idx=0):
1510
comboname = prefix + "-combo"
1511
label = self.window.get_widget(prefix + "-label")
1514
if label.get_property("visible"):
1515
value = label.get_text()
1517
value = self.get_combo_value(comboname, model_idx)
1521
def get_combo_value(self, widgetname, model_idx=0):
1522
combo = self.window.get_widget(widgetname)
1523
if combo.get_active() < 0:
1525
return combo.get_model()[combo.get_active()][model_idx]
1528
def config_overview_apply(self):
1530
enable_acpi = self.window.get_widget("overview-acpi").get_active()
1531
enable_apic = self.window.get_widget("overview-apic").get_active()
1532
clock_combo = self.window.get_widget("overview-clock-combo")
1533
if clock_combo.get_property("visible"):
1534
clock = clock_combo.get_model()[clock_combo.get_active()][0]
1536
clock = self.window.get_widget("overview-clock-label").get_text()
1539
if self.window.get_widget("security-dynamic").get_active():
1544
selabel = self.window.get_widget("security-label").get_text()
1546
if self.window.get_widget("security-type-box").get_sensitive():
1547
semodel = self.window.get_widget("security-model").get_text()
1550
desc_widget = self.window.get_widget("overview-description")
1551
desc = desc_widget.get_buffer().get_property("text") or ""
1553
return self._change_config_helper([self.vm.define_acpi,
1554
self.vm.define_apic,
1555
self.vm.define_clock,
1556
self.vm.define_seclabel,
1557
self.vm.define_description],
1561
(semodel, setype, selabel),
1565
def config_vcpus_apply(self):
1566
vcpus = self.config_get_vcpus()
1567
maxv = self.config_get_maxvcpus()
1568
cpuset = self.window.get_widget("config-vcpupin").get_text()
1570
do_top = self.window.get_widget("cpu-topology-enable").get_active()
1571
sockets = self.window.get_widget("cpu-sockets").get_value()
1572
cores = self.window.get_widget("cpu-cores").get_value()
1573
threads = self.window.get_widget("cpu-threads").get_value()
1574
model, vendor = self.get_config_cpu_model()
1576
logging.info("Setting vcpus for %s to %s, cpuset is %s" %
1577
(self.vm.get_name(), str(vcpus), cpuset))
1584
define_funcs = [self.vm.define_vcpus,
1585
self.vm.define_cpuset,
1587
self.vm.define_cpu_topology]
1588
define_args = [(vcpus, maxv),
1590
(model, vendor, self._cpu_copy_host),
1591
(sockets, cores, threads)]
1593
ret = self._change_config_helper(define_funcs, define_args,
1594
[self.vm.hotplug_vcpus,
1595
self.config_vcpu_pin_cpuset],
1596
[(vcpus,), (cpuset,)])
1599
self._cpu_copy_host = False
1601
def config_vcpu_pin(self, src_ignore, path, new_text):
1602
vcpu_list = self.window.get_widget("config-vcpu-list")
1603
vcpu_model = vcpu_list.get_model()
1604
row = vcpu_model[path]
1605
conn = self.vm.get_connection()
1608
vcpu_num = int(row[0])
1609
pinlist = virtinst.Guest.cpuset_str_to_tuple(conn.vmm, new_text)
1610
except Exception, e:
1611
self.err.val_err(_("Error building pin list: %s") % str(e))
1615
self.vm.pin_vcpu(vcpu_num, pinlist)
1616
except Exception, e:
1617
self.err.show_err(_("Error pinning vcpus: %s") % str(e),
1618
"".join(traceback.format_exc()))
1621
self._refresh_runtime_pinning()
1623
def config_vcpu_pin_cpuset(self, cpuset):
1624
conn = self.vm.get_connection()
1625
vcpu_list = self.window.get_widget("config-vcpu-list")
1626
vcpu_model = vcpu_list.get_model()
1628
if self.vm.vcpu_pinning() == cpuset:
1631
pinlist = virtinst.Guest.cpuset_str_to_tuple(conn.vmm, cpuset)
1632
for row in vcpu_model:
1634
self.vm.pin_vcpu(int(vcpu_num), pinlist)
1637
def config_memory_apply(self):
1639
maxmem = self.config_get_maxmem()
1640
if self.window.get_widget("config-memory").get_property("sensitive"):
1641
curmem = self.config_get_memory()
1644
curmem = int(curmem) * 1024
1646
maxmem = int(maxmem) * 1024
1648
return self._change_config_helper(self.vm.define_both_mem,
1650
self.vm.hotplug_both_mem,
1653
# Boot device / Autostart
1654
def config_boot_options_apply(self):
1655
auto = self.window.get_widget("config-autostart")
1657
if auto.get_property("sensitive"):
1659
self.vm.set_autostart(auto.get_active())
1660
except Exception, e:
1661
self.err.show_err((_("Error changing autostart value: %s") %
1662
str(e)), "".join(traceback.format_exc()))
1665
bootdevs = self.get_config_boot_devs()
1666
bootmenu = self.window.get_widget("boot-menu").get_active()
1668
kernel = self.window.get_widget("boot-kernel").get_text()
1669
initrd = self.window.get_widget("boot-kernel-initrd").get_text()
1670
args = self.window.get_widget("boot-kernel-args").get_text()
1672
return self._change_config_helper([self.vm.set_boot_device,
1673
self.vm.set_boot_menu,
1674
self.vm.set_boot_kernel],
1677
(kernel, initrd, args)])
1680
def change_storage_media(self, dev_id_info, newpath):
1681
return self._change_config_helper(self.vm.define_storage_media,
1682
(dev_id_info, newpath),
1683
self.vm.hotplug_storage_media,
1684
(dev_id_info, newpath))
1687
def config_disk_apply(self, dev_id_info):
1688
do_readonly = self.window.get_widget("disk-readonly").get_active()
1689
do_shareable = self.window.get_widget("disk-shareable").get_active()
1690
cache = self.get_combo_label_value("disk-cache")
1691
fmt = self.window.get_widget("disk-format").child.get_text()
1692
bus = self.get_combo_label_value("disk-bus")
1694
return self._change_config_helper([self.vm.define_disk_readonly,
1695
self.vm.define_disk_shareable,
1696
self.vm.define_disk_cache,
1697
self.vm.define_disk_driver_type,
1698
self.vm.define_disk_bus],
1699
[(dev_id_info, do_readonly),
1700
(dev_id_info, do_shareable),
1701
(dev_id_info, cache),
1703
(dev_id_info, bus)])
1706
def config_sound_apply(self, dev_id_info):
1707
model = self.get_combo_label_value("sound-model")
1709
return self._change_config_helper(self.vm.define_sound_model,
1710
(dev_id_info, model))
1713
def config_network_apply(self, dev_id_info):
1714
model = self.get_combo_label_value("network-model")
1715
return self._change_config_helper(self.vm.define_network_model,
1716
(dev_id_info, model))
1719
def config_graphics_apply(self, dev_id_info):
1720
passwd = self.window.get_widget("gfx-password").get_text() or None
1721
keymap = self.get_combo_label_value("gfx-keymap")
1723
return self._change_config_helper([self.vm.define_graphics_password,
1724
self.vm.define_graphics_keymap],
1725
[(dev_id_info, passwd),
1726
(dev_id_info, keymap)],
1727
[self.vm.hotplug_graphics_password],
1728
[(dev_id_info, passwd)])
1732
def config_video_apply(self, dev_id_info):
1733
model = self.get_combo_label_value("video-model")
1735
return self._change_config_helper(self.vm.define_video_model,
1736
(dev_id_info, model))
1739
def config_watchdog_apply(self, dev_id_info):
1740
model = self.get_combo_label_value("watchdog-model")
1741
action = self.get_combo_label_value("watchdog-action")
1743
return self._change_config_helper([self.vm.define_watchdog_model,
1744
self.vm.define_watchdog_action],
1745
[(dev_id_info, model),
1746
(dev_id_info, action)])
1749
def remove_device(self, dev_type, dev_id_info):
1750
logging.debug("Removing device: %s %s" % (dev_type, dev_id_info))
1751
do_prompt = self.config.get_confirm_removedev()
1754
res = self.err.warn_chkbox(
1755
text1=(_("Are you sure you want to remove this device?")),
1756
chktext=_("Don't ask me again."),
1757
buttons=gtk.BUTTONS_YES_NO)
1759
response, skip_prompt = res
1762
self.config.set_confirm_removedev(not skip_prompt)
1766
self.vm.remove_device(dev_id_info)
1767
except Exception, e:
1768
self.err.show_err(_("Error Removing Device: %s" % str(e)),
1769
"".join(traceback.format_exc()))
1775
if self.vm.is_active():
1776
self.vm.detach_device(dev_id_info)
1777
except Exception, e:
1778
logging.debug("Device could not be hotUNplugged: %s" % str(e))
1779
detach_err = (str(e), "".join(traceback.format_exc()))
1785
_("Device could not be removed from the running machine"),
1786
detach_err[0] + "\n\n" + detach_err[1],
1787
text2=_("This change will take effect after the next VM reboot."),
1788
buttons=gtk.BUTTONS_OK,
1789
dialog_type=gtk.MESSAGE_INFO)
1791
# Generic config change helpers
1792
def _change_config_helper(self,
1793
define_funcs, define_funcs_args,
1794
hotplug_funcs=None, hotplug_funcs_args=None):
1796
Requires at least a 'define' function and arglist to be specified
1797
(a function where we change the inactive guest config).
1799
Arguments can be a single arg or a list or appropriate arg type (e.g.
1800
a list of functions for define_funcs)
1805
if type(val) is not list:
1809
define_funcs = listify(define_funcs)
1810
define_funcs_args = listify(define_funcs_args)
1811
hotplug_funcs = listify(hotplug_funcs)
1812
hotplug_funcs_args = listify(hotplug_funcs_args)
1815
active = self.vm.is_active()
1819
if active and hotplug_funcs:
1820
for idx in range(len(hotplug_funcs)):
1821
func = hotplug_funcs[idx]
1822
args = hotplug_funcs_args[idx]
1825
except Exception, e:
1826
logging.debug("Hotplug failed: func=%s: %s" % (func,
1828
hotplug_err.append((str(e),
1829
"".join(traceback.format_exc())))
1831
# Persistent config change
1833
for idx in range(len(define_funcs)):
1834
func = define_funcs[idx]
1835
args = define_funcs_args[idx]
1838
self.vm.redefine_cached()
1839
except Exception, e:
1840
self.err.show_err((_("Error changing VM configuration: %s") %
1841
str(e)), "".join(traceback.format_exc()))
1842
# If we fail, make sure we flush the cache
1843
self.vm.refresh_xml()
1848
(active and not len(hotplug_funcs) == len(define_funcs))):
1849
if len(define_funcs) > 1:
1850
msg = _("Some changes may require a guest reboot "
1853
msg = _("These changes will take effect after "
1854
"the next guest reboot.")
1856
dtype = hotplug_err and gtk.MESSAGE_WARNING or gtk.MESSAGE_INFO
1858
for err1, tb in hotplug_err:
1859
hotplug_msg += (err1 + "\n\n" + tb + "\n")
1861
self.err.show_err(msg, details=hotplug_msg,
1862
buttons=gtk.BUTTONS_OK,
1867
########################
1868
# Details page refresh #
1869
########################
1871
def refresh_resources(self, ignore):
1872
details = self.window.get_widget("details-pages")
1873
page = details.get_current_page()
1875
# If the dialog is visible, we want to make sure the XML is always
1877
if self.is_visible():
1878
self.vm.refresh_xml()
1880
# Stats page needs to be refreshed every tick
1881
if (page == PAGE_DETAILS and
1882
self.get_hw_selection(HW_LIST_COL_TYPE) == HW_LIST_TYPE_STATS):
1883
self.refresh_stats_page()
1885
def page_refresh(self, page):
1886
if page != PAGE_DETAILS:
1889
# This function should only be called when the VM xml actually
1890
# changes (not everytime it is refreshed). This saves us from blindly
1891
# parsing the xml every tick
1893
# Add / remove new devices
1894
self.repopulate_hw_list()
1896
pagetype = self.get_hw_selection(HW_LIST_COL_TYPE)
1897
if pagetype is None:
1900
if self.window.get_widget("config-apply").get_property("sensitive"):
1901
# Apply button sensitive means user is making changes, don't
1905
self.hw_selected(page=pagetype)
1907
def refresh_overview_page(self):
1909
self.window.get_widget("overview-name").set_text(self.vm.get_name())
1910
self.window.get_widget("overview-uuid").set_text(self.vm.get_uuid())
1911
desc = self.vm.get_description() or ""
1912
desc_widget = self.window.get_widget("overview-description")
1913
desc_widget.get_buffer().set_text(desc)
1915
# Hypervisor Details
1916
self.window.get_widget("overview-hv").set_text(self.vm.get_pretty_hv_type())
1917
arch = self.vm.get_arch() or _("Unknown")
1918
emu = self.vm.get_emulator() or _("None")
1919
self.window.get_widget("overview-arch").set_text(arch)
1920
self.window.get_widget("overview-emulator").set_text(emu)
1923
acpi = self.vm.get_acpi()
1924
apic = self.vm.get_apic()
1925
clock = self.vm.get_clock()
1927
self.window.get_widget("overview-acpi").set_active(acpi)
1928
self.window.get_widget("overview-apic").set_active(apic)
1931
clock = _("Same as host")
1932
self.set_combo_label("overview-clock", 0, clock)
1935
semodel, ignore, vmlabel = self.vm.get_seclabel()
1936
caps = self.vm.get_connection().get_capabilities()
1938
if caps.host.secmodel and caps.host.secmodel.model:
1939
semodel = caps.host.secmodel.model
1940
self.window.get_widget("security-type-box").set_sensitive(bool(semodel))
1941
self.window.get_widget("security-model").set_text(semodel or _("None"))
1943
if self.vm.get_seclabel()[1] == "static":
1944
self.window.get_widget("security-static").set_active(True)
1946
self.window.get_widget("security-dynamic").set_active(True)
1948
self.window.get_widget("security-label").set_text(vmlabel)
1950
def refresh_stats_page(self):
1951
def _dsk_rx_tx_text(rx, tx, unit):
1952
return ('<span color="#82003B">%(rx)d %(unit)s read</span>\n'
1953
'<span color="#295C45">%(tx)d %(unit)s write</span>' %
1954
{"rx": rx, "tx": tx, "unit": unit})
1955
def _net_rx_tx_text(rx, tx, unit):
1956
return ('<span color="#82003B">%(rx)d %(unit)s in</span>\n'
1957
'<span color="#295C45">%(tx)d %(unit)s out</span>' %
1958
{"rx": rx, "tx": tx, "unit": unit})
1960
cpu_txt = _("Disabled")
1961
mem_txt = _("Disabled")
1962
dsk_txt = _("Disabled")
1963
net_txt = _("Disabled")
1965
cpu_txt = "%d %%" % self.vm.cpu_time_percentage()
1967
vm_memory = self.vm.current_memory()
1968
host_memory = self.vm.get_connection().host_memory_size()
1969
mem_txt = "%d MB of %d MB" % (int(round(vm_memory / 1024.0)),
1970
int(round(host_memory / 1024.0)))
1972
if self.config.get_stats_enable_disk_poll():
1973
dsk_txt = _dsk_rx_tx_text(self.vm.disk_read_rate(),
1974
self.vm.disk_write_rate(), "KB/s")
1976
if self.config.get_stats_enable_net_poll():
1977
net_txt = _net_rx_tx_text(self.vm.network_rx_rate(),
1978
self.vm.network_tx_rate(), "KB/s")
1980
self.window.get_widget("overview-cpu-usage-text").set_text(cpu_txt)
1981
self.window.get_widget("overview-memory-usage-text").set_text(mem_txt)
1982
self.window.get_widget("overview-network-traffic-text").set_markup(net_txt)
1983
self.window.get_widget("overview-disk-usage-text").set_markup(dsk_txt)
1985
self.cpu_usage_graph.set_property("data_array",
1986
self.vm.cpu_time_vector())
1987
self.memory_usage_graph.set_property("data_array",
1988
self.vm.current_memory_vector())
1989
self.disk_io_graph.set_property("data_array",
1990
self.vm.disk_io_vector())
1991
self.network_traffic_graph.set_property("data_array",
1992
self.vm.network_traffic_vector())
1994
def _refresh_cpu_count(self):
1995
conn = self.vm.get_connection()
1996
host_active_count = conn.host_active_processor_count()
1997
maxvcpus = self.vm.vcpu_max_count()
1998
curvcpus = self.vm.vcpu_count()
2000
curadj = self.window.get_widget("config-vcpus").get_adjustment()
2001
maxadj = self.window.get_widget("config-maxvcpus").get_adjustment()
2002
curadj.value = int(curvcpus)
2003
maxadj.value = int(maxvcpus)
2005
self.window.get_widget("state-host-cpus").set_text(
2006
str(host_active_count))
2008
# Warn about overcommit
2009
warn = bool(self.config_get_vcpus() > host_active_count)
2010
self.window.get_widget("config-vcpus-warn-box").set_property(
2012
def _refresh_cpu_pinning(self):
2013
# Populate VCPU pinning
2014
vcpupin = self.vm.vcpu_pinning()
2015
self.window.get_widget("config-vcpupin").set_text(vcpupin)
2017
def _refresh_runtime_pinning(self):
2018
conn = self.vm.get_connection()
2019
host_active_count = conn.host_active_processor_count()
2021
vcpu_list = self.window.get_widget("config-vcpu-list")
2022
vcpu_model = vcpu_list.get_model()
2026
if not self.vm.is_active():
2027
reason = _("VCPU info only available for running domain.")
2028
elif not self.vm.getvcpus_supported:
2029
reason = _("Virtual machine does not support runtime VPCU info.")
2032
vcpu_info, vcpu_pinning = self.vm.vcpu_info()
2033
except Exception, e:
2034
reason = _("Error getting VCPU info: %s") % str(e)
2036
vcpu_list.set_sensitive(not bool(reason))
2037
util.tooltip_wrapper(vcpu_list, reason or None)
2041
def build_cpuset_str(pin_info):
2043
for i in range(host_active_count):
2044
if i < len(pin_info) and pin_info[i]:
2045
pinstr += (",%s" % str(i))
2047
return pinstr.strip(",")
2049
for idx in range(len(vcpu_info)):
2050
vcpu = vcpu_info[idx][0]
2051
vcpucur = vcpu_info[idx][3]
2052
vcpupin = build_cpuset_str(vcpu_pinning[idx])
2054
vcpu_model.append([vcpu, vcpucur, vcpupin])
2056
def _refresh_cpu_config(self, cpu):
2057
model = cpu.model or ""
2059
show_top = bool(cpu.sockets or cpu.cores or cpu.threads)
2060
sockets = cpu.sockets or 1
2061
cores = cpu.cores or 1
2062
threads = cpu.threads or 1
2064
self.window.get_widget("cpu-topology-enable").set_active(show_top)
2065
self.window.get_widget("cpu-model").child.set_text(model)
2066
self.window.get_widget("cpu-sockets").set_value(sockets)
2067
self.window.get_widget("cpu-cores").set_value(cores)
2068
self.window.get_widget("cpu-threads").set_value(threads)
2070
def refresh_config_cpu(self):
2071
self._cpu_copy_host = False
2072
cpu = self.vm.get_cpu_config()
2074
self._refresh_cpu_count()
2075
self._refresh_cpu_pinning()
2076
self._refresh_runtime_pinning()
2077
self._refresh_cpu_config(cpu)
2079
def refresh_config_memory(self):
2080
host_mem_widget = self.window.get_widget("state-host-memory")
2081
host_mem = self.vm.get_connection().host_memory_size() / 1024
2082
vm_cur_mem = self.vm.get_memory() / 1024.0
2083
vm_max_mem = self.vm.maximum_memory() / 1024.0
2085
host_mem_widget.set_text("%d MB" % (int(round(host_mem))))
2087
curmem = self.window.get_widget("config-memory").get_adjustment()
2088
maxmem = self.window.get_widget("config-maxmem").get_adjustment()
2089
curmem.value = int(round(vm_cur_mem))
2090
maxmem.value = int(round(vm_max_mem))
2093
self.window.get_widget("config-memory").get_property("sensitive")):
2094
maxmem.lower = curmem.value
2097
def refresh_disk_page(self):
2098
disk = self.get_hw_selection(HW_LIST_COL_DEVICE)
2103
devtype = disk.device
2105
share = disk.shareable
2107
idx = disk.disk_bus_index
2108
cache = disk.driver_cache
2109
driver_type = disk.driver_type or ""
2115
vol = self.conn.get_vol_by_path(path)
2117
size = vol.get_pretty_capacity()
2118
elif not self.conn.is_remote():
2119
ignore, val = virtinst.VirtualDisk.stat_local_path(path)
2121
size = prettyify_bytes(val)
2123
is_cdrom = (devtype == virtinst.VirtualDisk.DEVICE_CDROM)
2124
is_floppy = (devtype == virtinst.VirtualDisk.DEVICE_FLOPPY)
2126
pretty_name = prettyify_disk(devtype, bus, idx)
2128
self.window.get_widget("disk-source-path").set_text(path or "-")
2129
self.window.get_widget("disk-target-type").set_text(pretty_name)
2131
self.window.get_widget("disk-readonly").set_active(ro)
2132
self.window.get_widget("disk-readonly").set_sensitive(not is_cdrom)
2133
self.window.get_widget("disk-shareable").set_active(share)
2134
self.window.get_widget("disk-size").set_text(size)
2135
self.set_combo_label("disk-cache", 0, cache)
2136
self.window.get_widget("disk-format").child.set_text(driver_type)
2138
no_default = not self.is_customize_dialog
2140
self.populate_disk_bus_combo(devtype, no_default)
2141
self.set_combo_label("disk-bus", 0, bus)
2143
button = self.window.get_widget("config-cdrom-connect")
2144
if is_cdrom or is_floppy:
2146
# source device not connected
2147
button.set_label(gtk.STOCK_CONNECT)
2149
button.set_label(gtk.STOCK_DISCONNECT)
2154
def refresh_network_page(self):
2155
net = self.get_hw_selection(HW_LIST_COL_DEVICE)
2160
source = net.get_source()
2164
if nettype == virtinst.VirtualNetworkInterface.TYPE_VIRTUAL:
2166
for uuid in self.conn.list_net_uuids():
2167
vnet = self.conn.get_net(uuid)
2168
name = vnet.get_name()
2169
name_dict[name] = vnet
2171
if source and source in name_dict:
2172
netobj = name_dict[source]
2174
desc = uihelpers.pretty_network_desc(nettype, source, netobj)
2176
self.window.get_widget("network-mac-address").set_text(net.macaddr)
2177
self.window.get_widget("network-source-device").set_text(desc)
2179
uihelpers.populate_netmodel_combo(self.vm,
2180
self.window.get_widget("network-model-combo"))
2181
self.set_combo_label("network-model", 0, model)
2183
def refresh_input_page(self):
2184
inp = self.get_hw_selection(HW_LIST_COL_DEVICE)
2188
ident = "%s:%s" % (inp.type, inp.bus)
2189
if ident == "tablet:usb":
2190
dev = _("EvTouch USB Graphics Tablet")
2191
elif ident == "mouse:usb":
2192
dev = _("Generic USB Mouse")
2193
elif ident == "mouse:xen":
2194
dev = _("Xen Mouse")
2195
elif ident == "mouse:ps2":
2196
dev = _("PS/2 Mouse")
2198
dev = inp.bus + " " + inp.type
2200
if inp.type == "tablet":
2201
mode = _("Absolute Movement")
2203
mode = _("Relative Movement")
2205
self.window.get_widget("input-dev-type").set_text(dev)
2206
self.window.get_widget("input-dev-mode").set_text(mode)
2208
# Can't remove primary Xen or PS/2 mice
2209
if inp.type == "mouse" and inp.bus in ("xen", "ps2"):
2210
self.window.get_widget("config-remove").set_sensitive(False)
2212
self.window.get_widget("config-remove").set_sensitive(True)
2214
def refresh_graphics_page(self):
2215
gfx = self.get_hw_selection(HW_LIST_COL_DEVICE)
2219
title = self.window.get_widget("graphics-title")
2220
table = self.window.get_widget("graphics-table")
2221
table.foreach(lambda w, ignore: w.hide(), ())
2223
def set_title(text):
2224
title.set_markup("<b>%s</b>" % text)
2226
def show_row(widget_name, suffix=""):
2227
base = "gfx-%s" % widget_name
2228
self.window.get_widget(base + "-title").show()
2229
self.window.get_widget(base + suffix).show()
2231
def show_text(widget_name, text):
2232
show_row(widget_name)
2233
self.window.get_widget("gfx-" + widget_name).set_text(text)
2235
def port_to_string(port):
2238
return (port == -1 and _("Automatically allocated") or str(port))
2241
is_vnc = (gtype == "vnc")
2242
is_sdl = (gtype == "sdl")
2243
is_spice = (gtype == "spice")
2244
is_other = not any([is_vnc, is_sdl, is_spice])
2246
set_title(_("%(graphicstype)s Server") %
2247
{"graphicstype" : str(gtype).upper()})
2249
if is_vnc or is_spice:
2250
port = port_to_string(gfx.port)
2251
address = (gfx.listen or "127.0.0.1")
2252
keymap = (gfx.keymap or None)
2254
show_text("port", port)
2255
show_text("address", address)
2257
show_row("keymap", "-box")
2258
self.set_combo_label("gfx-keymap", 0, keymap)
2261
passwd = gfx.passwd or ""
2262
show_text("password", passwd)
2265
tlsport = port_to_string(gfx.tlsPort)
2266
show_text("tlsport", tlsport)
2269
set_title(_("Local SDL Window"))
2271
display = gfx.display or _("Unknown")
2272
xauth = gfx.xauth or _("Unknown")
2274
show_text("display", display)
2275
show_text("xauth", xauth)
2278
show_text("type", str(gtype).upper())
2280
def refresh_sound_page(self):
2281
sound = self.get_hw_selection(HW_LIST_COL_DEVICE)
2285
self.set_combo_label("sound-model", 0, sound.model)
2287
def refresh_char_page(self):
2288
chardev = self.get_hw_selection(HW_LIST_COL_DEVICE)
2292
char_type = chardev.virtual_device_type.capitalize()
2293
target_port = chardev.target_port
2294
dev_type = chardev.char_type or "pty"
2295
src_path = chardev.source_path
2296
primary = hasattr(chardev, "virtmanager_console_dup")
2299
if char_type == "serial":
2300
typelabel = _("Serial Device")
2301
elif char_type == "parallel":
2302
typelabel = _("Parallel Device")
2303
elif char_type == "console":
2304
typelabel = _("Console Device")
2305
elif char_type == "channel":
2306
typelabel = _("Channel Device")
2308
typelabel = _("%s Device") % char_type.capitalize()
2310
if target_port is not None:
2311
typelabel += " %s" % (int(target_port) + 1)
2313
typelabel += " (%s)" % _("Primary Console")
2314
typelabel = "<b>%s</b>" % typelabel
2316
self.window.get_widget("char-type").set_markup(typelabel)
2317
self.window.get_widget("char-dev-type").set_text(dev_type)
2318
self.window.get_widget("char-source-path").set_text(src_path or "-")
2320
def refresh_hostdev_page(self):
2321
hostdev = self.get_hw_selection(HW_LIST_COL_DEVICE)
2325
devtype = hostdev.type
2327
nodedev = lookup_nodedev(self.vm.get_connection(), hostdev)
2329
pretty_name = nodedev.pretty_name()
2332
pretty_name = build_hostdev_label(hostdev)[0] or "-"
2334
devlabel = "<b>Physical %s Device</b>" % devtype.upper()
2335
self.window.get_widget("hostdev-title").set_markup(devlabel)
2336
self.window.get_widget("hostdev-source").set_text(pretty_name)
2338
def refresh_video_page(self):
2339
vid = self.get_hw_selection(HW_LIST_COL_DEVICE)
2343
model = vid.model_type
2347
ramlabel = ram and "%d MB" % (int(ram) / 1024) or "-"
2351
self.window.get_widget("video-ram").set_text(ramlabel)
2352
self.window.get_widget("video-heads").set_text(heads and heads or "-")
2354
self.set_combo_label("video-model", 0, model)
2356
def refresh_watchdog_page(self):
2357
watch = self.get_hw_selection(HW_LIST_COL_DEVICE)
2362
action = watch.action
2364
self.set_combo_label("watchdog-model", 0, model)
2365
self.set_combo_label("watchdog-action", 0, action)
2367
def refresh_controller_page(self):
2368
dev = self.get_hw_selection(HW_LIST_COL_DEVICE)
2372
type_label = virtinst.VirtualController.pretty_type(dev.type)
2373
self.window.get_widget("controller-type").set_text(type_label)
2375
def refresh_boot_page(self):
2378
# Older libvirt versions return None if not supported
2379
autoval = self.vm.get_autostart()
2380
except libvirt.libvirtError:
2383
autostart_chk = self.window.get_widget("config-autostart")
2384
enable_autostart = (autoval is not None)
2385
autostart_chk.set_sensitive(enable_autostart)
2386
autostart_chk.set_active(enable_autostart and autoval or False)
2388
menu = self.vm.get_boot_menu() or False
2389
self.window.get_widget("boot-menu").set_active(menu)
2391
kernel, initrd, args = self.vm.get_boot_kernel_info()
2392
expand = bool(kernel or initrd or args)
2393
self.window.get_widget("boot-kernel").set_text(kernel or "")
2394
self.window.get_widget("boot-kernel-initrd").set_text(initrd or "")
2395
self.window.get_widget("boot-kernel-args").set_text(args or "")
2397
self.window.get_widget("boot-kernel-expander").set_expanded(True)
2399
# Refresh Boot Device list
2400
self.repopulate_boot_list()
2403
############################
2404
# Hardware list population #
2405
############################
2407
def populate_disk_bus_combo(self, devtype, no_default):
2408
buslist = self.window.get_widget("disk-bus-combo")
2409
busmodel = buslist.get_model()
2413
if devtype == virtinst.VirtualDisk.DEVICE_FLOPPY:
2414
buses.append(["fdc", "Floppy"])
2415
elif devtype == virtinst.VirtualDisk.DEVICE_CDROM:
2416
buses.append(["ide", "IDE"])
2417
buses.append(["scsi", "SCSI"])
2419
if self.vm.is_hvm():
2420
buses.append(["ide", "IDE"])
2421
buses.append(["scsi", "SCSI"])
2422
buses.append(["usb", "USB"])
2423
if self.vm.get_hv_type() == "kvm":
2424
buses.append(["virtio", "Virtio"])
2425
if self.vm.get_connection().is_xen():
2426
buses.append(["xen", "Xen"])
2429
busmodel.append(row)
2431
busmodel.append([None, "default"])
2433
def populate_hw_list(self):
2434
hw_list_model = self.window.get_widget("hw-list").get_model()
2435
hw_list_model.clear()
2437
def add_hw_list_option(title, page_id, data, icon_name):
2438
hw_list_model.append([title, icon_name,
2439
gtk.ICON_SIZE_LARGE_TOOLBAR,
2442
add_hw_list_option("Overview", HW_LIST_TYPE_GENERAL, [], "computer")
2443
if not self.is_customize_dialog:
2444
add_hw_list_option("Performance", HW_LIST_TYPE_STATS, [],
2445
"utilities-system-monitor")
2446
add_hw_list_option("Processor", HW_LIST_TYPE_CPU, [], "device_cpu")
2447
add_hw_list_option("Memory", HW_LIST_TYPE_MEMORY, [], "device_mem")
2448
add_hw_list_option("Boot Options", HW_LIST_TYPE_BOOT, [], "system-run")
2450
self.repopulate_hw_list()
2452
def repopulate_hw_list(self):
2453
hw_list = self.window.get_widget("hw-list")
2454
hw_list_model = hw_list.get_model()
2458
def dev_cmp(origdev, newdev):
2462
if origdev == newdev:
2465
if not origdev.get_xml_node_path():
2468
return origdev.get_xml_node_path() == newdev.get_xml_node_path()
2470
def add_hw_list_option(idx, name, page_id, info, icon_name):
2471
hw_list_model.insert(idx, [name, icon_name,
2472
gtk.ICON_SIZE_LARGE_TOOLBAR,
2475
def update_hwlist(hwtype, info, name, icon_name):
2477
See if passed hw is already in list, and if so, update info.
2478
If not in list, add it!
2480
currentDevices.append(info)
2483
for row in hw_list_model:
2484
rowdev = row[HW_LIST_COL_DEVICE]
2485
if dev_cmp(rowdev, info):
2486
# Update existing HW info
2487
row[HW_LIST_COL_DEVICE] = info
2488
row[HW_LIST_COL_LABEL] = name
2489
row[HW_LIST_COL_ICON_NAME] = icon_name
2492
if row[HW_LIST_COL_TYPE] <= hwtype:
2495
# Add the new HW row
2496
add_hw_list_option(insertAt, name, hwtype, info, icon_name)
2498
# Populate list of disks
2499
for disk in self.vm.get_disk_devices():
2500
devtype = disk.device
2502
idx = disk.disk_bus_index
2504
icon = "drive-harddisk"
2505
if devtype == "cdrom":
2506
icon = "media-optical"
2507
elif devtype == "floppy":
2508
icon = "media-floppy"
2510
label = prettyify_disk(devtype, bus, idx)
2512
update_hwlist(HW_LIST_TYPE_DISK, disk, label, icon)
2514
# Populate list of NICs
2515
for net in self.vm.get_network_devices():
2518
update_hwlist(HW_LIST_TYPE_NIC, net,
2519
"NIC %s" % mac[-9:], "network-idle")
2521
# Populate list of input devices
2522
for inp in self.vm.get_input_devices():
2525
icon = "input-mouse"
2526
if inptype == "tablet":
2528
icon = "input-tablet"
2529
elif inptype == "mouse":
2534
update_hwlist(HW_LIST_TYPE_INPUT, inp, label, icon)
2536
# Populate list of graphics devices
2537
for gfx in self.vm.get_graphics_devices():
2538
update_hwlist(HW_LIST_TYPE_GRAPHICS, gfx,
2539
_("Display %s") % (gfx.type.upper()),
2542
# Populate list of sound devices
2543
for sound in self.vm.get_sound_devices():
2544
update_hwlist(HW_LIST_TYPE_SOUND, sound,
2545
_("Sound: %s" % sound.model), "audio-card")
2547
# Populate list of char devices
2548
for chardev in self.vm.get_char_devices():
2549
devtype = chardev.virtual_device_type
2550
port = chardev.target_port
2552
label = devtype.capitalize()
2553
if devtype != "console":
2554
# Don't show port for console
2555
label += " %s" % (int(port) + 1)
2557
update_hwlist(HW_LIST_TYPE_CHAR, chardev, label,
2560
# Populate host devices
2561
for hostdev in self.vm.get_hostdev_devices():
2562
devtype = hostdev.type
2563
label = build_hostdev_label(hostdev)[1]
2565
if devtype == "usb":
2569
update_hwlist(HW_LIST_TYPE_HOSTDEV, hostdev, label, icon)
2571
# Populate video devices
2572
for vid in self.vm.get_video_devices():
2573
update_hwlist(HW_LIST_TYPE_VIDEO, vid, _("Video"), "video-display")
2575
for watch in self.vm.get_watchdog_devices():
2576
update_hwlist(HW_LIST_TYPE_WATCHDOG, watch, _("Watchdog"),
2579
for cont in self.vm.get_controller_devices():
2580
pretty_type = virtinst.VirtualController.pretty_type(cont.type)
2581
update_hwlist(HW_LIST_TYPE_CONTROLLER, cont,
2582
_("Controller %s") % pretty_type,
2585
devs = range(len(hw_list_model))
2588
_iter = hw_list_model.iter_nth_child(None, i)
2589
olddev = hw_list_model[i][HW_LIST_COL_DEVICE]
2591
# Existing device, don't remove it
2592
if not olddev or olddev in currentDevices:
2595
hw_list_model.remove(_iter)
2597
def repopulate_boot_list(self, bootdevs=None, dev_select=None):
2598
boot_list = self.window.get_widget("config-boot-list")
2599
boot_model = boot_list.get_model()
2600
old_order = map(lambda x: x[BOOT_DEV_TYPE], boot_model)
2603
if bootdevs == None:
2604
bootdevs = self.vm.get_boot_device()
2607
"hd" : ["hd", "Hard Disk", "drive-harddisk", False],
2608
"cdrom" : ["cdrom", "CDROM", "media-optical", False],
2609
"network" : ["network", "Network (PXE)", "network-idle", False],
2610
"fd" : ["fd", "Floppy", "media-floppy", False],
2613
for dev in bootdevs:
2616
for key, row in boot_rows.items():
2623
# Some boot device listed that we don't know about.
2624
foundrow = [dev, "Boot type '%s'" % dev,
2625
"drive-harddisk", True]
2627
foundrow[BOOT_ACTIVE] = True
2628
boot_model.append(foundrow)
2630
# Append all remaining boot_rows that aren't enabled
2631
for dev in old_order:
2632
if dev in boot_rows:
2633
boot_model.append(boot_rows[dev])
2636
for row in boot_rows.values():
2637
boot_model.append(row)
2639
boot_list.set_model(boot_model)
2640
selection = boot_list.get_selection()
2644
for row in boot_model:
2645
if row[BOOT_DEV_TYPE] == dev_select:
2649
boot_list.get_selection().select_path(str(idx))
2651
elif not selection.get_selected()[1]:
2652
# Set a default selection
2653
selection.select_path("0")
2656
vmmGObjectUI.type_register(vmmDetails)