2
# Copyright (C) 2009 Red Hat, Inc.
3
# Copyright (C) 2009 Cole Robinson <crobinso@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
from virtinst import VirtualNetworkInterface
29
from virtinst import VirtualDisk
31
from virtManager import util
32
from virtManager.error import vmmErrorDialog
36
OPTICAL_IS_MEDIA_PRESENT = 2
41
# What user we guess the qemu:///system starts the emulator as. Some distros
42
# may use a nonroot user, so simply changing this will cause several UI
43
# pieces to attempt to verify that permissions are correct. Eventually this
44
# should be exposed via capabilities so we can determine this programmatically.
45
QEMU_SYSTEM_EMULATOR_USER = "root"
47
##############################################################
48
# Initialize an error object to use for validation functions #
49
##############################################################
51
err_dial = vmmErrorDialog(None,
52
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
53
_("Unexpected Error"),
54
_("An unexpected error occurred"))
56
def set_error_parent(parent):
58
err_dial.set_parent(parent)
61
############################################################
62
# Helpers for shared storage UI between create/addhardware #
63
############################################################
65
def set_sparse_tooltip(widget):
66
sparse_str = _("Fully allocating storage will take longer now, "
67
"but the OS install phase will be quicker. \n\n"
68
"Skipping allocation can also cause space issues on "
69
"the host machine, if the maximum image size exceeds "
70
"available storage space.")
71
util.tooltip_wrapper(widget, sparse_str)
73
def host_disk_space(conn, config):
74
pool = util.get_default_pool(conn)
75
path = util.get_default_dir(conn, config)
79
# FIXME: make sure not inactive?
80
# FIXME: use a conn specific function after we send pool-added
81
pool = virtinst.util.lookup_pool_by_path(conn.vmm, path)
84
avail = int(virtinst.util.get_xml_path(pool.XMLDesc(0),
87
elif not conn.is_remote():
88
vfs = os.statvfs(os.path.dirname(path))
89
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
91
return float(avail / 1024.0 / 1024.0 / 1024.0)
93
def host_space_tick(conn, config, widget):
94
max_storage = host_disk_space(conn, config)
96
def pretty_storage(size):
97
return "%.1f Gb" % float(size)
99
hd_label = ("%s available in the default location" %
100
pretty_storage(max_storage))
101
hd_label = ("<span color='#484848'>%s</span>" % hd_label)
102
widget.set_markup(hd_label)
106
#####################################################
107
# Hardware model list building (for details, addhw) #
108
#####################################################
109
def build_video_combo(vm, video_dev, no_default=False):
110
video_dev_model = gtk.ListStore(str)
111
video_dev.set_model(video_dev_model)
112
text = gtk.CellRendererText()
113
video_dev.pack_start(text, True)
114
video_dev.add_attribute(text, 'text', 0)
115
video_dev_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
117
tmpdev = virtinst.VirtualVideoDevice(vm.get_connection().vmm)
118
for m in tmpdev.model_types:
119
if m == tmpdev.MODEL_DEFAULT and no_default:
121
video_dev_model.append([m])
122
if len(video_dev_model) > 0:
123
video_dev.set_active(0)
125
def build_sound_combo(vm, combo, no_default=False):
126
dev_model = gtk.ListStore(str)
127
combo.set_model(dev_model)
128
text = gtk.CellRendererText()
129
combo.pack_start(text, True)
130
combo.add_attribute(text, 'text', 0)
131
dev_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
133
for m in virtinst.VirtualAudio.MODELS:
134
if m == virtinst.VirtualAudio.MODEL_DEFAULT and no_default:
136
dev_model.append([m])
137
if len(dev_model) > 0:
140
def build_watchdogmodel_combo(vm, combo, no_default=False):
141
dev_model = gtk.ListStore(str)
142
combo.set_model(dev_model)
143
text = gtk.CellRendererText()
144
combo.pack_start(text, True)
145
combo.add_attribute(text, 'text', 0)
146
dev_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
148
for m in virtinst.VirtualWatchdog.MODELS:
149
if m == virtinst.VirtualAudio.MODEL_DEFAULT and no_default:
151
dev_model.append([m])
152
if len(dev_model) > 0:
155
def build_watchdogaction_combo(vm, combo, no_default=False):
156
dev_model = gtk.ListStore(str, str)
157
combo.set_model(dev_model)
158
text = gtk.CellRendererText()
159
combo.pack_start(text, True)
160
combo.add_attribute(text, 'text', 1)
161
dev_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
163
for m in virtinst.VirtualWatchdog.ACTIONS:
164
if m == virtinst.VirtualWatchdog.ACTION_DEFAULT and no_default:
166
dev_model.append([m, virtinst.VirtualWatchdog.get_action_desc(m)])
167
if len(dev_model) > 0:
170
def build_netmodel_combo(vm, combo):
171
dev_model = gtk.ListStore(str, str)
172
combo.set_model(dev_model)
173
text = gtk.CellRendererText()
174
combo.pack_start(text, True)
175
combo.add_attribute(text, 'text', 1)
176
dev_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
178
populate_netmodel_combo(vm, combo)
181
def populate_netmodel_combo(vm, combo):
182
model = combo.get_model()
186
model.append([None, _("Hypervisor default")])
188
mod_list = [ "rtl8139", "ne2k_pci", "pcnet" ]
189
if vm.get_hv_type() == "kvm":
190
mod_list.append("e1000")
191
mod_list.append("virtio")
198
#######################################################################
199
# Widgets for listing network device options (in create, addhardware) #
200
#######################################################################
202
def pretty_network_desc(nettype, source=None, netobj=None):
203
if nettype == VirtualNetworkInterface.TYPE_USER:
204
return _("Usermode networking")
207
if nettype == VirtualNetworkInterface.TYPE_BRIDGE:
209
elif nettype == VirtualNetworkInterface.TYPE_VIRTUAL:
210
ret = _("Virtual network")
212
extra = ": %s" % netobj.pretty_forward_mode()
214
ret = nettype.capitalize()
217
ret += " '%s'" % source
223
def init_network_list(net_list, bridge_box):
224
# [ network type, source name, label, sensitive?, net is active,
226
net_model = gtk.ListStore(str, str, str, bool, bool, bool)
227
net_list.set_model(net_model)
229
net_list.connect("changed", net_list_changed, bridge_box)
231
text = gtk.CellRendererText()
232
net_list.pack_start(text, True)
233
net_list.add_attribute(text, 'text', 2)
234
net_list.add_attribute(text, 'sensitive', 3)
236
def net_list_changed(net_list, bridge_box):
237
active = net_list.get_active()
241
row = net_list.get_model()[active]
244
bridge_box.set_property("visible", show_bridge)
246
def get_network_selection(net_list, bridge_entry):
247
row = net_list.get_model()[net_list.get_active()]
250
net_check_bridge = row[5]
253
net_type = VirtualNetworkInterface.TYPE_BRIDGE
254
net_src = bridge_entry.get_text()
256
return net_type, net_src
258
def populate_network_list(net_list, conn):
259
model = net_list.get_model()
268
model.append(build_row(*args))
270
def build_row(nettype, name, label, is_sensitive, is_running,
271
manual_bridge=False):
272
return [nettype, name, label, is_sensitive, is_running, manual_bridge]
275
net_list.set_active(idx)
277
def add_dict(indict, model):
278
keylist = indict.keys()
280
rowlist = map(lambda key: indict[key], keylist)
284
# For qemu:///session
285
if conn.is_qemu_session():
286
nettype = VirtualNetworkInterface.TYPE_USER
287
add_row(nettype, None, pretty_network_desc(nettype), True)
294
for uuid in conn.list_net_uuids():
295
net = conn.get_net(uuid)
296
nettype = VirtualNetworkInterface.TYPE_VIRTUAL
298
label = pretty_network_desc(nettype, net.get_name(), net)
299
if not net.is_active():
300
label += " (%s)" % _("Inactive")
303
# FIXME: Should we use 'default' even if it's inactive?
304
# FIXME: This preference should be configurable
305
if net.get_name() == "default":
308
vnet_dict[label] = build_row(nettype, net.get_name(), label, True,
311
# Build a list of vnet bridges, so we know not to list them
312
# in the physical interface list
313
vnet_bridge = net.get_bridge_device()
315
vnet_bridges.append(vnet_bridge)
318
label = _("No virtual networks available")
319
vnet_dict[label] = build_row(None, None, label, False, False)
324
for name in conn.list_net_device_paths():
325
br = conn.get_net_device(name)
326
bridge_name = br.get_bridge()
327
nettype = VirtualNetworkInterface.TYPE_BRIDGE
329
if (bridge_name in vnet_bridges) or (br.get_name() in vnet_bridges):
330
# Don't list this, as it is basically duplicating virtual net info
337
brlabel = "(%s)" % pretty_network_desc(nettype, bridge_name)
340
brlabel = _("(Empty bridge)")
343
brlabel = "(%s)" % _("Not bridged")
345
label = _("Host device %s %s") % (br.get_name(), brlabel)
346
if hasShared and not brIdxLabel:
349
row = build_row(nettype, bridge_name, label, sensitive, True)
352
bridge_dict[label] = row
354
iface_dict[label] = row
356
add_dict(bridge_dict, model)
357
add_dict(vnet_dict, model)
358
add_dict(iface_dict, model)
360
# If there is a bridge device, default to that
361
# If not, use 'default' network
362
# If not present, use first list entry
363
# If list empty, use no network devices
365
label = brIdxLabel or netIdxLabel
367
for idx in range(len(model)):
369
is_inactive = not row[4]
373
return_warn = is_inactive
378
return_warn = is_inactive
382
row = build_row(None, None, _("No networking."), True, False)
386
# After all is said and done, add a manual bridge option
387
manual_row = build_row(None, None, _("Specify shared device name"),
388
True, False, manual_bridge=True)
389
model.append(manual_row)
394
def validate_network(parent, conn, nettype, devname, macaddr, model=None):
395
set_error_parent(parent)
402
# Make sure VirtualNetwork is running
403
if (nettype == VirtualNetworkInterface.TYPE_VIRTUAL and
404
devname not in conn.vmm.listNetworks()):
406
res = err_dial.yes_no(_("Virtual Network is not active."),
407
_("Virtual Network '%s' is not active. "
408
"Would you like to start the network "
413
# Try to start the network
415
virnet = conn.vmm.networkLookupByName(devname)
417
logging.info("Started network '%s'." % devname)
419
return err_dial.show_err(_("Could not start virtual network "
420
"'%s': %s") % (devname, str(e)),
421
"".join(traceback.format_exc()))
423
# Create network device
427
if nettype == VirtualNetworkInterface.TYPE_VIRTUAL:
429
elif nettype == VirtualNetworkInterface.TYPE_BRIDGE:
431
elif nettype == VirtualNetworkInterface.TYPE_USER:
434
net = VirtualNetworkInterface(type = nettype,
440
return err_dial.val_err(_("Error with network parameters."), str(e))
442
# Make sure there is no mac address collision
443
isfatal, errmsg = net.is_conflict_net(conn.vmm)
445
return err_dial.val_err(_("Mac address collision."), errmsg)
446
elif errmsg is not None:
447
retv = err_dial.yes_no(_("Mac address collision."),
448
_("%s Are you sure you want to use this "
449
"address?") % errmsg)
455
def generate_macaddr(conn):
458
net = VirtualNetworkInterface(conn=conn.vmm)
467
############################################
468
# Populate media widget (choosecd, create) #
469
############################################
471
def init_mediadev_combo(widget):
472
# [Device path, pretty label, has_media?, device key, media key,
473
# vmmMediaDevice, is valid device]
474
model = gtk.ListStore(str, str, bool, str, str, bool)
475
widget.set_model(model)
478
text = gtk.CellRendererText()
479
widget.pack_start(text, True)
480
widget.add_attribute(text, 'text', OPTICAL_LABEL)
481
widget.add_attribute(text, 'sensitive', OPTICAL_IS_VALID)
483
def populate_mediadev_combo(conn, widget, devtype):
486
model = widget.get_model()
488
set_mediadev_default(model)
490
sigs.append(conn.connect("mediadev-added", mediadev_added, widget, devtype))
491
sigs.append(conn.connect("mediadev-removed", mediadev_removed, widget))
493
widget.set_active(-1)
494
mediadev_set_default_selection(widget)
498
def set_mediadev_default(model):
500
model.append([None, _("No device present"), False, None, None, False])
502
def set_row_from_object(row, obj):
503
row[OPTICAL_DEV_PATH] = obj.get_path()
504
row[OPTICAL_LABEL] = obj.pretty_label()
505
row[OPTICAL_IS_MEDIA_PRESENT] = obj.has_media()
506
row[OPTICAL_DEV_KEY] = obj.get_key()
507
row[OPTICAL_MEDIA_KEY] = obj.get_media_key()
508
row[OPTICAL_IS_VALID] = True
510
def mediadev_removed(ignore_helper, key, widget):
511
model = widget.get_model()
512
active = widget.get_active()
516
if row[OPTICAL_DEV_KEY] == key:
517
# Whole device removed
520
if idx > active and active != -1:
521
widget.set_active(active-1)
523
widget.set_active(-1)
527
set_mediadev_default(model)
528
mediadev_set_default_selection(widget)
530
def mediadev_added(ignore_helper, newobj, widget, devtype):
531
model = widget.get_model()
533
if newobj.get_media_type() != devtype:
536
if len(model) == 1 and model[0][OPTICAL_IS_VALID] == False:
537
# Only entry is the 'No device' entry
540
newobj.connect("media-added", mediadev_media_changed, widget)
541
newobj.connect("media-removed", mediadev_media_changed, widget)
544
row = [None, None, None, None, None, None]
545
set_row_from_object(row, newobj)
548
mediadev_set_default_selection(widget)
550
def mediadev_media_changed(newobj, widget):
551
model = widget.get_model()
552
active = widget.get_active()
555
# Search for the row with matching device node and
556
# fill in info about inserted media. If model has no current
557
# selection, select the new media.
559
if row[OPTICAL_DEV_PATH] == newobj.get_path():
560
set_row_from_object(row, newobj)
561
has_media = row[OPTICAL_IS_MEDIA_PRESENT]
563
if has_media and active == -1:
564
widget.set_active(idx)
565
elif not has_media and active == idx:
566
widget.set_active(-1)
570
mediadev_set_default_selection(widget)
572
def mediadev_set_default_selection(widget):
573
# Set the first active cdrom device as selected, otherwise none
574
model = widget.get_model()
576
active = widget.get_active()
579
# already a selection, don't change it
583
if row[OPTICAL_IS_MEDIA_PRESENT] == True:
584
widget.set_active(idx)
588
widget.set_active(-1)
591
####################################################################
592
# Build toolbar shutdown button menu (manager and details toolbar) #
593
####################################################################
595
def build_shutdown_button_menu(config, widget, shutdown_cb, reboot_cb,
597
icon_name = config.get_shutdown_icon_name()
598
widget.set_icon_name(icon_name)
600
widget.set_menu(menu)
602
rebootimg = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
603
shutdownimg = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
604
destroyimg = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
606
reboot = gtk.ImageMenuItem(_("_Reboot"))
607
reboot.set_image(rebootimg)
609
reboot.connect("activate", reboot_cb)
612
shutdown = gtk.ImageMenuItem(_("_Shut Down"))
613
shutdown.set_image(shutdownimg)
615
shutdown.connect("activate", shutdown_cb)
618
destroy = gtk.ImageMenuItem(_("_Force Off"))
619
destroy.set_image(destroyimg)
621
destroy.connect("activate", destroy_cb)
624
#####################################
625
# Path permissions checker for qemu #
626
#####################################
627
def check_path_search_for_qemu(parent, config, conn, path):
628
set_error_parent(parent)
630
if conn.is_remote() or not conn.is_qemu_system():
633
user = QEMU_SYSTEM_EMULATOR_USER
635
skip_paths = config.get_perms_fix_ignore()
636
broken_paths = VirtualDisk.check_path_search_for_user(conn.vmm, path, user)
637
for p in broken_paths:
639
broken_paths.remove(p)
644
logging.debug("No search access for dirs: %s" % broken_paths)
645
resp, chkres = err_dial.warn_chkbox(
646
_("The emulator may not have search permissions "
647
"for the path '%s'.") % path,
648
_("Do you want to correct this now?"),
649
_("Don't ask about these directories again."),
650
buttons=gtk.BUTTONS_YES_NO)
653
config.add_perms_fix_ignore(broken_paths)
657
logging.debug("Attempting to correct permission issues.")
658
errors = VirtualDisk.fix_path_search_for_user(conn.vmm, path, user)
662
errmsg = _("Errors were encountered changing permissions for the "
663
"following directories:")
665
for path, error in errors.items():
666
if path not in broken_paths:
668
details += "%s : %s\n" % (path, error)
670
logging.debug("Permission errors:\n%s" % details)
672
ignore, chkres = err_dial.err_chkbox(errmsg, details,
673
_("Don't ask about these directories again."))
676
config.add_perms_fix_ignore(errors.keys())
678
######################################
679
# Interface startmode widget builder #
680
######################################
682
def build_startmode_combo(start_list):
683
start_model = gtk.ListStore(str)
684
start_list.set_model(start_model)
685
text = gtk.CellRendererText()
686
start_list.pack_start(text, True)
687
start_list.add_attribute(text, 'text', 0)
688
start_model.append(["none"])
689
start_model.append(["onboot"])
690
start_model.append(["hotplug"])