mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-03-09 08:58:27 +03:00
It's kind of needy to be doing a pool.refresh every 3 seconds, and really I'm fine with telling people they have to close and reopen the wizard to get up2date host size reporting.
1075 lines
32 KiB
Python
1075 lines
32 KiB
Python
#
|
|
# Copyright (C) 2009, 2013 Red Hat, Inc.
|
|
# Copyright (C) 2009 Cole Robinson <crobinso@redhat.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301 USA.
|
|
#
|
|
|
|
import logging
|
|
import os
|
|
import statvfs
|
|
|
|
# pylint: disable=E0611
|
|
from gi.repository import Gtk
|
|
# pylint: enable=E0611
|
|
|
|
import virtinst
|
|
|
|
from virtManager import util
|
|
from virtManager.error import vmmErrorDialog
|
|
|
|
OPTICAL_DEV_PATH = 0
|
|
OPTICAL_LABEL = 1
|
|
OPTICAL_IS_MEDIA_PRESENT = 2
|
|
OPTICAL_DEV_KEY = 3
|
|
OPTICAL_MEDIA_KEY = 4
|
|
OPTICAL_IS_VALID = 5
|
|
|
|
|
|
##############################################################
|
|
# Initialize an error object to use for validation functions #
|
|
##############################################################
|
|
|
|
err_dial = vmmErrorDialog()
|
|
|
|
|
|
def set_error_parent(parent):
|
|
global err_dial
|
|
err_dial.set_parent(parent)
|
|
err_dial = err_dial
|
|
|
|
|
|
def cleanup():
|
|
global err_dial
|
|
err_dial = None
|
|
|
|
|
|
def spin_get_helper(widget):
|
|
adj = widget.get_adjustment()
|
|
txt = widget.get_text()
|
|
|
|
try:
|
|
ret = int(txt)
|
|
except:
|
|
ret = adj.get_value()
|
|
return ret
|
|
|
|
############################################################
|
|
# Helpers for shared storage UI between create/addhardware #
|
|
############################################################
|
|
|
|
|
|
def set_sparse_tooltip(widget):
|
|
sparse_str = _("Fully allocating storage may take longer now, "
|
|
"but the OS install phase will be quicker. \n\n"
|
|
"Skipping allocation can also cause space issues on "
|
|
"the host machine, if the maximum image size exceeds "
|
|
"available storage space. \n\n"
|
|
"Tip: Storage format qcow2 and qed "
|
|
"do not support full allocation.")
|
|
widget.set_tooltip_text(sparse_str)
|
|
|
|
|
|
def host_disk_space(conn):
|
|
pool = util.get_default_pool(conn)
|
|
path = util.get_default_dir(conn)
|
|
|
|
avail = 0
|
|
if pool and pool.is_active():
|
|
# FIXME: make sure not inactive?
|
|
# FIXME: use a conn specific function after we send pool-added
|
|
pool.refresh()
|
|
avail = int(util.xpath(pool.get_xml(), "/pool/available"))
|
|
|
|
elif not conn.is_remote() and os.path.exists(path):
|
|
vfs = os.statvfs(os.path.dirname(path))
|
|
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
|
|
|
|
return float(avail / 1024.0 / 1024.0 / 1024.0)
|
|
|
|
|
|
def update_host_space(conn, widget):
|
|
try:
|
|
max_storage = host_disk_space(conn)
|
|
except:
|
|
logging.exception("Error determining host disk space")
|
|
|
|
def pretty_storage(size):
|
|
return "%.1f Gb" % float(size)
|
|
|
|
hd_label = ("%s available in the default location" %
|
|
pretty_storage(max_storage))
|
|
hd_label = ("<span color='#484848'>%s</span>" % hd_label)
|
|
widget.set_markup(hd_label)
|
|
|
|
|
|
def check_default_pool_active(topwin, conn):
|
|
default_pool = util.get_default_pool(conn)
|
|
if default_pool and not default_pool.is_active():
|
|
res = err_dial.yes_no(_("Default pool is not active."),
|
|
_("Storage pool '%s' is not active. "
|
|
"Would you like to start the pool "
|
|
"now?") % default_pool.get_name())
|
|
if not res:
|
|
return False
|
|
|
|
# Try to start the pool
|
|
try:
|
|
default_pool.start()
|
|
logging.info("Started pool '%s'", default_pool.get_name())
|
|
except Exception, e:
|
|
return topwin.err.show_err(_("Could not start storage_pool "
|
|
"'%s': %s") %
|
|
(default_pool.get_name(), str(e)))
|
|
return True
|
|
|
|
#####################################################
|
|
# Hardware model list building (for details, addhw) #
|
|
#####################################################
|
|
|
|
|
|
def build_video_combo(vm, video_dev, no_default=None):
|
|
video_dev_model = Gtk.ListStore(str, str)
|
|
video_dev.set_model(video_dev_model)
|
|
text = Gtk.CellRendererText()
|
|
video_dev.pack_start(text, True)
|
|
video_dev.add_attribute(text, 'text', 1)
|
|
video_dev_model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
|
|
|
populate_video_combo(vm, video_dev, no_default)
|
|
|
|
|
|
def populate_video_combo(vm, video_dev, no_default=None):
|
|
video_dev_model = video_dev.get_model()
|
|
has_spice = bool([g for g in vm.get_graphics_devices()
|
|
if g.type == g.TYPE_SPICE])
|
|
has_qxl = bool([v for v in vm.get_video_devices()
|
|
if v.model_type == "qxl"])
|
|
|
|
video_dev_model.clear()
|
|
tmpdev = virtinst.VirtualVideoDevice(vm.conn.get_backend())
|
|
for m in tmpdev.model_types:
|
|
if not vm.rhel6_defaults():
|
|
if m == "qxl" and not has_spice and not has_qxl:
|
|
# Only list QXL video option when VM has SPICE video
|
|
continue
|
|
|
|
if m == tmpdev.MODEL_DEFAULT and no_default:
|
|
continue
|
|
video_dev_model.append([m, tmpdev.pretty_model(m)])
|
|
|
|
if len(video_dev_model) > 0:
|
|
video_dev.set_active(0)
|
|
|
|
|
|
def build_sound_combo(vm, combo, no_default=False):
|
|
dev_model = Gtk.ListStore(str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 0)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
disable_rhel = not vm.rhel6_defaults()
|
|
rhel_soundmodels = ["ich6", "ac97"]
|
|
|
|
for m in virtinst.VirtualAudio.MODELS:
|
|
if m == virtinst.VirtualAudio.MODEL_DEFAULT and no_default:
|
|
continue
|
|
|
|
if (disable_rhel and m not in rhel_soundmodels):
|
|
continue
|
|
|
|
dev_model.append([m])
|
|
if len(dev_model) > 0:
|
|
combo.set_active(0)
|
|
|
|
|
|
def build_watchdogmodel_combo(vm, combo, no_default=False):
|
|
ignore = vm
|
|
dev_model = Gtk.ListStore(str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 0)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
for m in virtinst.VirtualWatchdog.MODELS:
|
|
if m == virtinst.VirtualAudio.MODEL_DEFAULT and no_default:
|
|
continue
|
|
dev_model.append([m])
|
|
if len(dev_model) > 0:
|
|
combo.set_active(0)
|
|
|
|
|
|
def build_watchdogaction_combo(vm, combo, no_default=False):
|
|
ignore = vm
|
|
dev_model = Gtk.ListStore(str, str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
for m in virtinst.VirtualWatchdog.ACTIONS:
|
|
if m == virtinst.VirtualWatchdog.ACTION_DEFAULT and no_default:
|
|
continue
|
|
dev_model.append([m, virtinst.VirtualWatchdog.get_action_desc(m)])
|
|
if len(dev_model) > 0:
|
|
combo.set_active(0)
|
|
|
|
|
|
def build_source_mode_combo(vm, combo):
|
|
source_mode = Gtk.ListStore(str, str)
|
|
combo.set_model(source_mode)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
|
|
populate_source_mode_combo(vm, combo)
|
|
combo.set_active(0)
|
|
|
|
|
|
def populate_source_mode_combo(vm, combo):
|
|
ignore = vm
|
|
model = combo.get_model()
|
|
model.clear()
|
|
|
|
# [xml value, label]
|
|
model.append([None, "Default"])
|
|
model.append(["vepa", "VEPA"])
|
|
model.append(["bridge", "Bridge"])
|
|
model.append(["private", "Private"])
|
|
model.append(["passthrough", "Passthrough"])
|
|
|
|
|
|
def build_smartcard_mode_combo(vm, combo):
|
|
dev_model = Gtk.ListStore(str, str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
populate_smartcard_mode_combo(vm, combo)
|
|
|
|
idx = -1
|
|
for rowid in range(len(combo.get_model())):
|
|
idx = 0
|
|
row = combo.get_model()[rowid]
|
|
if row[0] == virtinst.VirtualSmartCardDevice.MODE_DEFAULT:
|
|
idx = rowid
|
|
break
|
|
combo.set_active(idx)
|
|
|
|
|
|
def populate_smartcard_mode_combo(vm, combo):
|
|
ignore = vm
|
|
model = combo.get_model()
|
|
model.clear()
|
|
|
|
# [xml value, label]
|
|
model.append(["passthrough", "Passthrough"])
|
|
model.append(["host", "Host"])
|
|
|
|
|
|
def build_redir_type_combo(vm, combo):
|
|
source_mode = Gtk.ListStore(str, str, bool)
|
|
combo.set_model(source_mode)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
|
|
populate_redir_type_combo(vm, combo)
|
|
combo.set_active(0)
|
|
|
|
|
|
def populate_redir_type_combo(vm, combo):
|
|
ignore = vm
|
|
model = combo.get_model()
|
|
model.clear()
|
|
|
|
# [xml value, label, conn details]
|
|
model.append(["spicevmc", "Spice channel", False])
|
|
model.append(["tcp", "TCP", True])
|
|
|
|
|
|
def build_tpm_type_combo(vm, combo):
|
|
dev_model = Gtk.ListStore(str, str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
populate_tpm_type_combo(vm, combo)
|
|
|
|
idx = -1
|
|
for rowid in range(len(combo.get_model())):
|
|
idx = 0
|
|
row = combo.get_model()[rowid]
|
|
if row[0] == virtinst.VirtualTPMDevice.TYPE_DEFAULT:
|
|
idx = rowid
|
|
break
|
|
combo.set_active(idx)
|
|
|
|
|
|
def populate_tpm_type_combo(vm, combo):
|
|
ignore = vm
|
|
types = combo.get_model()
|
|
types.clear()
|
|
|
|
# [xml value, label]
|
|
types.append(["passthrough", "Passthrough device"])
|
|
|
|
|
|
|
|
def build_netmodel_combo(vm, combo):
|
|
dev_model = Gtk.ListStore(str, str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
populate_netmodel_combo(vm, combo)
|
|
combo.set_active(0)
|
|
|
|
|
|
def populate_netmodel_combo(vm, combo):
|
|
model = combo.get_model()
|
|
model.clear()
|
|
|
|
# [xml value, label]
|
|
model.append([None, _("Hypervisor default")])
|
|
if vm.is_hvm():
|
|
mod_list = ["rtl8139", "ne2k_pci", "pcnet", "e1000"]
|
|
if vm.get_hv_type() in ["kvm", "qemu", "test"]:
|
|
mod_list.append("virtio")
|
|
if (vm.get_hv_type() == "kvm" and
|
|
vm.get_machtype() == "pseries"):
|
|
mod_list.append("spapr-vlan")
|
|
if vm.get_hv_type() in ["xen", "test"]:
|
|
mod_list.append("netfront")
|
|
mod_list.sort()
|
|
|
|
for m in mod_list:
|
|
model.append([m, m])
|
|
|
|
|
|
def build_cache_combo(vm, combo, no_default=False):
|
|
ignore = vm
|
|
dev_model = Gtk.ListStore(str, str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
combo.set_active(-1)
|
|
for m in virtinst.VirtualDisk.cache_types:
|
|
dev_model.append([m, m])
|
|
|
|
if not no_default:
|
|
dev_model.append([None, "default"])
|
|
combo.set_active(0)
|
|
|
|
|
|
def build_io_combo(vm, combo, no_default=False):
|
|
ignore = vm
|
|
dev_model = Gtk.ListStore(str, str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
combo.set_active(-1)
|
|
for m in virtinst.VirtualDisk.io_modes:
|
|
dev_model.append([m, m])
|
|
|
|
if not no_default:
|
|
dev_model.append([None, "default"])
|
|
combo.set_active(0)
|
|
|
|
|
|
def build_disk_bus_combo(vm, combo, no_default=False):
|
|
ignore = vm
|
|
dev_model = Gtk.ListStore(str, str)
|
|
combo.set_model(dev_model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
dev_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
|
|
|
if not no_default:
|
|
dev_model.append([None, "default"])
|
|
combo.set_active(-1)
|
|
|
|
|
|
def build_vnc_keymap_combo(vm, combo, no_default=False):
|
|
ignore = vm
|
|
|
|
model = Gtk.ListStore(str, str)
|
|
combo.set_model(model)
|
|
text = Gtk.CellRendererText()
|
|
combo.pack_start(text, True)
|
|
combo.add_attribute(text, 'text', 1)
|
|
|
|
if not no_default:
|
|
model.append([None, "default"])
|
|
else:
|
|
model.append([None, "Auto"])
|
|
|
|
model.append([virtinst.VirtualGraphics.KEYMAP_LOCAL,
|
|
"Copy local keymap"])
|
|
for k in virtinst.VirtualGraphics.valid_keymaps():
|
|
model.append([k, k])
|
|
|
|
combo.set_active(-1)
|
|
|
|
#####################################
|
|
# Storage format list/combo helpers #
|
|
#####################################
|
|
|
|
|
|
def build_storage_format_combo(vm, combo):
|
|
dev_model = Gtk.ListStore(str)
|
|
combo.set_model(dev_model)
|
|
combo.set_entry_text_column(0)
|
|
|
|
formats = ["raw", "qcow2", "qed"]
|
|
if vm.rhel6_defaults():
|
|
formats.append("vmdk")
|
|
formats.append("vdi")
|
|
|
|
for m in formats:
|
|
dev_model.append([m])
|
|
|
|
combo.set_active(0)
|
|
|
|
#######################################################################
|
|
# Widgets for listing network device options (in create, addhardware) #
|
|
#######################################################################
|
|
|
|
|
|
def pretty_network_desc(nettype, source=None, netobj=None):
|
|
if nettype == virtinst.VirtualNetworkInterface.TYPE_USER:
|
|
return _("Usermode networking")
|
|
|
|
extra = None
|
|
if nettype == virtinst.VirtualNetworkInterface.TYPE_BRIDGE:
|
|
ret = _("Bridge")
|
|
elif nettype == virtinst.VirtualNetworkInterface.TYPE_VIRTUAL:
|
|
ret = _("Virtual network")
|
|
if netobj:
|
|
extra = ": %s" % netobj.pretty_forward_mode()
|
|
else:
|
|
ret = nettype.capitalize()
|
|
|
|
if source:
|
|
ret += " '%s'" % source
|
|
if extra:
|
|
ret += " %s" % extra
|
|
|
|
return ret
|
|
|
|
|
|
def init_network_list(net_list, bridge_box,
|
|
source_mode_box=None, source_mode_label=None,
|
|
vport_expander=None):
|
|
# [ network type, source name, label, sensitive?, net is active,
|
|
# manual bridge, net instance]
|
|
net_model = Gtk.ListStore(str, str, str, bool, bool, bool, object)
|
|
net_list.set_model(net_model)
|
|
|
|
net_list.connect("changed", net_list_changed, bridge_box, source_mode_box,
|
|
source_mode_label, vport_expander)
|
|
|
|
text = Gtk.CellRendererText()
|
|
net_list.pack_start(text, True)
|
|
net_list.add_attribute(text, 'text', 2)
|
|
net_list.add_attribute(text, 'sensitive', 3)
|
|
|
|
|
|
def net_list_changed(net_list, bridge_box,
|
|
source_mode_box, source_mode_label, vport_expander):
|
|
active = net_list.get_active()
|
|
if active < 0:
|
|
return
|
|
|
|
if not bridge_box:
|
|
return
|
|
|
|
row = net_list.get_model()[active]
|
|
|
|
if source_mode_box is not None:
|
|
show_source_mode = (row[0] ==
|
|
virtinst.VirtualNetworkInterface.TYPE_DIRECT)
|
|
source_mode_box.set_property("visible", show_source_mode)
|
|
source_mode_label.set_property("visible", show_source_mode)
|
|
vport_expander.set_property("visible", show_source_mode)
|
|
|
|
show_bridge = row[5]
|
|
|
|
bridge_box.set_property("visible", show_bridge)
|
|
|
|
|
|
def get_network_selection(net_list, bridge_entry):
|
|
idx = net_list.get_active()
|
|
if idx == -1:
|
|
return None, None
|
|
|
|
row = net_list.get_model()[net_list.get_active()]
|
|
net_type = row[0]
|
|
net_src = row[1]
|
|
net_check_bridge = row[5]
|
|
|
|
if net_check_bridge and bridge_entry:
|
|
net_type = virtinst.VirtualNetworkInterface.TYPE_BRIDGE
|
|
net_src = bridge_entry.get_text()
|
|
|
|
return net_type, net_src
|
|
|
|
|
|
def populate_network_list(net_list, conn, show_direct_interfaces=True):
|
|
model = net_list.get_model()
|
|
model.clear()
|
|
|
|
vnet_bridges = []
|
|
vnet_dict = {}
|
|
bridge_dict = {}
|
|
iface_dict = {}
|
|
|
|
def build_row(nettype, name, label, is_sensitive, is_running,
|
|
manual_bridge=False, key=None):
|
|
return [nettype, name, label,
|
|
is_sensitive, is_running, manual_bridge,
|
|
key]
|
|
|
|
def set_active(idx):
|
|
net_list.set_active(idx)
|
|
|
|
def add_dict(indict, model):
|
|
keylist = indict.keys()
|
|
keylist.sort()
|
|
rowlist = [indict[k] for k in keylist]
|
|
for row in rowlist:
|
|
model.append(row)
|
|
|
|
# For qemu:///session
|
|
if conn.is_qemu_session():
|
|
nettype = virtinst.VirtualNetworkInterface.TYPE_USER
|
|
r = build_row(nettype, None, pretty_network_desc(nettype), True, True)
|
|
model.append(r)
|
|
set_active(0)
|
|
return
|
|
|
|
hasNet = False
|
|
netIdxLabel = None
|
|
# Virtual Networks
|
|
for uuid in conn.list_net_uuids():
|
|
net = conn.get_net(uuid)
|
|
nettype = virtinst.VirtualNetworkInterface.TYPE_VIRTUAL
|
|
|
|
label = pretty_network_desc(nettype, net.get_name(), net)
|
|
if not net.is_active():
|
|
label += " (%s)" % _("Inactive")
|
|
|
|
hasNet = True
|
|
# FIXME: Should we use 'default' even if it's inactive?
|
|
# FIXME: This preference should be configurable
|
|
if net.get_name() == "default":
|
|
netIdxLabel = label
|
|
|
|
vnet_dict[label] = build_row(nettype, net.get_name(), label, True,
|
|
net.is_active(), key=net.get_uuid())
|
|
|
|
# Build a list of vnet bridges, so we know not to list them
|
|
# in the physical interface list
|
|
vnet_bridge = net.get_bridge_device()
|
|
if vnet_bridge:
|
|
vnet_bridges.append(vnet_bridge)
|
|
|
|
if not hasNet:
|
|
label = _("No virtual networks available")
|
|
vnet_dict[label] = build_row(None, None, label, False, False)
|
|
|
|
vnet_taps = []
|
|
for vm in conn.vms.values():
|
|
for nic in vm.get_network_devices(refresh_if_nec=False):
|
|
if nic.target_dev and nic.target_dev not in vnet_taps:
|
|
vnet_taps.append(nic.target_dev)
|
|
|
|
# Physical devices
|
|
hasShared = False
|
|
brIdxLabel = None
|
|
for name in conn.list_net_device_paths():
|
|
br = conn.get_net_device(name)
|
|
bridge_name = br.get_bridge()
|
|
nettype = virtinst.VirtualNetworkInterface.TYPE_BRIDGE
|
|
|
|
if ((bridge_name in vnet_bridges) or
|
|
(br.get_name() in vnet_bridges) or
|
|
(br.get_name() in vnet_taps) or
|
|
(br.get_name() in [v + "-nic" for v in vnet_bridges])):
|
|
# Don't list this, as it is basically duplicating virtual net info
|
|
continue
|
|
|
|
if br.is_shared():
|
|
sensitive = True
|
|
if br.get_bridge():
|
|
hasShared = True
|
|
brlabel = "(%s)" % pretty_network_desc(nettype, bridge_name)
|
|
else:
|
|
bridge_name = name
|
|
brlabel = _("(Empty bridge)")
|
|
else:
|
|
if (show_direct_interfaces and
|
|
conn.check_conn_support(
|
|
conn.SUPPORT_CONN_HV_DIRECT_INTERFACE)):
|
|
sensitive = True
|
|
nettype = virtinst.VirtualNetworkInterface.TYPE_DIRECT
|
|
bridge_name = name
|
|
brlabel = ": %s" % _("macvtap")
|
|
else:
|
|
sensitive = False
|
|
brlabel = "(%s)" % _("Not bridged")
|
|
|
|
label = _("Host device %s %s") % (br.get_name(), brlabel)
|
|
if hasShared and not brIdxLabel:
|
|
brIdxLabel = label
|
|
|
|
row = build_row(nettype, bridge_name, label, sensitive, True,
|
|
key=br.get_name())
|
|
|
|
if sensitive:
|
|
bridge_dict[label] = row
|
|
else:
|
|
iface_dict[label] = row
|
|
|
|
add_dict(bridge_dict, model)
|
|
add_dict(vnet_dict, model)
|
|
add_dict(iface_dict, model)
|
|
|
|
# If there is a bridge device, default to that
|
|
# If not, use 'default' network
|
|
# If not present, use first list entry
|
|
# If list empty, use no network devices
|
|
return_warn = False
|
|
label = brIdxLabel or netIdxLabel
|
|
|
|
for idx in range(len(model)):
|
|
row = model[idx]
|
|
is_inactive = not row[4]
|
|
if label:
|
|
if row[2] == label:
|
|
default = idx
|
|
return_warn = is_inactive
|
|
break
|
|
else:
|
|
if row[3] is True:
|
|
default = idx
|
|
return_warn = is_inactive
|
|
break
|
|
else:
|
|
return_warn = True
|
|
row = build_row(None, None, _("No networking"), True, False)
|
|
model.insert(0, row)
|
|
default = 0
|
|
|
|
# After all is said and done, add a manual bridge option
|
|
manual_row = build_row(None, None, _("Specify shared device name"),
|
|
True, False, manual_bridge=True)
|
|
model.append(manual_row)
|
|
|
|
set_active(default)
|
|
return return_warn
|
|
|
|
|
|
def validate_network(parent, conn, nettype, devname, macaddr, model=None):
|
|
set_error_parent(parent)
|
|
|
|
net = None
|
|
addr = None
|
|
|
|
if nettype is None:
|
|
return None
|
|
|
|
# Make sure VirtualNetwork is running
|
|
netobj = None
|
|
if nettype == virtinst.VirtualNetworkInterface.TYPE_VIRTUAL:
|
|
for net in conn.nets.values():
|
|
if net.get_name() == devname:
|
|
netobj = net
|
|
break
|
|
|
|
if netobj and not netobj.is_active():
|
|
res = err_dial.yes_no(_("Virtual Network is not active."),
|
|
_("Virtual Network '%s' is not active. "
|
|
"Would you like to start the network "
|
|
"now?") % devname)
|
|
if not res:
|
|
return False
|
|
|
|
# Try to start the network
|
|
try:
|
|
netobj.start()
|
|
netobj.tick()
|
|
logging.info("Started network '%s'", devname)
|
|
except Exception, e:
|
|
return err_dial.show_err(_("Could not start virtual network "
|
|
"'%s': %s") % (devname, str(e)))
|
|
|
|
# Create network device
|
|
try:
|
|
bridge = None
|
|
netname = None
|
|
if nettype == virtinst.VirtualNetworkInterface.TYPE_VIRTUAL:
|
|
netname = devname
|
|
elif nettype == virtinst.VirtualNetworkInterface.TYPE_BRIDGE:
|
|
bridge = devname
|
|
elif nettype == virtinst.VirtualNetworkInterface.TYPE_DIRECT:
|
|
bridge = devname
|
|
elif nettype == virtinst.VirtualNetworkInterface.TYPE_USER:
|
|
pass
|
|
|
|
net = virtinst.VirtualNetworkInterface(conn.get_backend(),
|
|
type=nettype,
|
|
bridge=bridge,
|
|
network=netname,
|
|
macaddr=macaddr,
|
|
model=model)
|
|
if net.model == "spapr-vlan":
|
|
addr = "spapr-vio"
|
|
|
|
net.set_address(addr)
|
|
|
|
except Exception, e:
|
|
return err_dial.val_err(_("Error with network parameters."), e)
|
|
|
|
# Make sure there is no mac address collision
|
|
isfatal, errmsg = net.is_conflict_net(conn.get_backend())
|
|
if isfatal:
|
|
return err_dial.val_err(_("Mac address collision."), errmsg)
|
|
elif errmsg is not None:
|
|
retv = err_dial.yes_no(_("Mac address collision."),
|
|
_("%s Are you sure you want to use this "
|
|
"address?") % errmsg)
|
|
if not retv:
|
|
return False
|
|
|
|
return net
|
|
|
|
|
|
def generate_macaddr(conn):
|
|
newmac = ""
|
|
try:
|
|
net = virtinst.VirtualNetworkInterface(conn.get_backend())
|
|
net.setup()
|
|
newmac = net.macaddr
|
|
except:
|
|
pass
|
|
|
|
return newmac
|
|
|
|
|
|
############################################
|
|
# Populate media widget (choosecd, create) #
|
|
############################################
|
|
|
|
def init_mediadev_combo(widget):
|
|
# [Device path, pretty label, has_media?, device key, media key,
|
|
# vmmMediaDevice, is valid device]
|
|
model = Gtk.ListStore(str, str, bool, str, str, bool)
|
|
widget.set_model(model)
|
|
model.clear()
|
|
|
|
text = Gtk.CellRendererText()
|
|
widget.pack_start(text, True)
|
|
widget.add_attribute(text, 'text', OPTICAL_LABEL)
|
|
widget.add_attribute(text, 'sensitive', OPTICAL_IS_VALID)
|
|
|
|
|
|
def populate_mediadev_combo(conn, widget, devtype):
|
|
sigs = []
|
|
|
|
model = widget.get_model()
|
|
model.clear()
|
|
set_mediadev_default(model)
|
|
|
|
sigs.append(conn.connect("mediadev-added", mediadev_added, widget, devtype))
|
|
sigs.append(conn.connect("mediadev-removed", mediadev_removed, widget))
|
|
|
|
widget.set_active(-1)
|
|
mediadev_set_default_selection(widget)
|
|
|
|
return sigs
|
|
|
|
|
|
def set_mediadev_default(model):
|
|
if len(model) == 0:
|
|
model.append([None, _("No device present"), False, None, None, False])
|
|
|
|
|
|
def set_row_from_object(row, obj):
|
|
row[OPTICAL_DEV_PATH] = obj.get_path()
|
|
row[OPTICAL_LABEL] = obj.pretty_label()
|
|
row[OPTICAL_IS_MEDIA_PRESENT] = obj.has_media()
|
|
row[OPTICAL_DEV_KEY] = obj.get_key()
|
|
row[OPTICAL_MEDIA_KEY] = obj.get_media_key()
|
|
row[OPTICAL_IS_VALID] = True
|
|
|
|
|
|
def mediadev_removed(ignore_helper, key, widget):
|
|
model = widget.get_model()
|
|
active = widget.get_active()
|
|
idx = 0
|
|
|
|
for row in model:
|
|
if row[OPTICAL_DEV_KEY] == key:
|
|
# Whole device removed
|
|
del(model[idx])
|
|
|
|
if idx > active and active != -1:
|
|
widget.set_active(active - 1)
|
|
elif idx == active:
|
|
widget.set_active(-1)
|
|
|
|
idx += 1
|
|
|
|
set_mediadev_default(model)
|
|
mediadev_set_default_selection(widget)
|
|
|
|
|
|
def mediadev_added(ignore_helper, newobj, widget, devtype):
|
|
model = widget.get_model()
|
|
|
|
if newobj.get_media_type() != devtype:
|
|
return
|
|
|
|
if len(model) == 1 and model[0][OPTICAL_IS_VALID] is False:
|
|
# Only entry is the 'No device' entry
|
|
model.clear()
|
|
|
|
newobj.connect("media-added", mediadev_media_changed, widget)
|
|
newobj.connect("media-removed", mediadev_media_changed, widget)
|
|
|
|
# Brand new device
|
|
row = [None, None, None, None, None, None]
|
|
set_row_from_object(row, newobj)
|
|
model.append(row)
|
|
|
|
mediadev_set_default_selection(widget)
|
|
|
|
|
|
def mediadev_media_changed(newobj, widget):
|
|
model = widget.get_model()
|
|
active = widget.get_active()
|
|
idx = 0
|
|
|
|
# Search for the row with matching device node and
|
|
# fill in info about inserted media. If model has no current
|
|
# selection, select the new media.
|
|
for row in model:
|
|
if row[OPTICAL_DEV_PATH] == newobj.get_path():
|
|
set_row_from_object(row, newobj)
|
|
has_media = row[OPTICAL_IS_MEDIA_PRESENT]
|
|
|
|
if has_media and active == -1:
|
|
widget.set_active(idx)
|
|
elif not has_media and active == idx:
|
|
widget.set_active(-1)
|
|
|
|
idx = idx + 1
|
|
|
|
mediadev_set_default_selection(widget)
|
|
|
|
|
|
def mediadev_set_default_selection(widget):
|
|
# Set the first active cdrom device as selected, otherwise none
|
|
model = widget.get_model()
|
|
idx = 0
|
|
active = widget.get_active()
|
|
|
|
if active != -1:
|
|
# already a selection, don't change it
|
|
return
|
|
|
|
for row in model:
|
|
if row[OPTICAL_IS_MEDIA_PRESENT] is True:
|
|
widget.set_active(idx)
|
|
return
|
|
idx += 1
|
|
|
|
widget.set_active(-1)
|
|
|
|
|
|
####################################################################
|
|
# Build toolbar shutdown button menu (manager and details toolbar) #
|
|
####################################################################
|
|
|
|
def build_shutdown_button_menu(widget, shutdown_cb, reboot_cb, reset_cb,
|
|
destroy_cb, save_cb):
|
|
widget.set_icon_name("system-shutdown")
|
|
menu = Gtk.Menu()
|
|
widget.set_menu(menu)
|
|
|
|
rebootimg = Gtk.Image.new_from_icon_name("system-shutdown",
|
|
Gtk.IconSize.MENU)
|
|
shutdownimg = Gtk.Image.new_from_icon_name("system-shutdown",
|
|
Gtk.IconSize.MENU)
|
|
destroyimg = Gtk.Image.new_from_icon_name("system-shutdown",
|
|
Gtk.IconSize.MENU)
|
|
resetimg = Gtk.Image.new_from_icon_name("system-shutdown",
|
|
Gtk.IconSize.MENU)
|
|
saveimg = Gtk.Image.new_from_icon_name(Gtk.STOCK_SAVE, Gtk.IconSize.MENU)
|
|
|
|
reboot = Gtk.ImageMenuItem.new_with_mnemonic(_("_Reboot"))
|
|
reboot.set_image(rebootimg)
|
|
reboot.show()
|
|
reboot.connect("activate", reboot_cb)
|
|
menu.add(reboot)
|
|
|
|
shutdown = Gtk.ImageMenuItem.new_with_mnemonic(_("_Shut Down"))
|
|
shutdown.set_image(shutdownimg)
|
|
shutdown.show()
|
|
shutdown.connect("activate", shutdown_cb)
|
|
menu.add(shutdown)
|
|
|
|
reset = Gtk.ImageMenuItem.new_with_mnemonic(_("_Force Reset"))
|
|
reset.set_image(resetimg)
|
|
reset.show()
|
|
reset.connect("activate", reset_cb)
|
|
menu.add(reset)
|
|
|
|
destroy = Gtk.ImageMenuItem.new_with_mnemonic(_("_Force Off"))
|
|
destroy.set_image(destroyimg)
|
|
destroy.show()
|
|
destroy.connect("activate", destroy_cb)
|
|
menu.add(destroy)
|
|
|
|
sep = Gtk.SeparatorMenuItem()
|
|
sep.show()
|
|
menu.add(sep)
|
|
|
|
save = Gtk.ImageMenuItem.new_with_mnemonic(_("Sa_ve"))
|
|
save.set_image(saveimg)
|
|
save.show()
|
|
save.connect("activate", save_cb)
|
|
menu.add(save)
|
|
|
|
#####################################
|
|
# Path permissions checker for qemu #
|
|
#####################################
|
|
|
|
|
|
def check_path_search_for_qemu(parent, conn, path):
|
|
set_error_parent(parent)
|
|
|
|
if conn.is_remote() or not conn.is_qemu_system():
|
|
return
|
|
|
|
user = util.running_config.default_qemu_user
|
|
|
|
skip_paths = util.running_config.get_perms_fix_ignore()
|
|
broken_paths = virtinst.VirtualDisk.check_path_search_for_user(
|
|
conn.get_backend(),
|
|
path, user)
|
|
for p in broken_paths:
|
|
if p in skip_paths:
|
|
broken_paths.remove(p)
|
|
|
|
if not broken_paths:
|
|
return
|
|
|
|
logging.debug("No search access for dirs: %s", broken_paths)
|
|
resp, chkres = err_dial.warn_chkbox(
|
|
_("The emulator may not have search permissions "
|
|
"for the path '%s'.") % path,
|
|
_("Do you want to correct this now?"),
|
|
_("Don't ask about these directories again."),
|
|
buttons=Gtk.ButtonsType.YES_NO)
|
|
|
|
if chkres:
|
|
util.running_config.add_perms_fix_ignore(broken_paths)
|
|
if not resp:
|
|
return
|
|
|
|
logging.debug("Attempting to correct permission issues.")
|
|
errors = virtinst.VirtualDisk.fix_path_search_for_user(conn.get_backend(),
|
|
path, user)
|
|
if not errors:
|
|
return
|
|
|
|
errmsg = _("Errors were encountered changing permissions for the "
|
|
"following directories:")
|
|
details = ""
|
|
for path, error in errors.items():
|
|
if path not in broken_paths:
|
|
continue
|
|
details += "%s : %s\n" % (path, error)
|
|
|
|
logging.debug("Permission errors:\n%s", details)
|
|
|
|
ignore, chkres = err_dial.err_chkbox(errmsg, details,
|
|
_("Don't ask about these directories again."))
|
|
|
|
if chkres:
|
|
util.running_config.add_perms_fix_ignore(errors.keys())
|
|
|
|
######################################
|
|
# Interface startmode widget builder #
|
|
######################################
|
|
|
|
|
|
def build_startmode_combo(start_list):
|
|
start_model = Gtk.ListStore(str)
|
|
start_list.set_model(start_model)
|
|
text = Gtk.CellRendererText()
|
|
start_list.pack_start(text, True)
|
|
start_list.add_attribute(text, 'text', 0)
|
|
start_model.append(["none"])
|
|
start_model.append(["onboot"])
|
|
start_model.append(["hotplug"])
|
|
|
|
|
|
#########################
|
|
# Console keycombo menu #
|
|
#########################
|
|
|
|
def build_keycombo_menu(cb):
|
|
menu = Gtk.Menu()
|
|
|
|
def make_item(name, combo):
|
|
item = Gtk.MenuItem.new_with_mnemonic(name)
|
|
item.connect("activate", cb, combo)
|
|
|
|
menu.add(item)
|
|
|
|
make_item("Ctrl+Alt+_Backspace", ["Control_L", "Alt_L", "BackSpace"])
|
|
make_item("Ctrl+Alt+_Delete", ["Control_L", "Alt_L", "Delete"])
|
|
menu.add(Gtk.SeparatorMenuItem())
|
|
|
|
for i in range(1, 13):
|
|
make_item("Ctrl+Alt+F_%d" % i, ["Control_L", "Alt_L", "F%d" % i])
|
|
menu.add(Gtk.SeparatorMenuItem())
|
|
|
|
make_item("_Printscreen", ["Print"])
|
|
|
|
menu.show_all()
|
|
return menu
|