2013-03-18 01:06:52 +04:00
#
2013-10-28 00:59:46 +04:00
# Copyright 2006-2009, 2013 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2013-03-18 01:06:52 +04:00
2016-08-24 23:14:33 +03:00
import os
2013-07-12 17:54:01 +04:00
import random
2024-11-11 22:25:25 +03:00
from . device import Device , DeviceAddress
2024-11-11 23:30:25 +03:00
from . . nodedev import NodeDevice
2019-06-17 04:12:39 +03:00
from . . logger import log
2018-03-20 19:27:37 +03:00
from . . xmlbuilder import XMLBuilder , XMLChildProperty , XMLProperty
2013-03-18 01:06:52 +04:00
2013-04-11 19:11:21 +04:00
2013-07-12 17:54:01 +04:00
def _random_mac ( conn ) :
""" Generate a random MAC address.
00 - 16 - 3 E allocated to xensource
52 - 54 - 00 used by qemu / kvm
2018-04-30 15:56:53 +03:00
The OUI list is available at https : / / standards . ieee . org / regauth / oui / oui . txt .
2013-07-12 17:54:01 +04:00
The remaining 3 fields are random , with the first bit of the first
random field set 0.
2013-04-11 19:11:21 +04:00
2013-07-12 17:54:01 +04:00
@return : MAC address string
"""
2013-04-11 19:11:21 +04:00
2014-09-12 17:36:35 +04:00
if conn . is_qemu ( ) :
oui = [ 0x52 , 0x54 , 0x00 ]
else :
# Xen
oui = [ 0x00 , 0x16 , 0x3E ]
2013-07-12 17:54:01 +04:00
mac = oui + [
random . randint ( 0x00 , 0xff ) ,
random . randint ( 0x00 , 0xff ) ,
random . randint ( 0x00 , 0xff ) ]
return ' : ' . join ( [ " %02x " % x for x in mac ] )
2013-04-11 19:11:21 +04:00
2013-04-13 22:34:52 +04:00
2016-08-24 23:14:33 +03:00
def _default_route ( ) :
route_file = " /proc/net/route "
2019-06-10 21:15:50 +03:00
if not os . path . exists ( route_file ) : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " route_file= %s does not exist " , route_file )
2016-08-24 23:14:33 +03:00
return None
2017-05-05 21:19:54 +03:00
for line in open ( route_file ) :
2016-08-24 23:14:33 +03:00
info = line . split ( )
2019-06-10 21:15:50 +03:00
if len ( info ) != 11 : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Unexpected field count= %s when parsing %s " ,
2016-08-24 23:14:33 +03:00
len ( info ) , route_file )
break
try :
route = int ( info [ 1 ] , 16 )
if route == 0 :
return info [ 0 ]
except ValueError :
continue
2019-06-10 21:15:50 +03:00
return None # pragma: no cover
2016-08-24 23:14:33 +03:00
2020-01-26 03:03:28 +03:00
def _host_default_bridge ( ) :
2016-08-24 23:14:33 +03:00
dev = _default_route ( )
if not dev :
2019-06-10 21:15:50 +03:00
return None # pragma: no cover
2016-08-24 23:14:33 +03:00
# New style peth0 == phys dev, eth0 == bridge, eth0 == default route
if os . path . exists ( " /sys/class/net/ %s /bridge " % dev ) :
2019-06-10 21:15:50 +03:00
return dev # pragma: no cover
2016-08-24 23:14:33 +03:00
# Old style, peth0 == phys dev, eth0 == netloop, xenbr0 == bridge,
2020-09-23 21:33:17 +03:00
# vif0.0 == netloop attached, eth0 == default route
2016-08-24 23:14:33 +03:00
try :
defn = int ( dev [ - 1 ] )
2019-06-10 21:15:50 +03:00
except Exception : # pragma: no cover
2016-08-24 23:14:33 +03:00
defn = - 1
if ( defn > = 0 and
os . path . exists ( " /sys/class/net/peth %d /brport " % defn ) and
os . path . exists ( " /sys/class/net/xenbr %d /bridge " % defn ) ) :
2019-06-10 21:15:50 +03:00
return " xenbr %d " # pragma: no cover
2016-08-24 23:14:33 +03:00
return None
2020-01-26 03:17:23 +03:00
# Cache the host default bridge lookup. It can change over the lifetime
# of a virt-manager run, but that should be rare, and this saves us
# possibly spamming logs if host lookup goes wrong
_HOST_DEFAULT_BRIDGE = - 1
2020-01-26 03:03:28 +03:00
def _default_bridge ( conn ) :
if conn . is_remote ( ) :
return None
2020-01-26 03:17:23 +03:00
global _HOST_DEFAULT_BRIDGE
if _HOST_DEFAULT_BRIDGE == - 1 :
try :
ret = _host_default_bridge ( )
2020-01-27 14:59:17 +03:00
except Exception : # pragma: no cover
2020-01-26 03:17:23 +03:00
log . debug ( " Error getting host default bridge " , exc_info = True )
ret = None
_HOST_DEFAULT_BRIDGE = ret
2020-01-27 14:59:17 +03:00
ret = _HOST_DEFAULT_BRIDGE
2020-01-26 03:03:28 +03:00
if conn . in_testsuite ( ) :
2020-01-27 14:59:17 +03:00
ret = " testsuitebr0 "
return ret
2016-08-24 23:14:33 +03:00
2021-10-12 20:56:51 +03:00
_MAC_COUNTER = 0
def _testsuite_mac ( ) :
# Generate predictable mac addresses for the test suite
# For some tests, we need to make sure that different mac addresses
# would _not_ be generated in normal operations, so we add some magic
# here to increment the generated address with a special env variable
global _MAC_COUNTER
base = " 00:11:22:33:44:55 "
ret = base [ : - 1 ] + str ( int ( base [ - 1 ] ) + _MAC_COUNTER )
_MAC_COUNTER + = 1
if " VIRTINST_TEST_SUITE_INCREMENT_MACADDR " not in os . environ :
_MAC_COUNTER = 0
return ret
2024-08-02 17:50:15 +03:00
class _Backend ( XMLBuilder ) :
XML_NAME = " backend "
type = XMLProperty ( " ./@type " )
logFile = XMLProperty ( " ./@logFile " , do_abspath = True )
2018-03-20 19:18:35 +03:00
class _VirtualPort ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " virtualport "
2013-07-24 20:02:37 +04:00
2013-09-11 19:47:09 +04:00
type = XMLProperty ( " ./@type " )
managerid = XMLProperty ( " ./parameters/@managerid " , is_int = True )
typeid = XMLProperty ( " ./parameters/@typeid " , is_int = True )
typeidversion = XMLProperty ( " ./parameters/@typeidversion " , is_int = True )
instanceid = XMLProperty ( " ./parameters/@instanceid " )
2015-08-28 14:12:49 +03:00
profileid = XMLProperty ( " ./parameters/@profileid " )
interfaceid = XMLProperty ( " ./parameters/@interfaceid " )
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2024-08-02 17:50:15 +03:00
class _PortForwardRange ( XMLBuilder ) :
XML_NAME = " range "
start = XMLProperty ( " ./@start " , is_int = True )
end = XMLProperty ( " ./@end " , is_int = True )
to = XMLProperty ( " ./@to " , is_int = True )
exclude = XMLProperty ( " ./@exclude " , is_yesno = True )
class _PortForward ( XMLBuilder ) :
XML_NAME = " portForward "
proto = XMLProperty ( " ./@proto " )
address = XMLProperty ( " ./@address " )
dev = XMLProperty ( " ./@dev " )
range = XMLChildProperty ( _PortForwardRange )
2024-11-11 22:25:25 +03:00
class _DeviceInterfaceSourceAddress ( DeviceAddress ) :
pass
2018-03-20 19:18:35 +03:00
class DeviceInterface ( Device ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " interface "
2013-03-18 01:06:52 +04:00
TYPE_BRIDGE = " bridge "
TYPE_VIRTUAL = " network "
TYPE_USER = " user "
2016-08-27 08:41:38 +03:00
TYPE_VHOSTUSER = " vhostuser "
2013-03-18 01:06:52 +04:00
TYPE_ETHERNET = " ethernet "
TYPE_DIRECT = " direct "
2013-07-12 17:54:01 +04:00
@staticmethod
def generate_mac ( conn ) :
"""
Generate a random MAC that doesn ' t conflict with any VMs on
the connection .
"""
2015-09-06 17:36:17 +03:00
if conn . fake_conn_predictable ( ) :
2021-10-12 20:56:51 +03:00
return _testsuite_mac ( )
2013-07-12 17:54:01 +04:00
for ignore in range ( 256 ) :
mac = _random_mac ( conn )
2018-09-03 23:54:23 +03:00
try :
2020-07-06 01:56:36 +03:00
DeviceInterface . check_mac_in_use ( conn , mac )
2013-07-12 17:54:01 +04:00
return mac
2019-06-10 21:15:50 +03:00
except RuntimeError : # pragma: no cover
2018-09-03 23:54:23 +03:00
continue
2013-07-12 17:54:01 +04:00
2019-06-17 04:12:39 +03:00
log . debug ( # pragma: no cover
2019-06-10 21:15:50 +03:00
" Failed to generate non-conflicting MAC " )
return None # pragma: no cover
2013-07-12 17:54:01 +04:00
@staticmethod
2020-07-06 01:56:36 +03:00
def check_mac_in_use ( conn , searchmac ) :
2013-07-12 17:54:01 +04:00
"""
2018-09-03 23:54:23 +03:00
Raise RuntimeError if the passed mac conflicts with a defined VM
2013-07-12 17:54:01 +04:00
"""
2020-07-06 01:39:15 +03:00
if not searchmac :
return
2018-08-31 22:20:50 +03:00
vms = conn . fetch_all_domains ( )
2013-07-12 17:54:01 +04:00
for vm in vms :
2018-03-21 00:23:34 +03:00
for nic in vm . devices . interface :
2013-07-12 17:54:01 +04:00
nicmac = nic . macaddr or " "
if nicmac . lower ( ) == searchmac . lower ( ) :
2018-09-03 23:54:23 +03:00
raise RuntimeError (
_ ( " The MAC address ' %s ' is in use "
" by another virtual machine. " ) % searchmac )
2013-07-12 17:54:01 +04:00
2020-01-26 03:39:26 +03:00
@staticmethod
def default_bridge ( conn ) :
"""
Return the bridge virt - install would use as a default value ,
if one is setup on the host
"""
return _default_bridge ( conn )
2013-03-18 01:06:52 +04:00
2013-09-24 18:00:01 +04:00
###############
# XML helpers #
###############
def _get_source ( self ) :
2013-03-18 01:06:52 +04:00
"""
2015-06-02 15:41:21 +03:00
Convenience function , try to return the relevant < source > value
2013-03-18 01:06:52 +04:00
per the network type .
"""
if self . type == self . TYPE_VIRTUAL :
2019-02-05 01:35:47 +03:00
return self . network
2013-03-18 01:06:52 +04:00
if self . type == self . TYPE_BRIDGE :
2019-02-05 01:35:47 +03:00
return self . bridge
2016-07-08 18:58:28 +03:00
if self . type == self . TYPE_DIRECT :
2019-02-05 01:35:47 +03:00
return self . source_dev
2019-06-10 23:08:45 +03:00
return None
2013-09-24 18:00:01 +04:00
def _set_source ( self , newsource ) :
2013-03-18 01:06:52 +04:00
"""
2015-06-02 15:41:21 +03:00
Convenience function , try to set the relevant < source > value
2013-03-18 01:06:52 +04:00
per the network type
"""
2019-02-05 01:35:47 +03:00
self . bridge = None
self . network = None
self . source_dev = None
2013-09-24 18:00:01 +04:00
2013-04-13 22:34:52 +04:00
if self . type == self . TYPE_VIRTUAL :
2019-02-05 01:35:47 +03:00
self . network = newsource
2013-03-18 01:06:52 +04:00
elif self . type == self . TYPE_BRIDGE :
2019-02-05 01:35:47 +03:00
self . bridge = newsource
2016-07-08 18:58:28 +03:00
elif self . type == self . TYPE_DIRECT :
2019-02-05 01:35:47 +03:00
self . source_dev = newsource
2013-09-24 18:00:01 +04:00
source = property ( _get_source , _set_source )
2013-03-18 01:06:52 +04:00
2013-07-15 21:56:49 +04:00
2013-09-24 18:00:01 +04:00
##################
# XML properties #
##################
2013-07-15 21:56:49 +04:00
2013-07-16 20:48:52 +04:00
_XML_PROP_ORDER = [
2024-08-02 17:50:15 +03:00
" backend " , " bridge " , " network " , " source_dev " , " source_type " ,
" source_path " , " source_mode " , " portgroup " , " macaddr " , " target_dev " ,
" model " , " virtualport " , " filterref " , " rom_bar " , " rom_file " , " mtu_size " ,
" portForward " ,
]
backend = XMLChildProperty ( _Backend , is_single = True )
2013-07-15 21:56:49 +04:00
2019-02-05 01:35:47 +03:00
bridge = XMLProperty ( " ./source/@bridge " )
network = XMLProperty ( " ./source/@network " )
source_dev = XMLProperty ( " ./source/@dev " )
2013-09-24 18:00:01 +04:00
2018-03-20 19:18:35 +03:00
virtualport = XMLChildProperty ( _VirtualPort , is_single = True )
2018-09-02 03:22:47 +03:00
type = XMLProperty ( " ./@type " )
2017-03-05 22:43:31 +03:00
trustGuestRxFilters = XMLProperty ( " ./@trustGuestRxFilters " , is_yesno = True )
2013-07-15 21:56:49 +04:00
2018-09-03 23:44:18 +03:00
macaddr = XMLProperty ( " ./mac/@address " )
2013-03-18 01:06:52 +04:00
2016-08-27 08:41:38 +03:00
source_type = XMLProperty ( " ./source/@type " )
source_path = XMLProperty ( " ./source/@path " )
2018-09-02 03:22:47 +03:00
source_mode = XMLProperty ( " ./source/@mode " )
2024-11-11 22:25:25 +03:00
source_address = XMLChildProperty ( _DeviceInterfaceSourceAddress ,
is_single = True ,
relative_xpath = " ./source " )
2024-11-11 23:30:25 +03:00
managed = XMLProperty ( " ./@managed " , is_yesno = True )
2024-11-11 22:25:25 +03:00
2014-05-31 22:30:07 +04:00
portgroup = XMLProperty ( " ./source/@portgroup " )
2013-09-19 21:27:30 +04:00
model = XMLProperty ( " ./model/@type " )
target_dev = XMLProperty ( " ./target/@dev " )
filterref = XMLProperty ( " ./filterref/@filter " )
2015-11-18 21:59:15 +03:00
link_state = XMLProperty ( " ./link/@state " )
2013-07-24 16:46:55 +04:00
2014-02-01 16:48:04 +04:00
driver_name = XMLProperty ( " ./driver/@name " )
driver_queues = XMLProperty ( " ./driver/@queues " , is_int = True )
2013-07-24 16:46:55 +04:00
2016-01-20 18:53:23 +03:00
rom_bar = XMLProperty ( " ./rom/@bar " , is_onoff = True )
rom_file = XMLProperty ( " ./rom/@file " )
2018-06-06 17:07:46 +03:00
mtu_size = XMLProperty ( " ./mtu/@size " , is_int = True )
2024-08-02 17:50:15 +03:00
portForward = XMLChildProperty ( _PortForward )
2016-01-20 18:53:23 +03:00
2013-09-24 18:00:01 +04:00
#############
# Build API #
#############
2014-02-06 02:45:46 +04:00
def set_default_source ( self ) :
2020-02-03 03:43:06 +03:00
if self . conn . is_qemu_unprivileged ( ) or self . conn . is_test ( ) :
2014-02-06 02:45:46 +04:00
self . type = self . TYPE_USER
2020-01-26 03:03:28 +03:00
return
nettype = DeviceInterface . TYPE_BRIDGE
2020-01-27 14:59:17 +03:00
source = DeviceInterface . default_bridge ( self . conn )
2020-01-26 03:03:28 +03:00
if not source :
nettype = DeviceInterface . TYPE_VIRTUAL
source = " default "
self . type = nettype
self . source = source
2018-09-02 03:22:47 +03:00
2024-11-11 23:30:25 +03:00
def set_from_nodedev ( self , nodedev ) :
log . debug ( " set_from_nodedev xml= \n %s " , nodedev . get_xml ( ) )
self . type = " hostdev "
if self . managed is None :
self . managed = True
if nodedev . device_type == NodeDevice . CAPABILITY_TYPE_PCI :
self . source_address . type = " pci "
self . source_address . domain = nodedev . domain
self . source_address . bus = nodedev . bus
self . source_address . slot = nodedev . slot
self . source_address . function = nodedev . function
else : # pragma: no cover
raise ValueError ( _ ( " Unsupported node device type ' %s ' " ) %
nodedev . device_type )
2018-09-02 03:22:47 +03:00
##################
# Default config #
##################
@staticmethod
2018-09-02 03:36:53 +03:00
def default_model ( guest ) :
2018-09-02 03:22:47 +03:00
if not guest . os . is_hvm ( ) :
return None
if guest . supports_virtionet ( ) :
return " virtio "
if guest . os . is_q35 ( ) :
return " e1000e "
2018-09-29 21:31:55 +03:00
if not guest . os . is_x86 ( ) :
return None
2018-09-02 03:22:47 +03:00
prefs = [ " e1000 " , " rtl8139 " , " ne2k_pci " , " pcnet " ]
supported_models = guest . osinfo . supported_netmodels ( )
for pref in prefs :
if pref in supported_models :
return pref
2018-09-29 21:31:55 +03:00
return " e1000 "
2018-09-02 03:22:47 +03:00
2024-08-02 17:50:15 +03:00
def _set_default_type ( self ) :
if self . type :
return
if self . backend and self . backend . type == " passt " :
self . type = self . TYPE_USER
return
self . type = self . TYPE_BRIDGE
2018-09-02 03:22:47 +03:00
def set_defaults ( self , guest ) :
2024-08-02 17:50:15 +03:00
self . _set_default_type ( )
2018-09-02 03:22:47 +03:00
if not self . macaddr :
self . macaddr = self . generate_mac ( self . conn )
2019-02-05 01:35:47 +03:00
if self . type == self . TYPE_BRIDGE and not self . bridge :
2020-01-26 03:03:28 +03:00
self . bridge = _default_bridge ( self . conn )
2018-09-02 03:22:47 +03:00
if not self . model :
2018-09-02 03:36:53 +03:00
self . model = self . default_model ( guest )