mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
A bare 'except:' catches all exceptions [1], including SystemExit, KeyboardInterrupt, and GeneratorExit (which is not an error and should not normally be caught by user code). In situations where you need to catch all “normal” errors, you can catch the base class for all normal exceptions, Exception [2]. [1] https://docs.python.org/2/howto/doanddont.html#except [2] https://docs.python.org/2/library/exceptions.html#Exception
335 lines
10 KiB
Python
335 lines
10 KiB
Python
#
|
|
# Copyright 2006-2009, 2013 Red Hat, Inc.
|
|
# Jeremy Katz <katzj@redhat.com>
|
|
#
|
|
# 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
|
|
import os
|
|
import random
|
|
|
|
from . import util
|
|
from .device import VirtualDevice
|
|
from .xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
|
|
|
|
|
|
def _random_mac(conn):
|
|
"""Generate a random MAC address.
|
|
|
|
00-16-3E allocated to xensource
|
|
52-54-00 used by qemu/kvm
|
|
|
|
The OUI list is available at http://standards.ieee.org/regauth/oui/oui.txt.
|
|
|
|
The remaining 3 fields are random, with the first bit of the first
|
|
random field set 0.
|
|
|
|
@return: MAC address string
|
|
"""
|
|
|
|
if conn.is_qemu():
|
|
oui = [0x52, 0x54, 0x00]
|
|
else:
|
|
# Xen
|
|
oui = [0x00, 0x16, 0x3E]
|
|
|
|
mac = oui + [
|
|
random.randint(0x00, 0xff),
|
|
random.randint(0x00, 0xff),
|
|
random.randint(0x00, 0xff)]
|
|
return ':'.join(["%02x" % x for x in mac])
|
|
|
|
|
|
def _default_route():
|
|
route_file = "/proc/net/route"
|
|
if not os.path.exists(route_file):
|
|
logging.debug("route_file=%s does not exist", route_file)
|
|
return None
|
|
|
|
for line in open(route_file):
|
|
info = line.split()
|
|
if len(info) != 11:
|
|
logging.debug("Unexpected field count=%s when parsing %s",
|
|
len(info), route_file)
|
|
break
|
|
|
|
try:
|
|
route = int(info[1], 16)
|
|
if route == 0:
|
|
return info[0]
|
|
except ValueError:
|
|
continue
|
|
|
|
return None
|
|
|
|
|
|
def _default_bridge(conn):
|
|
if "VIRTINST_TEST_SUITE" in os.environ:
|
|
return "eth0"
|
|
|
|
if conn.is_remote():
|
|
return None
|
|
|
|
dev = _default_route()
|
|
if not dev:
|
|
return None
|
|
|
|
# New style peth0 == phys dev, eth0 == bridge, eth0 == default route
|
|
if os.path.exists("/sys/class/net/%s/bridge" % dev):
|
|
return dev
|
|
|
|
# Old style, peth0 == phys dev, eth0 == netloop, xenbr0 == bridge,
|
|
# vif0.0 == netloop enslaved, eth0 == default route
|
|
try:
|
|
defn = int(dev[-1])
|
|
except Exception:
|
|
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)):
|
|
return "xenbr%d"
|
|
return None
|
|
|
|
|
|
def _default_network(conn):
|
|
ret = _default_bridge(conn)
|
|
if ret:
|
|
return ["bridge", ret]
|
|
|
|
# FIXME: Check that this exists
|
|
return ["network", "default"]
|
|
|
|
|
|
class VirtualPort(XMLBuilder):
|
|
_XML_ROOT_NAME = "virtualport"
|
|
|
|
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")
|
|
profileid = XMLProperty("./parameters/@profileid")
|
|
interfaceid = XMLProperty("./parameters/@interfaceid")
|
|
|
|
|
|
class VirtualNetworkInterface(VirtualDevice):
|
|
virtual_device_type = VirtualDevice.VIRTUAL_DEV_NET
|
|
|
|
TYPE_BRIDGE = "bridge"
|
|
TYPE_VIRTUAL = "network"
|
|
TYPE_USER = "user"
|
|
TYPE_VHOSTUSER = "vhostuser"
|
|
TYPE_ETHERNET = "ethernet"
|
|
TYPE_DIRECT = "direct"
|
|
network_types = [TYPE_BRIDGE, TYPE_VIRTUAL, TYPE_USER, TYPE_ETHERNET,
|
|
TYPE_DIRECT]
|
|
|
|
@staticmethod
|
|
def get_network_type_desc(net_type):
|
|
"""
|
|
Return human readable description for passed network type
|
|
"""
|
|
desc = net_type.capitalize()
|
|
|
|
if net_type == VirtualNetworkInterface.TYPE_BRIDGE:
|
|
desc = _("Shared physical device")
|
|
elif net_type == VirtualNetworkInterface.TYPE_VIRTUAL:
|
|
desc = _("Virtual networking")
|
|
elif net_type == VirtualNetworkInterface.TYPE_USER:
|
|
desc = _("Usermode networking")
|
|
|
|
return desc
|
|
|
|
@staticmethod
|
|
def generate_mac(conn):
|
|
"""
|
|
Generate a random MAC that doesn't conflict with any VMs on
|
|
the connection.
|
|
"""
|
|
if conn.fake_conn_predictable():
|
|
# Testing hack
|
|
return "00:11:22:33:44:55"
|
|
|
|
for ignore in range(256):
|
|
mac = _random_mac(conn)
|
|
ret = VirtualNetworkInterface.is_conflict_net(conn, mac)
|
|
if ret[1] is None:
|
|
return mac
|
|
|
|
logging.debug("Failed to generate non-conflicting MAC")
|
|
return None
|
|
|
|
@staticmethod
|
|
def is_conflict_net(conn, searchmac):
|
|
"""
|
|
@returns: a two element tuple:
|
|
first element is True if fatal collision occured
|
|
second element is a string description of the collision.
|
|
|
|
Non fatal collisions (mac addr collides with inactive guest) will
|
|
return (False, "description of collision")
|
|
"""
|
|
if searchmac is None:
|
|
return (False, None)
|
|
|
|
vms = conn.fetch_all_guests()
|
|
for vm in vms:
|
|
for nic in vm.get_devices("interface"):
|
|
nicmac = nic.macaddr or ""
|
|
if nicmac.lower() == searchmac.lower():
|
|
return (True, _("The MAC address '%s' is in use "
|
|
"by another virtual machine.") % searchmac)
|
|
return (False, None)
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
VirtualDevice.__init__(self, *args, **kwargs)
|
|
|
|
self._random_mac = None
|
|
self._default_bridge = None
|
|
|
|
|
|
###############
|
|
# XML helpers #
|
|
###############
|
|
|
|
def _generate_default_bridge(self):
|
|
ret = self._default_bridge
|
|
if ret is None:
|
|
ret = False
|
|
default = _default_bridge(self.conn)
|
|
if default:
|
|
ret = default
|
|
|
|
self._default_bridge = ret
|
|
return ret or None
|
|
|
|
def _get_default_bridge(self):
|
|
if self.type == self.TYPE_BRIDGE:
|
|
return self._generate_default_bridge()
|
|
return None
|
|
|
|
def _default_source_mode(self):
|
|
if self.type == self.TYPE_DIRECT:
|
|
return "vepa"
|
|
return None
|
|
|
|
def _get_default_mac(self):
|
|
if not self._random_mac:
|
|
self._random_mac = self.generate_mac(self.conn)
|
|
return self._random_mac
|
|
def _validate_mac(self, val):
|
|
util.validate_macaddr(val)
|
|
return val
|
|
|
|
def _get_source(self):
|
|
"""
|
|
Convenience function, try to return the relevant <source> value
|
|
per the network type.
|
|
"""
|
|
if self.type == self.TYPE_VIRTUAL:
|
|
return self._network
|
|
if self.type == self.TYPE_BRIDGE:
|
|
return self._bridge
|
|
if self.type == self.TYPE_DIRECT:
|
|
return self._source_dev
|
|
if self.type == self.TYPE_USER or self.type == self.TYPE_ETHERNET:
|
|
return None
|
|
return self._network or self._bridge or self._source_dev
|
|
def _set_source(self, newsource):
|
|
"""
|
|
Convenience function, try to set the relevant <source> value
|
|
per the network type
|
|
"""
|
|
self._bridge = None
|
|
self._network = None
|
|
self._source_dev = None
|
|
|
|
if self.type == self.TYPE_VIRTUAL:
|
|
self._network = newsource
|
|
elif self.type == self.TYPE_BRIDGE:
|
|
self._bridge = newsource
|
|
elif self.type == self.TYPE_DIRECT:
|
|
self._source_dev = newsource
|
|
source = property(_get_source, _set_source)
|
|
|
|
|
|
##################
|
|
# XML properties #
|
|
##################
|
|
|
|
_XML_PROP_ORDER = [
|
|
"_bridge", "_network", "_source_dev", "source_type", "source_path",
|
|
"source_mode", "portgroup", "macaddr", "target_dev", "model",
|
|
"virtualport", "filterref", "rom_bar", "rom_file"]
|
|
|
|
_bridge = XMLProperty("./source/@bridge", default_cb=_get_default_bridge)
|
|
_network = XMLProperty("./source/@network")
|
|
_source_dev = XMLProperty("./source/@dev")
|
|
|
|
virtualport = XMLChildProperty(VirtualPort, is_single=True)
|
|
type = XMLProperty("./@type",
|
|
default_cb=lambda s: s.TYPE_BRIDGE)
|
|
trustGuestRxFilters = XMLProperty("./@trustGuestRxFilters", is_yesno=True)
|
|
|
|
macaddr = XMLProperty("./mac/@address",
|
|
set_converter=_validate_mac,
|
|
default_cb=_get_default_mac)
|
|
|
|
source_type = XMLProperty("./source/@type")
|
|
source_path = XMLProperty("./source/@path")
|
|
source_mode = XMLProperty("./source/@mode",
|
|
default_cb=_default_source_mode)
|
|
portgroup = XMLProperty("./source/@portgroup")
|
|
model = XMLProperty("./model/@type")
|
|
target_dev = XMLProperty("./target/@dev")
|
|
filterref = XMLProperty("./filterref/@filter")
|
|
link_state = XMLProperty("./link/@state")
|
|
|
|
driver_name = XMLProperty("./driver/@name")
|
|
driver_queues = XMLProperty("./driver/@queues", is_int=True)
|
|
|
|
rom_bar = XMLProperty("./rom/@bar", is_onoff=True)
|
|
rom_file = XMLProperty("./rom/@file")
|
|
|
|
|
|
#############
|
|
# Build API #
|
|
#############
|
|
|
|
def setup(self, meter=None):
|
|
ignore = meter
|
|
if not self.macaddr:
|
|
return
|
|
|
|
ret, msg = self.is_conflict_net(self.conn, self.macaddr)
|
|
if msg is None:
|
|
return
|
|
if ret is False:
|
|
logging.warning(msg)
|
|
else:
|
|
raise RuntimeError(msg)
|
|
|
|
def set_default_source(self):
|
|
if (self.conn.is_qemu_session() or self.conn.is_test()):
|
|
self.type = self.TYPE_USER
|
|
else:
|
|
self.type, self.source = _default_network(self.conn)
|
|
|
|
|
|
VirtualNetworkInterface.register_type()
|