2014-01-28 01:58:45 +04:00
#
# Copyright (C) 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
#
import logging
from gi . repository import Gtk
from gi . repository import GObject
import virtinst
from virtManager import uiutil
from virtManager . baseclass import vmmGObjectUI
class vmmNetworkList ( vmmGObjectUI ) :
__gsignals__ = {
" changed " : ( GObject . SignalFlags . RUN_FIRST , None , [ ] ) ,
" changed-vport " : ( GObject . SignalFlags . RUN_FIRST , None , [ ] )
}
def __init__ ( self , conn , builder , topwin ) :
vmmGObjectUI . __init__ ( self , " netlist.ui " ,
None , builder = builder , topwin = topwin )
self . conn = conn
self . builder . connect_signals ( {
" on_net_source_changed " : self . _on_net_source_changed ,
" on_net_source_mode_changed " : self . _emit_changed ,
" on_net_bridge_name_changed " : self . _emit_changed ,
" on_vport_type_changed " : self . _emit_vport_changed ,
" on_vport_managerid_changed " : self . _emit_vport_changed ,
" on_vport_typeid_changed " : self . _emit_vport_changed ,
" on_vport_typeidversion_changed " : self . _emit_vport_changed ,
" on_vport_instanceid_changed " : self . _emit_vport_changed ,
} )
self . _init_ui ( )
self . top_label = self . widget ( " net-source-label " )
self . top_box = self . widget ( " net-source-box " )
self . top_vport = self . widget ( " vport-expander " )
def _cleanup ( self ) :
2014-01-28 23:40:17 +04:00
try :
self . conn . disconnect_by_func ( self . _repopulate_network_list )
2014-02-05 23:25:13 +04:00
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 )
2014-01-28 23:40:17 +04:00
except :
pass
2014-01-28 01:58:45 +04:00
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 )
2014-01-28 23:40:17 +04:00
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 )
2014-01-28 01:58:45 +04:00
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 ) :
2014-04-03 18:53:54 +04:00
return uiutil . get_list_selection ( self . widget ( " net-source " ) , None )
2014-01-28 01:58:45 +04:00
def get_network_selection ( self ) :
bridge_entry = self . widget ( " net-bridge-name " )
2014-04-03 18:53:54 +04:00
row = self . get_network_row ( )
2014-01-28 01:58:45 +04:00
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 " )
2014-01-28 23:40:17 +04:00
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
2014-01-28 01:58:45 +04:00
def _on_net_source_changed ( self , src ) :
2014-04-03 18:53:54 +04:00
ignore = src
2014-01-28 01:58:45 +04:00
self . _emit_changed ( )
2014-04-03 18:53:54 +04:00
row = self . get_network_row ( )
2014-01-28 01:58:45 +04:00
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 )