# # 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 # pylint: disable=E0611 from gi.repository import Gtk from gi.repository import GObject # pylint: enable=E0611 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")) def get_network_selection(self): net_list = self.widget("net-source") bridge_entry = self.widget("net-bridge-name") row = uiutil.get_list_selection(net_list) 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): self._emit_changed() row = uiutil.get_list_selection(src) 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)