virt-manager/virtManager/netlist.py
Cole Robinson 932e6e00bb details: Fix changing graphics type (bz 1083903)
And clean up the API usage to avoid these types of issues in the future.
2014-04-03 10:53:54 -04:00

489 lines
17 KiB
Python

#
# Copyright (C) 2014 Red Hat, Inc.
#
# 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
from gi.repository import Gtk
from gi.repository import GObject
import virtinst
from virtManager import uiutil
from virtManager.baseclass import vmmGObjectUI
class vmmNetworkList(vmmGObjectUI):
__gsignals__ = {
"changed": (GObject.SignalFlags.RUN_FIRST, None, []),
"changed-vport": (GObject.SignalFlags.RUN_FIRST, None, [])
}
def __init__(self, conn, builder, topwin):
vmmGObjectUI.__init__(self, "netlist.ui",
None, builder=builder, topwin=topwin)
self.conn = conn
self.builder.connect_signals({
"on_net_source_changed": self._on_net_source_changed,
"on_net_source_mode_changed": self._emit_changed,
"on_net_bridge_name_changed": self._emit_changed,
"on_vport_type_changed": self._emit_vport_changed,
"on_vport_managerid_changed": self._emit_vport_changed,
"on_vport_typeid_changed": self._emit_vport_changed,
"on_vport_typeidversion_changed": self._emit_vport_changed,
"on_vport_instanceid_changed": self._emit_vport_changed,
})
self._init_ui()
self.top_label = self.widget("net-source-label")
self.top_box = self.widget("net-source-box")
self.top_vport = self.widget("vport-expander")
def _cleanup(self):
try:
self.conn.disconnect_by_func(self._repopulate_network_list)
self.conn.disconnect_by_func(self._repopulate_network_list)
self.conn.disconnect_by_func(self._repopulate_network_list)
self.conn.disconnect_by_func(self._repopulate_network_list)
except:
pass
self.conn = None
##########################
# Initialization methods #
##########################
def _init_ui(self):
# [ network type, source name, label, sensitive?, net is active,
# manual bridge, net instance]
model = Gtk.ListStore(str, str, str, bool, bool, bool, object)
combo = self.widget("net-source")
combo.set_model(model)
text = Gtk.CellRendererText()
combo.pack_start(text, True)
combo.add_attribute(text, 'text', 2)
combo.add_attribute(text, 'sensitive', 3)
combo = self.widget("net-source-mode")
# [xml value, label]
model = Gtk.ListStore(str, str)
combo.set_model(model)
uiutil.set_combo_text_column(combo, 1)
model.append(["bridge", "Bridge"])
model.append(["vepa", "VEPA"])
model.append(["private", "Private"])
model.append(["passthrough", "Passthrough"])
combo.set_active(0)
self.conn.connect("net-added", self._repopulate_network_list)
self.conn.connect("net-removed", self._repopulate_network_list)
self.conn.connect("interface-added", self._repopulate_network_list)
self.conn.connect("interface-removed", self._repopulate_network_list)
def _pretty_network_desc(self, 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 _build_source_row(self, nettype, name,
label, is_sensitive, is_running, manual_bridge=False, key=None):
return [nettype, name, label,
is_sensitive, is_running, manual_bridge,
key]
def _find_virtual_networks(self):
vnet_dict = {}
vnet_bridges = []
hasNet = False
netIdxLabel = None
for uuid in self.conn.list_net_uuids():
net = self.conn.get_net(uuid)
nettype = virtinst.VirtualNetworkInterface.TYPE_VIRTUAL
label = self._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] = self._build_source_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] = self._build_source_row(
None, None, label, False, False)
return vnet_dict, vnet_bridges, netIdxLabel
def _find_physical_devices(self, vnet_bridges):
vnet_taps = []
for vm in self.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)
bridge_dict = {}
iface_dict = {}
hasShared = False
brIdxLabel = None
skip_ifaces = ["lo"]
for name in self.conn.list_net_device_paths():
br = self.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]) or
(br.get_name() in skip_ifaces)):
# 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)" % self._pretty_network_desc(nettype,
bridge_name)
else:
bridge_name = name
brlabel = _("(Empty bridge)")
else:
if self.conn.check_support(
self.conn.SUPPORT_CONN_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 = self._build_source_row(
nettype, bridge_name, label, sensitive, True,
key=br.get_name())
if sensitive:
bridge_dict[label] = row
else:
iface_dict[label] = row
return bridge_dict, iface_dict, brIdxLabel
def _populate_network_list(self):
net_list = self.widget("net-source")
model = net_list.get_model()
model.clear()
# For qemu:///session
if self.conn.is_qemu_session():
nettype = virtinst.VirtualNetworkInterface.TYPE_USER
r = self._build_source_row(
nettype, None, self._pretty_network_desc(nettype), True, True)
model.append(r)
net_list.set_active(0)
return
(vnet_dict, vnet_bridges, netIdxLabel) = self._find_virtual_networks()
(bridge_dict, iface_dict, brIdxLabel) = self._find_physical_devices(
vnet_bridges)
for indict in [bridge_dict, vnet_dict, iface_dict]:
keylist = indict.keys()
keylist.sort()
rowlist = [indict[k] for k in keylist]
for row in rowlist:
model.append(row)
# 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
label = brIdxLabel or netIdxLabel
default = 0
if not len(model):
row = self._build_source_row(
None, None, _("No networking"), True, False)
model.insert(0, row)
default = 0
elif label:
default = [idx for idx in range(len(model)) if
model[idx][2] == label][0]
# After all is said and done, add a manual bridge option
manual_row = self._build_source_row(
None, None, _("Specify shared device name"),
True, False, manual_bridge=True)
model.append(manual_row)
net_list.set_active(default)
###############
# Public APIs #
###############
def get_network_row(self):
return uiutil.get_list_selection(self.widget("net-source"), None)
def get_network_selection(self):
bridge_entry = self.widget("net-bridge-name")
row = self.get_network_row()
if not row:
return None, None, None
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()
mode = None
if self.widget("net-source-mode").is_visible():
mode = uiutil.get_list_selection(self.widget("net-source-mode"), 0)
return net_type, net_src, mode
def get_vport(self):
vport_type = self.widget("vport-type").get_text()
vport_managerid = self.widget("vport-managerid").get_text()
vport_typeid = self.widget("vport-typeid").get_text()
vport_idver = self.widget("vport-typeidversion").get_text()
vport_instid = self.widget("vport-instanceid").get_text()
return (vport_type, vport_managerid, vport_typeid,
vport_idver, vport_instid)
def validate_network(self, macaddr, model=None):
nettype, devname, mode = self.get_network_selection()
if nettype is None:
return None
net = None
# Make sure VirtualNetwork is running
netobj = None
if nettype == virtinst.VirtualNetworkInterface.TYPE_VIRTUAL:
for net in self.conn.nets.values():
if net.get_name() == devname:
netobj = net
break
if netobj and not netobj.is_active():
res = self.err.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 self.err.show_err(_("Could not start virtual network "
"'%s': %s") % (devname, str(e)))
# Create network device
try:
net = virtinst.VirtualNetworkInterface(self.conn.get_backend())
net.type = nettype
net.source = devname
net.macaddr = macaddr
net.model = model
net.source_mode = mode
if net.model == "spapr-vlan":
net.address.set_addrstr("spapr-vio")
if net.type == "direct":
(vport_type, vport_managerid, vport_typeid,
vport_idver, vport_instid) = self.get_vport()
net.virtualport.type = vport_type or None
net.virtualport.managerid = vport_managerid or None
net.virtualport.typeid = vport_typeid or None
net.virtualport.typeidversion = vport_idver or None
net.virtualport.instanceid = vport_instid or None
except Exception, e:
return self.err.val_err(_("Error with network parameters."), e)
# Make sure there is no mac address collision
isfatal, errmsg = net.is_conflict_net(net.conn, net.macaddr)
if isfatal:
return self.err.val_err(_("Mac address collision."), errmsg)
elif errmsg is not None:
retv = self.err.yes_no(_("Mac address collision."),
_("%s Are you sure you want to use this address?") % errmsg)
if not retv:
return False
return net
def reset_state(self):
self._populate_network_list()
net_warn = self.widget("net-source-warn")
net_err = self.conn.netdev_error
net_warn.set_visible(bool(net_err))
net_warn.set_tooltip_text(net_err or "")
self.widget("net-bridge-name").set_text("")
self.widget("net-source-mode").set_active(0)
self.widget("vport-type").set_text("")
self.widget("vport-managerid").set_text("")
self.widget("vport-typeid").set_text("")
self.widget("vport-typeidversion").set_text("")
self.widget("vport-instanceid").set_text("")
def set_dev(self, net):
self.reset_state()
nettype = net.type
source = net.source
source_mode = net.source_mode
is_direct = (net.type == "direct")
uiutil.set_combo_entry(self.widget("net-source-mode"), source_mode)
# Virtualport config
self.widget("vport-expander").set_visible(is_direct)
vport = net.virtualport
self.widget("vport-type").set_text(vport.type or "")
self.widget("vport-managerid").set_text(str(vport.managerid or ""))
self.widget("vport-typeid").set_text(str(vport.typeid or ""))
self.widget("vport-typeidversion").set_text(
str(vport.typeidversion or ""))
self.widget("vport-instanceid").set_text(vport.instanceid or "")
# Find the matching row in the net list
combo = self.widget("net-source")
rowiter = None
for row in combo.get_model():
if row[0] == nettype and row[1] == source:
rowiter = row.iter
break
if not rowiter:
if nettype == "bridge":
rowiter = combo.get_model()[-1].iter
self.widget("net-bridge-name").set_text(source)
if not rowiter:
desc = self._pretty_network_desc(nettype, source)
combo.get_model().insert(0,
self._build_source_row(nettype, source, desc, True, True))
rowiter = combo.get_model()[0].iter
combo.set_active_iter(rowiter)
combo.emit("changed")
#############
# Listeners #
#############
def _emit_changed(self, *args, **kwargs):
ignore = args
ignore = kwargs
self.emit("changed")
def _emit_vport_changed(self, *args, **kwargs):
ignore = args
ignore = kwargs
self.emit("changed-vport")
def _repopulate_network_list(self, *args, **kwargs):
ignore = args
ignore = kwargs
netlist = self.widget("net-source")
label = uiutil.get_list_selection(netlist, 2)
self._populate_network_list()
for row in netlist.get_model():
if label and row[2] == label:
netlist.set_active_iter(row.iter)
return
def _on_net_source_changed(self, src):
ignore = src
self._emit_changed()
row = self.get_network_row()
if not row:
return
is_direct = (row[0] == virtinst.VirtualNetworkInterface.TYPE_DIRECT)
self.widget("vport-expander").set_visible(is_direct)
uiutil.set_grid_row_visible(self.widget("net-source-mode"), is_direct)
uiutil.set_grid_row_visible(
self.widget("net-macvtap-warn-box"), is_direct)
if is_direct and self.widget("net-source-mode").get_active() == -1:
self.widget("net-source-mode").set_active(0)
show_bridge = row[5]
uiutil.set_grid_row_visible(
self.widget("net-bridge-name"), show_bridge)