mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-11 05:17:59 +03:00
e45a2228dc
Make it explicit that all uses of this is actually the object name. We already leaked this abstraction in several places so better to make it explicit. This also communicates to users that this is a field that is not immutable so it shouldn't be used as a unique key Signed-off-by: Cole Robinson <crobinso@redhat.com>
390 lines
13 KiB
Python
390 lines
13 KiB
Python
# Copyright (C) 2007, 2013-2014 Red Hat, Inc.
|
|
# Copyright (C) 2007 Daniel P. Berrange <berrange@redhat.com>
|
|
#
|
|
# This work is licensed under the GNU GPLv2 or later.
|
|
# See the COPYING file in the top-level directory.
|
|
|
|
from gi.repository import Gtk
|
|
from gi.repository import Pango
|
|
|
|
from virtinst import log
|
|
|
|
from .lib import uiutil
|
|
from .asyncjob import vmmAsyncJob
|
|
from .baseclass import vmmGObjectUI
|
|
from .createnet import vmmCreateNetwork
|
|
from .xmleditor import vmmXMLEditor
|
|
|
|
|
|
EDIT_NET_IDS = (
|
|
EDIT_NET_NAME,
|
|
EDIT_NET_AUTOSTART,
|
|
EDIT_NET_XML,
|
|
) = list(range(3))
|
|
|
|
|
|
ICON_RUNNING = "state_running"
|
|
ICON_SHUTOFF = "state_shutoff"
|
|
|
|
|
|
class vmmHostNets(vmmGObjectUI):
|
|
def __init__(self, conn, builder, topwin):
|
|
vmmGObjectUI.__init__(self, "hostnets.ui",
|
|
None, builder=builder, topwin=topwin)
|
|
self.conn = conn
|
|
|
|
self._addnet = None
|
|
self._xmleditor = None
|
|
|
|
self._active_edits = set()
|
|
self.top_box = self.widget("top-box")
|
|
|
|
self.builder.connect_signals({
|
|
"on_net_add_clicked": self._add_network_cb,
|
|
"on_net_delete_clicked": self._delete_network_cb,
|
|
"on_net_stop_clicked": self._stop_network_cb,
|
|
"on_net_start_clicked": self._start_network_cb,
|
|
"on_net_apply_clicked": (lambda *x: self._net_apply()),
|
|
"on_net_list_changed": self._net_selected_cb,
|
|
"on_net_autostart_toggled": (lambda *x:
|
|
self._enable_net_apply(EDIT_NET_AUTOSTART)),
|
|
"on_net_name_changed": (lambda *x:
|
|
self._enable_net_apply(EDIT_NET_NAME)),
|
|
})
|
|
|
|
self._init_ui()
|
|
self._populate_networks()
|
|
self._refresh_conn_state()
|
|
self.conn.connect("net-added", self._conn_nets_changed_cb)
|
|
self.conn.connect("net-removed", self._conn_nets_changed_cb)
|
|
self.conn.connect("state-changed", self._conn_state_changed_cb)
|
|
|
|
|
|
#######################
|
|
# Standard UI methods #
|
|
#######################
|
|
|
|
def _cleanup(self):
|
|
self.conn = None
|
|
|
|
if self._addnet:
|
|
self._addnet.cleanup()
|
|
self._addnet = None
|
|
|
|
self._xmleditor.cleanup()
|
|
self._xmleditor = None
|
|
|
|
def close(self, ignore1=None, ignore2=None):
|
|
if self._addnet:
|
|
self._addnet.close()
|
|
|
|
|
|
###########
|
|
# UI init #
|
|
###########
|
|
|
|
def _init_ui(self):
|
|
self.widget("network-pages").set_show_tabs(False)
|
|
|
|
self._xmleditor = vmmXMLEditor(self.builder, self.topwin,
|
|
self.widget("net-details-align"),
|
|
self.widget("net-details"))
|
|
self._xmleditor.connect("changed",
|
|
lambda s: self._enable_net_apply(EDIT_NET_XML))
|
|
self._xmleditor.connect("xml-requested",
|
|
self._xmleditor_xml_requested_cb)
|
|
self._xmleditor.connect("xml-reset",
|
|
self._xmleditor_xml_reset_cb)
|
|
|
|
# [ netobj, label, icon name, icon size, is_active ]
|
|
netListModel = Gtk.ListStore(object, str, str, int, bool)
|
|
self.widget("net-list").set_model(netListModel)
|
|
|
|
sel = self.widget("net-list").get_selection()
|
|
sel.set_select_function((lambda *x: self._confirm_changes()), None)
|
|
|
|
netCol = Gtk.TreeViewColumn(_("Networks"))
|
|
netCol.set_spacing(6)
|
|
net_txt = Gtk.CellRendererText()
|
|
net_txt.set_property("ellipsize", Pango.EllipsizeMode.END)
|
|
net_img = Gtk.CellRendererPixbuf()
|
|
netCol.pack_start(net_img, False)
|
|
netCol.pack_start(net_txt, True)
|
|
netCol.add_attribute(net_txt, 'text', 1)
|
|
netCol.add_attribute(net_txt, 'sensitive', 4)
|
|
netCol.add_attribute(net_img, 'icon-name', 2)
|
|
netCol.add_attribute(net_img, 'stock-size', 3)
|
|
self.widget("net-list").append_column(netCol)
|
|
netListModel.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
|
|
|
|
|
##############
|
|
# Public API #
|
|
##############
|
|
|
|
def refresh_page(self):
|
|
self.conn.schedule_priority_tick(pollnet=True)
|
|
|
|
|
|
#################
|
|
# UI populating #
|
|
#################
|
|
|
|
def _refresh_conn_state(self):
|
|
conn_active = self.conn.is_active()
|
|
self.widget("net-add").set_sensitive(conn_active and
|
|
self.conn.support.conn_network())
|
|
|
|
if conn_active and not self.conn.support.conn_network():
|
|
self._set_error_page( # pragma: no cover
|
|
_("Libvirt connection does not support virtual network "
|
|
"management."))
|
|
|
|
if conn_active:
|
|
uiutil.set_list_selection_by_number(self.widget("net-list"), 0)
|
|
return
|
|
|
|
self._populate_networks()
|
|
self._set_error_page(_("Connection not active."))
|
|
|
|
def _current_network(self):
|
|
return uiutil.get_list_selection(self.widget("net-list"))
|
|
|
|
def _set_error_page(self, msg):
|
|
self.widget("network-pages").set_current_page(1)
|
|
self.widget("network-error-label").set_text(msg)
|
|
self.widget("net-start").set_sensitive(False)
|
|
self.widget("net-stop").set_sensitive(False)
|
|
self.widget("net-delete").set_sensitive(False)
|
|
self._disable_net_apply()
|
|
|
|
def _refresh_current_network(self):
|
|
net = self._current_network()
|
|
if not net:
|
|
self._set_error_page(_("No virtual network selected."))
|
|
return
|
|
|
|
self.widget("network-pages").set_current_page(0)
|
|
|
|
try:
|
|
self._populate_net_state(net)
|
|
except Exception as e: # pragma: no cover
|
|
log.exception(e)
|
|
self._set_error_page(_("Error selecting network: %s") % e)
|
|
self._disable_net_apply()
|
|
|
|
def _populate_networks(self):
|
|
net_list = self.widget("net-list")
|
|
curnet = self._current_network()
|
|
|
|
model = net_list.get_model()
|
|
# Prevent events while the model is modified
|
|
net_list.set_model(None)
|
|
try:
|
|
net_list.get_selection().unselect_all()
|
|
model.clear()
|
|
for net in self.conn.list_nets():
|
|
net.disconnect_by_obj(self)
|
|
net.connect("state-changed", self._net_state_changed_cb)
|
|
model.append([net, net.get_name(), "network-idle",
|
|
Gtk.IconSize.LARGE_TOOLBAR,
|
|
bool(net.is_active())])
|
|
finally:
|
|
net_list.set_model(model)
|
|
|
|
uiutil.set_list_selection(net_list, curnet)
|
|
|
|
def _populate_net_ipv4_state(self, net):
|
|
(netstr, (dhcpstart, dhcpend)) = net.get_ipv4_network()
|
|
|
|
self.widget("net-ipv4-expander").set_visible(bool(netstr))
|
|
if not netstr:
|
|
return
|
|
|
|
forward = net.get_ipv4_forward_mode()
|
|
self.widget("net-ipv4-forwarding-icon").set_from_stock(
|
|
forward and Gtk.STOCK_CONNECT or Gtk.STOCK_DISCONNECT,
|
|
Gtk.IconSize.MENU)
|
|
self.widget("net-ipv4-forwarding").set_text(net.pretty_forward_mode())
|
|
|
|
dhcpstr = _("Disabled")
|
|
if dhcpstart:
|
|
dhcpstr = dhcpstart + " - " + dhcpend
|
|
self.widget("net-ipv4-dhcp-range").set_text(dhcpstr)
|
|
self.widget("net-ipv4-network").set_text(netstr)
|
|
|
|
def _populate_net_ipv6_state(self, net):
|
|
(netstr, (dhcpstart, dhcpend)) = net.get_ipv6_network()
|
|
|
|
self.widget("net-ipv6-expander").set_visible(bool(netstr))
|
|
self.widget("net-ipv6-forwarding-icon").set_from_stock(
|
|
netstr and Gtk.STOCK_CONNECT or Gtk.STOCK_DISCONNECT,
|
|
Gtk.IconSize.MENU)
|
|
|
|
if netstr:
|
|
prettymode = _("Routed network")
|
|
elif net.get_ipv6_enabled():
|
|
prettymode = _("Isolated network, internal routing only")
|
|
else:
|
|
prettymode = _("Isolated network, routing disabled")
|
|
self.widget("net-ipv6-forwarding").set_text(prettymode)
|
|
|
|
dhcpstr = _("Disabled")
|
|
if dhcpstart:
|
|
dhcpstr = dhcpstart + " - " + dhcpend
|
|
self.widget("net-ipv6-dhcp-range").set_text(dhcpstr)
|
|
self.widget("net-ipv6-network").set_text(netstr or "")
|
|
|
|
def _populate_net_state(self, net):
|
|
active = net.is_active()
|
|
|
|
self.widget("net-details").set_sensitive(True)
|
|
self.widget("net-name").set_text(net.get_name())
|
|
self.widget("net-name").set_editable(not active)
|
|
self.widget("net-device").set_text(net.get_bridge_device() or "")
|
|
self.widget("net-name-domain").set_text(net.get_name_domain() or "")
|
|
uiutil.set_grid_row_visible(self.widget("net-name-domain"),
|
|
bool(net.get_name_domain()))
|
|
|
|
state = active and _("Active") or _("Inactive")
|
|
icon = (active and ICON_RUNNING or ICON_SHUTOFF)
|
|
self.widget("net-state").set_text(state)
|
|
self.widget("net-state-icon").set_from_icon_name(icon,
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
self.widget("net-start").set_sensitive(not active)
|
|
self.widget("net-stop").set_sensitive(active)
|
|
self.widget("net-delete").set_sensitive(not active)
|
|
|
|
autostart = net.get_autostart()
|
|
self.widget("net-autostart").set_active(autostart)
|
|
self.widget("net-autostart").set_label(_("On Boot"))
|
|
|
|
self._populate_net_ipv4_state(net)
|
|
self._populate_net_ipv6_state(net)
|
|
|
|
self._xmleditor.set_xml_from_libvirtobject(net)
|
|
|
|
|
|
#############################
|
|
# Network lifecycle actions #
|
|
#############################
|
|
|
|
def _delete_network_cb(self, src):
|
|
net = self._current_network()
|
|
if net is None:
|
|
return # pragma: no cover
|
|
|
|
result = self.err.yes_no(_("Are you sure you want to permanently "
|
|
"delete the network %s?") % net.get_name())
|
|
if not result:
|
|
return
|
|
|
|
log.debug("Deleting network '%s'", net.get_name())
|
|
vmmAsyncJob.simple_async_noshow(net.delete, [], self,
|
|
_("Error deleting network '%s'") % net.get_name())
|
|
|
|
def _start_network_cb(self, src):
|
|
net = self._current_network()
|
|
if net is None:
|
|
return # pragma: no cover
|
|
|
|
log.debug("Starting network '%s'", net.get_name())
|
|
vmmAsyncJob.simple_async_noshow(net.start, [], self,
|
|
_("Error starting network '%s'") % net.get_name())
|
|
|
|
def _stop_network_cb(self, src):
|
|
net = self._current_network()
|
|
if net is None:
|
|
return # pragma: no cover
|
|
|
|
log.debug("Stopping network '%s'", net.get_name())
|
|
vmmAsyncJob.simple_async_noshow(net.stop, [], self,
|
|
_("Error stopping network '%s'") % net.get_name())
|
|
|
|
def _add_network_cb(self, src):
|
|
log.debug("Launching 'Add Network'")
|
|
try:
|
|
if self._addnet is None:
|
|
self._addnet = vmmCreateNetwork(self.conn)
|
|
self._addnet.show(self.topwin)
|
|
except Exception as e: # pragma: no cover
|
|
self.err.show_err(_("Error launching network wizard: %s") % str(e))
|
|
|
|
|
|
############################
|
|
# Net apply/config actions #
|
|
############################
|
|
|
|
def _net_apply(self):
|
|
net = self._current_network()
|
|
if net is None:
|
|
return # pragma: no cover
|
|
|
|
log.debug("Applying changes for network '%s'", net.get_name())
|
|
try:
|
|
if EDIT_NET_AUTOSTART in self._active_edits:
|
|
auto = self.widget("net-autostart").get_active()
|
|
net.set_autostart(auto)
|
|
if EDIT_NET_NAME in self._active_edits:
|
|
net.define_name(self.widget("net-name").get_text())
|
|
self.idle_add(self._populate_networks)
|
|
if EDIT_NET_XML in self._active_edits:
|
|
net.define_xml(self._xmleditor.get_xml())
|
|
|
|
except Exception as e:
|
|
self.err.show_err(_("Error changing network settings: %s") % str(e))
|
|
return
|
|
finally:
|
|
self._disable_net_apply()
|
|
|
|
def _disable_net_apply(self):
|
|
self._active_edits = set()
|
|
self.widget("net-apply").set_sensitive(False)
|
|
self._xmleditor.details_changed = False
|
|
|
|
def _enable_net_apply(self, edittype):
|
|
self.widget("net-apply").set_sensitive(True)
|
|
self._active_edits.add(edittype)
|
|
self._xmleditor.details_changed = True
|
|
|
|
def _confirm_changes(self):
|
|
if (self.is_visible() and
|
|
self._active_edits and
|
|
self.err.confirm_unapplied_changes()):
|
|
self._net_apply()
|
|
|
|
self._disable_net_apply()
|
|
return True
|
|
|
|
|
|
################
|
|
# UI listeners #
|
|
################
|
|
|
|
def _conn_state_changed_cb(self, conn):
|
|
self._refresh_conn_state()
|
|
|
|
def _conn_nets_changed_cb(self, src, net):
|
|
self._populate_networks()
|
|
|
|
def _net_state_changed_cb(self, net):
|
|
# Update net state inline in the tree model
|
|
for row in self.widget("net-list").get_model():
|
|
if row[0] == net:
|
|
row[4] = net.is_active()
|
|
|
|
# If refreshed network is the current net, refresh the UI
|
|
curnet = self._current_network()
|
|
if curnet == net:
|
|
self._refresh_current_network()
|
|
|
|
def _net_selected_cb(self, selection):
|
|
self._refresh_current_network()
|
|
|
|
def _xmleditor_xml_requested_cb(self, src):
|
|
self._refresh_current_network()
|
|
|
|
def _xmleditor_xml_reset_cb(self, src):
|
|
self._refresh_current_network()
|