diff --git a/ui/addhardware.ui b/ui/addhardware.ui index ce818503..73818d40 100644 --- a/ui/addhardware.ui +++ b/ui/addhardware.ui @@ -1,7 +1,7 @@ - + - + 67000 1 @@ -575,210 +575,166 @@ - + True False - 9 - 6 + vertical + 6 - + True False - 6 + 9 + 6 - + True - True - False - True - 0.5 - True - - - - False - True - 0 - - - - - True - True - 17 - - aa:bb:cc:dd:ee:ff - - - MAC Address Field + False + 6 + + + True + True + False + True + 0 + True + + + False + True + 0 + + + + + True + True + 17 + + aa:bb:cc:dd:ee:ff + + + MAC Address Field + + + + + False + True + 1 + - False - True - 1 + 1 + 1 + 1 + 1 - - - 1 - 2 - 1 - 1 - - - - - True - False - 0 - 0.23999999463558197 - _Host device: - True - net-list - - - 0 - 0 - 1 - 1 - - - - - True - False - 0 - _MAC address: - True - True - create-mac-address - - - 0 - 2 - 1 - 1 - - - - - True - False - 0 - D_evice model: - True - net-model - - - 0 - 3 - 1 - 1 - - - - - True - False - 6 - + True False - _Bridge name: + 0 + _MAC address: + True True - net-bridge + create-mac-address - False - True - 0 + 0 + 1 + 1 + 1 - - True - True - - - - False - True - 1 - - - - - 1 - 1 - 1 - 1 - - - - - True - False - 6 - - + True False + 0 + Device mode_l: + True + net-model - False - True - 0 + 0 + 2 + 1 + 1 - + True False - gtk-dialog-warning + start - False - True - 1 + 1 + 2 + 1 + 1 + + + + + True + False + False + False + + + + + + 0 + 0 + 1 + 1 + + + + + True + False + False + False + + + + + + 1 + 0 + 1 + 1 - 1 - 0 - 1 - 1 + False + True + 0 - - True - False - start - - - 1 - 3 - 1 - 1 - - - - + True False + False + False - 0 - 1 - 1 - 1 + False + True + 1 diff --git a/ui/create.ui b/ui/create.ui index f37ca0d7..ca614c3b 100644 --- a/ui/create.ui +++ b/ui/create.ui @@ -1,7 +1,7 @@ - + - + 1000000 0.10000000000000001 @@ -2651,12 +2651,60 @@ is not yet supported.</small> False 6 - + True False - 6 + 6 - + + Set a fixed _MAC address + True + True + False + True + 0 + True + + + + 0 + 2 + 1 + 1 + + + + + True + True + start + False + 20 + + + 0 + 3 + 1 + 1 + + + + + True + False + + + + + + 0 + 1 + 1 + 1 + + + + True False 6 @@ -2676,122 +2724,8 @@ is not yet supported.</small> True False - 0 - warn - - - True - True - 1 - - - - - False - True - 0 - - - - - True - False - - - True - False - 6 - - - True - False - - - - False - False - 0 - - - - - True - False - gtk-dialog-warning - - - False - True - 1 - - - - - False - False - 0 - - - - - True - False - 6 - 12 - - - True - False - - - True - False - 6 - - - True - False - _Bridge name: - True - config-netdev-bridge - - - False - True - 0 - - - - - True - True - - - - False - True - 1 - - - - - 3 - 0 - 1 - 1 - - - - - - - - - - - - - + <small>pxe warning</small> + True False @@ -2801,63 +2735,10 @@ is not yet supported.</small> - False - False - 1 - - - - - Set a fixed _MAC address - True - True - False - True - 0.5 - True - - - - False - False - 2 - - - - - True - False - 6 - - - True - True - - - False - True - 0 - - - - - True - False - - - - - - True - True - 1 - - - - - False - True - 3 + 0 + 0 + 1 + 1 diff --git a/ui/details.ui b/ui/details.ui index 45256822..26ad590a 100644 --- a/ui/details.ui +++ b/ui/details.ui @@ -4024,21 +4024,7 @@ 1 - 3 - 1 - 1 - - - - - True - False - 1 - Source device: - - - 0 - 0 + 2 1 1 @@ -4049,12 +4035,12 @@ False 1 0.47999998927116394 - Device m_odel: + Device mode_l: True 0 - 2 + 1 1 1 @@ -4069,95 +4055,7 @@ 0 - 3 - 1 - 1 - - - - - True - False - 1 - Source mode: - True - - - 0 - 4 - 1 - 1 - - - - - True - False - 6 - - - True - False - _Bridge name: - True - - - False - True - 0 - - - - - True - True - - - - - False - True - 1 - - - - - 1 - 1 - 1 - 1 - - - - - True - False - - - - - - 0 - 1 - 1 - 1 - - - - - True - False - True - - - - True - - - - - 1 - 4 + 2 1 1 @@ -4166,6 +4064,8 @@ True False + start + False True @@ -4176,16 +4076,37 @@ 1 - 2 + 1 1 1 - + True False - + False + False + + + + + + 0 + 0 + 1 + 1 + + + + + True + False + False + False + + + 1 @@ -4214,199 +4135,15 @@ - - True + + True + False - - True - False - 2 - - - True - False - 5 - 2 - 6 - 3 - - - True - True - - 30 - - - - 1 - 2 - 4 - 5 - GTK_FILL - - - - - - True - False - 1 - Instance id: - True - - - 4 - 5 - GTK_FILL - - - - - - True - True - - 30 - - - - 1 - 2 - 3 - 4 - GTK_FILL - - - - - - True - False - 1 - Typeid version: - True - - - 3 - 4 - GTK_FILL - - - - - - True - True - - 30 - - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - - True - False - 1 - Typeid: - True - - - 2 - 3 - GTK_FILL - - - - - - True - True - - 30 - - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - - True - False - 1 - Managerid: - True - - - 1 - 2 - GTK_FILL - - - - - - True - False - 1 - 0.47999998927116394 - Type: - True - True - - - GTK_FILL - - - - - - True - True - - 30 - - - - 1 - 2 - GTK_FILL - - - - - - False - True - 0 - - - - - - - True - False - Virtual port - True - + - False + True True 1 diff --git a/ui/netlist.ui b/ui/netlist.ui new file mode 100644 index 00000000..d7d09f8b --- /dev/null +++ b/ui/netlist.ui @@ -0,0 +1,365 @@ + + + + + + True + False + 6 + 6 + + + True + False + start + False + 0 + _Bridge name: + True + net-bridge-name + + + 0 + 1 + 1 + 1 + + + + + True + False + start + False + 0 + Source m_ode: + True + net-source-mode + + + 0 + 2 + 1 + 1 + + + + + True + True + start + True + + + + 1 + 1 + 1 + 1 + + + + + True + False + start + False + 6 + + + True + False + gtk-dialog-warning + + + False + True + 0 + + + + + True + False + 0 + <small>In most configurations, macvtap does not work for host to guest network communication.</small> + True + True + 35 + + + False + True + 1 + + + + + 0 + 3 + 2 + 1 + + + + + True + False + start + True + True + + + + False + + + + + 1 + 2 + 1 + 1 + + + + + True + False + 6 + + + True + False + start + False + + + + False + True + 0 + + + + + True + False + gtk-dialog-warning + + + False + True + 1 + + + + + 0 + 0 + 2 + 1 + + + + + True + False + 0 + 0 + 3 + _Network source: + True + + + True + + + True + False + 2 + + + True + False + 5 + 2 + 6 + 3 + + + True + True + + 30 + + + + 1 + 2 + 4 + 5 + GTK_FILL + + + + + + True + False + 1 + Instance id: + True + + + 4 + 5 + GTK_FILL + + + + + + True + True + + 30 + + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + + True + False + 1 + Typeid version: + True + + + 3 + 4 + GTK_FILL + + + + + + True + True + + 30 + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + False + 1 + Typeid: + True + + + 2 + 3 + GTK_FILL + + + + + + True + True + + 30 + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + False + 1 + Managerid: + True + + + 1 + 2 + GTK_FILL + + + + + + True + False + 1 + 0.47999998927116394 + Type: + True + True + + + GTK_FILL + + + + + + True + True + + 30 + + + + 1 + 2 + GTK_FILL + + + + + + False + True + 0 + + + + + + + True + False + Virtual port + True + + + + diff --git a/virtManager/addhardware.py b/virtManager/addhardware.py index 1148d146..10c62f66 100644 --- a/virtManager/addhardware.py +++ b/virtManager/addhardware.py @@ -37,6 +37,7 @@ from virtinst import VirtualController from virtManager import sharedui from virtManager import uiutil from virtManager.fsdetails import vmmFSDetails +from virtManager.netlist import vmmNetworkList from virtManager.asyncjob import vmmAsyncJob from virtManager.storagebrowse import vmmStorageBrowser from virtManager.baseclass import vmmGObjectUI @@ -68,12 +69,15 @@ class vmmAddHardware(vmmGObjectUI): self.is_customize_dialog = is_customize_dialog self.storage_browser = None - self.fs_units = "mb" self._dev = None self.fsDetails = vmmFSDetails(vm, self.builder, self.topwin) self.widget("fs-box").add(self.fsDetails.top_box) + self.netlist = vmmNetworkList(self.conn, self.builder, self.topwin) + self.widget("network-source-label-align").add(self.netlist.top_label) + self.widget("network-source-ui-align").add(self.netlist.top_box) + self.widget("network-vport-align").add(self.netlist.top_vport) self.builder.connect_signals({ "on_create_cancel_clicked" : self.close, @@ -134,6 +138,9 @@ class vmmAddHardware(vmmGObjectUI): self.storage_browser = None self.fsDetails.cleanup() + self.fsDetails = None + self.netlist.cleanup() + self.netlist = None def is_visible(self): return self.topwin.get_visible() @@ -172,13 +179,8 @@ class vmmAddHardware(vmmGObjectUI): hw_col.add_attribute(text, 'sensitive', 3) hw_list.append_column(hw_col) - # Virtual network list - net_list = self.widget("net-list") - bridge_box = self.widget("net-bridge-box") - sharedui.build_network_list(net_list, bridge_box) - # Network model list - netmodel_list = self.widget("net-model") + netmodel_list = self.widget("net-model") self.build_network_model_combo(self.vm, netmodel_list) # Disk bus type @@ -415,16 +417,7 @@ class vmmAddHardware(vmmGObjectUI): self.widget("create-mac-address").set_text(newmac) self.change_macaddr_use() - net_list = self.widget("net-list") - net_warn = self.widget("net-list-warn") - sharedui.populate_network_list(net_list, self.conn) - - error = self.conn.netdev_error - if error: - net_warn.show() - net_warn.set_tooltip_text(error) - else: - net_warn.hide() + self.netlist.reset_state() netmodel = self.widget("net-model") self.populate_network_model_combo(self.vm, netmodel) @@ -586,27 +579,6 @@ class vmmAddHardware(vmmGObjectUI): if len(model) > 0: combo.set_active(0) - @staticmethod - def populate_network_source_mode_combo(vm, combo): - ignore = vm - model = combo.get_model() - model.clear() - - # [xml value, label] - model.append(["bridge", "Bridge"]) - model.append(["vepa", "VEPA"]) - model.append(["private", "Private"]) - model.append(["passthrough", "Passthrough"]) - - @staticmethod - def build_network_source_mode_combo(vm, combo): - model = Gtk.ListStore(str, str) - combo.set_model(model) - uiutil.set_combo_text_column(combo, 1) - - vmmAddHardware.populate_network_source_mode_combo(vm, combo) - combo.set_active(0) - @staticmethod def populate_network_model_combo(vm, combo): model = combo.get_model() @@ -1059,15 +1031,6 @@ class vmmAddHardware(vmmGObjectUI): return self.widget("graphics-password").get_text() # Network getters - def get_config_network(self): - net_list = self.widget("net-list") - bridge_ent = self.widget("net-bridge") - - net_type, net_src = sharedui.get_network_selection(net_list, - bridge_ent) - - return net_type, net_src - def get_config_net_model(self): return uiutil.get_list_selection(self.widget("net-model"))[0] @@ -1689,7 +1652,7 @@ class vmmAddHardware(vmmGObjectUI): def validate_page_network(self): - nettype, devname = self.get_config_network() + nettype = self.netlist.get_network_selection()[0] mac = self.get_config_macaddr() model = self.get_config_net_model() @@ -1701,8 +1664,7 @@ class vmmAddHardware(vmmGObjectUI): return self.err.val_err(_("Invalid MAC address"), _("A MAC address must be entered.")) - ret = sharedui.validate_network(self.err, self.conn, - nettype, devname, mac, model) + ret = self.netlist.validate_network(mac, model) if ret is False: return False diff --git a/virtManager/create.py b/virtManager/create.py index aeb2c4ba..92d48fe4 100644 --- a/virtManager/create.py +++ b/virtManager/create.py @@ -39,6 +39,7 @@ from virtManager.asyncjob import vmmAsyncJob from virtManager.storagebrowse import vmmStorageBrowser from virtManager.details import vmmDetails from virtManager.domain import vmmDomainVirtinst +from virtManager.netlist import vmmNetworkList # Number of seconds to wait for media detection DETECT_TIMEOUT = 20 @@ -107,6 +108,10 @@ class vmmCreate(vmmGObjectUI): self.config_window = None self.config_window_signals = [] + self.netlist = vmmNetworkList(self.conn, self.builder, self.topwin) + self.widget("config-netdev-ui-align").add(self.netlist.top_box) + self.netlist.connect("changed", self.netdev_changed) + self.builder.connect_signals({ "on_vmm_newcreate_delete_event" : self.close, @@ -145,7 +150,6 @@ class vmmCreate(vmmGObjectUI): "on_config_storage_browse_clicked": self.browse_storage, "on_config_storage_select_toggled": self.toggle_storage_select, - "on_config_netdev_changed": self.netdev_changed, "on_config_set_macaddr_toggled": self.toggle_macaddr, "on_config_hv_changed": self.hv_changed, @@ -209,6 +213,7 @@ class vmmCreate(vmmGObjectUI): self.remove_conn() self.conn = newconn + self.netlist.conn = self.conn if self.conn: self.set_conn_state() @@ -288,12 +293,6 @@ class vmmCreate(vmmGObjectUI): cd_list = self.widget("install-local-cdrom-combo") sharedui.build_mediadev_combo(cd_list) - # Networking - # [ interface type, device name, label, sensitive ] - net_list = self.widget("config-netdev") - bridge_box = self.widget("config-netdev-bridge-box") - sharedui.build_network_list(net_list, bridge_box) - # Archtecture # [value, label] archList = self.widget("config-arch") @@ -617,40 +616,13 @@ class vmmCreate(vmmGObjectUI): storage_area.set_tooltip_text(storage_tooltip or "") # Networking - net_list = self.widget("config-netdev") - net_warn_icon = self.widget("config-netdev-warn-icon") - net_warn_box = self.widget("config-netdev-warn-box") - net_expander = self.widget("config-advanced-expander") - net_warn_icon.hide() - net_warn_box.hide() - net_expander.set_expanded(False) - - do_warn = sharedui.populate_network_list(net_list, self.conn, False) - self.set_net_warn(self.conn.netdev_error or do_warn, - self.conn.netdev_error, True) - newmac = virtinst.VirtualNetworkInterface.generate_mac( self.conn.get_backend()) self.widget("config-set-macaddr").set_active(bool(newmac)) self.widget("config-macaddr").set_text(newmac) - def set_net_warn(self, show_warn, msg, do_tooltip): - net_warn_icon = self.widget("config-netdev-warn-icon") - net_warn_box = self.widget("config-netdev-warn-box") - net_warn_label = self.widget("config-netdev-warn-label") - net_expander = self.widget("config-advanced-expander") - - if show_warn: - net_expander.set_expanded(True) - - if do_tooltip: - net_warn_icon.set_visible(bool(show_warn)) - if msg: - net_warn_icon.set_tooltip_text(show_warn and msg or "") - else: - net_warn_box.set_visible(show_warn) - markup = show_warn and ("%s" % msg) or "" - net_warn_label.set_markup(markup) + self.widget("config-advanced-expander").set_expanded(False) + self.netlist.reset_state() def populate_hv(self): hv_list = self.widget("config-hv") @@ -979,6 +951,8 @@ class vmmCreate(vmmGObjectUI): self.widget("summary-cpu").set_text(cpu) self.widget("summary-storage").set_markup(storage) + self.netdev_changed(None) + # get_* methods def get_config_name(self): return self.widget("create-vm-name").get_text() @@ -1104,16 +1078,6 @@ class vmmCreate(vmmGObjectUI): return (path, size, sparse) - def get_config_network_info(self): - net_list = self.widget("config-netdev") - bridge_ent = self.widget("config-netdev-bridge") - macaddr = self.widget("config-macaddr").get_text() - - net_type, net_src = sharedui.get_network_selection(net_list, - bridge_ent) - - return net_type, net_src, macaddr.strip() - def get_config_customize(self): return self.widget("summary-customize").get_active() def is_detect_active(self): @@ -1146,17 +1110,16 @@ class vmmCreate(vmmGObjectUI): self.widget("config-dtb-warn-virtio"), show_dtb_virtio) def netdev_changed(self, ignore): - self.check_network_selection() - - def check_network_selection(self): - row = uiutil.get_list_selection(self.widget("config-netdev")) + row = self.netlist.get_network_row() show_pxe_warn = True pxe_install = (self.get_config_install_page() == INSTALL_PAGE_PXE) + expand = False if row: ntype = row[0] key = row[6] + expand = (ntype != "network" and ntype != "bridge") if (ntype is None or ntype == virtinst.VirtualNetworkInterface.TYPE_USER): show_pxe_warn = True @@ -1166,11 +1129,13 @@ class vmmCreate(vmmGObjectUI): obj = self.conn.get_net(key) show_pxe_warn = not obj.can_pxe() - if not (show_pxe_warn and pxe_install): - return + show_warn = (show_pxe_warn and pxe_install) - self.set_net_warn(True, - _("Network selection does not support PXE"), False) + if expand or show_warn: + self.widget("config-advanced-expander").set_expanded(True) + self.widget("config-netdev-warn-box").set_visible(show_warn) + self.widget("config-netdev-warn-label").set_markup( + "%s" % _("Network selection does not support PXE")) def hv_changed(self, src): hv = uiutil.get_list_selection(src, 1) @@ -1424,7 +1389,6 @@ class vmmCreate(vmmGObjectUI): elif pagenum == PAGE_FINISH: self.widget("create-finish").grab_focus() self.populate_summary() - self.widget("config-netdev").emit("changed") for nr in range(self.widget("create-pages").get_n_pages()): page = self.widget("create-pages").get_nth_page(nr) @@ -1822,7 +1786,8 @@ class vmmCreate(vmmGObjectUI): if not self.validate_storage_page(): return False - nettype, devname, macaddr = self.get_config_network_info() + macaddr = self.widget("config-macaddr").get_text().strip() + nettype = self.netlist.get_network_selection()[0] if nettype is None: # No network device available @@ -1838,8 +1803,7 @@ class vmmCreate(vmmGObjectUI): _("Network device required for %s install.") % methname) - nic = sharedui.validate_network(self.err, - self.conn, nettype, devname, macaddr) + nic = self.netlist.validate_network(macaddr) if nic is False: return False diff --git a/virtManager/details.py b/virtManager/details.py index f5aa099f..1d41a816 100644 --- a/virtManager/details.py +++ b/virtManager/details.py @@ -31,17 +31,18 @@ import libvirt from virtManager import sharedui from virtManager import uiutil -from virtManager.storagebrowse import vmmStorageBrowser from virtManager.baseclass import vmmGObjectUI from virtManager.addhardware import vmmAddHardware from virtManager.choosecd import vmmChooseCD -from virtManager.snapshots import vmmSnapshotPage -from virtManager.graphwidgets import Sparkline from virtManager.fsdetails import vmmFSDetails -from virtinst import VirtualRNGDevice +from virtManager.netlist import vmmNetworkList +from virtManager.snapshots import vmmSnapshotPage +from virtManager.storagebrowse import vmmStorageBrowser +from virtManager.graphwidgets import Sparkline import virtinst from virtinst import util +from virtinst import VirtualRNGDevice # Parameters that can be editted in the details window @@ -386,6 +387,15 @@ class vmmDetails(vmmGObjectUI): self.fsDetails.connect("changed", lambda *x: self.enable_apply(x, EDIT_FS)) + self.netlist = vmmNetworkList(self.conn, self.builder, self.topwin) + self.widget("network-source-label-align").add(self.netlist.top_label) + self.widget("network-source-ui-align").add(self.netlist.top_box) + self.widget("network-vport-align").add(self.netlist.top_vport) + self.netlist.connect("changed", + lambda x: self.enable_apply(x, EDIT_NET_SOURCE)) + self.netlist.connect("changed-vport", + lambda x: self.enable_apply(x, EDIT_NET_VPORT)) + # Set default window size w, h = self.vm.get_details_window_size() self.topwin.set_default_size(w or 800, h or 600) @@ -487,21 +497,8 @@ class vmmDetails(vmmGObjectUI): "on_disk_serial_changed": lambda *x: self.enable_apply(x, EDIT_DISK_SERIAL), "on_disk_iotune_changed": self.iotune_changed, - "on_network_source_combo_changed": lambda *x: self.enable_apply(x, EDIT_NET_SOURCE), - "on_network_bridge_changed": lambda *x: self.enable_apply(x, EDIT_NET_SOURCE), - "on_network-source-mode-combo_changed": lambda *x: self.enable_apply(x, EDIT_NET_SOURCE), "on_network_model_combo_changed": lambda *x: self.enable_apply(x, EDIT_NET_MODEL), - "on_vport_type_changed": lambda *x: self.enable_apply(x, EDIT_NET_VPORT), - "on_vport_managerid_changed": lambda *x: self.enable_apply(x, - EDIT_NET_VPORT), - "on_vport_typeid_changed": lambda *x: self.enable_apply(x, - EDIT_NET_VPORT), - "on_vport_typeidversion_changed": lambda *x: self.enable_apply(x, - EDIT_NET_VPORT), - "on_vport_instanceid_changed": lambda *x: self.enable_apply(x, - EDIT_NET_VPORT), - "on_gfx_type_combo_changed": lambda *x: self.enable_apply(x, EDIT_GFX_TYPE), "on_vnc_keymap_combo_changed": lambda *x: self.enable_apply(x, EDIT_GFX_KEYMAP), @@ -583,6 +580,9 @@ class vmmDetails(vmmGObjectUI): self.addhwmenu = None self.fsDetails.cleanup() + self.fsDetails = None + self.netlist.cleanup() + self.netlist = None def show(self): logging.debug("Showing VM details: %s", self.vm) @@ -926,18 +926,6 @@ class vmmDetails(vmmGObjectUI): if not (self.conn.is_qemu() or self.conn.is_test_conn()): self.widget("iotune-expander").set_visible(False) - # Network source - net_source = self.widget("network-source") - net_bridge = self.widget("network-bridge-box") - source_mode_combo = self.widget("network-source-mode") - vport_expander = self.widget("vport-expander") - sharedui.build_network_list(net_source, net_bridge, - source_mode_combo, vport_expander) - - # source mode - source_mode = self.widget("network-source-mode") - vmmAddHardware.build_network_source_mode_combo(self.vm, source_mode) - # Network model net_model = self.widget("network-model") vmmAddHardware.build_network_model_combo(self.vm, net_model) @@ -988,33 +976,6 @@ class vmmDetails(vmmGObjectUI): combo.set_active(-1) - def set_combo_entry(self, widget, value, label="", comparefunc=None): - label = label or value - model_combo = self.widget(widget) - - idx = -1 - if comparefunc: - model_in_list, idx = comparefunc(model_combo.get_model(), value) - else: - model_list = [x[0] for x in model_combo.get_model()] - model_in_list = (value in model_list) - if model_in_list: - idx = model_list.index(value) - - model_combo.set_active(idx) - if idx == -1 and model_combo.get_has_entry(): - model_combo.get_child().set_text(value or "") - - def get_combo_entry(self, widgetname): - combo = self.widget(widgetname) - ret = uiutil.get_list_selection(combo, 0) - if ret: - return ret - if not combo.get_has_entry(): - return None - return combo.get_child().get_text().strip() - - ########################## # Window state listeners # ########################## @@ -1906,7 +1867,7 @@ class vmmDetails(vmmGObjectUI): add_hotplug(self.vm.hotplug_title, title) if self.edited(EDIT_MACHTYPE): - machtype = self.get_combo_entry("machine-type") + machtype = uiutil.get_combo_entry(self.widget("machine-type")) add_define(self.vm.define_machtype, machtype) if self.edited(EDIT_DESC): @@ -2045,15 +2006,15 @@ class vmmDetails(vmmGObjectUI): add_define(self.vm.define_disk_removable, dev_id_info, do_removable) if self.edited(EDIT_DISK_CACHE): - cache = self.get_combo_entry("disk-cache") + cache = uiutil.get_combo_entry(self.widget("disk-cache")) add_define(self.vm.define_disk_cache, dev_id_info, cache) if self.edited(EDIT_DISK_IO): - io = self.get_combo_entry("disk-io") + io = uiutil.get_combo_entry(self.widget("disk-io")) add_define(self.vm.define_disk_io, dev_id_info, io) if self.edited(EDIT_DISK_FORMAT): - fmt = self.get_combo_entry("disk-format") + fmt = uiutil.get_combo_entry(self.widget("disk-format")) add_define(self.vm.define_disk_driver_type, dev_id_info, fmt) if self.edited(EDIT_DISK_SERIAL): @@ -2077,7 +2038,7 @@ class vmmDetails(vmmGObjectUI): # Do this last since it can change uniqueness info of the dev if self.edited(EDIT_DISK_BUS): - bus = self.get_combo_entry("disk-bus") + bus = uiutil.get_combo_entry(self.widget("disk-bus")) addr = None if bus == "spapr-vscsi": bus = "scsi" @@ -2092,7 +2053,7 @@ class vmmDetails(vmmGObjectUI): ignore = add_hotplug if self.edited(EDIT_SOUND_MODEL): - model = self.get_combo_entry("sound-model") + model = uiutil.get_combo_entry(self.widget("sound-model")) if model: add_define(self.vm.define_sound_model, dev_id_info, model) @@ -2104,7 +2065,7 @@ class vmmDetails(vmmGObjectUI): ignore = add_hotplug if self.edited(EDIT_SMARTCARD_MODE): - model = self.get_combo_entry("smartcard-mode") + model = uiutil.get_combo_entry(self.widget("smartcard-mode")) if model: add_define(self.vm.define_smartcard_mode, dev_id_info, model) @@ -2116,30 +2077,20 @@ class vmmDetails(vmmGObjectUI): ignore = add_hotplug if self.edited(EDIT_NET_MODEL): - model = self.get_combo_entry("network-model") + model = uiutil.get_combo_entry(self.widget("network-model")) addr = None if model == "spapr-vlan": addr = "spapr-vio" add_define(self.vm.define_network_model, dev_id_info, model, addr) if self.edited(EDIT_NET_SOURCE): - mode = None - net_list = self.widget("network-source") - net_bridge = self.widget("network-bridge") - nettype, source = sharedui.get_network_selection(net_list, - net_bridge) - if nettype == "direct": - mode = self.get_combo_entry("network-source-mode") - + nettype, source, mode = self.netlist.get_network_selection() add_define(self.vm.define_network_source, dev_id_info, nettype, source, mode) if self.edited(EDIT_NET_VPORT): - vport_type = self.get_text("vport-type") - vport_managerid = self.get_text("vport-managerid") - vport_typeid = self.get_text("vport-typeid") - vport_idver = self.get_text("vport-typeidversion") - vport_instid = self.get_text("vport-instanceid") + (vport_type, vport_managerid, vport_typeid, + vport_idver, vport_instid) = self.netlist.get_vport() add_define(self.vm.define_virtualport, dev_id_info, vport_type, vport_managerid, vport_typeid, @@ -2162,12 +2113,12 @@ class vmmDetails(vmmGObjectUI): passwd) if self.edited(EDIT_GFX_KEYMAP): - keymap = self.get_combo_entry("gfx-keymap") + keymap = uiutil.get_combo_entry(self.widget("gfx-keymap")) add_define(self.vm.define_graphics_keymap, dev_id_info, keymap) # Do this last since it can change graphics unique ID if self.edited(EDIT_GFX_TYPE): - gtype = self.get_combo_entry("gfx-type") + gtype = uiutil.get_combo_entry(self.widget("gfx-type")) add_define(self.vm.define_graphics_type, dev_id_info, gtype) return self._change_config_helper(df, da, hf, ha) @@ -2178,7 +2129,7 @@ class vmmDetails(vmmGObjectUI): ignore = add_hotplug if self.edited(EDIT_VIDEO_MODEL): - model = self.get_combo_entry("video-model") + model = uiutil.get_combo_entry(self.widget("video-model")) if model: add_define(self.vm.define_video_model, dev_id_info, model) @@ -2190,7 +2141,7 @@ class vmmDetails(vmmGObjectUI): ignore = add_hotplug if self.edited(EDIT_CONTROLLER_MODEL): - model = self.get_combo_entry("controller-model") + model = uiutil.get_combo_entry(self.widget("controller-model")) if model: add_define(self.vm.define_controller_model, dev_id_info, model) @@ -2202,11 +2153,11 @@ class vmmDetails(vmmGObjectUI): ignore = add_hotplug if self.edited(EDIT_WATCHDOG_MODEL): - model = self.get_combo_entry("watchdog-model") + model = uiutil.get_combo_entry(self.widget("watchdog-model")) add_define(self.vm.define_watchdog_model, dev_id_info, model) if self.edited(EDIT_WATCHDOG_ACTION): - action = self.get_combo_entry("watchdog-action") + action = uiutil.get_combo_entry(self.widget("watchdog-action")) add_define(self.vm.define_watchdog_action, dev_id_info, action) return self._change_config_helper(df, da, hf, ha) @@ -2400,7 +2351,7 @@ class vmmDetails(vmmGObjectUI): machtype = self.vm.get_machtype() if not arch in ["i686", "x86_64"]: if machtype is not None: - self.set_combo_entry("machine-type", machtype) + uiutil.set_combo_entry(self.widget("machine-type"), machtype) def refresh_inspection_page(self): inspection_supported = self.config.support_inspection @@ -2640,8 +2591,8 @@ class vmmDetails(vmmGObjectUI): uiutil.set_grid_row_visible(self.widget("disk-removable"), can_set_removable) self.widget("disk-size").set_text(size) - self.set_combo_entry("disk-cache", cache) - self.set_combo_entry("disk-io", io) + uiutil.set_combo_entry(self.widget("disk-cache"), cache) + uiutil.set_combo_entry(self.widget("disk-io"), io) self.widget("disk-format").set_sensitive(show_format) self.widget("disk-format").get_child().set_text(driver_type) @@ -2649,7 +2600,7 @@ class vmmDetails(vmmGObjectUI): no_default = not self.is_customize_dialog self.populate_disk_bus_combo(devtype, no_default) - self.set_combo_entry("disk-bus", bus) + uiutil.set_combo_entry(self.widget("disk-bus"), bus) self.widget("disk-serial").set_text(serial or "") self.widget("disk-iotune-rbs").set_value(iotune_rbs) @@ -2675,70 +2626,12 @@ class vmmDetails(vmmGObjectUI): if not net: return - nettype = net.type - source = net.source - source_mode = net.source_mode - model = net.model - - netobj = None - if nettype == virtinst.VirtualNetworkInterface.TYPE_VIRTUAL: - name_dict = {} - for uuid in self.conn.list_net_uuids(): - vnet = self.conn.get_net(uuid) - name = vnet.get_name() - name_dict[name] = vnet - - if source and source in name_dict: - netobj = name_dict[source] - - desc = sharedui.pretty_network_desc(nettype, source, netobj) - + vmmAddHardware.populate_network_model_combo( + self.vm, self.widget("network-model")) + uiutil.set_combo_entry(self.widget("network-model"), net.model) self.widget("network-mac-address").set_text(net.macaddr) - sharedui.populate_network_list( - self.widget("network-source"), - self.conn) - self.widget("network-source").set_active(-1) - self.widget("network-bridge").set_text("") - def compare_network(model, info): - for idx in range(len(model)): - row = model[idx] - if row[0] == info[0] and row[1] == info[1]: - return True, idx - - if info[0] == virtinst.VirtualNetworkInterface.TYPE_BRIDGE: - idx = (len(model) - 1) - self.widget("network-bridge").set_text(str(info[1])) - return True, idx - - return False, 0 - - self.set_combo_entry("network-source", - (nettype, source), label=desc, - comparefunc=compare_network) - - is_direct = (nettype == "direct") - uiutil.set_grid_row_visible(self.widget("network-source-mode"), - is_direct) - self.widget("vport-expander").set_visible(is_direct) - - # source mode - vmmAddHardware.populate_network_source_mode_combo(self.vm, - self.widget("network-source-mode")) - self.set_combo_entry("network-source-mode", source_mode) - - # Virtualport config - 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 "") - - vmmAddHardware.populate_network_model_combo(self.vm, - self.widget("network-model")) - self.set_combo_entry("network-model", model) + self.netlist.set_dev(net) def refresh_input_page(self): inp = self.get_hw_selection(HW_LIST_COL_DEVICE) @@ -2807,7 +2700,7 @@ class vmmDetails(vmmGObjectUI): self.widget("gfx-port").set_text(port_to_string(gfx.port)) self.widget("gfx-address").set_text(gfx.listen or "127.0.0.1") - self.set_combo_entry("gfx-keymap", gfx.keymap or None) + uiutil.set_combo_entry(self.widget("gfx-keymap"), gfx.keymap or None) self.widget("gfx-password").set_text(gfx.passwd or "") self.widget("gfx-use-password").set_active(use_passwd) @@ -2832,7 +2725,7 @@ class vmmDetails(vmmGObjectUI): if settype: show_row("gfx-type") - self.set_combo_entry("gfx-type", gtype, label=settype) + uiutil.set_combo_entry(self.widget("gfx-type"), gtype) self.widget("graphics-title").set_markup("%s" % title) @@ -2842,14 +2735,14 @@ class vmmDetails(vmmGObjectUI): if not sound: return - self.set_combo_entry("sound-model", sound.model) + uiutil.set_combo_entry(self.widget("sound-model"), sound.model) def refresh_smartcard_page(self): sc = self.get_hw_selection(HW_LIST_COL_DEVICE) if not sc: return - self.set_combo_entry("smartcard-mode", sc.mode) + uiutil.set_combo_entry(self.widget("smartcard-mode"), sc.mode) def refresh_redir_page(self): rd = self.get_hw_selection(HW_LIST_COL_DEVICE) @@ -3064,8 +2957,7 @@ class vmmDetails(vmmGObjectUI): self.widget("video-ram").set_text(ramlabel) self.widget("video-heads").set_text(heads and str(heads) or "-") - self.set_combo_entry("video-model", model, - label=vid.pretty_model(model)) + uiutil.set_combo_entry(self.widget("video-model"), model) def refresh_watchdog_page(self): watch = self.get_hw_selection(HW_LIST_COL_DEVICE) @@ -3075,8 +2967,8 @@ class vmmDetails(vmmGObjectUI): model = watch.model action = watch.action - self.set_combo_entry("watchdog-model", model) - self.set_combo_entry("watchdog-action", action) + uiutil.set_combo_entry(self.widget("watchdog-model"), model) + uiutil.set_combo_entry(self.widget("watchdog-action"), action) def refresh_controller_page(self): dev = self.get_hw_selection(HW_LIST_COL_DEVICE) @@ -3105,7 +2997,8 @@ class vmmDetails(vmmGObjectUI): else: self.widget("config-remove").set_sensitive(True) - self.set_combo_entry("controller-model", dev.model or "default") + uiutil.set_combo_entry(self.widget("controller-model"), + dev.model or "default") def refresh_filesystem_page(self): dev = self.get_hw_selection(HW_LIST_COL_DEVICE) diff --git a/virtManager/netlist.py b/virtManager/netlist.py new file mode 100644 index 00000000..f90e6325 --- /dev/null +++ b/virtManager/netlist.py @@ -0,0 +1,466 @@ +# +# 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): + 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) + + 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 _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) diff --git a/virtManager/sharedui.py b/virtManager/sharedui.py index 3096a3f1..3c539061 100644 --- a/virtManager/sharedui.py +++ b/virtManager/sharedui.py @@ -29,7 +29,6 @@ from gi.repository import Gtk import virtinst from virtManager import config -from virtManager import uiutil ############################################################ @@ -224,299 +223,6 @@ def check_path_search_for_qemu(err, conn, path): config.running_config.add_perms_fix_ignore(errors.keys()) -######################################### -# VM device listing helpers # -######################################### - -def _net_list_changed(net_list, bridge_box, - source_mode_combo, vport_expander): - if not bridge_box: - return - - row = uiutil.get_list_selection(net_list) - if not row: - return - - if source_mode_combo is not None: - doshow = (row[0] == virtinst.VirtualNetworkInterface.TYPE_DIRECT) - uiutil.set_grid_row_visible(source_mode_combo, doshow) - vport_expander.set_visible(doshow) - - show_bridge = row[5] - uiutil.set_grid_row_visible(bridge_box, show_bridge) - - -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 build_network_list(net_list, bridge_box, source_mode_combo=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_combo, 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 get_network_selection(net_list, bridge_entry): - row = uiutil.get_list_selection(net_list) - if not row: - return 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() - - 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) - - skip_ifaces = ["lo"] - - # 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]) 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)" % pretty_network_desc(nettype, bridge_name) - else: - bridge_name = name - brlabel = _("(Empty bridge)") - else: - if (show_direct_interfaces and - conn.check_support( - 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 = 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(err, conn, nettype, devname, macaddr, model=None): - net = 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.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.show_err(_("Could not start virtual network " - "'%s': %s") % (devname, str(e))) - - # Create network device - try: - net = virtinst.VirtualNetworkInterface(conn.get_backend()) - net.type = nettype - net.source = devname - net.macaddr = macaddr - net.model = model - if net.model == "spapr-vlan": - net.address.set_addrstr("spapr-vio") - - - except Exception, e: - return err.val_err(_("Error with network parameters."), e) - - # Make sure there is no mac address collision - isfatal, errmsg = net.is_conflict_net(conn.get_backend(), net.macaddr) - if isfatal: - return err.val_err(_("Mac address collision."), errmsg) - elif errmsg is not None: - retv = err.yes_no(_("Mac address collision."), - _("%s Are you sure you want to use this " - "address?") % errmsg) - if not retv: - return False - - return net - - ############################################ # Populate media widget (choosecd, create) # ############################################ diff --git a/virtManager/uiutil.py b/virtManager/uiutil.py index 83ada711..ea48ddfc 100644 --- a/virtManager/uiutil.py +++ b/virtManager/uiutil.py @@ -122,6 +122,37 @@ def set_row_selection(listwidget, prevkey): selection.emit("changed") +def set_combo_entry(combo, value): + """ + Search the passed combobox for value, comparing against the + first item in each row. If found, select it. If not found, and + the combobox has a text entry, stick the value in their and + select it. + """ + idx = -1 + model_list = [x[0] for x in combo.get_model()] + model_in_list = (value in model_list) + if model_in_list: + idx = model_list.index(value) + + combo.set_active(idx) + if idx == -1 and combo.get_has_entry(): + combo.get_child().set_text(value or "") + + +def get_combo_entry(combo): + """ + Helper to get the value specified in a combo box, with or + without and entry + """ + ret = get_list_selection(combo, 0) + if ret: + return ret + if not combo.get_has_entry(): + return None + return combo.get_child().get_text().strip() + + def child_get_property(parent, child, propname): """ Wrapper for child_get_property, which pygobject doesn't properly