2019-04-14 22:34:40 +03:00
# 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.
import logging
from gi . repository import Gtk
2019-04-18 22:31:14 +03:00
from gi . repository import Pango
2019-04-14 22:34:40 +03:00
from virtinst import NodeDevice
from . import uiutil
from . asyncjob import vmmAsyncJob
from . baseclass import vmmGObjectUI
from . createnet import vmmCreateNetwork
2019-04-15 00:54:25 +03:00
from . xmleditor import vmmXMLEditor
2019-04-14 22:34:40 +03:00
EDIT_NET_IDS = (
EDIT_NET_NAME ,
EDIT_NET_AUTOSTART ,
EDIT_NET_QOS ,
2019-04-15 00:54:25 +03:00
EDIT_NET_XML ,
) = list ( range ( 4 ) )
2019-04-14 22:34:40 +03:00
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
2019-04-15 00:54:25 +03:00
self . _xmleditor = None
2019-04-14 22:34:40 +03:00
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 ) ) ,
" on_qos_inbound_average_changed " : ( lambda * x :
self . _enable_net_apply ( EDIT_NET_QOS ) ) ,
" on_qos_inbound_peak_changed " : ( lambda * x :
self . _enable_net_apply ( EDIT_NET_QOS ) ) ,
" on_qos_inbound_burst_changed " : ( lambda * x :
self . _enable_net_apply ( EDIT_NET_QOS ) ) ,
" on_qos_outbound_average_changed " : ( lambda * x :
self . _enable_net_apply ( EDIT_NET_QOS ) ) ,
" on_qos_outbound_peak_changed " : ( lambda * x :
self . _enable_net_apply ( EDIT_NET_QOS ) ) ,
" on_qos_outbound_burst_changed " : ( lambda * x :
self . _enable_net_apply ( EDIT_NET_QOS ) ) ,
" on_net_qos_inbound_enable_toggled " : self . _change_qos_cb ,
" on_net_qos_outbound_enable_toggled " : self . _change_qos_cb ,
} )
self . _init_ui ( )
self . _populate_networks ( )
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
2019-04-15 00:54:25 +03:00
self . _xmleditor . cleanup ( )
self . _xmleditor = None
2019-04-14 22:34:40 +03:00
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 )
# [ unique, label, icon name, icon size, is_active ]
netListModel = Gtk . ListStore ( str , 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 ( )
2019-04-18 22:31:14 +03:00
net_txt . set_property ( " ellipsize " , Pango . EllipsizeMode . END )
2019-04-14 22:34:40 +03:00
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 )
# Virtual Function list
# [vf-name]
vf_list = self . widget ( " vf-list " )
vf_list_model = Gtk . ListStore ( str )
vf_list . set_model ( vf_list_model )
vf_list . set_headers_visible ( False )
vfTextCol = Gtk . TreeViewColumn ( )
vf_txt = Gtk . CellRendererText ( )
vfTextCol . pack_start ( vf_txt , True )
vfTextCol . add_attribute ( vf_txt , ' text ' , 0 )
vf_list . append_column ( vfTextCol )
2019-04-15 00:54:25 +03:00
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 )
2019-04-14 22:34:40 +03:00
##############
# Public API #
##############
def refresh_page ( self ) :
self . _populate_networks ( )
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 . is_network_capable ( ) )
if conn_active and not self . conn . is_network_capable ( ) :
self . _set_error_page (
_ ( " Libvirt connection does not support virtual network "
" management. " ) )
if conn_active :
uiutil . set_list_selection_by_number ( self . widget ( " net-list " ) , 0 )
return
self . _set_error_page ( _ ( " Connection not active. " ) )
self . _populate_networks ( )
def _current_network ( self ) :
connkey = uiutil . get_list_selection ( self . widget ( " net-list " ) )
return connkey and self . conn . get_net ( connkey )
def _set_error_page ( self , msg ) :
self . widget ( " network-pages " ) . set_current_page ( 1 )
self . widget ( " network-error-label " ) . set_text ( msg )
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 :
logging . 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 . get_connkey ( ) , 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 and curnet . get_connkey ( ) or None )
def _populate_net_ipv4_state ( self , net ) :
( netstr ,
( dhcpstart , dhcpend ) ,
( routeaddr , routevia ) ) = 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 )
uiutil . set_grid_row_visible (
self . widget ( " net-ipv4-route " ) , bool ( routevia ) )
if routevia :
routevia = routeaddr + " , gateway= " + routevia
self . widget ( " net-ipv4-route " ) . set_text ( routevia or " " )
def _populate_net_ipv6_state ( self , net ) :
( netstr ,
( dhcpstart , dhcpend ) ,
( routeaddr , routevia ) ) = 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 " " )
uiutil . set_grid_row_visible (
self . widget ( " net-ipv6-route " ) , bool ( routevia ) )
if routevia :
routevia = routeaddr + " , gateway= " + routevia
self . widget ( " net-ipv6-route " ) . set_text ( routevia or " " )
def _update_qos_widgets ( self ) :
enabled = self . widget ( " net-qos-inbound-enable " ) . get_active ( )
self . widget ( " net-qos-inbound-grid " ) . set_visible ( enabled )
enabled = self . widget ( " net-qos-outbound-enable " ) . get_active ( )
self . widget ( " net-qos-outbound-grid " ) . set_visible ( enabled )
def _populate_qos_state ( self , net ) :
qos = net . get_qos ( )
2019-06-09 22:29:44 +03:00
def is_inbound ( ) :
return bool ( qos . inbound_average or qos . inbound_peak or
qos . inbound_burst or qos . inbound_floor )
def is_outbound ( ) :
return bool ( qos . outbound_average or qos . outbound_peak or
qos . outbound_burst )
self . widget ( " net-qos-inbound-enable " ) . set_active ( is_inbound ( ) )
self . widget ( " net-qos-outbound-enable " ) . set_active ( is_outbound ( ) )
2019-04-14 22:34:40 +03:00
self . _update_qos_widgets ( )
self . widget ( " qos-inbound-average " ) . set_text ( qos . inbound_average or " " )
self . widget ( " qos-inbound-peak " ) . set_text ( qos . inbound_peak or " " )
self . widget ( " qos-inbound-burst " ) . set_text ( qos . inbound_burst or " " )
self . widget ( " qos-outbound-average " ) . set_text ( qos . outbound_average or " " )
self . widget ( " qos-outbound-peak " ) . set_text ( qos . outbound_peak or " " )
self . widget ( " qos-outbound-burst " ) . set_text ( qos . outbound_burst or " " )
def _populate_sriov_state ( self , net ) :
( is_vf_pool , pf_name , vfs ) = net . get_sriov_vf_networks ( )
self . widget ( " net-sriov-expander " ) . set_visible ( is_vf_pool )
if not pf_name :
self . widget ( " pf-name " ) . set_text ( " N/A " )
return
self . widget ( " pf-name " ) . set_text ( pf_name )
vf_list_model = self . widget ( " vf-list " ) . get_model ( )
vf_list_model . clear ( )
for vf in vfs :
addrStr = " %x : %x : %x . %x " % ( vf . domain , vf . bus , vf . slot , vf . function )
pcidev = NodeDevice . lookupNodedevFromString ( self . conn . get_backend ( ) ,
addrStr )
vf_name = None
netdevs = self . conn . filter_nodedevs ( " net " )
for netdev in netdevs :
logging . debug ( netdev . xmlobj . parent )
if pcidev . name == netdev . xmlobj . parent :
vf_name = netdev . xmlobj . interface
break
vf_list_model . append ( [ vf_name or addrStr ] )
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 . _populate_qos_state ( net )
self . _populate_sriov_state ( net )
2019-04-15 00:54:25 +03:00
self . _xmleditor . set_xml_from_libvirtobject ( net )
2019-04-14 22:34:40 +03:00
#############################
# Network lifecycle actions #
#############################
def _delete_network_cb ( self , src ) :
net = self . _current_network ( )
if net is None :
return
result = self . err . yes_no ( _ ( " Are you sure you want to permanently "
" delete the network %s ? " ) % net . get_name ( ) )
if not result :
return
logging . 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
logging . 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
logging . debug ( " Stopping network ' %s ' " , net . get_name ( ) )
self . widget ( " vf-list " ) . get_model ( ) . clear ( )
vmmAsyncJob . simple_async_noshow ( net . stop , [ ] , self ,
_ ( " Error stopping network ' %s ' " ) % net . get_name ( ) )
def _add_network_cb ( self , src ) :
logging . debug ( " Launching ' Add Network ' " )
try :
if self . _addnet is None :
self . _addnet = vmmCreateNetwork ( self . conn )
self . _addnet . show ( self . topwin )
except Exception as e :
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
logging . 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_QOS in self . _active_edits :
in_qos = self . widget ( " net-qos-inbound-enable " ) . get_active ( )
out_qos = self . widget ( " net-qos-outbound-enable " ) . get_active ( )
def get_value ( name , enabled ) :
if not enabled :
return None
return self . widget ( name ) . get_text ( ) or None
args = { }
args [ ' inbound_average ' ] = get_value ( " qos-inbound-average " , in_qos )
args [ ' inbound_peak ' ] = get_value ( " qos-inbound-peak " , in_qos )
args [ ' inbound_burst ' ] = get_value ( " qos-inbound-burst " , in_qos )
args [ ' outbound_average ' ] = get_value ( " qos-outbound-average " , out_qos )
args [ ' outbound_peak ' ] = get_value ( " qos-outbound-peak " , out_qos )
args [ ' outbound_burst ' ] = get_value ( " qos-outbound-burst " , out_qos )
if net . set_qos ( * * args ) :
self . err . show_err (
_ ( " Network could not be updated " ) ,
text2 = _ ( " This change will take effect when the "
" network is restarted " ) ,
buttons = Gtk . ButtonsType . OK ,
dialog_type = Gtk . MessageType . INFO )
2019-04-15 00:54:25 +03:00
if EDIT_NET_XML in self . _active_edits :
net . define_xml ( self . _xmleditor . get_xml ( ) )
2019-04-14 22:34:40 +03:00
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 )
2019-06-15 12:58:43 +03:00
self . _xmleditor . details_changed = False
2019-04-14 22:34:40 +03:00
def _enable_net_apply ( self , edittype ) :
self . widget ( " net-apply " ) . set_sensitive ( True )
self . _active_edits . add ( edittype )
2019-06-15 12:58:43 +03:00
self . _xmleditor . details_changed = True
2019-04-14 22:34:40 +03:00
def _confirm_changes ( self ) :
2019-04-14 23:28:58 +03:00
if ( self . is_visible ( ) and
self . _active_edits and
self . err . confirm_unapplied_changes ( ) ) :
2019-04-14 22:34:40 +03:00
self . _net_apply ( )
2019-04-14 23:28:58 +03:00
self . _disable_net_apply ( )
2019-04-14 22:34:40 +03:00
return True
################
# UI listeners #
################
def _conn_state_changed_cb ( self , conn ) :
self . _refresh_conn_state ( )
def _conn_nets_changed_cb ( self , src , connkey ) :
self . _populate_networks ( )
def _change_qos_cb ( self , src ) :
self . _enable_net_apply ( EDIT_NET_QOS )
self . _update_qos_widgets ( )
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 . get_connkey ( ) :
row [ 4 ] = net . is_active ( )
# If refreshed network is the current net, refresh the UI
curnet = self . _current_network ( )
if curnet and curnet . get_connkey ( ) == net . get_connkey ( ) :
self . _refresh_current_network ( )
def _net_selected_cb ( self , selection ) :
self . _refresh_current_network ( )
2019-04-15 00:54:25 +03:00
def _xmleditor_xml_requested_cb ( self , src ) :
self . _refresh_current_network ( )
def _xmleditor_xml_reset_cb ( self , src ) :
self . _refresh_current_network ( )